[GTK3] Implement basic debug console (no sidebar)

This commit is contained in:
Maximilian Mader 2019-10-08 00:36:16 +02:00
parent e1a1c3efbd
commit 10ac1bd0a5
Signed by: Max
GPG Key ID: F71D56A3151C4FB3
4 changed files with 259 additions and 34 deletions

View File

@ -66,15 +66,21 @@ static uint8_t oamHeight;
static uint8_t pressed_buttons; static uint8_t pressed_buttons;
static GMutex debugger_input_mutex;
static GCond debugger_input_cond;
static GMutex console_output_lock;
static GPtrArray *debugger_input_queue;
// List of GActions for the `app` prefix // List of GActions for the `app` prefix
static const GActionEntry app_entries[] = { static const GActionEntry app_entries[] = {
{ "quit", activate_quit, NULL, NULL, NULL }, { "quit", activate_quit, NULL, NULL, NULL },
{ "about", activate_about, NULL, NULL, NULL }, { "about", activate_about, NULL, NULL, NULL },
{ "open", activate_open, NULL, NULL, NULL }, { "open", activate_open, NULL, NULL, NULL },
{ "show_console", activate_show_console, NULL, NULL, NULL },
{ "open_gtk_debugger", activate_open_gtk_debugger, NULL, NULL, NULL }, { "open_gtk_debugger", activate_open_gtk_debugger, NULL, NULL, NULL },
{ "preferences", activate_preferences, NULL, NULL, NULL },
{ "open_vram_viewer", activate_open_vram_viewer, NULL, NULL, NULL },
{ "open_memory_viewer", activate_open_memory_viewer, NULL, NULL, NULL }, { "open_memory_viewer", activate_open_memory_viewer, NULL, NULL, NULL },
{ "open_vram_viewer", activate_open_vram_viewer, NULL, NULL, NULL },
{ "preferences", activate_preferences, NULL, NULL, NULL },
}; };
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {
@ -320,6 +326,91 @@ static void gb_audio_callback(GB_gameboy_t *gb, GB_sample_t *sample) {
SDL_QueueAudio(device_id, sample, sizeof(*sample)); SDL_QueueAudio(device_id, sample, sizeof(*sample));
} }
static char *sync_console_input(GB_gameboy_t *gb) {
g_mutex_lock(&debugger_input_mutex);
g_cond_wait(&debugger_input_cond, &debugger_input_mutex);
gchar *input = NULL;
const gchar *_input = g_ptr_array_index(debugger_input_queue, 0);
input = g_strdup(_input);
gpointer ptr = g_ptr_array_remove_index(debugger_input_queue, 0);
if (ptr) g_free(ptr);
g_mutex_unlock(&debugger_input_mutex);
return input;
}
static char *async_console_input(GB_gameboy_t *gb) {
if (debugger_input_queue->len == 0) return NULL;
g_mutex_lock(&debugger_input_mutex);
gchar *input = NULL;
const gchar *_input = g_ptr_array_index(debugger_input_queue, 0);
if (_input) {
input = g_strdup(_input);
gpointer ptr = g_ptr_array_remove_index(debugger_input_queue, 0);
if (ptr) g_free(ptr);
}
g_mutex_unlock(&debugger_input_mutex);
return input;
}
typedef struct LogData {
GB_gameboy_t *gb;
const char *string;
GB_log_attributes attributes;
} LogData;
static void on_console_log(gpointer user_data_gptr) {
LogData *log_data = (LogData *)user_data_gptr;
GB_gameboy_t *gb = log_data->gb;
GB_log_attributes attributes = log_data->attributes;
g_mutex_lock(&console_output_lock);
GtkTextView *text_view = builder_get(GTK_TEXT_VIEW, "console_screen");
GtkTextBuffer *text_buf = gtk_text_view_get_buffer(text_view);
GtkTextIter iter;
GtkTextIter start;
gtk_text_buffer_get_end_iter(text_buf, &iter);
GtkTextMark *start_mark = gtk_text_buffer_create_mark(text_buf, NULL, &iter, TRUE);
gtk_text_buffer_insert(text_buf, &iter, g_strdup(log_data->string), -1);
gtk_text_buffer_get_iter_at_mark(text_buf, &start, start_mark);
if (attributes & GB_LOG_BOLD) {
gtk_text_buffer_apply_tag_by_name(text_buf, "bold", &start, &iter);
}
if (attributes & GB_LOG_DASHED_UNDERLINE) {
gtk_text_buffer_apply_tag_by_name(text_buf, "dashed_underline", &start, &iter);
}
if (attributes & GB_LOG_UNDERLINE) {
gtk_text_buffer_apply_tag_by_name(text_buf, "underline", &start, &iter);
}
g_free((gpointer)log_data->string);
g_free(log_data);
gtk_text_buffer_delete_mark(text_buf, start_mark);
g_mutex_unlock(&console_output_lock);
}
static void console_log(GB_gameboy_t *gb, const char *string, GB_log_attributes attributes) {
if (string != NULL && !g_str_equal("", string)) {
LogData *log_data = g_malloc(sizeof(LogData));
log_data->gb = gb;
log_data->string = g_strdup(string);
log_data->attributes = attributes;
g_idle_add((GSourceFunc) on_console_log, log_data);
}
}
// Returns a `GApplication`s `GMenuModel` by ID // Returns a `GApplication`s `GMenuModel` by ID
// GApplication menus are loaded from `gtk/menus.ui`, `gtk/menus-traditional.ui` and `gtk/menus-common.ui`. // GApplication menus are loaded from `gtk/menus.ui`, `gtk/menus-traditional.ui` and `gtk/menus-common.ui`.
static GMenuModel *get_menu_model(GApplication *app, const char *id) { static GMenuModel *get_menu_model(GApplication *app, const char *id) {
@ -623,6 +714,12 @@ static void activate(GApplication *app, gpointer user_data_gptr) {
gtk_css_provider_load_from_resource(provider, RESOURCE_PREFIX "css/main.css"); gtk_css_provider_load_from_resource(provider, RESOURCE_PREFIX "css/main.css");
gtk_style_context_add_provider_for_screen (screen, GTK_STYLE_PROVIDER(provider), GTK_STYLE_PROVIDER_PRIORITY_APPLICATION); gtk_style_context_add_provider_for_screen (screen, GTK_STYLE_PROVIDER(provider), GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
GtkTextView *text_view = builder_get(GTK_TEXT_VIEW, "console_screen");
GtkTextBuffer *text_buf = gtk_text_view_get_buffer(text_view);
gtk_text_buffer_create_tag(text_buf, "bold", "weight", PANGO_WEIGHT_BOLD, NULL);
gtk_text_buffer_create_tag(text_buf, "underline", "underline", PANGO_UNDERLINE_SINGLE, "underline-set", TRUE, NULL);
gtk_text_buffer_create_tag(text_buf, "dashed_underline", "underline", PANGO_UNDERLINE_DOUBLE, "underline-set", TRUE, NULL);
gtk_application_add_window(GTK_APPLICATION(app), GTK_WINDOW(main_window)); gtk_application_add_window(GTK_APPLICATION(app), GTK_WINDOW(main_window));
gtk_widget_show_all(GTK_WIDGET(main_window)); gtk_widget_show_all(GTK_WIDGET(main_window));
@ -705,6 +802,18 @@ static void activate_about(GSimpleAction *action, GVariant *parameter, gpointer
gtk_widget_hide(GTK_WIDGET(dialog)); gtk_widget_hide(GTK_WIDGET(dialog));
} }
// app.show_console GAction
// Opens the console
static void activate_show_console(GSimpleAction *action, GVariant *parameter, gpointer app) {
if (debugger_input_queue) {
while (debugger_input_queue->len) {
g_ptr_array_remove_index_fast(debugger_input_queue, debugger_input_queue->len - 1);
}
}
gtk_widget_show_all(builder_get(GTK_WIDGET, "console"));
}
// app.open_gtk_debugger GAction // app.open_gtk_debugger GAction
// Opens the GTK debugger // Opens the GTK debugger
static void activate_open_gtk_debugger(GSimpleAction *action, GVariant *parameter, gpointer app) { static void activate_open_gtk_debugger(GSimpleAction *action, GVariant *parameter, gpointer app) {
@ -1151,6 +1260,18 @@ G_MODULE_EXPORT void on_use_integer_scaling_changed(GtkWidget *w, gpointer user_
update_viewport(); update_viewport();
} }
G_MODULE_EXPORT void console_on_enter(GtkWidget *w, gpointer user_data_gptr) {
GtkEntry *input = GTK_ENTRY(w);
const gchar *_text = gtk_entry_get_text(input);
const gchar *text = g_strdup(_text);
gtk_entry_set_text(input, "");
g_mutex_lock(&debugger_input_mutex);
g_ptr_array_add(debugger_input_queue, (gpointer)text);
g_cond_signal(&debugger_input_cond);
g_mutex_unlock(&debugger_input_mutex);
}
static uint32_t rgb_encode(GB_gameboy_t *gb, uint8_t r, uint8_t g, uint8_t b) { static uint32_t rgb_encode(GB_gameboy_t *gb, uint8_t r, uint8_t g, uint8_t b) {
return 0xFF000000 | (r << 16) | (g << 8) | b; return 0xFF000000 | (r << 16) | (g << 8) | b;
} }
@ -1515,6 +1636,13 @@ static gpointer run(gpointer user_data_gptr) {
GB_model_t prev_model = GB_get_model(&gb); GB_model_t prev_model = GB_get_model(&gb);
GB_model_t model = user_data->model? user_data->model : GB_MODEL_CGB_E; // TODO: Model from config GB_model_t model = user_data->model? user_data->model : GB_MODEL_CGB_E; // TODO: Model from config
if (!debugger_input_queue) {
g_mutex_init(&debugger_input_mutex);
g_cond_init(&debugger_input_cond);
g_mutex_init(&console_output_lock);
debugger_input_queue = g_ptr_array_sized_new(4);
}
if (GB_is_inited(&gb)) { if (GB_is_inited(&gb)) {
GB_switch_model_and_reset(&gb, model); GB_switch_model_and_reset(&gb, model);
@ -1534,7 +1662,7 @@ static gpointer run(gpointer user_data_gptr) {
GB_init(&gb, model); GB_init(&gb, model);
update_window_geometry(); update_window_geometry();
GB_set_vblank_callback(&gb, (GB_vblank_callback_t) vblank); GB_set_vblank_callback(&gb, vblank);
GB_set_pixels_output(&gb, get_current_buffer()); GB_set_pixels_output(&gb, get_current_buffer());
GB_set_rgb_encode_callback(&gb, rgb_encode); GB_set_rgb_encode_callback(&gb, rgb_encode);
GB_set_sample_rate(&gb, DEFAULT_AUDIO_SAMPLE_RATE); GB_set_sample_rate(&gb, DEFAULT_AUDIO_SAMPLE_RATE);
@ -1543,6 +1671,9 @@ static gpointer run(gpointer user_data_gptr) {
GB_set_rewind_length(&gb, config.rewind_duration); GB_set_rewind_length(&gb, config.rewind_duration);
GB_set_update_input_hint_callback(&gb, handle_events); GB_set_update_input_hint_callback(&gb, handle_events);
GB_apu_set_sample_callback(&gb, gb_audio_callback); GB_apu_set_sample_callback(&gb, gb_audio_callback);
GB_set_input_callback(&gb, sync_console_input);
GB_set_async_input_callback(&gb, async_console_input);
GB_set_log_callback(&gb, console_log);
} }
GError *error; GError *error;

View File

@ -72,6 +72,7 @@ static gboolean on_key_press(GtkWidget *w, GdkEventKey *event, gpointer data);
// App actions // App actions
static void activate_about(GSimpleAction *action, GVariant *parameter, gpointer app); static void activate_about(GSimpleAction *action, GVariant *parameter, gpointer app);
static void activate_show_console(GSimpleAction *action, GVariant *parameter, gpointer app);
static void activate_open_gtk_debugger(GSimpleAction *action, GVariant *parameter, gpointer app); static void activate_open_gtk_debugger(GSimpleAction *action, GVariant *parameter, gpointer app);
static void activate_open_memory_viewer(GSimpleAction *action, GVariant *parameter, gpointer app); static void activate_open_memory_viewer(GSimpleAction *action, GVariant *parameter, gpointer app);
static void activate_open_vram_viewer(GSimpleAction *action, GVariant *parameter, gpointer app); static void activate_open_vram_viewer(GSimpleAction *action, GVariant *parameter, gpointer app);

View File

@ -7,4 +7,39 @@
min-height: 11px; min-height: 11px;
min-width: 12px; min-width: 12px;
font-size: 0.8rem; font-size: 0.8rem;
} }
/* dark theme for the console, based on GNOME Terminal colors */
.debug-console,
.debug-console entry,
.debug-console scrolledwindow,
.debug-console textview,
.debug-console textview text {
background: #2E3436;
color: #D3D7CF;
font-size: 12px;
border-color: #3d4548;
}
.debug-console entry {
background: #252a2c;
border: none; /* 1px solid #1d2022 */
border-radius: 0;
}
.debug-console .border-none {
border-top: none;
border-left: none;
border-bottom: none;
border-right: none;
border: none;
}
.debug-console .border-right {
border-right: 1px solid #3d4548;
}
.debug-console .border-bottom {
border-bottom: 1px solid #3d4548;
}

View File

@ -115,6 +115,7 @@ Maximilian Mader https://github.com/max-m</property>
</object> </object>
<object class="GtkWindow" id="console"> <object class="GtkWindow" id="console">
<property name="can_focus">False</property> <property name="can_focus">False</property>
<property name="title" translatable="yes">Debug Console</property>
<property name="default_width">920</property> <property name="default_width">920</property>
<property name="default_height">400</property> <property name="default_height">400</property>
<child type="titlebar"> <child type="titlebar">
@ -143,17 +144,35 @@ Maximilian Mader https://github.com/max-m</property>
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can_focus">False</property>
<child> <child>
<object class="GtkLabel"> <object class="GtkTextView" id="console_screen">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can_focus">True</property>
<property name="label" translatable="yes">Placeholder</property> <property name="editable">False</property>
<property name="wrap_mode">word</property>
<property name="left_margin">5</property>
<property name="right_margin">5</property>
<property name="top_margin">5</property>
<property name="bottom_margin">5</property>
<property name="cursor_visible">False</property>
<property name="accepts_tab">False</property>
<property name="monospace">True</property>
<style>
<class name="border-none"/>
</style>
</object> </object>
</child> </child>
<style>
<class name="border-none"/>
</style>
</object> </object>
</child> </child>
<style>
<class name="border-none"/>
<class name="border-right"/>
</style>
</object> </object>
<packing> <packing>
<property name="expand">False</property> <property name="expand">True</property>
<property name="fill">True</property> <property name="fill">True</property>
<property name="position">0</property> <property name="position">0</property>
</packing> </packing>
@ -164,25 +183,41 @@ Maximilian Mader https://github.com/max-m</property>
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can_focus">False</property>
<property name="orientation">vertical</property> <property name="orientation">vertical</property>
<property name="homogeneous">True</property>
<child> <child>
<object class="GtkScrolledWindow"> <object class="GtkScrolledWindow">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">True</property> <property name="can_focus">True</property>
<property name="shadow_type">in</property> <property name="shadow_type">in</property>
<property name="min_content_width">320</property>
<property name="min_content_height">80</property>
<child> <child>
<object class="GtkViewport"> <object class="GtkViewport">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can_focus">False</property>
<child> <child>
<object class="GtkLabel"> <object class="GtkTextView">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can_focus">True</property>
<property name="label" translatable="yes">Placeholder</property> <property name="left_margin">5</property>
<property name="right_margin">5</property>
<property name="top_margin">5</property>
<property name="bottom_margin">5</property>
<property name="accepts_tab">False</property>
<property name="monospace">True</property>
<style>
<class name="border-none"/>
</style>
</object> </object>
</child> </child>
<style>
<class name="border-none"/>
</style>
</object> </object>
</child> </child>
<style>
<class name="border-none"/>
<class name="border-bottom"/>
</style>
</object> </object>
<packing> <packing>
<property name="expand">False</property> <property name="expand">False</property>
@ -200,17 +235,32 @@ Maximilian Mader https://github.com/max-m</property>
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can_focus">False</property>
<child> <child>
<object class="GtkLabel"> <object class="GtkTextView">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can_focus">True</property>
<property name="label" translatable="yes">Placeholder</property> <property name="editable">False</property>
<property name="left_margin">5</property>
<property name="right_margin">5</property>
<property name="top_margin">5</property>
<property name="bottom_margin">5</property>
<property name="accepts_tab">False</property>
<property name="monospace">True</property>
<style>
<class name="border-none"/>
</style>
</object> </object>
</child> </child>
<style>
<class name="border-none"/>
</style>
</object> </object>
</child> </child>
<style>
<class name="border-none"/>
</style>
</object> </object>
<packing> <packing>
<property name="expand">False</property> <property name="expand">True</property>
<property name="fill">True</property> <property name="fill">True</property>
<property name="position">1</property> <property name="position">1</property>
</packing> </packing>
@ -222,6 +272,10 @@ Maximilian Mader https://github.com/max-m</property>
<property name="position">1</property> <property name="position">1</property>
</packing> </packing>
</child> </child>
<style>
<class name="border-none"/>
<class name="border-bottom"/>
</style>
</object> </object>
<packing> <packing>
<property name="expand">True</property> <property name="expand">True</property>
@ -230,7 +284,7 @@ Maximilian Mader https://github.com/max-m</property>
</packing> </packing>
</child> </child>
<child> <child>
<object class="GtkEntry"> <object class="GtkEntry" id="console_input">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">True</property> <property name="can_focus">True</property>
<property name="margin_left">3</property> <property name="margin_left">3</property>
@ -238,6 +292,7 @@ Maximilian Mader https://github.com/max-m</property>
<property name="margin_top">3</property> <property name="margin_top">3</property>
<property name="margin_bottom">3</property> <property name="margin_bottom">3</property>
<property name="placeholder_text" translatable="yes">Console input</property> <property name="placeholder_text" translatable="yes">Console input</property>
<signal name="activate" handler="console_on_enter" swapped="no"/>
</object> </object>
<packing> <packing>
<property name="expand">False</property> <property name="expand">False</property>
@ -247,6 +302,9 @@ Maximilian Mader https://github.com/max-m</property>
</child> </child>
</object> </object>
</child> </child>
<style>
<class name="debug-console"/>
</style>
</object> </object>
<object class="GtkListStore" id="dmg_models"> <object class="GtkListStore" id="dmg_models">
<columns> <columns>
@ -595,23 +653,6 @@ Maximilian Mader https://github.com/max-m</property>
<property name="position">3</property> <property name="position">3</property>
</packing> </packing>
</child> </child>
<child>
<object class="GtkCheckButton" id="integer_scaling_toggle">
<property name="label" translatable="yes">Use Integer Scaling</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="margin_top">5</property>
<property name="margin_bottom">10</property>
<property name="draw_indicator">True</property>
<signal name="toggled" handler="on_use_integer_scaling_changed" swapped="no"/>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">4</property>
</packing>
</child>
<child> <child>
<object class="GtkCheckButton" id="aspect_ratio_toggle"> <object class="GtkCheckButton" id="aspect_ratio_toggle">
<property name="label" translatable="yes">Keep Aspect Ratio</property> <property name="label" translatable="yes">Keep Aspect Ratio</property>
@ -629,6 +670,23 @@ Maximilian Mader https://github.com/max-m</property>
<property name="position">4</property> <property name="position">4</property>
</packing> </packing>
</child> </child>
<child>
<object class="GtkCheckButton" id="integer_scaling_toggle">
<property name="label" translatable="yes">Use Integer Scaling</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="margin_top">5</property>
<property name="margin_bottom">10</property>
<property name="draw_indicator">True</property>
<signal name="toggled" handler="on_use_integer_scaling_changed" swapped="no"/>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">4</property>
</packing>
</child>
<child> <child>
<object class="GtkLabel" id="menubar_override_selector_label"> <object class="GtkLabel" id="menubar_override_selector_label">
<property name="can_focus">False</property> <property name="can_focus">False</property>