#include "gdkasync.h"
#include "gdkeventsource.h"
#include "gdkdisplay-x11.h"
+#include "gdkframeclockidle.h"
#include "gdkprivate-x11.h"
#include <stdlib.h>
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);
}
}
+static void
+set_sync_counter(Display *display,
+ XSyncCounter counter,
+ gint64 value)
+{
+ XSyncValue sync_value;
+
+ XSyncIntsToValue(&sync_value,
+ value & G_GINT64_CONSTANT(0xFFFFFFFF),
+ value >> 32);
+ XSyncSetCounter(display, counter, sync_value);
+}
+
+static void
+window_pre_damage (GdkWindow *window)
+{
+ GdkWindow *toplevel_window = gdk_window_get_toplevel (window);
+ GdkWindowImplX11 *impl;
+
+ if (!toplevel_window || !WINDOW_IS_TOPLEVEL (toplevel_window))
+ return;
+
+ impl = GDK_WINDOW_IMPL_X11 (toplevel_window->impl);
+
+ if (impl->toplevel->in_frame &&
+ impl->toplevel->current_counter_value % 2 == 0)
+ {
+ impl->toplevel->current_counter_value += 1;
+ set_sync_counter(GDK_WINDOW_XDISPLAY (impl->wrapper),
+ impl->toplevel->extended_update_counter,
+ impl->toplevel->current_counter_value);
+ }
+}
+
+static void
+on_surface_changed (void *data)
+{
+ GdkWindow *window = data;
+
+ window_pre_damage (window);
+}
+
+/* We want to know when cairo drawing causes damage to the window,
+ * so we engage in the _NET_WM_FRAME_DRAWN protocol with the
+ * window only when there actually is drawing. To do that we use
+ * a technique (hack) suggested by Uli Schlachter - if we set
+ * a dummy "mime data" on the cairo surface (this facility is
+ * used to attach JPEG data to an imager), then cairo wil flush
+ * and remove the mime data before making any changes to the window.
+ */
+
+static void
+hook_surface_changed (GdkWindow *window)
+{
+ GdkWindowImplX11 *impl = GDK_WINDOW_IMPL_X11 (window->impl);
+
+ if (impl->cairo_surface)
+ cairo_surface_set_mime_data (impl->cairo_surface,
+ "x-gdk/change-notify",
+ (unsigned char *)"X",
+ 1,
+ on_surface_changed,
+ window);
+}
+
+static void
+unhook_surface_changed (GdkWindow *window)
+{
+ GdkWindowImplX11 *impl = GDK_WINDOW_IMPL_X11 (window->impl);
+
+ if (impl->cairo_surface)
+ cairo_surface_set_mime_data (impl->cairo_surface,
+ "x-gdk/change-notify",
+ NULL, 0,
+ NULL, NULL);
+}
+
+static void
+gdk_x11_window_begin_frame (GdkWindow *window)
+{
+ GdkWindowImplX11 *impl;
+
+ g_return_if_fail (GDK_IS_WINDOW (window));
+
+ impl = GDK_WINDOW_IMPL_X11 (window->impl);
+
+ if (!WINDOW_IS_TOPLEVEL (window) ||
+ impl->toplevel->extended_update_counter == None)
+ return;
+
+ impl->toplevel->in_frame = TRUE;
+
+ hook_surface_changed (window);
+}
+
+static void
+gdk_x11_window_end_frame (GdkWindow *window)
+{
+ GdkWindowImplX11 *impl;
+
+ g_return_if_fail (GDK_IS_WINDOW (window));
+
+ impl = GDK_WINDOW_IMPL_X11 (window->impl);
+
+ if (!WINDOW_IS_TOPLEVEL (window) ||
+ impl->toplevel->extended_update_counter == None ||
+ !impl->toplevel->in_frame)
+ return;
+
+ impl->toplevel->in_frame = FALSE;
+
+ if (impl->toplevel->current_counter_value % 2 == 1)
+ {
+ impl->toplevel->current_counter_value += 1;
+ set_sync_counter(GDK_WINDOW_XDISPLAY (impl->wrapper),
+ impl->toplevel->extended_update_counter,
+ impl->toplevel->current_counter_value);
+
+ if (gdk_x11_screen_supports_net_wm_hint (gdk_window_get_screen (window),
+ gdk_atom_intern_static_string ("_NET_WM_FRAME_DRAWN")))
+ {
+ impl->toplevel->frame_pending = TRUE;
+ gdk_frame_clock_freeze (gdk_window_get_frame_clock (window));
+ }
+ }
+
+ unhook_surface_changed (window);
+}
+
/*****************************************************
* X11 specific implementations of generic functions *
*****************************************************/
if (impl->cairo_surface)
cairo_surface_set_user_data (impl->cairo_surface, &gdk_x11_cairo_key,
impl, gdk_x11_cairo_surface_destroy);
+
+ if (WINDOW_IS_TOPLEVEL (window) && impl->toplevel->in_frame)
+ hook_surface_changed (window);
}
else
cairo_surface_reference (impl->cairo_surface);
wrapper = impl->wrapper;
+ if (WINDOW_IS_TOPLEVEL (wrapper) && impl->toplevel->in_frame)
+ unhook_surface_changed (wrapper);
+
_gdk_x11_window_grab_check_destroy (wrapper);
if (!GDK_WINDOW_DESTROYED (wrapper))
{
GdkDisplay *display = GDK_WINDOW_DISPLAY (window);
GdkToplevelX11 *toplevel = _gdk_x11_window_get_toplevel (window);
- GdkWindowImplX11 *impl = GDK_WINDOW_IMPL_X11 (window->impl);
- if (toplevel && impl->use_synchronized_configure &&
+ if (toplevel &&
toplevel->update_counter == None &&
GDK_X11_DISPLAY (display)->use_sync)
{
Display *xdisplay = GDK_DISPLAY_XDISPLAY (display);
XSyncValue value;
Atom atom;
+ XID counters[2];
XSyncIntToValue (&value, 0);
toplevel->update_counter = XSyncCreateCounter (xdisplay, value);
+ toplevel->extended_update_counter = XSyncCreateCounter (xdisplay, value);
atom = gdk_x11_get_xatom_by_name_for_display (display,
"_NET_WM_SYNC_REQUEST_COUNTER");
-
+
+ counters[0] = toplevel->update_counter;
+ counters[1] = toplevel->extended_update_counter;
XChangeProperty (xdisplay, GDK_WINDOW_XID (window),
atom, XA_CARDINAL,
32, PropModeReplace,
- (guchar *)&toplevel->update_counter, 1);
+ (guchar *)counters, 2);
- XSyncIntToValue (&toplevel->current_counter_value, 0);
+ toplevel->current_counter_value = 0;
}
}
#endif
gdk_x11_window_set_user_time (window, GDK_X11_DISPLAY (x11_screen->display)->user_time);
ensure_sync_counter (window);
+
+ /* Start off in a frozen state - we'll finish this when we first paint */
+ gdk_x11_window_begin_frame (window);
+}
+
+static void
+on_frame_clock_before_paint (GdkFrameClock *clock,
+ GdkWindow *window)
+{
+ gdk_x11_window_begin_frame (window);
+}
+
+static void
+on_frame_clock_after_paint (GdkFrameClock *clock,
+ GdkWindow *window)
+{
+ gdk_x11_window_end_frame (window);
}
void
GdkWindowImplX11 *impl;
GdkX11Screen *x11_screen;
GdkX11Display *display_x11;
+ GdkFrameClock *clock;
Window xparent;
Visual *xvisual;
gdk_x11_event_source_select_events ((GdkEventSource *) display_x11->event_source,
GDK_WINDOW_XID (window), event_mask,
StructureNotifyMask | PropertyChangeMask);
+
+ clock = g_object_new (GDK_TYPE_FRAME_CLOCK_IDLE, NULL);
+ gdk_window_set_frame_clock (window, clock);
+ g_signal_connect (clock, "before-paint",
+ G_CALLBACK (on_frame_clock_before_paint), window);
+ g_signal_connect (clock, "after-paint",
+ G_CALLBACK (on_frame_clock_after_paint), window);
+
+ if (GDK_WINDOW_TYPE (window) != GDK_WINDOW_CHILD)
+ gdk_window_freeze_toplevel_updates_libgtk_only (window);
}
static GdkEventMask
toplevel->update_counter);
toplevel->update_counter = None;
- XSyncIntToValue (&toplevel->current_counter_value, 0);
+ toplevel->current_counter_value = 0;
}
#endif
}
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
if (GDK_WINDOW_TYPE (window) == GDK_WINDOW_CHILD)
{
+ /* The window isn't actually damaged, but it's parent is */
+ window_pre_damage (window);
_gdk_x11_window_move_resize_child (window,
x, y,
window->width, window->height);
if (height < 1)
height = 1;
+ window_pre_damage (window);
+
if (GDK_WINDOW_TYPE (window) == GDK_WINDOW_CHILD)
{
_gdk_x11_window_move_resize_child (window,
if (height < 1)
height = 1;
+ window_pre_damage (window);
+
if (GDK_WINDOW_TYPE (window) == GDK_WINDOW_CHILD)
{
_gdk_x11_window_move_resize_child (window, x, y, width, height);
}
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,
if (toplevel && toplevel->update_counter != None &&
GDK_X11_DISPLAY (display)->use_sync &&
- !XSyncValueIsZero (toplevel->current_counter_value))
+ toplevel->configure_counter_value != 0)
{
- XSyncSetCounter (GDK_WINDOW_XDISPLAY (window),
- toplevel->update_counter,
- toplevel->current_counter_value);
-
- XSyncIntToValue (&toplevel->current_counter_value, 0);
+ set_sync_counter (GDK_WINDOW_XDISPLAY (window),
+ toplevel->update_counter,
+ toplevel->configure_counter_value);
+
+ toplevel->current_counter_value = toplevel->configure_counter_value;
+ if ((toplevel->current_counter_value % 2) == 1)
+ toplevel->current_counter_value += 1;
+
+ toplevel->configure_counter_value = 0;
+
+ set_sync_counter (GDK_WINDOW_XDISPLAY (window),
+ toplevel->extended_update_counter,
+ toplevel->current_counter_value);
}
}
#endif
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;