[gtk+] x11: implement gdk_window_apply_fullscreen_mode()
- From: Olivier Fourdan <ofourdan src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gtk+] x11: implement gdk_window_apply_fullscreen_mode()
- Date: Fri, 25 Jan 2013 12:17:33 +0000 (UTC)
commit dac6a76ef2282c57aa5deb72481b9e09e603a0b6
Author: Olivier Fourdan <ofourdan redhat com>
Date: Mon Jan 21 11:52:32 2013 +0100
x11: implement gdk_window_apply_fullscreen_mode()
for the X11 backend using the EWMH mechanism
_NET_WM_FULLSCREEN_MONITORS.
https://bugzilla.gnome.org/show_bug.cgi?id=691856
gdk/x11/gdkscreen-x11.c | 167 +++++++++++++++++++++++++++++++++++++++++++++++
gdk/x11/gdkscreen-x11.h | 10 +++
gdk/x11/gdkwindow-x11.c | 116 +++++++++++++++++++++++++++++++-
3 files changed, 289 insertions(+), 4 deletions(-)
---
diff --git a/gdk/x11/gdkscreen-x11.c b/gdk/x11/gdkscreen-x11.c
index b5767b5..65576f7 100644
--- a/gdk/x11/gdkscreen-x11.c
+++ b/gdk/x11/gdkscreen-x11.c
@@ -779,12 +779,179 @@ init_xfree_xinerama (GdkScreen *screen)
return FALSE;
}
+static gboolean
+init_solaris_xinerama_indices (GdkX11Screen *x11_screen)
+{
+#ifdef HAVE_SOLARIS_XINERAMA
+ XRectangle x_monitors[MAXFRAMEBUFFERS];
+ unsigned char hints[16];
+ gint result;
+ gint monitor_num;
+ gint x_n_monitors;
+ gint i;
+
+ if (!XineramaGetState (x11_screen->xdisplay, x11_screen->screen_num))
+ return FALSE;
+
+ result = XineramaGetInfo (x11_screen->xdisplay, x11_screen->screen_num,
+ x_monitors, hints, &x_n_monitors);
+
+ if (result == 0)
+ return FALSE;
+
+
+ for (monitor_num = 0; monitor_num < x11_screen->n_monitors; ++monitor_num)
+ {
+ for (i = 0; i < x_n_monitors; ++i)
+ {
+ if (x11_screen->monitors[monitor_num].geometry.x == x_monitors[i].x &&
+ x11_screen->monitors[monitor_num].geometry.y == x_monitors[i].y &&
+ x11_screen->monitors[monitor_num].geometry.width == x_monitors[i].width &&
+ x11_screen->monitors[monitor_num].geometry.height == x_monitors[i].height)
+ {
+ g_hash_table_insert (x11_screen->xinerama_matches,
+ GINT_TO_POINTER (monitor_num),
+ GINT_TO_POINTER (i));
+ }
+ }
+ }
+ return TRUE;
+#endif /* HAVE_SOLARIS_XINERAMA */
+
+ return FALSE;
+}
+
+static gboolean
+init_xfree_xinerama_indices (GdkX11Screen *x11_screen)
+{
+#ifdef HAVE_XFREE_XINERAMA
+ XineramaScreenInfo *x_monitors;
+ gint monitor_num;
+ gint x_n_monitors;
+ gint i;
+
+ if (!XineramaIsActive (x11_screen->xdisplay))
+ return FALSE;
+
+ x_monitors = XineramaQueryScreens (x11_screen->xdisplay, &x_n_monitors);
+ if (x_n_monitors <= 0 || x_monitors == NULL)
+ {
+ if (x_monitors)
+ XFree (x_monitors);
+
+ return FALSE;
+ }
+
+ for (monitor_num = 0; monitor_num < x11_screen->n_monitors; ++monitor_num)
+ {
+ for (i = 0; i < x_n_monitors; ++i)
+ {
+ if (x11_screen->monitors[monitor_num].geometry.x == x_monitors[i].x_org &&
+ x11_screen->monitors[monitor_num].geometry.y == x_monitors[i].y_org &&
+ x11_screen->monitors[monitor_num].geometry.width == x_monitors[i].width &&
+ x11_screen->monitors[monitor_num].geometry.height == x_monitors[i].height)
+ {
+ g_hash_table_insert (x11_screen->xinerama_matches,
+ GINT_TO_POINTER (monitor_num),
+ GINT_TO_POINTER (i));
+ }
+ }
+ }
+ XFree (x_monitors);
+ return TRUE;
+#endif /* HAVE_XFREE_XINERAMA */
+
+ return FALSE;
+}
+
+static void
+init_xinerama_indices (GdkX11Screen *x11_screen)
+{
+ int opcode, firstevent, firsterror;
+
+ x11_screen->xinerama_matches = g_hash_table_new (g_direct_hash, g_direct_equal);
+ if (XQueryExtension (x11_screen->xdisplay, "XINERAMA",
+ &opcode, &firstevent, &firsterror))
+ {
+ x11_screen->xinerama_matches = g_hash_table_new (g_direct_hash, g_direct_equal);
+
+ /* Solaris Xinerama first, then XFree/Xorg Xinerama
+ * to match the order in init_multihead()
+ */
+ if (init_solaris_xinerama_indices (x11_screen) == FALSE)
+ init_xfree_xinerama_indices (x11_screen);
+ }
+}
+
+gint
+_gdk_x11_screen_get_xinerama_index (GdkScreen *screen,
+ gint monitor_num)
+{
+ GdkX11Screen *x11_screen = GDK_X11_SCREEN (screen);
+ gpointer val;
+
+ g_return_val_if_fail (monitor_num < x11_screen->n_monitors, -1);
+
+ if (x11_screen->xinerama_matches == NULL)
+ init_xinerama_indices (x11_screen);
+
+ if (g_hash_table_lookup_extended (x11_screen->xinerama_matches, GINT_TO_POINTER (monitor_num), NULL, &val))
+ return (GPOINTER_TO_INT(val));
+
+ return -1;
+}
+
+void
+_gdk_x11_screen_get_edge_monitors (GdkScreen *screen,
+ gint *top,
+ gint *bottom,
+ gint *left,
+ gint *right)
+{
+ GdkX11Screen *x11_screen = GDK_X11_SCREEN (screen);
+ gint top_most_pos = HeightOfScreen (GDK_X11_SCREEN (screen)->xscreen);
+ gint left_most_pos = WidthOfScreen (GDK_X11_SCREEN (screen)->xscreen);
+ gint bottom_most_pos = 0;
+ gint right_most_pos = 0;
+ gint monitor_num;
+
+ for (monitor_num = 0; monitor_num < x11_screen->n_monitors; monitor_num++)
+ {
+ gint monitor_x = x11_screen->monitors[monitor_num].geometry.x;
+ gint monitor_y = x11_screen->monitors[monitor_num].geometry.y;
+ gint monitor_max_x = monitor_x + x11_screen->monitors[monitor_num].geometry.width;
+ gint monitor_max_y = monitor_y + x11_screen->monitors[monitor_num].geometry.height;
+
+ if (left && left_most_pos > monitor_x)
+ {
+ left_most_pos = monitor_x;
+ *left = monitor_num;
+ }
+ if (right && right_most_pos < monitor_max_x)
+ {
+ right_most_pos = monitor_max_x;
+ *right = monitor_num;
+ }
+ if (top && top_most_pos > monitor_y)
+ {
+ top_most_pos = monitor_y;
+ *top = monitor_num;
+ }
+ if (bottom && bottom_most_pos < monitor_max_y)
+ {
+ bottom_most_pos = monitor_max_y;
+ *bottom = monitor_num;
+ }
+ }
+}
+
static void
deinit_multihead (GdkScreen *screen)
{
GdkX11Screen *x11_screen = GDK_X11_SCREEN (screen);
free_monitors (x11_screen->monitors, x11_screen->n_monitors);
+ g_clear_pointer (&x11_screen->xinerama_matches, g_hash_table_destroy);
x11_screen->n_monitors = 0;
x11_screen->monitors = NULL;
diff --git a/gdk/x11/gdkscreen-x11.h b/gdk/x11/gdkscreen-x11.h
index d807a17..f4a15b8 100644
--- a/gdk/x11/gdkscreen-x11.h
+++ b/gdk/x11/gdkscreen-x11.h
@@ -91,6 +91,9 @@ struct _GdkX11Screen
/* cache for window->translate vfunc */
GC subwindow_gcs[32];
+
+ /* cache for Xinerama monitor indices */
+ GHashTable *xinerama_matches;
};
struct _GdkX11ScreenClass
@@ -110,6 +113,13 @@ void _gdk_x11_screen_size_changed (GdkScreen *screen,
XEvent *event);
void _gdk_x11_screen_process_owner_change (GdkScreen *screen,
XEvent *event);
+gint _gdk_x11_screen_get_xinerama_index (GdkScreen *screen,
+ gint monitor_num);
+void _gdk_x11_screen_get_edge_monitors (GdkScreen *screen,
+ gint *top,
+ gint *bottom,
+ gint *left,
+ gint *right);
G_END_DECLS
diff --git a/gdk/x11/gdkwindow-x11.c b/gdk/x11/gdkwindow-x11.c
index 70a6ba0..e86cf7d 100644
--- a/gdk/x11/gdkwindow-x11.c
+++ b/gdk/x11/gdkwindow-x11.c
@@ -97,6 +97,7 @@ const int _gdk_x11_event_mask_table[21] =
const gint _gdk_x11_event_mask_table_size = G_N_ELEMENTS (_gdk_x11_event_mask_table);
/* Forward declarations */
+static void gdk_x11_window_apply_fullscreen_mode (GdkWindow *window);
static void gdk_window_set_static_win_gravity (GdkWindow *window,
gboolean on);
static gboolean gdk_window_icon_name_set (GdkWindow *window);
@@ -1354,6 +1355,13 @@ gdk_window_x11_show (GdkWindow *window, gboolean already_mapped)
if (unset_bg)
_gdk_x11_window_tmp_reset_bg (window, TRUE);
+
+ /* Fullscreen on current monitor is the default, no need to apply this mode
+ * when mapping a window. This also ensures that the default behavior remains
+ * consistent with pre-fullscreen mode implementation.
+ */
+ if (window->fullscreen_mode != GDK_FULLSCREEN_ON_CURRENT_MONITOR)
+ gdk_x11_window_apply_fullscreen_mode (window);
}
static void
@@ -3626,17 +3634,116 @@ gdk_x11_window_unmaximize (GdkWindow *window)
}
static void
-gdk_x11_window_fullscreen (GdkWindow *window)
+gdk_x11_window_apply_fullscreen_mode (GdkWindow *window)
{
if (GDK_WINDOW_DESTROYED (window) ||
!WINDOW_IS_TOPLEVEL_OR_FOREIGN (window))
return;
+ /* _NET_WM_FULLSCREEN_MONITORS gives an indication to the window manager as
+ * to which monitors so span across when the window is fullscreen, but it's
+ * not a state in itself so this would have no effect if the window is not
+ * mapped.
+ */
+
if (GDK_WINDOW_IS_MAPPED (window))
- gdk_wmspec_change_state (TRUE, window,
- gdk_atom_intern_static_string ("_NET_WM_STATE_FULLSCREEN"),
- GDK_NONE);
+ {
+ XClientMessageEvent xclient;
+ gint gdk_monitors[4];
+ gint i;
+
+ memset (&xclient, 0, sizeof (xclient));
+ xclient.type = ClientMessage;
+ xclient.window = GDK_WINDOW_XID (window);
+ xclient.display = GDK_WINDOW_XDISPLAY (window);
+ xclient.format = 32;
+
+ switch (window->fullscreen_mode)
+ {
+ case GDK_FULLSCREEN_ON_CURRENT_MONITOR:
+
+ /* FIXME: This is not part of the EWMH spec!
+ *
+ * There is no documented mechanism to remove the property
+ * _NET_WM_FULLSCREEN_MONITORS once set, so we use use a set of
+ * invalid, largest possible value.
+ *
+ * When given values larger than actual possible monitor values, most
+ * window managers who support the _NET_WM_FULLSCREEN_MONITORS spec
+ * will simply unset _NET_WM_FULLSCREEN_MONITORS and revert to their
+ * default behavior.
+ *
+ * Successfully tested on mutter/metacity, kwin, compiz and xfwm4.
+ *
+ * Note, this (non documented) mechanism is unlikely to be an issue
+ * as it's used only for transitionning back from "all monitors" to
+ * "current monitor" mode.
+ *
+ * Applications who don't change the default mode won't trigger this
+ * mechanism.
+ */
+ for (i = 0; i < 4; ++i)
+ xclient.data.l[i] = G_MAXLONG;
+
+ break;
+
+ case GDK_FULLSCREEN_ON_ALL_MONITORS:
+
+ _gdk_x11_screen_get_edge_monitors (GDK_WINDOW_SCREEN (window),
+ &gdk_monitors[0],
+ &gdk_monitors[1],
+ &gdk_monitors[2],
+ &gdk_monitors[3]);
+ /* Translate all 4 monitors from the GDK set into XINERAMA indices */
+ for (i = 0; i < 4; ++i)
+ {
+ xclient.data.l[i] = _gdk_x11_screen_get_xinerama_index (GDK_WINDOW_SCREEN (window),
+ gdk_monitors[i]);
+ /* Sanity check, if XINERAMA is not available, we could have invalid
+ * negative values for the XINERAMA indices.
+ */
+ if (xclient.data.l[i] < 0)
+ {
+ g_warning ("gdk_x11_window_apply_fullscreen_mode: Invalid XINERAMA monitor index");
+ return;
+ }
+ }
+ break;
+
+ default:
+ g_warning ("gdk_x11_window_apply_fullscreen_mode: Unhandled fullscreen mode %d",
+ window->fullscreen_mode);
+ return;
+ }
+
+ /* Send fullscreen monitors client message */
+ xclient.data.l[4] = 1; /* source indication */
+ xclient.message_type = gdk_x11_get_xatom_by_name_for_display (GDK_WINDOW_DISPLAY (window),
+ "_NET_WM_FULLSCREEN_MONITORS");
+ XSendEvent (GDK_WINDOW_XDISPLAY (window), GDK_WINDOW_XROOTWIN (window), False,
+ SubstructureRedirectMask | SubstructureNotifyMask,
+ (XEvent *)&xclient);
+ }
+}
+
+static void
+gdk_x11_window_fullscreen (GdkWindow *window)
+{
+ if (GDK_WINDOW_DESTROYED (window) ||
+ !WINDOW_IS_TOPLEVEL_OR_FOREIGN (window))
+ return;
+ if (GDK_WINDOW_IS_MAPPED (window))
+ {
+ gdk_wmspec_change_state (TRUE, window,
+ gdk_atom_intern_static_string ("_NET_WM_STATE_FULLSCREEN"),
+ GDK_NONE);
+ /* Actual XRandR layout may have change since we computed the fullscreen
+ * monitors in GDK_FULLSCREEN_ON_ALL_MONITORS mode.
+ */
+ if (window->fullscreen_mode == GDK_FULLSCREEN_ON_ALL_MONITORS)
+ gdk_x11_window_apply_fullscreen_mode (window);
+ }
else
gdk_synthesize_window_state (window,
0,
@@ -5022,6 +5129,7 @@ gdk_window_impl_x11_class_init (GdkWindowImplX11Class *klass)
impl_class->maximize = gdk_x11_window_maximize;
impl_class->unmaximize = gdk_x11_window_unmaximize;
impl_class->fullscreen = gdk_x11_window_fullscreen;
+ impl_class->apply_fullscreen_mode = gdk_x11_window_apply_fullscreen_mode;
impl_class->unfullscreen = gdk_x11_window_unfullscreen;
impl_class->set_keep_above = gdk_x11_window_set_keep_above;
impl_class->set_keep_below = gdk_x11_window_set_keep_below;
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]