diff --git a/gtk3/main.c b/gtk3/main.c index 61d597d..87d8a5b 100644 --- a/gtk3/main.c +++ b/gtk3/main.c @@ -23,6 +23,17 @@ typedef struct{ uint16_t w, h; } Rect; +#define BUTTON_MASK_A 0x01 +#define BUTTON_MASK_B 0x02 +#define BUTTON_MASK_START 0x04 +#define BUTTON_MASK_SELECT 0x08 +#define BUTTON_MASK_UP 0x10 +#define BUTTON_MASK_DOWN 0x20 +#define BUTTON_MASK_LEFT 0x40 +#define BUTTON_MASK_RIGHT 0x80 + +static uint8_t pressed_buttons; + static void run(GApplication *app, UserData *user_data); static GtkApplication *main_application; @@ -38,6 +49,7 @@ static GtkWindow *printer; static shader_t shader; +static UserData user_data = { NULL }; static GB_gameboy_t gb; static uint32_t *image_buffers[3]; static unsigned char current_buffer; @@ -90,6 +102,21 @@ static void render_texture(void *pixels, void *previous) { render_bitmap_with_shader(&shader, _pixels, previous, GB_get_screen_width(&gb), GB_get_screen_height(&gb), rect.x, rect.y, rect.w, rect.h); } +static void handle_events(GB_gameboy_t *gb) { + while (gtk_events_pending()) { + gtk_main_iteration(); + } + + GB_set_key_state(gb, GB_KEY_RIGHT, pressed_buttons & BUTTON_MASK_RIGHT); + GB_set_key_state(gb, GB_KEY_LEFT, pressed_buttons & BUTTON_MASK_LEFT); + GB_set_key_state(gb, GB_KEY_UP, pressed_buttons & BUTTON_MASK_UP); + GB_set_key_state(gb, GB_KEY_DOWN, pressed_buttons & BUTTON_MASK_DOWN); + GB_set_key_state(gb, GB_KEY_A, pressed_buttons & BUTTON_MASK_A); + GB_set_key_state(gb, GB_KEY_B, pressed_buttons & BUTTON_MASK_B); + GB_set_key_state(gb, GB_KEY_SELECT, pressed_buttons & BUTTON_MASK_SELECT); + GB_set_key_state(gb, GB_KEY_START, pressed_buttons & BUTTON_MASK_START); +} + static void vblank(GB_gameboy_t *gb) { flip(); GB_set_pixels_output(gb, get_pixels()); @@ -105,10 +132,6 @@ static void vblank(GB_gameboy_t *gb) { // Queue a redraw of the VRAM viewer gtk_widget_queue_draw(GTK_WIDGET(vram_viewer)); } - - while (gtk_events_pending()) { - gtk_main_iteration(); - } } static void update_viewport(void) { @@ -206,17 +229,21 @@ static void quit(GApplication *app) { // Tell our own main loop to quit. // This will allow our run() and therefore our activate() methods to end. running = false; + + // Quit our application properly. + // This fires the “shutdown” signal. + g_application_quit(app); } // app.quit GAction // Exits the application -static void activate_quit(GSimpleAction *action, GVariant *parameter, gpointer user_data) { - quit(G_APPLICATION(user_data)); +static void activate_quit(GSimpleAction *action, GVariant *parameter, gpointer app) { + quit(G_APPLICATION(app)); } // app.about GAction // Opens the about dialog -static void activate_about(GSimpleAction *action, GVariant *parameter, gpointer user_data) { +static void activate_about(GSimpleAction *action, GVariant *parameter, gpointer app) { GObject *dialog = get_object("about_dialog"); gtk_dialog_run(GTK_DIALOG(dialog)); gtk_widget_hide(GTK_WIDGET(dialog)); @@ -224,38 +251,79 @@ static void activate_about(GSimpleAction *action, GVariant *parameter, gpointer // app.open_gtk_debugger GAction // Opens the GTK debugger -static void activate_open_gtk_debugger(GSimpleAction *action, GVariant *parameter, gpointer user_data) { +static void activate_open_gtk_debugger(GSimpleAction *action, GVariant *parameter, gpointer app) { gtk_window_set_interactive_debugging(true); } // app.preferences GAction // Opens the preferences window -static void activate_preferences(GSimpleAction *action, GVariant *parameter, gpointer user_data) { +static void activate_preferences(GSimpleAction *action, GVariant *parameter, gpointer app) { gtk_widget_show_all(GTK_WIDGET(get_object("preferences"))); } // app.open_vram_viewer GAction // Opens the VRAM viewer window -static void activate_open_vram_viewer(GSimpleAction *action, GVariant *parameter, gpointer user_data) { +static void activate_open_vram_viewer(GSimpleAction *action, GVariant *parameter, gpointer app) { gtk_widget_show_all(GTK_WIDGET(vram_viewer)); } // app.open_memory_viewer GAction // Opens the memory viewer window -static void activate_open_memory_viewer(GSimpleAction *action, GVariant *parameter, gpointer user_data) { +static void activate_open_memory_viewer(GSimpleAction *action, GVariant *parameter, gpointer app) { gtk_widget_show_all(GTK_WIDGET(memory_viewer)); } +// app.open GAction +// Opens a ROM file +static void activate_open(GSimpleAction *action, GVariant *parameter, gpointer app) { + GtkFileChooserNative *native = gtk_file_chooser_native_new("Open File", GTK_WINDOW(main_window), GTK_FILE_CHOOSER_ACTION_OPEN, "_Open", "_Cancel"); + gint res = gtk_native_dialog_run(GTK_NATIVE_DIALOG(native)); + + if (res == GTK_RESPONSE_ACCEPT) { + g_print("%s\n", gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(native))); + } + + g_object_unref(native); +} + // List of GActions for the `app` prefix static GActionEntry app_entries[] = { { "quit", activate_quit, NULL, NULL, NULL }, { "about", activate_about, NULL, NULL, NULL }, + { "open", activate_open, 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 }, }; +G_MODULE_EXPORT gboolean on_key_press(GtkWidget *w, GdkEventKey *event, gpointer data) { + uint8_t mask; + + // TODO: Allow control remapping in the GUI + switch (event->keyval) { + case GDK_KEY_w: mask = BUTTON_MASK_UP; break; + case GDK_KEY_a: mask = BUTTON_MASK_LEFT; break; + case GDK_KEY_s: mask = BUTTON_MASK_DOWN; break; + case GDK_KEY_d: mask = BUTTON_MASK_RIGHT; break; + + case GDK_KEY_g: mask = BUTTON_MASK_SELECT; break; + case GDK_KEY_h: mask = BUTTON_MASK_START; break; + + case GDK_KEY_k: mask = BUTTON_MASK_B; break; + case GDK_KEY_l: mask = BUTTON_MASK_A; break; + } + + if (event->type == GDK_KEY_PRESS) { + pressed_buttons |= mask; + } + else if (event->type == GDK_KEY_RELEASE) { + pressed_buttons &= ~mask; + } + + return FALSE; +} + G_MODULE_EXPORT void on_quit(GtkWidget *w, gpointer app) { quit(G_APPLICATION(app)); } @@ -473,6 +541,11 @@ static void startup(GApplication *app, gpointer user_data_gptr) { set_combo_box_row_separator_func(GTK_CONTAINER(memory_viewer)); // Connect signal handlers + gtk_widget_add_events(GTK_WIDGET(main_window), GDK_KEY_PRESS_MASK); + gtk_widget_add_events(GTK_WIDGET(main_window), GDK_KEY_RELEASE_MASK); + g_signal_connect(main_window, "key_press_event", G_CALLBACK(on_key_press), NULL); + g_signal_connect(main_window, "key_release_event", G_CALLBACK(on_key_press), NULL); + g_signal_connect(gl_area, "realize", G_CALLBACK(gl_init), NULL); g_signal_connect(gl_area, "render", G_CALLBACK(gl_draw), NULL); g_signal_connect(gl_area, "resize", G_CALLBACK(gl_resize), NULL); @@ -725,7 +798,7 @@ static void run(GApplication *app, UserData *user_data) { GB_set_color_correction_mode(&gb, get_color_correction_mode()); GB_set_highpass_filter_mode(&gb, get_highpass_mode()); 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); } @@ -794,43 +867,35 @@ static void run(GApplication *app, UserData *user_data) { } } - if (user_data->file != NULL) { - GB_load_rom(&gb, g_file_get_path(user_data->file)); - } - - /* Run emulation */ - while (running) { - if (paused || rewind_paused) { - while (gtk_events_pending()) { - gtk_main_iteration(); + if (user_data->file != NULL && GB_load_rom(&gb, g_file_get_path(user_data->file)) == 0) { + /* Run emulation */ + while (running) { + if (paused || rewind_paused) { + while (gtk_events_pending()) { + gtk_main_iteration(); + } } - } - else { - if (do_rewind) { - GB_rewind_pop(&gb); - if (turbo_down) { + else { + if (do_rewind) { GB_rewind_pop(&gb); + if (turbo_down) { + GB_rewind_pop(&gb); + } + if (!GB_rewind_pop(&gb)) { + rewind_paused = true; + } + do_rewind = false; } - if (!GB_rewind_pop(&gb)) { - rewind_paused = true; - } - do_rewind = false; + GB_run(&gb); } - GB_run(&gb); } } - - // Quit our application properly. - // This fires the “shutdown” signal. - g_application_quit(app); } int main(int argc, char *argv[]) { // Create our GApplication and tell GTK that we are able to handle files main_application = gtk_application_new(APP_ID, G_APPLICATION_HANDLES_OPEN); - UserData user_data = { NULL }; - // Define our command line parameters GOptionEntry entries[] = { { "version", 'v', G_OPTION_FLAG_NONE, G_OPTION_ARG_NONE, NULL, "Show the application version", NULL },