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