diff --git a/src/bin/Makefile.am b/src/bin/Makefile.am index 4d1493c..60169a6 100644 --- a/src/bin/Makefile.am +++ b/src/bin/Makefile.am @@ -31,6 +31,7 @@ edi_file.h \ edi_logpanel.h \ edi_searchpanel.h \ edi_consolepanel.h \ +edi_debugpanel.h \ mainview/edi_mainview_item.h \ mainview/edi_mainview.h @@ -50,6 +51,7 @@ edi_file.c \ edi_logpanel.c \ edi_searchpanel.c \ edi_consolepanel.c \ +edi_debugpanel.c \ mainview/edi_mainview_item.c \ mainview/edi_mainview.c \ edi_main.c \ diff --git a/src/bin/edi_debugpanel.c b/src/bin/edi_debugpanel.c new file mode 100644 index 0000000..9291ba0 --- /dev/null +++ b/src/bin/edi_debugpanel.c @@ -0,0 +1,376 @@ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include + +#include "edi_debugpanel.h" +#include "edi_config.h" + +#include "edi_private.h" + +static Ecore_Exe *_debug_exe = NULL; +static Evas_Object *_info_widget, *_entry_widget, *_button_start, *_button_quit; +static Evas_Object *_button_int, *_button_term; +static Elm_Code *_debug_output; + +static void +_edi_debugpanel_line_cb(void *data EINA_UNUSED, const Efl_Event *event) +{ + Elm_Code_Line *line; + + line = (Elm_Code_Line *)event->info; + + if (line->data) + line->status = ELM_CODE_STATUS_TYPE_ERROR; +} + +static Eina_Bool +_edi_debugpanel_config_changed(void *data EINA_UNUSED, int type EINA_UNUSED, void *event EINA_UNUSED) +{ + elm_code_widget_font_set(_info_widget, _edi_project_config->font.name, _edi_project_config->font.size); + + return ECORE_CALLBACK_RENEW; +} + +static Eina_Bool +_debugpanel_stdout_handler(void *data EINA_UNUSED, int type EINA_UNUSED, void *event) +{ + Ecore_Exe_Event_Data *ev; + char buf[65535]; + char *pos, *start, *end = NULL; + + ev = event; + if (ev) + { + if (ev->size >= (int)(sizeof(buf) -2)) return ECORE_CALLBACK_DONE; + + snprintf(buf, ev->size + 1, "%s", (char *)ev->data); + pos = buf; + start = pos; + if (*start == '\n') start++; + while (*pos++ != '\0') + { + if (*pos == '\n') + end = pos; + + if (start && end) + { + elm_code_file_line_append(_debug_output->file, start, end - start, NULL); + start = end + 1; + end = NULL; + } + } + } + + // FIXME: elm_code reneder hack + elm_code_callback_fire(_debug_output->file->parent, &ELM_CODE_EVENT_FILE_LOAD_DONE, _debug_output->file); + + return ECORE_CALLBACK_DONE; +} + +static void +_edi_debugpanel_keypress_cb(void *data EINA_UNUSED, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info) +{ + Evas_Event_Key_Down *event; + const char *text; + char *command; + Eina_Bool res; + + event = event_info; + + if (!event) return; + + if (!event->key) return; + + if (!strcmp(event->key, "Return")) + { + if (!_debug_exe) return; + + text = elm_object_part_text_get(_entry_widget, NULL); + if (strlen(text)) + { + command = malloc(strlen(text) + 2); + snprintf(command, strlen(text) + 2, "%s\n", text); + res = ecore_exe_send(_debug_exe, command, strlen(command)); + if (res) + elm_code_file_line_append(_debug_output->file, command, strlen(command) - 1, NULL); + + free(command); + } + elm_object_part_text_set(_entry_widget, NULL, ""); + } +} + +/* Get the process ID of the child process being debugged in *our* session */ +static int +_edi_debug_process_id(void) +{ + Eina_List *files, *l; + const char *program_name, *temp_name; + char path[PATH_MAX]; + char buf[4096]; + char *p, *name, *end; + FILE *f; + int count, my_pid, parent_pid, pid, child_pid = -1; + + if (!_edi_project_config->launch.path) + return -1; + + if (!_debug_exe) return -1; + + my_pid = ecore_exe_pid_get(_debug_exe); + + program_name = ecore_file_file_get(_edi_project_config->launch.path); + + files = ecore_file_ls("/proc"); + + EINA_LIST_FOREACH(files, l, name) + { + pid = atoi(name); + if (!pid) continue; + snprintf(path, sizeof(path), "/proc/%d/cmdline", pid); + f = fopen(path, "r"); + if (!f) continue; + fgets(buf, sizeof(buf), f); + fclose(f); + temp_name = ecore_file_file_get(buf); + if (!strcmp(temp_name, program_name)) + { + parent_pid = 0; + // Match success - program name with pid. + child_pid = pid; + snprintf(path, sizeof(path), "/proc/%d/stat", pid); + f = fopen(path, "r"); + if (f) + { + count = 0; + fgets(buf, sizeof(buf), f); + p = buf; + while (*p++ != '\0') + { + if (*p++ == ' ') count++; + if (count == 3) break; + } + end = strchr(p, ' '); + if (end) + { + *end = '\0'; + // parent pid matches - right process. + parent_pid = atoi(p); + } + fclose(f); + } + if (parent_pid == my_pid) + break; + } + } + + if (files) + eina_list_free(files); + + return child_pid; +} + +static void +_edi_debugpanel_bt_sigterm_cb(void *data EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event EINA_UNUSED) +{ + pid_t pid; + + pid = _edi_debug_process_id(); + if (pid <= 0) return; + + kill(pid, SIGTERM); +} + +static void +_edi_debugpanel_bt_sigint_cb(void *data EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event EINA_UNUSED) +{ + pid_t pid; + + pid = _edi_debug_process_id(); + if (pid <= 0) return; + + kill(pid, SIGINT); +} + +static void +_edi_debugpanel_button_quit_cb(void *data EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event EINA_UNUSED) +{ + edi_debugpanel_stop(); +} + +static void +_edi_debugpanel_button_start_cb(void *data EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event EINA_UNUSED) +{ + edi_debugpanel_start(); +} + +static Eina_Bool +_edi_debug_active_check_cb(void *data EINA_UNUSED) +{ + int pid = ecore_exe_pid_get(_debug_exe); + if (pid == -1) + { + if (_debug_exe) ecore_exe_free(_debug_exe); + _debug_exe = NULL; + elm_object_disabled_set(_button_quit, EINA_TRUE); + elm_object_disabled_set(_button_start, EINA_FALSE); + elm_object_disabled_set(_button_int, EINA_TRUE); + elm_object_disabled_set(_button_term, EINA_TRUE); + } + return ECORE_CALLBACK_RENEW; +} + +void edi_debugpanel_stop(void) +{ + int pid; + + if (_debug_exe) + ecore_exe_terminate(_debug_exe); + + pid = ecore_exe_pid_get(_debug_exe); + if (pid != -1) + ecore_exe_quit(_debug_exe); + + if (_debug_exe) ecore_exe_free(_debug_exe); + + _debug_exe = NULL; + + elm_object_disabled_set(_button_quit, EINA_TRUE); + elm_object_disabled_set(_button_int, EINA_TRUE); + elm_object_disabled_set(_button_term, EINA_TRUE); +} + +void edi_debugpanel_start(void) +{ + char cmd[1024]; + char *args; + int len; + const char *fmt = "set args %s\n"; + + if (!_edi_project_config->launch.path) + { + edi_launcher_config_missing(); + return; + } + + if (_debug_exe) return; + + snprintf(cmd, sizeof(cmd), "gdb %s", _edi_project_config->launch.path); + + _debug_exe = ecore_exe_pipe_run(cmd, ECORE_EXE_PIPE_WRITE | + ECORE_EXE_PIPE_ERROR | + ECORE_EXE_PIPE_READ, NULL); + + ecore_event_handler_add(ECORE_EXE_EVENT_DATA, _debugpanel_stdout_handler, NULL); + + elm_object_disabled_set(_button_int, EINA_FALSE); + elm_object_disabled_set(_button_term, EINA_FALSE); + elm_object_disabled_set(_button_quit, EINA_FALSE); + + if (_edi_project_config->launch.args) + { + len = strlen(fmt) + strlen(_edi_project_config->launch.args) + 1; + args = malloc(len); + snprintf(args, len, fmt, _edi_project_config->launch.args); + ecore_exe_send(_debug_exe, args, strlen(args)); + free(args); + } + + ecore_exe_send(_debug_exe, "run\n", 4); + elm_object_disabled_set(_button_start, EINA_TRUE); +} + +void edi_debugpanel_add(Evas_Object *parent) +{ + Evas_Object *table, *entry, *bt_term, *bt_int, *bt_start, *bt_quit; + Evas_Object *separator; + Evas_Object *ico_start, *ico_quit, *ico_int, *ico_term; + Elm_Code_Widget *widget; + Elm_Code *code; + Ecore_Timer *timer; + + code = elm_code_create(); + widget = elm_code_widget_add(parent, code); + elm_obj_code_widget_font_set(widget, _edi_project_config->font.name, _edi_project_config->font.size); + elm_obj_code_widget_gravity_set(widget, 0.0, 1.0); + efl_event_callback_add(widget, &ELM_CODE_EVENT_LINE_LOAD_DONE, _edi_debugpanel_line_cb, NULL); + evas_object_size_hint_weight_set(widget, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); + evas_object_size_hint_align_set(widget, EVAS_HINT_FILL, EVAS_HINT_FILL); + evas_object_show(widget); + + table = elm_table_add(parent); + evas_object_size_hint_weight_set(table, EVAS_HINT_EXPAND, 0); + evas_object_size_hint_align_set(table, EVAS_HINT_FILL, 0); + + separator = elm_separator_add(parent); + elm_separator_horizontal_set(separator, EINA_FALSE); + evas_object_show(separator); + + _button_term = bt_term = elm_button_add(parent); + ico_term = elm_icon_add(parent); + elm_icon_standard_set(ico_term, "media-playback-stop"); + elm_object_part_content_set(bt_term, "icon", ico_term); + elm_object_tooltip_text_set(bt_term, "Send SIGTERM"); + elm_object_disabled_set(bt_term, EINA_TRUE); + evas_object_smart_callback_add(bt_term, "clicked", _edi_debugpanel_bt_sigterm_cb, NULL); + evas_object_show(bt_term); + + _button_int = bt_int = elm_button_add(parent); + ico_int = elm_icon_add(parent); + elm_icon_standard_set(ico_int, "media-playback-pause"); + elm_object_part_content_set(bt_int, "icon", ico_int); + elm_object_tooltip_text_set(bt_int, "Send SIGINT"); + elm_object_disabled_set(bt_int, EINA_TRUE); + evas_object_smart_callback_add(bt_int, "clicked", _edi_debugpanel_bt_sigint_cb, NULL); + evas_object_show(bt_int); + + _button_start = bt_start = elm_button_add(parent); + ico_start = elm_icon_add(parent); + elm_icon_standard_set(ico_start, "media-playback-start"); + elm_object_tooltip_text_set(bt_start, "Start Debugging"); + elm_object_part_content_set(bt_start, "icon", ico_start); + evas_object_smart_callback_add(bt_start, "clicked", _edi_debugpanel_button_start_cb, NULL); + evas_object_show(bt_start); + + _button_quit = bt_quit = elm_button_add(parent); + ico_quit = elm_icon_add(parent); + elm_icon_standard_set(ico_quit, "application-exit"); + elm_object_part_content_set(bt_quit, "icon", ico_quit); + elm_object_tooltip_text_set(bt_quit, "Stop Debugging"); + elm_object_disabled_set(bt_quit, EINA_TRUE); + evas_object_smart_callback_add(bt_quit, "clicked", _edi_debugpanel_button_quit_cb, NULL); + evas_object_show(bt_quit); + + entry = elm_entry_add(parent); + elm_entry_single_line_set(entry, EINA_TRUE); + elm_entry_scrollable_set(entry, EINA_TRUE); + elm_entry_editable_set(entry, EINA_TRUE); + evas_object_size_hint_weight_set(entry, EVAS_HINT_EXPAND, 0); + evas_object_size_hint_align_set(entry, EVAS_HINT_FILL, EVAS_HINT_FILL); + evas_object_event_callback_add(entry, EVAS_CALLBACK_KEY_DOWN, _edi_debugpanel_keypress_cb, NULL); + evas_object_show(entry); + + elm_table_pack(table, entry, 0, 0, 1, 1); + elm_table_pack(table, bt_term, 1, 0, 1, 1); + elm_table_pack(table, bt_int, 2, 0, 1, 1); + elm_table_pack(table, separator, 3, 0, 1, 1); + elm_table_pack(table, bt_start, 4, 0, 1, 1); + elm_table_pack(table, bt_quit, 5, 0, 1, 1); + evas_object_show(table); + + _debug_output = code; + _info_widget = widget; + _entry_widget = entry; + + timer = ecore_timer_add(1.0, _edi_debug_active_check_cb, NULL); + (void) timer; + + elm_box_pack_end(parent, widget); + elm_box_pack_end(parent, table); + + ecore_event_handler_add(EDI_EVENT_CONFIG_CHANGED, _edi_debugpanel_config_changed, NULL); +} diff --git a/src/bin/edi_debugpanel.h b/src/bin/edi_debugpanel.h new file mode 100644 index 0000000..bbdbeb9 --- /dev/null +++ b/src/bin/edi_debugpanel.h @@ -0,0 +1,58 @@ +#ifndef EDI_DEBUGPANEL_H_ +# define EDI_DEBUGPANEL_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @file + * @brief These routines are used for managing the Edi debug panel. + */ + +/** + * @brief UI management functions. + * @defgroup UI + * + * @{ + * + * Initialisation and management of the debugpanel UI + * + */ + +/** + * Initialize a new Edi debugpanel and add it to the parent panel. + * + * @param parent The panel into which the panel will be loaded. + * + * @ingroup UI + */ +void edi_debugpanel_add(Evas_Object *parent); + +/** + * Start a new debugging session + * + * @ingroup UI + */ +void edi_debugpanel_start(); + +/** + * Stop existing debugging session + * + * @ingroup UI + */ +void edi_debugpanel_stop(); + +/** + * @} + */ + + + +#ifdef __cplusplus +} +#endif + +#endif /* EDI_DEBUGPANEL_H_ */ diff --git a/src/bin/edi_main.c b/src/bin/edi_main.c index 120ade9..3449b44 100644 --- a/src/bin/edi_main.c +++ b/src/bin/edi_main.c @@ -21,6 +21,7 @@ #include "edi_logpanel.h" #include "edi_consolepanel.h" #include "edi_searchpanel.h" +#include "edi_debugpanel.h" #include "mainview/edi_mainview.h" #include "screens/edi_screens.h" @@ -42,8 +43,8 @@ typedef struct _Edi_Panel_Slide_Effect #define COPYRIGHT "Copyright © 2014-2015 Andy Williams and various contributors (see AUTHORS)." static Evas_Object *_edi_toolbar, *_edi_leftpanes, *_edi_bottompanes; -static Evas_Object *_edi_logpanel, *_edi_consolepanel, *_edi_testpanel, *_edi_searchpanel, *_edi_taskspanel; -static Elm_Object_Item *_edi_logpanel_item, *_edi_consolepanel_item, *_edi_testpanel_item, *_edi_searchpanel_item, *_edi_taskspanel_item; +static Evas_Object *_edi_logpanel, *_edi_consolepanel, *_edi_testpanel, *_edi_searchpanel, *_edi_taskspanel, *_edi_debugpanel; +static Elm_Object_Item *_edi_logpanel_item, *_edi_consolepanel_item, *_edi_testpanel_item, *_edi_searchpanel_item, *_edi_taskspanel_item, *_edi_debugpanel_item; static Elm_Object_Item *_edi_selected_bottompanel; static Evas_Object *_edi_filepanel, *_edi_filepanel_icon; @@ -116,6 +117,8 @@ _edi_panel_tab_for_index(int index) return _edi_searchpanel; if (index == 4) return _edi_taskspanel; + if (index == 5) + return _edi_debugpanel; return _edi_logpanel; } @@ -260,7 +263,7 @@ _edi_toggle_panel(void *data, Evas_Object *obj, void *event_info) if (obj) elm_object_focus_set(obj, EINA_FALSE); - for (c = 0; c <= 4; c++) + for (c = 0; c <= 5; c++) if (c != index) evas_object_hide(_edi_panel_tab_for_index(c)); @@ -324,6 +327,13 @@ edi_taskspanel_show() elm_toolbar_item_selected_set(_edi_taskspanel_item, EINA_TRUE); } +void +edi_debugpanel_show() +{ + if (_edi_selected_bottompanel != _edi_debugpanel_item) + elm_toolbar_item_selected_set(_edi_debugpanel_item, EINA_TRUE); +} + static Evas_Object * edi_content_setup(Evas_Object *win, const char *path) { @@ -344,6 +354,7 @@ edi_content_setup(Evas_Object *win, const char *path) _edi_testpanel = elm_box_add(win); _edi_searchpanel = elm_box_add(win); _edi_taskspanel = elm_box_add(win); + _edi_debugpanel = elm_box_add(win); // add main content content_out = elm_box_add(win); @@ -418,6 +429,8 @@ edi_content_setup(Evas_Object *win, const char *path) _edi_toggle_panel, "3"); _edi_taskspanel_item = elm_toolbar_item_append(tb, "stock_up", "Tasks", _edi_toggle_panel, "4"); + _edi_debugpanel_item = elm_toolbar_item_append(tb, "stock_up", "Debug", + _edi_toggle_panel, "5"); // add lower panel panes logpanels = elm_table_add(logpane); @@ -451,6 +464,12 @@ edi_content_setup(Evas_Object *win, const char *path) edi_taskspanel_add(_edi_taskspanel); elm_table_pack(logpanels, _edi_taskspanel, 0, 0, 1, 1); + edi_debugpanel_add(_edi_debugpanel); + elm_table_pack(logpanels, _edi_debugpanel, 0, 0, 1, 1); + + evas_object_size_hint_weight_set(_edi_debugpanel, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); + evas_object_size_hint_align_set(_edi_debugpanel, EVAS_HINT_FILL, EVAS_HINT_FILL); + elm_object_part_content_set(logpane, "bottom", logpanels); if (_edi_project_config->gui.bottomopen) @@ -476,6 +495,11 @@ edi_content_setup(Evas_Object *win, const char *path) elm_toolbar_item_icon_set(_edi_taskspanel_item, "stock_down"); _edi_selected_bottompanel = _edi_taskspanel_item; } + else if (_edi_project_config->gui.bottomtab == 5) + { + elm_toolbar_item_icon_set(_edi_debugpanel_item, "stock_down"); + _edi_selected_bottompanel = _edi_debugpanel_item; + } else { elm_toolbar_item_icon_set(_edi_logpanel_item, "stock_down"); @@ -663,10 +687,10 @@ _tb_run_cb(void *data EINA_UNUSED, Evas_Object *obj, void *event_info EINA_UNUSE } static void -_tb_clean_cb(void *data EINA_UNUSED, Evas_Object *obj, void *event_info EINA_UNUSED) +_tb_debug_cb(void *data EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED) { - if (_edi_build_prep(obj)) - edi_builder_clean(); + edi_debugpanel_show(); + edi_debugpanel_start(); } static void @@ -759,7 +783,7 @@ static void _edi_menu_quit_cb(void *data EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED) { - elm_exit(); + edi_close(); } static void @@ -861,6 +885,14 @@ _edi_menu_clean_cb(void *data EINA_UNUSED, Evas_Object *obj EINA_UNUSED, edi_builder_clean(); } +static void +_edi_menu_debug_cb(void *data EINA_UNUSED, Evas_Object *obj EINA_UNUSED, + void *event_info EINA_UNUSED) +{ + edi_debugpanel_show(); + edi_debugpanel_start(); +} + static void _edi_menu_website_cb(void *data EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED) @@ -914,6 +946,7 @@ _edi_menu_setup(Evas_Object *obj) elm_menu_item_add(menu, menu_it, "system-run", "Build", _edi_menu_build_cb, NULL); elm_menu_item_add(menu, menu_it, "media-record", "Test", _edi_menu_test_cb, NULL); elm_menu_item_add(menu, menu_it, "media-playback-start", "Run", _edi_menu_run_cb, NULL); + elm_menu_item_add(menu, menu_it, "utilities-terminal", "Debug", _edi_menu_debug_cb, NULL); elm_menu_item_add(menu, menu_it, "edit-clear", "Clean", _edi_menu_clean_cb, NULL); menu_it = elm_menu_item_add(menu, NULL, NULL, "Help", NULL, NULL); @@ -979,7 +1012,7 @@ edi_toolbar_setup(Evas_Object *win) _edi_toolbar_item_add(tb, "system-run", "Build", _tb_build_cb); _edi_toolbar_item_add(tb, "media-record", "Test", _tb_test_cb); _edi_toolbar_item_add(tb, "media-playback-start", "Run", _tb_run_cb); - _edi_toolbar_item_add(tb, "edit-clear", "Clean", _tb_clean_cb); + _edi_toolbar_item_add(tb, "utilities-terminal", "Debug", _tb_debug_cb); tb_it = elm_toolbar_item_append(tb, "separator", "", NULL, NULL); elm_toolbar_item_separator_set(tb_it, EINA_TRUE); @@ -1125,6 +1158,12 @@ _edi_open_tabs() } } +static void +_win_delete_cb(void *data EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event EINA_UNUSED) +{ + edi_close(); +} + Eina_Bool edi_open(const char *inputpath) { @@ -1199,6 +1238,8 @@ edi_open(const char *inputpath) _edi_open_tabs(); _edi_icon_update(); + evas_object_smart_callback_add(win, "delete,request", _win_delete_cb, NULL); + ecore_event_handler_add(EDI_EVENT_CONFIG_CHANGED, _edi_config_changed, NULL); ecore_event_handler_add(EDI_EVENT_TAB_CHANGED, _edi_tab_changed, NULL); ecore_event_handler_add(EDI_EVENT_FILE_CHANGED, _edi_file_changed, NULL); @@ -1222,6 +1263,7 @@ edi_open_file(const char *filepath) void edi_close() { + edi_debugpanel_stop(); elm_exit(); }