diff --git a/gtk/src/gtk_control.cpp b/gtk/src/gtk_control.cpp
index 2d4e3f47..f79be2ad 100644
--- a/gtk/src/gtk_control.cpp
+++ b/gtk/src/gtk_control.cpp
@@ -99,6 +99,7 @@ const BindingLink b_links[] =
{ "b_seek_to_frame", "GTK_seek_to_frame" },
{ "b_swap_controllers", "GTK_swap_controllers" },
{ "b_rewind", "GTK_rewind" },
+ { "b_grab_mouse", "GTK_grab_mouse" },
{ NULL, NULL }
};
@@ -112,7 +113,7 @@ const int b_breaks[] =
43, /* End of Graphic options */
69, /* End of save/load states */
78, /* End of sound buttons */
- 85, /* End of miscellaneous buttons */
+ 86, /* End of miscellaneous buttons */
-1
};
@@ -289,6 +290,11 @@ void S9xHandlePortCommand (s9xcommand_t cmd, int16 data1, int16 data2)
{
change_slot (-1);
}
+
+ else if (cmd.port[0] == PORT_GRABMOUSE)
+ {
+ top_level->toggle_grab_mouse ();
+ }
}
}
@@ -437,6 +443,11 @@ s9xcommand_t S9xGetPortCommandT (const char *name)
cmd.port[0] = PORT_DECREMENTSLOT;
}
+ else if (strstr (name, "GTK_grab_mouse"))
+ {
+ cmd.port[0] = PORT_GRABMOUSE;
+ }
+
else
{
diff --git a/gtk/src/gtk_control.h b/gtk/src/gtk_control.h
index 1130645c..31a0e34c 100644
--- a/gtk/src/gtk_control.h
+++ b/gtk/src/gtk_control.h
@@ -49,6 +49,7 @@ enum
PORT_DECREMENTLOADSLOT = 22,
PORT_INCREMENTSLOT = 23,
PORT_DECREMENTSLOT = 24,
+ PORT_GRABMOUSE = 25
};
typedef struct BindingLink
@@ -61,7 +62,7 @@ typedef struct BindingLink
extern const BindingLink b_links[];
extern const int b_breaks[];
const int NUM_JOYPAD_LINKS = 24;
-const int NUM_EMU_LINKS = 61;
+const int NUM_EMU_LINKS = 62;
typedef struct JoypadBinding
{
diff --git a/gtk/src/gtk_s9xwindow.cpp b/gtk/src/gtk_s9xwindow.cpp
index 9b9d01e5..def31d5f 100644
--- a/gtk/src/gtk_s9xwindow.cpp
+++ b/gtk/src/gtk_s9xwindow.cpp
@@ -232,6 +232,19 @@ event_motion_notify (GtkWidget *widget,
return FALSE;
}
+ if (window->mouse_grabbed)
+ {
+ if (event->x_root == window->mouse_reported_x &&
+ event->y_root == window->mouse_reported_y)
+ return FALSE;
+
+ window->mouse_loc_x += (event->x_root - window->mouse_reported_x);
+ window->mouse_loc_y += (event->y_root - window->mouse_reported_y);
+ window->center_mouse ();
+
+ return FALSE;
+ }
+
#if GTK_CHECK_VERSION(3,10,0)
int scale_factor = gdk_window_get_scale_factor (gtk_widget_get_window (GTK_WIDGET (window->get_window ())));
#else
@@ -594,6 +607,7 @@ Snes9xWindow::Snes9xWindow (Snes9xConfig *config) :
paused_from_focus_loss = 0;
cr = NULL;
cairo_owned = 0;
+ mouse_grabbed = 0;
if (gtk_icon_theme_has_icon (gtk_icon_theme_get_default (), "snes9x"))
{
@@ -1804,6 +1818,84 @@ Snes9xWindow::show_mouse_cursor ()
config->pointer_is_visible = TRUE;
}
+void
+Snes9xWindow::center_mouse ()
+{
+ GdkWindow *gdk_window = gtk_widget_get_window (window);
+ GdkDisplay *gdk_display = gdk_window_get_display (gdk_window);
+ GdkScreen *gdk_screen = gdk_window_get_screen (gdk_window);
+ int x, y, w, h;
+
+ gdk_window_get_origin (gdk_window, &x, &y);
+ w = gdk_window_get_width (gdk_window);
+ h = gdk_window_get_height (gdk_window);
+
+ mouse_reported_x = x + w / 2;
+ mouse_reported_y = y + h / 2;
+
+#if GTK_MAJOR_VERSION < 3
+ gdk_display_warp_pointer (gdk_display, gdk_screen, mouse_reported_x,
+ mouse_reported_y);
+#elif GTK_MINOR_VERSION < 20
+ GdkDeviceManager *manager = gdk_display_get_device_manager (gdk_display);
+ GdkDevice *pointer = gdk_device_manager_get_client_pointer (manager);
+
+ gdk_device_warp (pointer, gdk_screen, mouse_reported_x, mouse_reported_y);
+#else
+ GdkSeat *seat = gdk_display_get_default_seat (gdk_display);
+ GdkDevice *pointer = gdk_seat_get_pointer (seat);
+
+ gdk_device_warp (pointer, gdk_screen, mouse_reported_x, mouse_reported_y);
+#endif
+}
+
+void
+Snes9xWindow::toggle_grab_mouse ()
+{
+ GdkWindow *gdk_window = gtk_widget_get_window (window);
+ GdkDisplay *gdk_display = gdk_window_get_display (gdk_window);
+
+ if ((!mouse_grabbed && !S9xIsMousePluggedIn ()) || !config->rom_loaded)
+ return;
+
+#if GTK_MAJOR_VERSION < 3
+ if (!mouse_grabbed)
+ {
+ gdk_pointer_grab (gdk_window, TRUE, (GdkEventMask) 1020, gdk_window, empty_cursor, GDK_CURRENT_TIME);
+ center_mouse ();
+ }
+ else
+ {
+ gdk_pointer_ungrab (GDK_CURRENT_TIME);
+ if (config->pointer_is_visible)
+ show_mouse_cursor ();
+ }
+#elif GTK_MINOR_VERSION < 20
+ GdkDeviceManager *manager = gdk_display_get_device_manager (gdk_display);
+ GdkDevice *pointer = gdk_device_manager_get_client_pointer (manager);
+
+ if (!mouse_grabbed)
+ gdk_device_grab (pointer, gdk_window, GDK_OWNERSHIP_NONE, TRUE,
+ (GdkEventMask) 1020, empty_cursor, GDK_CURRENT_TIME);
+ else
+ gdk_device_ungrab (pointer, GDK_CURRENT_TIME);
+#else
+ GdkSeat *seat = gdk_display_get_default_seat (gdk_display);
+
+ if (!mouse_grabbed)
+ gdk_seat_grab (seat, gdk_window, GDK_SEAT_CAPABILITY_ALL_POINTING, TRUE,
+ empty_cursor, NULL, NULL, NULL);
+ else
+ gdk_seat_ungrab (seat);
+#endif
+
+ S9xReportPointer (BINDING_MOUSE_POINTER, 0, 0);
+ mouse_loc_x = 0; mouse_loc_y = 0;
+ mouse_grabbed ^= 1;
+ if (mouse_grabbed)
+ center_mouse ();
+}
+
void
Snes9xWindow::show ()
{
diff --git a/gtk/src/gtk_s9xwindow.h b/gtk/src/gtk_s9xwindow.h
index ea9c2888..742882a7 100644
--- a/gtk/src/gtk_s9xwindow.h
+++ b/gtk/src/gtk_s9xwindow.h
@@ -30,6 +30,8 @@ class Snes9xWindow : public GtkBuilderWindow
/* Cursor modifying functions */
void show_mouse_cursor ();
void hide_mouse_cursor ();
+ void toggle_grab_mouse ();
+ void center_mouse ();
/* Rom-related functions */
void open_rom_dialog ();
@@ -75,6 +77,8 @@ class Snes9xWindow : public GtkBuilderWindow
int focused;
int paused_from_focus_loss;
uint16 mouse_loc_x, mouse_loc_y;
+ uint16 mouse_reported_x, mouse_reported_y;
+ int mouse_grabbed;
GdkPixbuf *icon, *splash;
GdkCursor *default_cursor, *empty_cursor;
GtkDrawingArea *drawing_area;
diff --git a/gtk/src/snes9x.ui b/gtk/src/snes9x.ui
index 6cff9698..c6ae0daa 100644
--- a/gtk/src/snes9x.ui
+++ b/gtk/src/snes9x.ui
@@ -8709,7 +8709,7 @@
False
GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
10
- 7
+ 8
2
10
5
@@ -8936,6 +8936,37 @@
GTK_FILL
+
+
+
+ 7
+ 8
+ GTK_FILL
+
+
+
+
+
+
+ 1
+ 2
+ 7
+ 8
+ GTK_FILL
+
+
4