diff --git a/gtk3/main.c b/gtk3/main.c index 6328a7c..6b5c9ed 100644 --- a/gtk3/main.c +++ b/gtk3/main.c @@ -66,15 +66,21 @@ static uint8_t oamHeight; 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 static const GActionEntry app_entries[] = { { "quit", activate_quit, NULL, NULL, NULL }, { "about", activate_about, 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 }, - { "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_vram_viewer", activate_open_vram_viewer, NULL, NULL, NULL }, + { "preferences", activate_preferences, NULL, NULL, NULL }, }; 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)); } +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 // 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) { @@ -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_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_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)); } +// 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 // Opens the GTK debugger 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(); } +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) { 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 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)) { GB_switch_model_and_reset(&gb, model); @@ -1534,7 +1662,7 @@ static gpointer run(gpointer user_data_gptr) { GB_init(&gb, model); 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_rgb_encode_callback(&gb, rgb_encode); 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_update_input_hint_callback(&gb, handle_events); 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; diff --git a/gtk3/main.h b/gtk3/main.h index eb138a5..85210b9 100644 --- a/gtk3/main.h +++ b/gtk3/main.h @@ -72,6 +72,7 @@ static gboolean on_key_press(GtkWidget *w, GdkEventKey *event, gpointer data); // App actions 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_memory_viewer(GSimpleAction *action, GVariant *parameter, gpointer app); static void activate_open_vram_viewer(GSimpleAction *action, GVariant *parameter, gpointer app); diff --git a/gtk3/resources/css/main.css b/gtk3/resources/css/main.css index dc59e7d..fbbb1db 100644 --- a/gtk3/resources/css/main.css +++ b/gtk3/resources/css/main.css @@ -7,4 +7,39 @@ min-height: 11px; min-width: 12px; font-size: 0.8rem; -} \ No newline at end of file +} + +/* 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; +} + diff --git a/gtk3/resources/ui/window.ui b/gtk3/resources/ui/window.ui index cddc669..a4e1264 100644 --- a/gtk3/resources/ui/window.ui +++ b/gtk3/resources/ui/window.ui @@ -115,6 +115,7 @@ Maximilian Mader https://github.com/max-m False + Debug Console 920 400 @@ -143,17 +144,35 @@ Maximilian Mader https://github.com/max-m True False - + True - False - Placeholder + True + False + word + 5 + 5 + 5 + 5 + False + False + True + + + - False + True True 0 @@ -164,25 +183,41 @@ Maximilian Mader https://github.com/max-m True False vertical - True True True in + 320 + 80 True False - + True - False - Placeholder + True + 5 + 5 + 5 + 5 + False + True + + + False @@ -200,17 +235,32 @@ Maximilian Mader https://github.com/max-m True False - + True - False - Placeholder + True + False + 5 + 5 + 5 + 5 + False + True + + + - False + True True 1 @@ -222,6 +272,10 @@ Maximilian Mader https://github.com/max-m 1 + True @@ -230,7 +284,7 @@ Maximilian Mader https://github.com/max-m - + True True 3 @@ -238,6 +292,7 @@ Maximilian Mader https://github.com/max-m 3 3 Console input + False @@ -247,6 +302,9 @@ Maximilian Mader https://github.com/max-m + @@ -595,23 +653,6 @@ Maximilian Mader https://github.com/max-m 3 - - - Use Integer Scaling - True - True - False - 5 - 10 - True - - - - False - True - 4 - - Keep Aspect Ratio @@ -629,6 +670,23 @@ Maximilian Mader https://github.com/max-m 4 + + + Use Integer Scaling + True + True + False + 5 + 10 + True + + + + False + True + 4 + + False