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
+ 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
+
+