1 /*****************************************************************************\
2      Snes9x - Portable Super Nintendo Entertainment System (TM) emulator.
3                 This file is licensed under the Snes9x License.
4    For further information, consult the LICENSE file in the root directory.
5 \*****************************************************************************/
6 
7 #include "gtk_2_3_compat.h"
8 #include "gtk_config.h"
9 #ifdef GDK_WINDOWING_X11
10 #include <X11/Xatom.h>
11 #endif
12 #include <gdk/gdkkeysyms.h>
13 #include <cairo.h>
14 
15 #ifdef USE_XV
16 #include <X11/extensions/XShm.h>
17 #include <X11/extensions/Xv.h>
18 #include <X11/extensions/Xvlib.h>
19 #endif
20 
21 #ifdef USE_OPENGL
22 #include "gtk_shader_parameters.h"
23 #endif
24 
25 #include "gtk_s9x.h"
26 #include "gtk_preferences.h"
27 #include "gtk_icon.h"
28 #include "gtk_display.h"
29 #include "gtk_file.h"
30 #include "gtk_sound.h"
31 #include "gtk_control.h"
32 #include "gtk_cheat.h"
33 #include "gtk_netplay.h"
34 
35 static gboolean
event_main_window_delete(GtkWidget * widget,GdkEvent * event,gpointer data)36 event_main_window_delete (GtkWidget *widget,
37                           GdkEvent  *event,
38                           gpointer  data)
39 {
40     S9xExit ();
41 
42     return true;
43 }
44 
45 static gboolean
event_main_window_state_event(GtkWidget * widget,GdkEventWindowState * event,gpointer data)46 event_main_window_state_event (GtkWidget           *widget,
47                                GdkEventWindowState *event,
48                                gpointer            data)
49 {
50     Snes9xWindow *window = (Snes9xWindow *) data;
51     window->fullscreen_state = event->new_window_state & GDK_WINDOW_STATE_FULLSCREEN;
52     window->maximized_state  = event->new_window_state & GDK_WINDOW_STATE_MAXIMIZED;
53 
54     return false;
55 }
56 
57 static gboolean
event_continue_item_activate(GtkWidget * widget,gpointer data)58 event_continue_item_activate (GtkWidget *widget, gpointer data)
59 {
60     Snes9xWindow *window = (Snes9xWindow *) data;
61 
62     window->unpause_from_user ();
63 
64     return true;
65 }
66 
67 static gboolean
event_open_cheats(GtkWidget * widget,gpointer data)68 event_open_cheats (GtkWidget *widget, gpointer data)
69 {
70     Snes9xCheats *cheats;
71     cheats = new Snes9xCheats ();
72 
73     cheats->show ();
74     delete cheats;
75 
76     return true;
77 }
78 
79 static gboolean
event_open_multicart(GtkWidget * widget,gpointer data)80 event_open_multicart (GtkWidget *widget, gpointer data)
81 {
82     ((Snes9xWindow *) data)->open_multicart_dialog ();
83 
84     return true;
85 }
86 
87 static gboolean
event_rom_info(GtkWidget * widget,gpointer data)88 event_rom_info (GtkWidget *widget, gpointer data)
89 {
90     Snes9xWindow *window = (Snes9xWindow *) data;
91 
92     window->show_rom_info ();
93 
94     return true;
95 }
96 
97 static gboolean
event_toggle_interface(GtkWidget * widget,gpointer data)98 event_toggle_interface (GtkWidget *widget, gpointer data)
99 {
100     Snes9xWindow *window = (Snes9xWindow *) data;
101 
102     window->toggle_ui ();
103 
104     return true;
105 }
106 
107 static gboolean
event_sync_clients(GtkWidget * widget,gpointer data)108 event_sync_clients (GtkWidget *widget, gpointer data)
109 {
110     S9xNetplaySyncClients ();
111 
112     return true;
113 }
114 
115 static gboolean
event_pause_item_activate(GtkWidget * widget,gpointer data)116 event_pause_item_activate (GtkWidget *widget, gpointer data)
117 {
118     Snes9xWindow *window = (Snes9xWindow *) data;
119 
120     window->pause_from_user ();
121 
122     return true;
123 }
124 
125 static gboolean
event_open_netplay(GtkWidget * widget,gpointer data)126 event_open_netplay (GtkWidget *widget, gpointer data)
127 {
128     S9xNetplayDialogOpen ();
129 
130     return true;
131 }
132 
133 #if GTK_MAJOR_VERSION >= 3
134 static gboolean
event_drawingarea_draw(GtkWidget * widget,cairo_t * cr,gpointer data)135 event_drawingarea_draw (GtkWidget *widget,
136                         cairo_t   *cr,
137                         gpointer  data)
138 {
139 
140     Snes9xWindow *window = (Snes9xWindow *) data;
141     window->cr = cr;
142     window->cairo_owned = false;
143     window->expose ();
144     window->cr = NULL;
145 
146     return true;
147 }
148 
149 #else
150 
151 static gboolean
event_drawingarea_expose(GtkWidget * widget,GdkEventExpose * event,gpointer data)152 event_drawingarea_expose (GtkWidget      *widget,
153                           GdkEventExpose *event,
154                           gpointer       data)
155 {
156     ((Snes9xWindow *) data)->expose ();
157 
158     return true;
159 }
160 #endif
161 
162 static gboolean
event_key(GtkWidget * widget,GdkEventKey * event,gpointer data)163 event_key (GtkWidget *widget, GdkEventKey *event, gpointer data)
164 {
165     Snes9xWindow        *window = (Snes9xWindow *) data;
166     static unsigned int keyval  = 0;
167     static GdkEventType type    = GDK_NOTHING;
168     Binding             b;
169     s9xcommand_t        cmd;
170 
171     /* Ignore multiple identical keypresses to discard repeating keys */
172     if (event->keyval == keyval && event->type == type)
173     {
174         return true;
175     }
176 
177     /* Provide escape key to get out of fullscreen */
178     if (event->keyval == GDK_Escape)
179     {
180         if (event->type == GDK_KEY_RELEASE)
181         {
182             if (window->config->default_esc_behavior == ESC_EXIT_FULLSCREEN)
183                 window->leave_fullscreen_mode ();
184             else if (window->config->default_esc_behavior == ESC_EXIT_SNES9X)
185                 S9xExit ();
186             else
187                 window->toggle_ui ();
188         }
189 
190         return true;
191     }
192 
193     keyval = event->keyval;
194     type = event->type;
195 
196     b = Binding (event);
197 
198     /* If no mapping for modifier version exists, try non-modifier */
199     cmd = S9xGetMapping (b.hex ());
200     if (cmd.type == S9xNoMapping)
201     {
202         b = Binding (event->keyval, false, false, false);
203         cmd = S9xGetMapping (b.hex ());
204     }
205 
206     if (cmd.type != S9xNoMapping)
207     {
208         S9xReportButton (b.hex (), (event->type == GDK_KEY_PRESS));
209         return true;
210     }
211 
212     return false; /* Pass the key to GTK */
213 }
214 
215 gboolean
event_motion_notify(GtkWidget * widget,GdkEventMotion * event,gpointer user_data)216 event_motion_notify (GtkWidget      *widget,
217                      GdkEventMotion *event,
218                      gpointer       user_data)
219 {
220     Snes9xWindow *window = (Snes9xWindow *) user_data;
221 
222     if (!window->config->rom_loaded ||
223         window->last_width <= 0     ||
224         window->last_height <= 0)
225     {
226         return false;
227     }
228 
229     if (window->mouse_grabbed)
230     {
231         if (event->x_root == window->gdk_mouse_x &&
232             event->y_root == window->gdk_mouse_y)
233             return false;
234 
235         window->snes_mouse_x += (event->x_root - window->gdk_mouse_x);
236         window->snes_mouse_y += (event->y_root - window->gdk_mouse_y);
237         window->center_mouse ();
238 
239         return false;
240     }
241 
242 #if GTK_CHECK_VERSION(3,10,0)
243     int scale_factor = gdk_window_get_scale_factor (gtk_widget_get_window (GTK_WIDGET (window->get_window ())));
244 #else
245     int scale_factor = 1;
246 #endif
247 
248     window->snes_mouse_x = (uint16)
249         ((int) (event->x * scale_factor) - window->mouse_region_x) * 256 /
250         (window->mouse_region_width <= 0 ? 1 : window->mouse_region_width);
251 
252     window->snes_mouse_y = (uint16)
253         ((int) (event->y * scale_factor) - window->mouse_region_y) * (gui_config->overscan ? SNES_HEIGHT_EXTENDED : SNES_HEIGHT) /
254         (window->mouse_region_height <= 0 ? 1 : window->mouse_region_height);
255 
256     if (!window->config->pointer_is_visible)
257     {
258         if (!S9xIsMousePluggedIn ())
259             window->show_mouse_cursor ();
260     }
261 
262     window->config->pointer_timestamp = g_get_monotonic_time ();
263 
264     return false;
265 }
266 
267 gboolean
event_button_press(GtkWidget * widget,GdkEventButton * event,gpointer user_data)268 event_button_press (GtkWidget      *widget,
269                     GdkEventButton *event,
270                     gpointer       user_data)
271 {
272     auto window = (Snes9xWindow *)user_data;
273 
274     if (S9xIsMousePluggedIn())
275     {
276         switch (event->button)
277         {
278         case 1:
279             S9xReportButton(BINDING_MOUSE_BUTTON0, 1);
280             break;
281         case 2:
282             S9xReportButton(BINDING_MOUSE_BUTTON2, 1);
283             break;
284         case 3:
285             S9xReportButton(BINDING_MOUSE_BUTTON1, 1);
286             break;
287         }
288     }
289     else if (event->button == 3)
290     {
291 #if GTK_MAJOR_VERSION >= 3
292         gtk_menu_popup_at_pointer(GTK_MENU(window->get_widget("view_menu_menu")), NULL);
293 #else
294         gtk_menu_popup(GTK_MENU(window->get_widget("view_menu_menu")), NULL,
295                        NULL, NULL, NULL, 3, event->time);
296 #endif
297     }
298     return false;
299 }
300 
301 gboolean
event_button_release(GtkWidget * widget,GdkEventButton * event,gpointer user_data)302 event_button_release (GtkWidget      *widget,
303                       GdkEventButton *event,
304                       gpointer       user_data)
305 {
306     switch (event->button)
307     {
308         case 1:
309             S9xReportButton (BINDING_MOUSE_BUTTON0, 0);
310             break;
311         case 2:
312             S9xReportButton (BINDING_MOUSE_BUTTON1, 0);
313             break;
314         case 3:
315             S9xReportButton (BINDING_MOUSE_BUTTON2, 0);
316             break;
317     }
318 
319     return false;
320 }
321 
322 static void
event_fullscreen(GtkWidget * widget,gpointer data)323 event_fullscreen (GtkWidget *widget, gpointer data)
324 {
325     Snes9xWindow *window = (Snes9xWindow *) data;
326 
327     if (!window->config->fullscreen)
328         window->enter_fullscreen_mode ();
329     else
330         window->leave_fullscreen_mode ();
331 }
332 
333 
334 static void
event_exact_pixels_1x(GtkWidget * widget,gpointer data)335 event_exact_pixels_1x (GtkWidget *widget, gpointer data)
336 {
337     ((Snes9xWindow *) data)->resize_to_multiple (1);
338 }
339 
340 static void
event_exact_pixels_2x(GtkWidget * widget,gpointer data)341 event_exact_pixels_2x (GtkWidget *widget, gpointer data)
342 {
343     ((Snes9xWindow *) data)->resize_to_multiple (2);
344 }
345 
346 static void
event_exact_pixels_3x(GtkWidget * widget,gpointer data)347 event_exact_pixels_3x (GtkWidget *widget, gpointer data)
348 {
349     ((Snes9xWindow *) data)->resize_to_multiple (3);
350 }
351 
352 static void
event_exact_pixels_4x(GtkWidget * widget,gpointer data)353 event_exact_pixels_4x (GtkWidget *widget, gpointer data)
354 {
355     ((Snes9xWindow *) data)->resize_to_multiple (4);
356 }
357 
358 static void
event_exact_pixels_5x(GtkWidget * widget,gpointer data)359 event_exact_pixels_5x (GtkWidget *widget, gpointer data)
360 {
361     ((Snes9xWindow *) data)->resize_to_multiple (5);
362 }
363 
364 static void
event_record_movie(GtkWidget * widget,gpointer data)365 event_record_movie (GtkWidget *widget, gpointer data)
366 {
367     if (S9xMovieActive ())
368         S9xMovieStop (false);
369 
370     S9xMovieCreate (S9xChooseMovieFilename (false),
371                     0xFF,
372                     MOVIE_OPT_FROM_RESET,
373                     NULL,
374                     0);
375 }
376 
377 static void
event_open_movie(GtkWidget * widget,gpointer data)378 event_open_movie (GtkWidget *widget, gpointer data)
379 {
380     if (S9xMovieActive ())
381         S9xMovieStop (false);
382 
383     S9xMovieOpen (S9xChooseMovieFilename (true), false);
384 }
385 
386 static void
event_shader_parameters(GtkWidget * widget,gpointer data)387 event_shader_parameters (GtkWidget *widget, gpointer data)
388 {
389 #ifdef USE_OPENGL
390     Snes9xWindow *window = (Snes9xWindow *) data;
391 
392     gtk_shader_parameters_dialog (window->get_window ());
393 #endif
394 }
395 
396 static void
event_stop_recording(GtkWidget * widget,gpointer data)397 event_stop_recording (GtkWidget *widget, gpointer data)
398 {
399     if (S9xMovieActive ())
400         S9xMovieStop (false);
401 }
402 
403 static void
event_jump_to_frame(GtkWidget * widget,gpointer data)404 event_jump_to_frame (GtkWidget *widget, gpointer data)
405 {
406     Snes9xWindow *window = (Snes9xWindow *) data;
407 
408     window->movie_seek_dialog ();
409 }
410 
411 static void
event_reset(GtkWidget * widget,gpointer data)412 event_reset (GtkWidget *widget, gpointer data)
413 {
414     S9xSoftReset ();
415 }
416 
417 static void
event_hard_reset(GtkWidget * widget,gpointer data)418 event_hard_reset (GtkWidget *widget, gpointer data)
419 {
420     S9xReset ();
421 }
422 
423 static void
event_save_state(GtkWidget * widget,gpointer data)424 event_save_state (GtkWidget *widget, gpointer data)
425 {
426     int  slot;
427     char *name = (char *) gtk_buildable_get_name (GTK_BUILDABLE (widget));
428 
429     slot = atoi (&(name[11]));
430 
431     S9xQuickSaveSlot (slot);
432 }
433 
434 static void
event_save_state_file(GtkWidget * widget,gpointer data)435 event_save_state_file (GtkWidget *widget, gpointer data)
436 {
437     ((Snes9xWindow *) data)->save_state_dialog ();
438 }
439 
440 static void
event_load_state(GtkWidget * widget,gpointer data)441 event_load_state (GtkWidget *widget, gpointer data)
442 {
443     int  slot;
444     char *name = (char *) gtk_buildable_get_name (GTK_BUILDABLE (widget));
445 
446     slot = atoi (&(name[11]));
447 
448     S9xQuickLoadSlot (slot);
449 }
450 
451 static void
event_load_state_undo(GtkWidget * widget,gpointer data)452 event_load_state_undo (GtkWidget *widget, gpointer data)
453 {
454     S9xUnfreezeGame (S9xGetFilename (".undo", SNAPSHOT_DIR));
455 }
456 
457 
458 static void
event_load_state_file(GtkWidget * widget,gpointer data)459 event_load_state_file (GtkWidget *widget, gpointer data)
460 {
461     ((Snes9xWindow *) data)->load_state_dialog ();
462 }
463 
464 static void
event_open_rom(GtkWidget * widget,gpointer data)465 event_open_rom (GtkWidget *widget, gpointer data)
466 {
467     ((Snes9xWindow *) data)->open_rom_dialog ();
468 }
469 
470 static void
event_recent_open(GtkRecentChooser * chooser,gpointer data)471 event_recent_open (GtkRecentChooser *chooser, gpointer data)
472 {
473     Snes9xWindow *window   = (Snes9xWindow *) data;
474     gchar        *uri      = gtk_recent_chooser_get_current_uri (chooser);
475     gchar        *filename = g_filename_from_uri (uri, NULL, NULL);
476 
477     window->try_open_rom (filename);
478 
479     g_free (filename);
480     g_free (uri);
481 }
482 
483 static void
event_save_spc(GtkWidget * widget,gpointer data)484 event_save_spc (GtkWidget *widget, gpointer data)
485 {
486     ((Snes9xWindow *) data)->save_spc_dialog ();
487 }
488 
489 static gboolean
event_focus_in(GtkWidget * widget,GdkEventFocus * event,gpointer data)490 event_focus_in (GtkWidget *widget, GdkEventFocus *event, gpointer data)
491 {
492     ((Snes9xWindow *) data)->focus_notify (true);
493 
494     return false;
495 }
496 
497 static gboolean
event_focus_out(GtkWidget * widget,GdkEventFocus * event,gpointer data)498 event_focus_out (GtkWidget *widget, GdkEventFocus *event, gpointer data)
499 {
500     ((Snes9xWindow *) data)->focus_notify (false);
501 
502     return false;
503 }
504 
505 static void
event_port(GtkWidget * widget,gpointer data)506 event_port (GtkWidget *widget, gpointer data)
507 {
508     const gchar *name = gtk_buildable_get_name (GTK_BUILDABLE (widget));
509 
510     if (!(gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM (widget))))
511         return;
512 
513     if (!strcasecmp (name, "joypad1"))
514     {
515         S9xSetController (0, CTL_JOYPAD, 0, 0, 0, 0);
516     }
517 
518     else if (!strcasecmp (name, "joypad2"))
519     {
520         S9xSetController (1, CTL_JOYPAD, 1, 0, 0, 0);
521     }
522 
523     else if (!strcasecmp (name, "mouse1"))
524     {
525         S9xSetController (0, CTL_MOUSE, 0, 0, 0, 0);
526     }
527 
528     else if (!strcasecmp (name, "mouse2"))
529     {
530         S9xSetController (1, CTL_MOUSE, 0, 0, 0, 0);
531     }
532 
533     else if (!strcasecmp (name, "superscope1"))
534     {
535         S9xSetController (0, CTL_SUPERSCOPE, 0, 0, 0, 0);
536     }
537 
538     else if (!strcasecmp (name, "superscope2"))
539     {
540         S9xSetController (1, CTL_SUPERSCOPE, 0, 0, 0, 0);
541     }
542 
543     else if (!strcasecmp (name, "multitap1"))
544     {
545         S9xSetController (0, CTL_MP5, 0, 1, 2, 3);
546     }
547 
548     else if (!strcasecmp (name, "multitap2"))
549     {
550         S9xSetController (1, CTL_MP5, 1, 2, 3, 4);
551     }
552 
553     else if (!strcasecmp (name, "nothingpluggedin2"))
554     {
555         S9xSetController (1, CTL_NONE, 0, 0, 0, 0);
556     }
557 }
558 
Snes9xWindow(Snes9xConfig * config)559 Snes9xWindow::Snes9xWindow (Snes9xConfig *config) :
560     GtkBuilderWindow ("main_window")
561 {
562     GtkBuilderWindowCallbacks callbacks[] =
563     {
564         { "main_window_delete_event", G_CALLBACK (event_main_window_delete) },
565         { "main_window_state_event", G_CALLBACK (event_main_window_state_event) },
566         { "on_continue_item_activate", G_CALLBACK (event_continue_item_activate) },
567         { "on_pause_item_activate", G_CALLBACK (event_pause_item_activate) },
568         { "main_window_key_press_event", G_CALLBACK (event_key) },
569         { "main_window_key_release_event", G_CALLBACK (event_key) },
570         { "on_fullscreen_item_activate", G_CALLBACK (event_fullscreen) },
571         { "on_open_rom_activate", G_CALLBACK (event_open_rom) },
572         { "on_reset_item_activate", G_CALLBACK (event_reset) },
573         { "on_shader_parameters_item_activate", G_CALLBACK (event_shader_parameters) },
574         { "hard_reset", G_CALLBACK (event_hard_reset) },
575         { "on_port_activate", G_CALLBACK (event_port) },
576         { "load_save_state", G_CALLBACK (event_load_state) },
577         { "load_state_file", G_CALLBACK (event_load_state_file) },
578         { "load_state_undo", G_CALLBACK (event_load_state_undo) },
579         { "save_save_state", G_CALLBACK (event_save_state) },
580         { "save_state_file", G_CALLBACK (event_save_state_file) },
581         { "drawingarea_button_press", G_CALLBACK (event_button_press) },
582         { "drawingarea_button_release", G_CALLBACK (event_button_release) },
583         { "drawingarea_motion_notify", G_CALLBACK (event_motion_notify) },
584         { "save_spc", G_CALLBACK (event_save_spc) },
585         { "open_movie", G_CALLBACK (event_open_movie) },
586         { "stop_recording", G_CALLBACK (event_stop_recording) },
587         { "jump_to_frame", G_CALLBACK (event_jump_to_frame) },
588         { "record_movie", G_CALLBACK (event_record_movie) },
589         { "open_cheats", G_CALLBACK (event_open_cheats) },
590         { "on_preferences_item_activate", G_CALLBACK (snes9x_preferences_open) },
591         { "focus_in_event", G_CALLBACK (event_focus_in) },
592         { "focus_out_event", G_CALLBACK (event_focus_out) },
593         { "open_netplay", G_CALLBACK (event_open_netplay) },
594         { "rom_info", G_CALLBACK (event_rom_info) },
595         { "sync_clients", G_CALLBACK (event_sync_clients) },
596         { "toggle_interface", G_CALLBACK (event_toggle_interface) },
597         { "exact_1x", G_CALLBACK (event_exact_pixels_1x) },
598         { "exact_2x", G_CALLBACK (event_exact_pixels_2x) },
599         { "exact_3x", G_CALLBACK (event_exact_pixels_3x) },
600         { "exact_4x", G_CALLBACK (event_exact_pixels_4x) },
601         { "exact_5x", G_CALLBACK (event_exact_pixels_5x) },
602         { "open_multicart", G_CALLBACK (event_open_multicart) },
603 
604         { NULL, NULL }
605     };
606 
607     user_pause             = 0;
608     sys_pause              = 0;
609     last_width             = -1;
610     last_height            = -1;
611     this->config           = config;
612     empty_cursor           = NULL;
613     default_cursor         = NULL;
614     recent_menu            = NULL;
615     fullscreen_state       = 0;
616     maximized_state        = 0;
617     focused                = true;
618     paused_from_focus_loss = false;
619     cr                     = NULL;
620     cairo_owned            = false;
621     mouse_grabbed          = false;
622 
623     if (gtk_icon_theme_has_icon (gtk_icon_theme_get_default (), "snes9x"))
624     {
625         gtk_window_set_default_icon_name ("snes9x");
626     }
627     else
628     {
629         GdkPixbufLoader *loader = gdk_pixbuf_loader_new ();
630         if (gdk_pixbuf_loader_write (loader, (const guchar *)app_icon, sizeof (app_icon), NULL) &&
631             gdk_pixbuf_loader_close (loader, NULL) &&
632             (icon = gdk_pixbuf_loader_get_pixbuf (loader)))
633         {
634             gtk_window_set_default_icon (icon);
635         }
636         g_object_unref(loader);
637     }
638 
639     drawing_area = GTK_DRAWING_AREA (get_widget ("drawingarea"));
640 #if GTK_MAJOR_VERSION < 3
641     gtk_widget_set_double_buffered (GTK_WIDGET (drawing_area), false);
642     gtk_widget_set_app_paintable (GTK_WIDGET (drawing_area), true);
643 
644 #endif
645 
646     gtk_widget_realize (window);
647     gtk_widget_realize (GTK_WIDGET (drawing_area));
648 #if GTK_MAJOR_VERSION < 3
649     gdk_window_set_back_pixmap (gtk_widget_get_window (window), NULL, false);
650     gdk_window_set_back_pixmap (gtk_widget_get_window (GTK_WIDGET (drawing_area)), NULL, false);
651 #endif
652 
653 #ifndef USE_OPENGL
654     gtk_widget_hide (get_widget ("shader_parameters_separator"));
655     gtk_widget_hide (get_widget ("shader_parameters_item"));
656 #else
657     enable_widget ("shader_parameters_item", false);
658 #endif
659 
660 #if GTK_MAJOR_VERSION >= 3
661     g_signal_connect_data (drawing_area,
662                            "draw",
663                            G_CALLBACK (event_drawingarea_draw),
664                            this,
665                            NULL,
666                            (GConnectFlags) 0);
667 #else
668     g_signal_connect_data (drawing_area,
669                            "expose-event",
670                            G_CALLBACK (event_drawingarea_expose),
671                            this,
672                            NULL,
673                            (GConnectFlags) 0);
674 #endif
675     signal_connect (callbacks);
676 
677     if (config->window_width < 100 || config->window_height < 100)
678     {
679         config->window_width = 256;
680         config->window_height = 224;
681     }
682 
683     default_cursor = gdk_cursor_new_for_display (gdk_display_get_default (),GDK_LEFT_PTR);
684     gdk_window_set_cursor (gtk_widget_get_window (window), default_cursor);
685 
686     resize (config->window_width, config->window_height);
687 }
688 
689 extern int gtk_splash_smtpe_size;
690 extern unsigned char gtk_splash_smtpe[];
691 extern int gtk_splash_combo_size;
692 extern unsigned char gtk_splash_combo[];
693 extern int gtk_splash_pattern_size;
694 extern unsigned char gtk_splash_pattern[];
695 
setup_splash()696 void Snes9xWindow::setup_splash()
697 {
698     uint16 *screen_ptr = GFX.Screen;
699 
700     /* Load splash image (RGB24) into Snes9x buffer (RGB15) */
701     last_width = 256;
702     last_height = 224;
703 
704     if (config->splash_image == SPLASH_IMAGE_PATTERN ||
705         config->splash_image == SPLASH_IMAGE_SMTPE   ||
706         config->splash_image == SPLASH_IMAGE_COMBO) {
707         unsigned char *pattern = NULL;
708         int pattern_size = 0;
709 
710         if (config->splash_image == SPLASH_IMAGE_PATTERN) {
711             pattern = gtk_splash_pattern;
712             pattern_size = gtk_splash_pattern_size;
713         } else if (config->splash_image == SPLASH_IMAGE_SMTPE) {
714             pattern = gtk_splash_smtpe;
715             pattern_size = gtk_splash_smtpe_size;
716         } else {
717             pattern = gtk_splash_combo;
718             pattern_size = gtk_splash_combo_size;
719         }
720 
721         auto pixbuf_loader = gdk_pixbuf_loader_new_with_type("png", NULL);
722         gdk_pixbuf_loader_write(pixbuf_loader, pattern, pattern_size, NULL);
723         gdk_pixbuf_loader_close(pixbuf_loader, NULL);
724         auto pixbuf = gdk_pixbuf_loader_get_pixbuf(pixbuf_loader);
725         const unsigned char *splash_ptr = gdk_pixbuf_get_pixels(pixbuf);
726         const int channels = gdk_pixbuf_get_n_channels(pixbuf);
727 
728         for (int y = 0; y < 224; y++, screen_ptr += (GFX.Pitch / 2)) {
729             for (int x = 0; x < 256; x++) {
730                 unsigned int red = splash_ptr[0];
731                 unsigned int green = splash_ptr[1];
732                 unsigned int blue = splash_ptr[2];
733 
734                 screen_ptr[x] = ((red & 0xF8) << 8) +
735                                 ((green & 0xF8) << 3) +
736                                 ((green & 0x80) >> 2) +
737                                 ((blue & 0xF8) >> 3);
738 
739                 splash_ptr += channels;
740             }
741         }
742 
743         g_object_unref(pixbuf_loader);
744 
745         return;
746     }
747 
748     if (config->splash_image == SPLASH_IMAGE_BLUE) {
749         for (int y = 0; y < 224; y++, screen_ptr += (GFX.Pitch / 2)) {
750             uint16 colora = (uint16)y / 7;
751             uint16 colorb = ((uint16)y - 3) / 7;
752             if (colorb > 32)
753                 colorb = 0;
754 
755             for (int x = 0; x < 256; x++) {
756                 screen_ptr[x] = ((x ^ y) & 1) ? colorb : colora;
757             }
758         }
759 
760         return;
761     }
762 
763     for (int y = 0; y < 224; y++, screen_ptr += (GFX.Pitch / 2)) {
764         memset(screen_ptr, 0, 256 * sizeof(uint16));
765     }
766 }
767 
768 void
expose()769 Snes9xWindow::expose ()
770 {
771     if (!(config->fullscreen) && !(maximized_state))
772     {
773         config->window_width = get_width();
774         config->window_height = get_height();
775     }
776 
777     if (last_width < 0)
778     {
779         setup_splash();
780     }
781 
782     S9xDisplayRefresh (last_width, last_height);
783 
784     if (!(config->fullscreen))
785     {
786         config->window_width = get_width ();
787         config->window_height = get_height ();
788     }
789 
790     if (is_paused () || NetPlay.Paused)
791     {
792         S9xDeinitUpdate (last_width, last_height);
793     }
794 }
795 
796 void
focus_notify(bool state)797 Snes9xWindow::focus_notify (bool state)
798 {
799     focused = state;
800 
801     if (!state && config->pause_emulation_on_switch)
802     {
803         sys_pause++;
804         propagate_pause_state ();
805         paused_from_focus_loss = true;
806     }
807 
808     if (state && paused_from_focus_loss)
809     {
810         unpause_from_focus_change ();
811         paused_from_focus_loss = false;
812     }
813 }
814 
815 void
open_multicart_dialog()816 Snes9xWindow::open_multicart_dialog ()
817 {
818     int result;
819     GtkBuilderWindow *dialog = new GtkBuilderWindow ("multicart_dialog");
820     GtkFileChooser *slota, *slotb;
821     GtkWidget *multicart_dialog = GTK_WIDGET (dialog->get_window ());
822 
823     gtk_window_set_transient_for (dialog->get_window (), get_window ());
824 
825     pause_from_focus_change ();
826 
827     slota = GTK_FILE_CHOOSER (dialog->get_widget ("multicart_slota"));
828     slotb = GTK_FILE_CHOOSER (dialog->get_widget ("multicart_slotb"));
829 
830     gtk_file_chooser_set_current_folder (slota, config->last_directory.c_str ());
831     gtk_file_chooser_set_current_folder (slotb, config->last_directory.c_str ());
832 
833     result = gtk_dialog_run (GTK_DIALOG (multicart_dialog));
834 
835     gtk_widget_hide (multicart_dialog);
836 
837     if (result == GTK_RESPONSE_OK)
838     {
839         const gchar *filename;
840 
841         filename = gtk_file_chooser_get_filename (slota);
842         if (filename)
843             strncpy (Settings.CartAName, filename, PATH_MAX);
844         else
845             Settings.CartAName[0] = '\0';
846 
847         filename = gtk_file_chooser_get_filename (slotb);
848         if (filename)
849             strncpy (Settings.CartBName, filename, PATH_MAX);
850         else
851             Settings.CartBName[0] = '\0';
852 
853         Settings.Multi = true;
854 
855         if (S9xOpenROM (NULL))
856         {
857             GtkWidget *msg;
858 
859             msg = gtk_message_dialog_new (GTK_WINDOW (this->window),
860                                           GTK_DIALOG_DESTROY_WITH_PARENT,
861                                           GTK_MESSAGE_ERROR,
862                                           GTK_BUTTONS_CLOSE,
863                                           _("Couldn't load files."));
864             gtk_window_set_title (GTK_WINDOW (msg), _("Error"));
865             gtk_dialog_run (GTK_DIALOG (msg));
866             gtk_widget_destroy (msg);
867         }
868     }
869 
870     delete dialog;
871 
872     unpause_from_focus_change ();
873 }
874 
875 const char *
open_movie_dialog(bool readonly)876 Snes9xWindow::open_movie_dialog (bool readonly)
877 {
878     GtkWidget     *dialog;
879     GtkFileFilter *filter;
880     char          *filename;
881     gint          result;
882     const char    *extensions[] =
883     {
884             "*.smv", "*.SMV",
885             NULL
886     };
887 
888     this->pause_from_focus_change ();
889 
890     if (readonly)
891     {
892         dialog = gtk_file_chooser_dialog_new (_("Open SNES Movie"),
893                                               GTK_WINDOW (this->window),
894                                               GTK_FILE_CHOOSER_ACTION_OPEN,
895                                               "gtk-cancel", GTK_RESPONSE_CANCEL,
896                                               "gtk-open", GTK_RESPONSE_ACCEPT,
897                                               NULL);
898     }
899     else
900     {
901         char def[PATH_MAX];
902         char default_name[PATH_MAX];
903         char drive[_MAX_DRIVE];
904         char dir[_MAX_DIR];
905         char ext[_MAX_EXT];
906 
907         _splitpath (Memory.ROMFilename, drive, dir, def, ext);
908 
909         snprintf (default_name, PATH_MAX, "%s.smv", def);
910 
911         dialog = gtk_file_chooser_dialog_new (_("New SNES Movie"),
912                                               GTK_WINDOW (this->window),
913                                               GTK_FILE_CHOOSER_ACTION_SAVE,
914                                               "gtk-cancel", GTK_RESPONSE_CANCEL,
915                                               "gtk-open", GTK_RESPONSE_ACCEPT,
916                                               NULL);
917 
918         gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (dialog),
919                                            default_name);
920 
921     }
922 
923     filter = gtk_file_filter_new ();
924     gtk_file_filter_set_name (filter, _("SNES Movies"));
925     for (int i = 0; extensions[i]; i++)
926     {
927         gtk_file_filter_add_pattern (filter, extensions[i]);
928     }
929     gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
930 
931     filter = gtk_file_filter_new ();
932     gtk_file_filter_set_name (filter, _("All Files"));
933     gtk_file_filter_add_pattern (filter, "*.*");
934     gtk_file_filter_add_pattern (filter, "*");
935     gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
936 
937     gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (dialog),
938                                          S9xGetDirectory (SRAM_DIR));
939 
940     result = gtk_dialog_run (GTK_DIALOG (dialog));
941     gtk_widget_hide (dialog);
942 
943     if (result == GTK_RESPONSE_ACCEPT)
944     {
945         filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
946     }
947     else
948     {
949         filename = strdup ("");
950     }
951 
952     gtk_widget_destroy (dialog);
953 
954     this->unpause_from_focus_change ();
955 
956     return filename;
957 }
958 
959 void
open_rom_dialog()960 Snes9xWindow::open_rom_dialog ()
961 {
962     char *filename;
963 
964     pause_from_focus_change ();
965 
966     filename = S9xOpenROMDialog ();
967 
968     if (filename)
969     {
970         Settings.Multi = false;
971         try_open_rom (filename);
972 
973         g_free (filename);
974     }
975 
976     unpause_from_focus_change ();
977 }
978 
try_open_rom(const char * filename)979 bool Snes9xWindow::try_open_rom(const char *filename)
980 {
981     pause_from_focus_change ();
982 
983     Settings.Multi = false;
984 
985     if (S9xOpenROM (filename))
986     {
987         GtkWidget *msg;
988 
989         msg = gtk_message_dialog_new (GTK_WINDOW (this->window),
990                                       GTK_DIALOG_DESTROY_WITH_PARENT,
991                                       GTK_MESSAGE_ERROR,
992                                       GTK_BUTTONS_CLOSE,
993                                       _("Couldn't load file '%s'"),
994                                       filename);
995         gtk_window_set_title (GTK_WINDOW (msg), _("Error"));
996         gtk_dialog_run (GTK_DIALOG (msg));
997         gtk_widget_destroy (msg);
998 
999         unpause_from_focus_change ();
1000 
1001         return false;
1002     }
1003     else
1004     {
1005         const char *groups[] = { "cartridge", NULL };
1006 
1007         GtkRecentData recent_data =
1008         {
1009                 NULL,
1010                 (gchar *) "SNES ROM",
1011                 (gchar *) "application/x-snes-rom",
1012                 (gchar *) "Snes9x",
1013                 NULL,
1014                 (gchar **) groups,
1015                 false
1016         };
1017         gchar *u_filename;
1018 
1019         u_filename = g_filename_to_uri (filename, NULL, NULL);
1020 
1021         recent_data.app_exec = g_strjoin (" ",
1022                                           g_get_prgname (),
1023                                           "%f",
1024                                           NULL);
1025 
1026         gtk_recent_manager_add_full (gtk_recent_manager_get_default (),
1027                                      u_filename,
1028                                      &recent_data);
1029 
1030         g_free (recent_data.app_exec);
1031         g_free (u_filename);
1032 
1033         this->unpause_from_user ();
1034 
1035     }
1036 
1037     this->unpause_from_focus_change ();
1038 
1039     return true;
1040 }
1041 
1042 void
load_state_dialog()1043 Snes9xWindow::load_state_dialog ()
1044 {
1045     GtkWidget     *dialog;
1046     GtkFileFilter *filter;
1047     char          *filename;
1048     gint          result;
1049 
1050     this->pause_from_focus_change ();
1051 
1052     dialog = gtk_file_chooser_dialog_new (_("Load Saved State"),
1053                                           GTK_WINDOW (this->window),
1054                                           GTK_FILE_CHOOSER_ACTION_OPEN,
1055                                           "gtk-cancel", GTK_RESPONSE_CANCEL,
1056                                           "gtk-open", GTK_RESPONSE_ACCEPT,
1057                                           NULL);
1058 
1059     gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (dialog),
1060                                          S9xGetDirectory (SNAPSHOT_DIR));
1061 
1062     filter = gtk_file_filter_new ();
1063     gtk_file_filter_set_name (filter, _("Save States"));
1064     gtk_file_filter_add_pattern (filter, "*.sst");
1065     gtk_file_filter_add_pattern (filter, "*.zst");
1066     gtk_file_filter_add_pattern (filter, "*.ZST");
1067     gtk_file_filter_add_pattern (filter, "*.000");
1068     gtk_file_filter_add_pattern (filter, "*.001");
1069     gtk_file_filter_add_pattern (filter, "*.002");
1070     gtk_file_filter_add_pattern (filter, "*.003");
1071     gtk_file_filter_add_pattern (filter, "*.004");
1072     gtk_file_filter_add_pattern (filter, "*.005");
1073     gtk_file_filter_add_pattern (filter, "*.006");
1074     gtk_file_filter_add_pattern (filter, "*.007");
1075     gtk_file_filter_add_pattern (filter, "*.008");
1076     gtk_file_filter_add_pattern (filter, "*.009");
1077     gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
1078 
1079     filter = gtk_file_filter_new ();
1080     gtk_file_filter_set_name (filter, _("All Files"));
1081     gtk_file_filter_add_pattern (filter, "*.*");
1082     gtk_file_filter_add_pattern (filter, "*");
1083     gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
1084 
1085     result = gtk_dialog_run (GTK_DIALOG (dialog));
1086     gtk_widget_hide (dialog);
1087 
1088     if (result == GTK_RESPONSE_ACCEPT)
1089     {
1090         filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
1091 
1092         S9xLoadState (filename);
1093 
1094         g_free (filename);
1095     }
1096 
1097     else
1098     {
1099     }
1100 
1101     gtk_widget_destroy (dialog);
1102 
1103     this->unpause_from_focus_change ();
1104 }
1105 
1106 void
movie_seek_dialog()1107 Snes9xWindow::movie_seek_dialog ()
1108 {
1109     char      str[1024];
1110     gint      result;
1111 
1112     if (!S9xMovieActive ())
1113         return;
1114 
1115     GtkBuilderWindow *seek_dialog = new GtkBuilderWindow ("frame_advance_dialog");
1116     GtkWindow *seek_window = seek_dialog->get_window ();
1117 
1118     pause_from_focus_change ();
1119 
1120     snprintf (str, 1024, _("The current frame in the movie is <b>%d</b>."), S9xMovieGetFrameCounter ());
1121     gtk_label_set_label (GTK_LABEL (seek_dialog->get_widget ("current_frame_label")), str);
1122 
1123     snprintf (str, 1024, "%d", S9xMovieGetFrameCounter ());
1124     seek_dialog->set_entry_text ("frame_entry", str);
1125 
1126     gtk_window_set_transient_for (seek_window, get_window ());
1127 
1128     result = gtk_dialog_run (GTK_DIALOG (seek_window));
1129 
1130     int entry_value = seek_dialog->get_entry_value ("frame_entry");
1131 
1132     switch (result)
1133     {
1134         case GTK_RESPONSE_OK:
1135 
1136             if (entry_value > 0 &&
1137                 entry_value > (int) S9xMovieGetFrameCounter ())
1138             {
1139                 Settings.HighSpeedSeek =
1140                     entry_value - S9xMovieGetFrameCounter ();
1141             }
1142 
1143             break;
1144     }
1145 
1146     delete seek_dialog;
1147 
1148     unpause_from_focus_change ();
1149 }
1150 
1151 void
save_state_dialog()1152 Snes9xWindow::save_state_dialog ()
1153 {
1154     GtkWidget     *dialog;
1155     GtkFileFilter *filter;
1156     char          *filename;
1157     gint          result;
1158     char          def[PATH_MAX];
1159     char          default_name[PATH_MAX];
1160     char          drive[_MAX_DRIVE];
1161     char          dir[_MAX_DIR];
1162     char          ext[_MAX_EXT];
1163 
1164     this->pause_from_focus_change ();
1165 
1166     _splitpath (Memory.ROMFilename, drive, dir, def, ext);
1167 
1168     snprintf (default_name, PATH_MAX, "%s.sst", def);
1169 
1170     dialog = gtk_file_chooser_dialog_new (_("Save State"),
1171                                           GTK_WINDOW (this->window),
1172                                           GTK_FILE_CHOOSER_ACTION_SAVE,
1173                                           "gtk-cancel", GTK_RESPONSE_CANCEL,
1174                                           "gtk-save", GTK_RESPONSE_ACCEPT,
1175                                           NULL);
1176 
1177     gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (dialog),
1178                                          S9xGetDirectory (SNAPSHOT_DIR));
1179     gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (dialog),
1180                                        default_name);
1181 
1182     filter = gtk_file_filter_new ();
1183     gtk_file_filter_set_name (filter, _("Save States"));
1184     gtk_file_filter_add_pattern (filter, "*.sst");
1185     gtk_file_filter_add_pattern (filter, "*.zst");
1186     gtk_file_filter_add_pattern (filter, "*.ZST");
1187     gtk_file_filter_add_pattern (filter, "*.000");
1188     gtk_file_filter_add_pattern (filter, "*.001");
1189     gtk_file_filter_add_pattern (filter, "*.002");
1190     gtk_file_filter_add_pattern (filter, "*.003");
1191     gtk_file_filter_add_pattern (filter, "*.004");
1192     gtk_file_filter_add_pattern (filter, "*.005");
1193     gtk_file_filter_add_pattern (filter, "*.006");
1194     gtk_file_filter_add_pattern (filter, "*.007");
1195     gtk_file_filter_add_pattern (filter, "*.008");
1196     gtk_file_filter_add_pattern (filter, "*.009");
1197     gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
1198 
1199     filter = gtk_file_filter_new ();
1200     gtk_file_filter_set_name (filter, _("All Files"));
1201     gtk_file_filter_add_pattern (filter, "*.*");
1202     gtk_file_filter_add_pattern (filter, "*");
1203     gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
1204 
1205     result = gtk_dialog_run (GTK_DIALOG (dialog));
1206 
1207     gtk_widget_hide (dialog);
1208 
1209     if (result == GTK_RESPONSE_ACCEPT)
1210     {
1211         filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
1212 
1213         S9xSaveState (filename);
1214 
1215         g_free (filename);
1216     }
1217 
1218     else
1219     {
1220     }
1221 
1222     gtk_widget_destroy (dialog);
1223 
1224     this->unpause_from_focus_change ();
1225 }
1226 
1227 void
save_spc_dialog()1228 Snes9xWindow::save_spc_dialog ()
1229 {
1230     GtkWidget     *dialog;
1231     GtkFileFilter *filter;
1232     char          *filename;
1233     gint          result;
1234     char          def[PATH_MAX];
1235     char          default_name[PATH_MAX];
1236     char          drive[_MAX_DRIVE];
1237     char          dir[_MAX_DIR];
1238     char          ext[_MAX_EXT];
1239 
1240     this->pause_from_focus_change ();
1241 
1242     _splitpath (Memory.ROMFilename, drive, dir, def, ext);
1243 
1244     snprintf (default_name, PATH_MAX, "%s.spc", def);
1245 
1246     dialog = gtk_file_chooser_dialog_new (_("Save SPC file..."),
1247                                           GTK_WINDOW (this->window),
1248                                           GTK_FILE_CHOOSER_ACTION_SAVE,
1249                                           "gtk-cancel", GTK_RESPONSE_CANCEL,
1250                                           "gtk-save", GTK_RESPONSE_ACCEPT,
1251                                           NULL);
1252 
1253     gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (dialog),
1254                                          S9xGetDirectory (SNAPSHOT_DIR));
1255     gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (dialog),
1256                                        default_name);
1257 
1258     filter = gtk_file_filter_new ();
1259     gtk_file_filter_set_name (filter, _("SPC Files"));
1260     gtk_file_filter_add_pattern (filter, "*.spc");
1261     gtk_file_filter_add_pattern (filter, "*.SPC");
1262     gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
1263 
1264     filter = gtk_file_filter_new ();
1265     gtk_file_filter_set_name (filter, _("All Files"));
1266     gtk_file_filter_add_pattern (filter, "*.*");
1267     gtk_file_filter_add_pattern (filter, "*");
1268     gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
1269 
1270     result = gtk_dialog_run (GTK_DIALOG (dialog));
1271 
1272     gtk_widget_hide (dialog);
1273 
1274     if (result == GTK_RESPONSE_ACCEPT)
1275     {
1276         filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
1277 
1278         if (S9xSPCDump (filename))
1279         {
1280             /* Success ? */
1281         }
1282         else
1283         {
1284             GtkWidget *msg;
1285 
1286             msg = gtk_message_dialog_new (GTK_WINDOW (this->window),
1287                                           GTK_DIALOG_DESTROY_WITH_PARENT,
1288                                           GTK_MESSAGE_ERROR,
1289                                           GTK_BUTTONS_CLOSE,
1290                                           _("Couldn't save SPC file '%s'"),
1291                                           filename);
1292             gtk_window_set_title (GTK_WINDOW (msg), _("Error"));
1293             gtk_dialog_run (GTK_DIALOG (msg));
1294             gtk_widget_destroy (msg);
1295         }
1296 
1297         g_free (filename);
1298     }
1299 
1300     else
1301     {
1302     }
1303 
1304     gtk_widget_destroy (dialog);
1305 
1306     this->unpause_from_focus_change ();
1307 }
1308 
1309 void
set_menu_item_selected(const char * name)1310 Snes9xWindow::set_menu_item_selected (const char *name)
1311 {
1312     GtkCheckMenuItem *item;
1313 
1314     item = GTK_CHECK_MENU_ITEM (get_widget (name));
1315 
1316     gtk_check_menu_item_set_active (item, 1);
1317 }
1318 
1319 void
show_rom_info()1320 Snes9xWindow::show_rom_info ()
1321 {
1322     GtkWidget *msg;
1323 
1324     pause_from_focus_change ();
1325 
1326     msg = gtk_message_dialog_new_with_markup (GTK_WINDOW (window),
1327                                               GTK_DIALOG_DESTROY_WITH_PARENT,
1328                                               GTK_MESSAGE_OTHER,
1329                                               GTK_BUTTONS_CLOSE,
1330                                               _("<b>Information for %s</b>\n\n"
1331                                               "<i>Name:</i> %s\n"
1332                                               "<i>Speed:</i> %02X/%s\n"
1333                                               "<i>Map:</i> %s\n"
1334                                               "<i>Type:</i> %02x\n"
1335                                               "<i>Contents:</i> %s\n"
1336                                               "<i>ROM Size:</i> %s\n"
1337                                               "<i>Calculated Size:</i> %d\n"
1338                                               "<i>SRAM Size:</i> %s\n"
1339                                               "<i>Header Checksum:</i> %04X\n"
1340                                               "<i>Checksum Compliment:</i> %04X\n"
1341                                               "<i>Actual Checksum:</i> %04X\n"
1342                                               "<i>Video:</i> %s\n"
1343                                               "<i>CRC32:</i> %08X\n"
1344                                               "<i>Revision:</i> %s"
1345                                               "<b><i>%s%s</i></b>"),
1346                                               Memory.ROMFilename,
1347                                               Memory.ROMName,
1348                                               Memory.ROMSpeed,
1349                                               ((Memory.ROMSpeed & 0x10) != 0) ?
1350                                                       "FastROM" : "SlowROM",
1351                                               (Memory.HiROM) ?
1352                                                       "HiROM" : "LoROM",
1353                                               Memory.ROMType,
1354                                               Memory.KartContents (),
1355                                               Memory.Size (),
1356                                               Memory.CalculatedSize / 0x20000,
1357                                               Memory.StaticRAMSize (),
1358                                               Memory.ROMChecksum,
1359                                               Memory.ROMComplementChecksum,
1360                                               Memory.CalculatedChecksum,
1361                                               (Memory.ROMRegion > 12 ||
1362                                                Memory.ROMRegion < 2) ?
1363                                                   "NTSC 60Hz" : "PAL 50Hz",
1364                                               Memory.ROMCRC32,
1365                                               Memory.Revision (),
1366                                               (Settings.IsPatched) ? _("\n\nThis ROM has been auto-patched with ") :
1367                                               (Memory.ROMChecksum !=
1368                                                   Memory.CalculatedChecksum) ?
1369                                                _("\n\nThis ROM has been modified or damaged")
1370                                                : "",
1371                                                Settings.IsPatched == 1 ? "IPS" :
1372                                                Settings.IsPatched == 2 ? "BPS" :
1373                                                Settings.IsPatched == 3 ? "UPS" :
1374                                                "");
1375     gtk_window_set_title (GTK_WINDOW (msg), _("File Information"));
1376 
1377     gtk_dialog_run (GTK_DIALOG (msg));
1378 
1379     unpause_from_focus_change ();
1380 
1381     gtk_widget_destroy (msg);
1382 }
1383 
1384 void
configure_widgets()1385 Snes9xWindow::configure_widgets ()
1386 {
1387     enable_widget ("continue_item", config->rom_loaded);
1388     enable_widget ("pause_item", config->rom_loaded);
1389     enable_widget ("reset_item", config->rom_loaded);
1390     enable_widget ("load_state_item", config->rom_loaded);
1391     enable_widget ("save_state_item", config->rom_loaded);
1392     enable_widget ("save_spc_item", config->rom_loaded);
1393     enable_widget ("hard_reset_item", config->rom_loaded);
1394     enable_widget ("record_movie_item", config->rom_loaded);
1395     enable_widget ("stop_recording_item", config->rom_loaded);
1396     enable_widget ("open_movie_item", config->rom_loaded);
1397     enable_widget ("jump_to_frame_item", config->rom_loaded);
1398     enable_widget ("cheats_item", config->rom_loaded);
1399     enable_widget ("rom_info_item", config->rom_loaded);
1400 
1401     enable_widget ("sync_clients_item",
1402                    config->rom_loaded &&
1403                    Settings.NetPlay   &&
1404                    Settings.NetPlayServer);
1405 
1406     if (config->default_esc_behavior != ESC_TOGGLE_MENUBAR)
1407     {
1408         enable_widget ("fullscreen_item", config->rom_loaded);
1409 
1410         config->ui_visible = true;
1411 
1412         if (!config->fullscreen)
1413         {
1414             gtk_widget_show (get_widget ("menubar"));
1415         }
1416         else
1417         {
1418             gtk_widget_hide (get_widget ("menubar"));
1419         }
1420 
1421         gtk_widget_hide (get_widget ("hide_ui"));
1422         gtk_widget_hide (get_widget ("hide_ui_separator"));
1423     }
1424     else
1425     {
1426         enable_widget ("fullscreen_item", true);
1427 
1428         gtk_widget_show (get_widget ("hide_ui"));
1429         gtk_widget_show (get_widget ("hide_ui_separator"));
1430 
1431         if (config->ui_visible)
1432         {
1433             gtk_widget_show (get_widget ("menubar"));
1434         }
1435         else
1436         {
1437             gtk_widget_hide (get_widget ("menubar"));
1438         }
1439     }
1440 
1441     propagate_pause_state ();
1442 
1443     if (config->rom_loaded && !Settings.Paused)
1444         hide_mouse_cursor ();
1445     else
1446         show_mouse_cursor ();
1447 }
1448 
1449 void
set_mouseable_area(int x,int y,int width,int height)1450 Snes9xWindow::set_mouseable_area (int x, int y, int width, int height)
1451 {
1452     mouse_region_x      = x;
1453     mouse_region_y      = y;
1454     mouse_region_width  = width;
1455     mouse_region_height = height;
1456 }
1457 
1458 void
reset_screensaver()1459 Snes9xWindow::reset_screensaver ()
1460 {
1461     if (!focused)
1462         return;
1463 
1464 #ifdef GDK_WINDOWING_X11
1465     if (GDK_IS_X11_WINDOW (gtk_widget_get_window (GTK_WIDGET (window))))
1466     {
1467         XResetScreenSaver (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()));
1468     }
1469 #endif
1470 #ifdef GDK_WINDOWING_WAYLAND
1471     if (GDK_IS_WAYLAND_WINDOW (gtk_widget_get_window (GTK_WIDGET (window))))
1472     {
1473     // TODO screensaver for wayland
1474     }
1475 #endif
1476 
1477     config->screensaver_needs_reset = false;
1478 }
1479 
1480 void
toggle_fullscreen_mode()1481 Snes9xWindow::toggle_fullscreen_mode ()
1482 {
1483     if (config->fullscreen)
1484         leave_fullscreen_mode ();
1485     else
1486         enter_fullscreen_mode ();
1487 }
1488 
XRRGetExactRefreshRate(Display * dpy,Window window)1489 static double XRRGetExactRefreshRate (Display *dpy, Window window)
1490 {
1491     XRRScreenResources *resources = NULL;
1492     XRRCrtcInfo        *crtc_info = NULL;
1493     int event_base;
1494     int error_base;
1495     int version_major;
1496     int version_minor;
1497     double refresh_rate = 0.0;
1498     int i;
1499 
1500     if (!XRRQueryExtension (dpy, &event_base, &error_base) ||
1501         !XRRQueryVersion (dpy, &version_major, &version_minor))
1502     {
1503         return refresh_rate;
1504     }
1505 
1506     if (version_minor < 3)
1507         return refresh_rate;
1508 
1509     resources   = XRRGetScreenResourcesCurrent (dpy, window);
1510     crtc_info   = XRRGetCrtcInfo (dpy, resources, resources->crtcs[0]);
1511 
1512     for (i = 0; i < resources->nmode; i++)
1513     {
1514         if (resources->modes[i].id == crtc_info->mode)
1515         {
1516             XRRModeInfo *m = &resources->modes[i];
1517 
1518             refresh_rate = (double) m->dotClock / m->hTotal / m->vTotal;
1519             refresh_rate /= m->modeFlags & RR_DoubleScan     ? 2 : 1;
1520             refresh_rate /= m->modeFlags & RR_ClockDivideBy2 ? 2 : 1;
1521             refresh_rate *= m->modeFlags & RR_DoubleClock    ? 2 : 1;
1522 
1523             break;
1524         }
1525     }
1526 
1527     XRRFreeCrtcInfo (crtc_info);
1528     XRRFreeScreenResources (resources);
1529 
1530     return refresh_rate;
1531 }
1532 
1533 double
get_refresh_rate()1534 Snes9xWindow::get_refresh_rate ()
1535 {
1536     double refresh_rate = 0.0;
1537     GdkDisplay *display = gtk_widget_get_display (window);
1538     GdkWindow *gdk_window =  gtk_widget_get_window (window);
1539 
1540 #ifdef GDK_WINDOWING_X11
1541     if (GDK_IS_X11_DISPLAY (display))
1542     {
1543         Window xid = gdk_x11_window_get_xid (gtk_widget_get_window (window));
1544         Display *dpy = gdk_x11_display_get_xdisplay (gtk_widget_get_display (window));
1545         refresh_rate = XRRGetExactRefreshRate (dpy, xid);
1546     }
1547 #endif
1548 
1549 #ifdef GDK_WINDOWING_WAYLAND
1550     if (GDK_IS_WAYLAND_DISPLAY (display))
1551     {
1552         GdkMonitor *monitor = gdk_display_get_monitor_at_window(display, gdk_window);
1553         refresh_rate = (double) gdk_monitor_get_refresh_rate(monitor) / 1000.0;
1554     }
1555 #endif
1556 
1557     if (refresh_rate < 10.0)
1558     {
1559         printf ("Warning: Couldn't read refresh rate.\n");
1560         refresh_rate = 60.0;
1561     }
1562 
1563     return refresh_rate;
1564 }
1565 
1566 int
get_auto_input_rate()1567 Snes9xWindow::get_auto_input_rate ()
1568 {
1569     double refresh_rate = get_refresh_rate ();
1570 
1571     if (refresh_rate == 0.0)
1572         return 0;
1573 
1574     // Try for a close multiple of 60hz
1575     if (refresh_rate > 119.0 && refresh_rate < 121.0)
1576         refresh_rate /= 2.0;
1577     if (refresh_rate > 179.0 && refresh_rate < 181.0)
1578         refresh_rate /= 3.0;
1579     if (refresh_rate > 239.0 && refresh_rate < 241.0)
1580         refresh_rate /= 4.0;
1581 
1582     double new_input_rate = refresh_rate * 32040.0 / 60.09881389744051 + 0.5;
1583 
1584     if (new_input_rate > 32040.0 * 1.05 || new_input_rate < 32040.0 * 0.95)
1585         new_input_rate = 0.0;
1586 
1587     return new_input_rate;
1588 }
1589 
1590 #ifdef GDK_WINDOWING_X11
set_bypass_compositor(Display * dpy,Window window,unsigned char bypass)1591 static void set_bypass_compositor (Display *dpy, Window window, unsigned char bypass)
1592 {
1593     uint32 value = bypass;
1594     Atom net_wm_bypass_compositor = XInternAtom (dpy, "_NET_WM_BYPASS_COMPOSITOR", False);
1595     XChangeProperty (dpy, window, net_wm_bypass_compositor, XA_CARDINAL, 32, PropModeReplace, (const unsigned char *) &value, 1);
1596 }
1597 #endif
1598 
1599 void
enter_fullscreen_mode()1600 Snes9xWindow::enter_fullscreen_mode ()
1601 {
1602     int rom_loaded = config->rom_loaded;
1603 
1604     if (config->fullscreen)
1605         return;
1606 
1607     config->rom_loaded = 0;
1608 
1609     nfs_width = config->window_width;
1610     nfs_height = config->window_height;
1611 
1612     gtk_window_get_position (GTK_WINDOW (window), &nfs_x, &nfs_y);
1613 
1614     if (config->change_display_resolution)
1615     {
1616         GdkDisplay *gdk_display = gtk_widget_get_display (window);
1617         Display *dpy = gdk_x11_display_get_xdisplay (gdk_display);
1618 
1619         gdk_display_sync (gdk_display);
1620         if (XRRSetCrtcConfig (dpy,
1621                               config->xrr_screen_resources,
1622                               config->xrr_screen_resources->crtcs[0],
1623                               CurrentTime,
1624                               config->xrr_crtc_info->x,
1625                               config->xrr_crtc_info->y,
1626                               config->xrr_screen_resources->modes[config->xrr_index].id,
1627                               config->xrr_crtc_info->rotation,
1628                               &config->xrr_crtc_info->outputs[0],
1629                               1) != 0)
1630         {
1631             config->change_display_resolution = 0;
1632         }
1633 
1634         if (gui_config->auto_input_rate)
1635         {
1636             Settings.SoundInputRate = top_level->get_auto_input_rate ();
1637             S9xUpdateDynamicRate (1, 2);
1638         }
1639     }
1640 
1641     /* Make sure everything is done synchronously */
1642     gdk_display_sync (gdk_display_get_default ());
1643     gtk_window_fullscreen (GTK_WINDOW (window));
1644 
1645     gdk_display_sync (gdk_display_get_default ());
1646     gtk_window_present (GTK_WINDOW (window));
1647 #ifdef GDK_WINDOWING_X11
1648     if (GDK_IS_X11_WINDOW (gtk_widget_get_window (GTK_WIDGET (window))) &&
1649         config->default_esc_behavior != ESC_TOGGLE_MENUBAR)
1650     {
1651         set_bypass_compositor (gdk_x11_display_get_xdisplay (gtk_widget_get_display (GTK_WIDGET (window))),
1652                                gdk_x11_window_get_xid (gtk_widget_get_window (GTK_WIDGET (window))),
1653                                1);
1654     }
1655 #endif
1656     config->fullscreen = 1;
1657     config->rom_loaded = rom_loaded;
1658 
1659     /* If we're running a game, disable ui when entering fullscreen */
1660     if (!Settings.Paused && config->rom_loaded)
1661         config->ui_visible = false;
1662 
1663     configure_widgets ();
1664 }
1665 
1666 void
leave_fullscreen_mode()1667 Snes9xWindow::leave_fullscreen_mode ()
1668 {
1669     int rom_loaded = config->rom_loaded;
1670 
1671     if (!config->fullscreen)
1672         return;
1673 
1674     config->rom_loaded = 0;
1675 
1676     if (config->change_display_resolution)
1677     {
1678         GdkDisplay *gdk_display = gtk_widget_get_display (window);
1679         Display *dpy = gdk_x11_display_get_xdisplay (gdk_display);
1680 
1681         if (config->xrr_index > config->xrr_screen_resources->nmode)
1682             config->xrr_index = 0;
1683 
1684         gdk_display_sync (gdk_display);
1685         XRRSetCrtcConfig (dpy,
1686                           config->xrr_screen_resources,
1687                           config->xrr_screen_resources->crtcs[0],
1688                           CurrentTime,
1689                           config->xrr_crtc_info->x,
1690                           config->xrr_crtc_info->y,
1691                           config->xrr_crtc_info->mode,
1692                           config->xrr_crtc_info->rotation,
1693                           &config->xrr_crtc_info->outputs[0],
1694                           1);
1695 
1696         if (gui_config->auto_input_rate)
1697         {
1698             Settings.SoundInputRate = top_level->get_auto_input_rate ();
1699             S9xUpdateDynamicRate (1, 2);
1700         }
1701     }
1702 
1703     gtk_window_unfullscreen (GTK_WINDOW (window));
1704 
1705 #ifdef GDK_WINDOWING_X11
1706     if (GDK_IS_X11_WINDOW (gtk_widget_get_window (GTK_WIDGET (window))))
1707     {
1708         set_bypass_compositor (gdk_x11_display_get_xdisplay (gtk_widget_get_display (GTK_WIDGET (window))),
1709                                gdk_x11_window_get_xid (gtk_widget_get_window (GTK_WIDGET (window))),
1710                                0);
1711     }
1712 #endif
1713 
1714     resize (nfs_width, nfs_height);
1715     gtk_window_move (GTK_WINDOW (window), nfs_x, nfs_y);
1716 
1717     config->rom_loaded = rom_loaded;
1718 
1719     config->fullscreen = 0;
1720 
1721     configure_widgets ();
1722 }
1723 
1724 void
resize_viewport(int width,int height)1725 Snes9xWindow::resize_viewport (int width, int height)
1726 {
1727     GtkWidget     *item;
1728     GtkAllocation allocation;
1729     int           y_padding = 0;
1730 
1731     item = get_widget ("menubar");
1732     gtk_widget_get_allocation (item, &allocation);
1733     y_padding += gtk_widget_get_visible (item) ? allocation.height : 0;
1734 
1735     resize (width, height + y_padding);
1736 }
1737 
1738 void
hide_mouse_cursor()1739 Snes9xWindow::hide_mouse_cursor ()
1740 {
1741     if (!empty_cursor)
1742     {
1743         empty_cursor = gdk_cursor_new_for_display (gdk_display_get_default (), GDK_BLANK_CURSOR);
1744     }
1745 
1746     gdk_window_set_cursor (gtk_widget_get_window (GTK_WIDGET (drawing_area)),
1747                            empty_cursor);
1748     config->pointer_is_visible = false;
1749 }
1750 
1751 void
show_mouse_cursor()1752 Snes9xWindow::show_mouse_cursor ()
1753 {
1754     gdk_window_set_cursor (gtk_widget_get_window (GTK_WIDGET (drawing_area)),
1755                            NULL);
1756     config->pointer_is_visible = true;
1757 }
1758 
1759 void
center_mouse()1760 Snes9xWindow::center_mouse ()
1761 {
1762     GdkWindow *gdk_window = gtk_widget_get_window (window);
1763     GdkDisplay *gdk_display = gdk_window_get_display (gdk_window);
1764     GdkScreen *gdk_screen = gdk_window_get_screen (gdk_window);
1765     int x, y, w, h;
1766 
1767     gdk_window_get_origin (gdk_window, &x, &y);
1768     w = gdk_window_get_width (gdk_window);
1769     h = gdk_window_get_height (gdk_window);
1770 
1771     gdk_mouse_x = x + w / 2;
1772     gdk_mouse_y = y + h / 2;
1773 
1774 #if GTK_MAJOR_VERSION < 3
1775     gdk_display_warp_pointer (gdk_display, gdk_screen, gdk_mouse_x,
1776                               gdk_mouse_y);
1777 #else
1778     GdkSeat *seat = gdk_display_get_default_seat (gdk_display);
1779     GdkDevice *pointer = gdk_seat_get_pointer (seat);
1780 
1781     gdk_device_warp (pointer, gdk_screen, gdk_mouse_x, gdk_mouse_y);
1782 #endif
1783 }
1784 
1785 void
toggle_grab_mouse()1786 Snes9xWindow::toggle_grab_mouse ()
1787 {
1788     GdkWindow *gdk_window = gtk_widget_get_window (window);
1789     GdkDisplay *gdk_display = gdk_window_get_display (gdk_window);
1790 
1791     if ((!mouse_grabbed && !S9xIsMousePluggedIn ()) || !config->rom_loaded)
1792         return;
1793 
1794 #if GTK_MAJOR_VERSION < 3
1795     if (!mouse_grabbed)
1796     {
1797         gdk_pointer_grab (gdk_window, true, (GdkEventMask) 1020, gdk_window, empty_cursor, GDK_CURRENT_TIME);
1798         center_mouse ();
1799     }
1800     else
1801     {
1802         gdk_pointer_ungrab (GDK_CURRENT_TIME);
1803         if (config->pointer_is_visible)
1804             show_mouse_cursor ();
1805     }
1806 #else
1807     GdkSeat *seat = gdk_display_get_default_seat (gdk_display);
1808 
1809     if (!mouse_grabbed)
1810         gdk_seat_grab (seat, gdk_window, GDK_SEAT_CAPABILITY_ALL_POINTING, true,
1811                        empty_cursor, NULL, NULL, NULL);
1812     else
1813         gdk_seat_ungrab (seat);
1814 #endif
1815 
1816     S9xReportPointer (BINDING_MOUSE_POINTER, 0, 0);
1817     snes_mouse_x = 0.0; snes_mouse_y = 0.0;
1818     mouse_grabbed = !mouse_grabbed;
1819     if (mouse_grabbed)
1820         center_mouse ();
1821 }
1822 
1823 void
show()1824 Snes9xWindow::show ()
1825 {
1826     gtk_widget_show (window);
1827 
1828     if (!recent_menu)
1829     {
1830         /* Add recent menu after showing window to avoid "No items" bug */
1831         recent_menu = gtk_recent_chooser_menu_new_for_manager (
1832                           gtk_recent_manager_get_default ());
1833 
1834         GtkRecentFilter *filter = gtk_recent_filter_new ();
1835         GtkRecentChooser *chooser = GTK_RECENT_CHOOSER (recent_menu);
1836 
1837         gtk_recent_filter_add_group (filter, "cartridge");
1838         gtk_recent_chooser_set_local_only (chooser, true);
1839         gtk_recent_chooser_set_show_icons (chooser, false);
1840         gtk_recent_chooser_set_sort_type (chooser, GTK_RECENT_SORT_MRU);
1841         gtk_recent_chooser_add_filter (chooser, filter);
1842 
1843         gtk_menu_item_set_submenu (
1844             GTK_MENU_ITEM (get_widget ("open_recent_item")),
1845             recent_menu);
1846 
1847         g_signal_connect (G_OBJECT (recent_menu),
1848                           "item-activated",
1849                           G_CALLBACK (event_recent_open),
1850                           (gpointer) this);
1851 
1852         gtk_widget_show (recent_menu);
1853     }
1854 }
1855 
1856 void
propagate_pause_state()1857 Snes9xWindow::propagate_pause_state ()
1858 {
1859     int oldpause = Settings.Paused;
1860 
1861     Settings.Paused = (sys_pause || user_pause || !(config->rom_loaded));
1862 
1863     if (Settings.Paused != oldpause)
1864     {
1865         if (!is_paused ())
1866         {
1867             S9xSoundStart ();
1868             if (config->rom_loaded)
1869                 enable_widget ("pause_item", true);
1870 
1871             S9xDisplayClearBuffers ();
1872         }
1873         else
1874         {
1875             S9xSoundStop ();
1876             enable_widget ("pause_item", false);
1877 
1878         }
1879 
1880         configure_widgets ();
1881     }
1882 }
1883 
1884 void
toggle_ui()1885 Snes9xWindow::toggle_ui ()
1886 {
1887     config->ui_visible = !config->ui_visible;
1888 
1889     configure_widgets ();
1890 }
1891 
1892 /* gui_[un]pause Handles when system needs to pause the emulator */
1893 void
pause_from_focus_change()1894 Snes9xWindow::pause_from_focus_change ()
1895 {
1896     sys_pause += config->modal_dialogs;
1897 
1898     propagate_pause_state ();
1899 }
1900 
1901 void
unpause_from_focus_change()1902 Snes9xWindow::unpause_from_focus_change ()
1903 {
1904     if (--sys_pause < 0)
1905         sys_pause = 0;
1906     propagate_pause_state ();
1907 }
1908 
1909 /* client_[un]pause Handles when user manually chooses to pause */
1910 void
pause_from_user()1911 Snes9xWindow::pause_from_user ()
1912 {
1913     user_pause = true;
1914     propagate_pause_state ();
1915 }
1916 
1917 void
unpause_from_user()1918 Snes9xWindow::unpause_from_user ()
1919 {
1920     user_pause = false;
1921     propagate_pause_state ();
1922 }
1923 
is_paused()1924 bool Snes9xWindow::is_paused()
1925 {
1926     if (user_pause || sys_pause || Settings.Paused || !(config->rom_loaded))
1927         return true;
1928 
1929     return false;
1930 }
1931 
1932 void
set_menu_item_accel_to_binding(const char * name,const char * binding)1933 Snes9xWindow::set_menu_item_accel_to_binding (const char *name,
1934                                               const char *binding)
1935 {
1936     Binding bin;
1937     char str[255];
1938     GtkAccelGroup *accel_group = NULL;
1939 
1940     if (!strcmp (binding, "Escape Key"))
1941     {
1942         bin = Binding (GDK_Escape, false, false, false);
1943     }
1944     else
1945     {
1946         bin = S9xGetBindingByName (binding);
1947     }
1948 
1949     snprintf (str, 255, "<Snes9x>/%s", name);
1950     if (!(bin.is_key ()))
1951     {
1952         gtk_accel_map_change_entry (str,
1953                                     0,
1954                                     (GdkModifierType) 0,
1955                                     true);
1956         return;
1957     }
1958 
1959     GSList *accel_group_list = gtk_accel_groups_from_object (G_OBJECT (window));
1960 
1961     if (accel_group_list)
1962     {
1963         accel_group = GTK_ACCEL_GROUP (accel_group_list->data);
1964     }
1965     else
1966     {
1967         accel_group = gtk_accel_group_new ();
1968         gtk_window_add_accel_group (GTK_WINDOW (window), accel_group);
1969     }
1970 
1971     gtk_widget_set_accel_path (get_widget (name), str, accel_group);
1972 
1973     if (!gtk_accel_map_lookup_entry (str, NULL))
1974     {
1975         gtk_accel_map_add_entry (str,
1976                                  bin.get_key (),
1977                                  bin.get_gdk_modifiers ());
1978 
1979     }
1980     else
1981     {
1982         gtk_accel_map_change_entry (str,
1983                                     bin.get_key (),
1984                                     bin.get_gdk_modifiers (),
1985                                     true);
1986     }
1987 }
1988 
1989 void
update_accels()1990 Snes9xWindow::update_accels ()
1991 {
1992     set_menu_item_accel_to_binding ("fullscreen_item", "GTK_fullscreen");
1993     set_menu_item_accel_to_binding ("reset_item", "SoftReset");
1994     set_menu_item_accel_to_binding ("save_state_0", "QuickSave000");
1995     set_menu_item_accel_to_binding ("save_state_1", "QuickSave001");
1996     set_menu_item_accel_to_binding ("save_state_2", "QuickSave002");
1997     set_menu_item_accel_to_binding ("save_state_3", "QuickSave003");
1998     set_menu_item_accel_to_binding ("save_state_4", "QuickSave004");
1999     set_menu_item_accel_to_binding ("save_state_5", "QuickSave005");
2000     set_menu_item_accel_to_binding ("save_state_6", "QuickSave006");
2001     set_menu_item_accel_to_binding ("save_state_7", "QuickSave007");
2002     set_menu_item_accel_to_binding ("save_state_8", "QuickSave008");
2003     set_menu_item_accel_to_binding ("save_state_9", "QuickSave009");
2004     set_menu_item_accel_to_binding ("load_state_0", "QuickLoad000");
2005     set_menu_item_accel_to_binding ("load_state_1", "QuickLoad001");
2006     set_menu_item_accel_to_binding ("load_state_2", "QuickLoad002");
2007     set_menu_item_accel_to_binding ("load_state_3", "QuickLoad003");
2008     set_menu_item_accel_to_binding ("load_state_4", "QuickLoad004");
2009     set_menu_item_accel_to_binding ("load_state_5", "QuickLoad005");
2010     set_menu_item_accel_to_binding ("load_state_6", "QuickLoad006");
2011     set_menu_item_accel_to_binding ("load_state_7", "QuickLoad007");
2012     set_menu_item_accel_to_binding ("load_state_8", "QuickLoad008");
2013     set_menu_item_accel_to_binding ("load_state_9", "QuickLoad009");
2014     set_menu_item_accel_to_binding ("pause_item", "GTK_pause");
2015     set_menu_item_accel_to_binding ("save_spc_item", "GTK_save_spc");
2016     set_menu_item_accel_to_binding ("open_rom_item", "GTK_open_rom");
2017     set_menu_item_accel_to_binding ("record_movie_item", "BeginRecordingMovie");
2018     set_menu_item_accel_to_binding ("open_movie_item", "LoadMovie");
2019     set_menu_item_accel_to_binding ("stop_recording_item", "EndRecordingMovie");
2020     set_menu_item_accel_to_binding ("jump_to_frame_item", "GTK_seek_to_frame");
2021     set_menu_item_accel_to_binding ("reset_item", "SoftReset");
2022     set_menu_item_accel_to_binding ("hard_reset_item", "Reset");
2023     set_menu_item_accel_to_binding ("exit_item", "GTK_quit");
2024 
2025     /* Special UI assignment */
2026     set_menu_item_accel_to_binding ("hide_ui", "Escape Key");
2027 }
2028 
2029 void
resize_to_multiple(int factor)2030 Snes9xWindow::resize_to_multiple (int factor)
2031 {
2032     int h = (config->overscan ? 239 : 224) * factor;
2033     int w = h * S9xGetAspect () + 0.5;
2034 
2035     resize_viewport (w, h);
2036 }
2037 
2038 cairo_t *
get_cairo()2039 Snes9xWindow::get_cairo ()
2040 {
2041     if (cr)
2042         return cr;
2043 
2044     GtkWidget *drawing_area = GTK_WIDGET (this->drawing_area);
2045 
2046 #if GTK_MAJOR_VERSION < 3
2047     cr = gdk_cairo_create (gtk_widget_get_window (drawing_area));
2048 #else
2049     GtkAllocation allocation;
2050     gtk_widget_get_allocation (drawing_area, &allocation);
2051 
2052     cairo_rectangle_int_t rect = { 0, 0, allocation.width, allocation.height };
2053     cairo_region = cairo_region_create_rectangle (&rect);
2054     gdk_drawing_context = gdk_window_begin_draw_frame (gtk_widget_get_window (drawing_area),
2055                                                        cairo_region);
2056     cr = gdk_drawing_context_get_cairo_context (gdk_drawing_context);
2057 #endif
2058 
2059     cairo_owned = true;
2060     return cr;
2061 }
2062 
2063 void
release_cairo()2064 Snes9xWindow::release_cairo ()
2065 {
2066     if (cairo_owned)
2067     {
2068 #if GTK_MAJOR_VERSION < 3
2069         cairo_destroy (cr);
2070 #else
2071         gdk_window_end_draw_frame (gtk_widget_get_window (GTK_WIDGET (drawing_area)), gdk_drawing_context);
2072         cairo_region_destroy (cairo_region);
2073 #endif
2074         cairo_owned = false;
2075         cr = NULL;
2076     }
2077 }
2078