* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
/*
#include "gdkdeviceprivate.h"
#include "gdkvisualprivate.h"
#include "gdkmarshalers.h"
+#include "gdkframeclockidle.h"
#include "gdkwindowimpl.h"
#include <math.h>
* <firstterm>composited</firstterm> window it is the responsibility of the
* application to render the window contents at the right spot.
* </para>
- * <example id="composited-window-example">
- * <title>Composited windows</title>
- * <programlisting>
- * <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" parse="text" href="../../../../examples/gdk/composited-window-example.c"><xi:fallback>FIXME: MISSING XINCLUDE CONTENT</xi:fallback></xi:include>
- * </programlisting></example>
- * <para>
- * In the example <xref linkend="composited-window-example"/>, a button is
- * placed inside of an event box inside of a window. The event box is set as
- * composited and therefore is no longer automatically drawn to the screen.
- *
- * When the contents of the event box change, an expose event is generated on
- * its parent window (which, in this case, belongs to the toplevel #GtkWindow).
- * The expose handler for this widget is responsible for merging the changes
- * back on the screen in the way that it wishes.
- *
- * In our case, we merge the contents with a 50% transparency. We also set the
- * background colour of the window to red. The effect is that the background
- * shows through the button.
- * </para>
* </refsect2>
* <refsect2 id="OFFSCREEN-WINDOWS">
* <title>Offscreen Windows</title>
* be it a toplevel window or a child window. In this setup the
* GdkWindow (and other GdkDrawables) were platform independent classes,
* and the actual platform specific implementation was in a delegate
- * object availible as "impl" in the window object.
+ * object available as "impl" in the window object.
*
* With the addition of client side windows and offscreen windows this
* changes a bit. The application-visible GdkWindow object behaves as
enum {
PROP_0,
- PROP_CURSOR
+ PROP_CURSOR,
+ PROP_FRAME_CLOCK
};
typedef enum {
cairo_region_t *region;
cairo_surface_t *surface;
cairo_region_t *flushed;
+ guint8 alpha;
guint uses_implicit : 1;
};
gboolean invalidate_children,
ClearBg clear_bg);
static void _gdk_window_propagate_has_alpha_background (GdkWindow *window);
+static cairo_surface_t *gdk_window_ref_impl_surface (GdkWindow *window);
+
+static void gdk_window_process_all_updates_internal (gboolean default_clock_only);
+
+static void gdk_ensure_default_frame_clock (void);
static guint signals[LAST_SIGNAL] = { 0 };
G_DEFINE_ABSTRACT_TYPE (GdkWindow, gdk_window, G_TYPE_OBJECT)
+#ifdef DEBUG_WINDOW_PRINTING
+char *
+print_region (cairo_region_t *region)
+{
+ GString *s = g_string_new ("{");
+ if (cairo_region_is_empty (region))
+ {
+ g_string_append (s, "empty");
+ }
+ else
+ {
+ int num = cairo_region_num_rectangles (region);
+ cairo_rectangle_int_t r;
+
+ if (num == 1)
+ {
+ cairo_region_get_rectangle (region, 0, &r);
+ g_string_append_printf (s, "%dx%d @%d,%d", r.width, r.height, r.x, r.y);
+ }
+ else
+ {
+ cairo_region_get_extents (region, &r);
+ g_string_append_printf (s, "extent: %dx%d @%d,%d, details: ", r.width, r.height, r.x, r.y);
+ for (int i = 0; i < num; i++)
+ {
+ g_string_append_printf (s, "[%dx%d @%d,%d]", r.width, r.height, r.x, r.y);
+ if (i != num -1)
+ g_string_append (s, ", ");
+ }
+ }
+ }
+ g_string_append (s, "}");
+ return g_string_free (s, FALSE);
+}
+#endif
+
GType
_gdk_paintable_get_type (void)
{
window->window_type = GDK_WINDOW_CHILD;
window->state = GDK_WINDOW_STATE_WITHDRAWN;
+ window->fullscreen_mode = GDK_FULLSCREEN_ON_CURRENT_MONITOR;
window->width = 1;
window->height = 1;
window->toplevel_window_type = -1;
GDK_TYPE_CURSOR,
G_PARAM_READWRITE));
+ /**
+ * GdkWindow:paint-clock:
+ *
+ * The frame clock for a #GdkWindow, see #GdkFrameClock
+ *
+ * The frame clock remains the same for the lifetime of the window.
+ *
+ * Since: 3.0
+ */
+ g_object_class_install_property (object_class,
+ PROP_FRAME_CLOCK,
+ g_param_spec_object ("paint-clock",
+ P_("Frame clock"),
+ P_("Frame clock"),
+ GDK_TYPE_FRAME_CLOCK,
+ G_PARAM_READWRITE));
+
/**
* GdkWindow::pick-embedded-child:
* @window: the window on which the signal is emitted
/**
* GdkWindow::to-embedder:
* @window: the offscreen window on which the signal is emitted
- * @offscreen-x: x coordinate in the offscreen window
- * @offscreen-y: y coordinate in the offscreen window
- * @embedder-x: (out) (type double): return location for the x
+ * @offscreen_x: x coordinate in the offscreen window
+ * @offscreen_y: y coordinate in the offscreen window
+ * @embedder_x: (out) (type double): return location for the x
* coordinate in the embedder window
- * @embedder-y: (out) (type double): return location for the y
+ * @embedder_y: (out) (type double): return location for the y
* coordinate in the embedder window
*
* The ::to-embedder signal is emitted to translate coordinates
/**
* GdkWindow::from-embedder:
* @window: the offscreen window on which the signal is emitted
- * @embedder-x: x coordinate in the embedder window
- * @embedder-y: y coordinate in the embedder window
- * @offscreen-x: (out) (type double): return location for the x
+ * @embedder_x: x coordinate in the embedder window
+ * @embedder_y: y coordinate in the embedder window
+ * @offscreen_x: (out) (type double): return location for the x
* coordinate in the offscreen window
- * @offscreen-y: (out) (type double): return location for the y
+ * @offscreen_y: (out) (type double): return location for the y
* coordinate in the offscreen window
*
* The ::from-embedder signal is emitted to translate coordinates
gdk_window_set_cursor (window, g_value_get_object (value));
break;
+ case PROP_FRAME_CLOCK:
+ gdk_window_set_frame_clock (window, g_value_get_object (value));
+ break;
+
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
g_value_set_object (value, gdk_window_get_cursor (window));
break;
+ case PROP_FRAME_CLOCK:
+ g_value_set_object (value, gdk_window_get_frame_clock (window));
+ break;
+
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
return window->impl_window != window;
}
+static gboolean
+gdk_window_has_alpha (GdkWindow *window)
+{
+ return !gdk_window_has_impl (window) &&
+ (window->has_alpha_background || window->alpha != 255);
+}
+
static void
remove_layered_child_area (GdkWindow *window,
cairo_region_t *region)
continue;
/* Only non-impl children with alpha add to the layered region */
- if (!child->has_alpha_background && gdk_window_has_impl (child))
+ if (!gdk_window_has_alpha (child))
continue;
r.x = child->x;
}
}
- if (child->has_alpha_background)
+ if (gdk_window_has_alpha (child))
{
if (layered_region != NULL)
cairo_region_union (layered_region, child_region);
* lists due to some non-native child window.
*/
if (gdk_window_is_toplevel (private) ||
- mask & GDK_BUTTON_PRESS_MASK)
- mask |=
- GDK_POINTER_MOTION_MASK |
- GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
- GDK_SCROLL_MASK;
+ mask & GDK_BUTTON_PRESS_MASK)
+ mask |=
+ GDK_TOUCH_MASK |
+ GDK_POINTER_MOTION_MASK |
+ GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
+ GDK_SCROLL_MASK;
return mask;
}
* @parent: (allow-none): a #GdkWindow, or %NULL to create the window as a child of
* the default root window for the default display.
* @attributes: attributes of the new window
- * @attributes_mask: mask indicating which fields in @attributes are valid
+ * @attributes_mask: (type GdkWindowAttributesType): mask indicating which
+ * fields in @attributes are valid
*
* Creates a new #GdkWindow using the attributes from
* @attributes. See #GdkWindowAttr and #GdkWindowAttributesType for
return NULL;
}
+ if (attributes_mask & GDK_WA_VISUAL)
+ {
+ g_return_val_if_fail (gdk_visual_get_screen (attributes->visual) == screen, NULL);
+ }
+
display = gdk_screen_get_display (screen);
window = _gdk_display_create_window (display);
window->y = y;
window->width = (attributes->width > 1) ? (attributes->width) : (1);
window->height = (attributes->height > 1) ? (attributes->height) : (1);
+ window->alpha = 255;
if (attributes->wclass == GDK_INPUT_ONLY)
{
g_return_val_if_fail (GDK_IS_WINDOW (window), 0);
- surface = _gdk_window_ref_cairo_surface (window);
+ surface = gdk_window_ref_impl_surface (window);
content = cairo_surface_get_content (surface);
cairo_surface_destroy (surface);
* directly to the window or a child window.
*/
static gboolean
-gdk_window_begin_implicit_paint (GdkWindow *window, GdkRectangle *rect)
+gdk_window_begin_implicit_paint (GdkWindow *window, GdkRectangle *rect,
+ gboolean with_alpha, guint8 alpha)
{
GdkWindowPaint *paint;
if (GDK_IS_PAINTABLE (window->impl))
return FALSE; /* Implementation does double buffering */
- if (window->paint_stack != NULL ||
- window->implicit_paint != NULL)
+ if (window->paint_stack != NULL)
return FALSE; /* Don't stack implicit paints */
/* Never do implicit paints for foreign windows, they don't need
paint->region = cairo_region_create (); /* Empty */
paint->uses_implicit = FALSE;
paint->flushed = NULL;
+ paint->alpha = alpha;
paint->surface = gdk_window_create_similar_surface (window,
- gdk_window_get_content (window),
+ with_alpha ? CAIRO_CONTENT_COLOR_ALPHA : gdk_window_get_content (window),
MAX (rect->width, 1),
MAX (rect->height, 1));
cairo_surface_set_device_offset (paint->surface, -rect->x, -rect->y);
- window->implicit_paint = paint;
+ window->implicit_paint = g_slist_prepend (window->implicit_paint, paint);
return TRUE;
}
return cr;
}
-/* Ensure that all content related to this (sub)window is pushed to the
- native region. If there is an active paint then that area is not
- pushed, in order to not show partially finished double buffers. */
+/* This is called whenever something is drawing directly to the
+ * window, bypassing the double buffering. When this happens we
+ * need to mark any the currently drawn data in the double buffer
+ * as invalid to avoid later drawing it back over the directly
+ * rendered pixels. We also need to mark this region as "flushed"
+ * so that if we later try to paint on it double-buffered we need
+ * to read back the on-window pixels rather than relying on what
+ * is in the current double-buffer pixmap.
+ *
+ * Note that this doesn't correctly handle the case where the
+ * non-double buffered drawing uses transparency and relies on
+ * what the windows below it draws. A fix for that would require
+ * drawing the existing double-buffered background to the window,
+ * but that causes ugly flashes. Non-double buffered drawing is
+ * typically only used in old code or when the drawed widget
+ * already has a double-buffering layer, and in these cases the
+ * pixels are opaque anyway. If you need transparency, don't
+ * disable double buffering.
+ */
static void
gdk_window_flush_implicit_paint (GdkWindow *window)
{
GdkWindow *impl_window;
GdkWindowPaint *paint;
cairo_region_t *region;
- GSList *list;
+ GSList *l;
impl_window = gdk_window_get_impl_window (window);
if (impl_window->implicit_paint == NULL)
return;
- paint = impl_window->implicit_paint;
- region = cairo_region_copy (window->clip_region_with_children);
-
- cairo_region_translate (region, window->abs_x, window->abs_y);
- cairo_region_intersect (region, paint->region);
+ /* There is no proper way to flush any non-toplevel implicit
+ paint, because direct rendering is not compatible with
+ opacity rendering, as it requires an opacity group.
- /* Don't flush active double buffers, as that may show partially done
- * rendering */
- for (list = window->paint_stack; list != NULL; list = list->next)
- {
- GdkWindowPaint *tmp_paint = list->data;
+ We warn and flush just the outermost paint in this case.
+ */
+ l = g_slist_last (impl_window->implicit_paint);
+ if (l != impl_window->implicit_paint)
+ g_warning ("Non double buffered drawing not supported inside transparent windows");
- cairo_region_subtract (region, tmp_paint->region);
- }
-
- if (!GDK_WINDOW_DESTROYED (window) && !cairo_region_is_empty (region))
- {
- cairo_t *cr;
+ paint = l->data;
- /* Remove flushed region from the implicit paint */
- cairo_region_subtract (paint->region, region);
+ region = cairo_region_copy (window->clip_region_with_children);
+ cairo_region_translate (region, window->abs_x, window->abs_y);
- /* Some regions are valid, push these to window now */
- cr = gdk_cairo_create_for_impl (window);
- gdk_cairo_region (cr, region);
- cairo_clip (cr);
- cairo_set_source_surface (cr, paint->surface, 0, 0);
- cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
- cairo_paint (cr);
- cairo_destroy (cr);
+ /* Anything in the whole flushed window that was drawn is now
+ considered unpainted, so that we don't push it back at the
+ end of the implicit paint overwriting the directly rendered
+ pixels. */
+ cairo_region_subtract (paint->region, region);
- paint->flushed = region;
- }
+ /* Save flushed area so we can read it back if we draw over it later */
+ if (paint->flushed == NULL)
+ paint->flushed = region;
else
- cairo_region_destroy (region);
+ {
+ cairo_region_union (paint->flushed, region);
+ cairo_region_destroy (region);
+ }
}
/* Ends an implicit paint, paired with gdk_window_begin_implicit_paint returning TRUE */
g_assert (window->implicit_paint != NULL);
- paint = window->implicit_paint;
+ paint = window->implicit_paint->data;
- window->implicit_paint = NULL;
+ window->implicit_paint = g_slist_delete_link (window->implicit_paint,
+ window->implicit_paint);
- if (!GDK_WINDOW_DESTROYED (window) && !cairo_region_is_empty (paint->region))
+ if (!GDK_WINDOW_DESTROYED (window) && !cairo_region_is_empty (paint->region) && paint->alpha > 0)
{
+ GdkWindowPaint *parent_paint = NULL;
cairo_t *cr;
/* Some regions are valid, push these to window now */
- cr = gdk_cairo_create_for_impl (window);
+ if (window->implicit_paint)
+ {
+ parent_paint = window->implicit_paint->data;
+
+ /* If the toplevel implicit paint was flushed, restore it now
+ before we blend over it. */
+ if (parent_paint->flushed != NULL &&
+ !cairo_region_is_empty (parent_paint->flushed))
+ {
+ cairo_surface_t *source_surface = gdk_window_ref_impl_surface (window);
+ cairo_region_t *flushed = cairo_region_copy (parent_paint->flushed);
+
+ cr = cairo_create (parent_paint->surface);
+ cairo_set_source_surface (cr, source_surface, 0, 0);
+ cairo_surface_destroy (source_surface);
+
+ cairo_region_intersect (flushed, paint->region);
+ gdk_cairo_region (cr, flushed);
+ cairo_clip (cr);
+
+ cairo_region_subtract (parent_paint->flushed, flushed);
+ cairo_region_destroy (flushed);
+
+ cairo_paint (cr);
+ cairo_destroy (cr);
+ }
+
+ cr = cairo_create (parent_paint->surface);
+ }
+ else
+ cr = gdk_cairo_create_for_impl (window);
+
gdk_cairo_region (cr, paint->region);
cairo_clip (cr);
cairo_set_source_surface (cr, paint->surface, 0, 0);
- cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
- cairo_paint (cr);
+ if (paint->alpha == 255)
+ {
+ cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
+ cairo_paint (cr);
+ }
+ else
+ {
+ cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
+ cairo_paint_with_alpha (cr, paint->alpha / 255.0);
+ }
+
cairo_destroy (cr);
+
+ if (parent_paint)
+ cairo_region_union (parent_paint->region, paint->region);
}
-
+
cairo_region_destroy (paint->region);
if (paint->flushed)
cairo_region_destroy (paint->flushed);
}
impl_window = gdk_window_get_impl_window (window);
- implicit_paint = impl_window->implicit_paint;
+ implicit_paint = impl_window->implicit_paint != NULL ? impl_window->implicit_paint->data : NULL;
paint = g_new (GdkWindowPaint, 1);
paint->region = cairo_region_copy (region);
by being drawn in back to front order. However, if implicit paints are not used, for
instance if it was flushed due to a non-double-buffered paint in the middle of the
expose we need to copy in the existing data here. */
- if (!gdk_window_has_impl (window) &&
- window->has_alpha_background &&
+ if (gdk_window_has_alpha (window) &&
(!implicit_paint ||
(implicit_paint && implicit_paint->flushed != NULL && !cairo_region_is_empty (implicit_paint->flushed))))
{
if (implicit_paint)
{
cairo_region_t *flushed = cairo_region_copy (implicit_paint->flushed);
- cairo_region_intersect (flushed, region);
/* Convert from impl coords */
cairo_region_translate (flushed, -window->abs_x, -window->abs_y);
+ cairo_region_intersect (flushed, paint->region);
gdk_cairo_region (cr, flushed);
cairo_clip (cr);
cairo_region_t *region, /* In impl window coords */
int dx, int dy)
{
+ GSList *l;
+
if ((dx == 0 && dy == 0) ||
cairo_region_is_empty (region))
{
/* If we're currently exposing this window, don't copy to this
destination, as it will be overdrawn when the expose is done,
instead invalidate it and repaint later. */
- if (impl_window->implicit_paint)
+ for (l = impl_window->implicit_paint; l != NULL; l = l->next)
{
- GdkWindowPaint *implicit_paint = impl_window->implicit_paint;
+ GdkWindowPaint *implicit_paint = l->data;
cairo_region_t *exposing;
exposing = cairo_region_copy (implicit_paint->region);
gdk_window_flush_implicit_paint (window);
}
-/* If we're about to move/resize or otherwise change the
- * hierarchy of a client side window in an impl and we're
- * called from an expose event handler then we need to
- * flush any already painted parts of the implicit paint
- * that are not part of the current paint, as these may
- * be used when scrolling or may overdraw the changes
- * caused by the hierarchy change.
- */
-static void
-gdk_window_flush_if_exposing (GdkWindow *window)
-{
- GdkWindow *impl_window;
-
- impl_window = gdk_window_get_impl_window (window);
-
- /* If we're in an implicit paint (i.e. in an expose handler, flush
- all the already finished exposes to get things to an uptodate state. */
- if (impl_window->implicit_paint)
- gdk_window_flush (window);
-}
-
-
static void
gdk_window_flush_recursive_helper (GdkWindow *window,
GdkWindowImpl *impl)
/* Code for dirty-region queueing
*/
static GSList *update_windows = NULL;
-static guint update_idle = 0;
+static GdkFrameClock *_gdk_default_frame_clock = NULL;
static gboolean debug_updates = FALSE;
static inline gboolean
update_windows = g_slist_remove (update_windows, window);
}
-static gboolean
-gdk_window_update_idle (gpointer data)
+static void
+gdk_window_paint_default_clock_updates (gpointer data)
{
- gdk_window_process_all_updates ();
+ gdk_window_process_all_updates_internal (TRUE);
+}
- return FALSE;
+static void
+gdk_ensure_default_frame_clock (void)
+{
+ if (_gdk_default_frame_clock == NULL)
+ {
+ _gdk_default_frame_clock = g_object_new (GDK_TYPE_FRAME_CLOCK_IDLE,
+ NULL);
+
+ g_signal_connect (G_OBJECT (_gdk_default_frame_clock),
+ "paint",
+ G_CALLBACK (gdk_window_paint_default_clock_updates),
+ NULL);
+ }
}
static gboolean
gdk_window_is_toplevel_frozen (window)))
return;
- if (!update_idle)
- update_idle =
- gdk_threads_add_idle_full (GDK_PRIORITY_REDRAW,
- gdk_window_update_idle,
- NULL, NULL);
+ gdk_frame_clock_request_phase (gdk_window_get_frame_clock (window),
+ GDK_FRAME_CLOCK_PHASE_PAINT);
}
void
{
GdkWindow *child;
cairo_region_t *clipped_expose_region;
+ GdkRectangle clip_box;
GList *l, *children;
+ gboolean end_implicit;
if (cairo_region_is_empty (expose_region))
return;
gdk_window_has_impl (window))
_gdk_window_add_damage ((GdkWindow *) window->impl_window, expose_region);
+ end_implicit = FALSE;
+ if (window->alpha != 255 && !gdk_window_has_impl (window))
+ {
+ if (window->alpha == 0)
+ return;
+
+ cairo_region_get_extents (expose_region, &clip_box);
+ clip_box.x += window->abs_x;
+ clip_box.y += window->abs_y;
+ end_implicit = gdk_window_begin_implicit_paint (window->impl_window, &clip_box, TRUE, window->alpha);
+ }
/* Paint the window before the children, clipped to the window region
with visible child windows removed */
g_list_free_full (children, g_object_unref);
+ if (end_implicit)
+ gdk_window_end_implicit_paint (window->impl_window);
}
/* Process and remove any invalid area on the native window by creating
*/
cairo_region_get_extents (update_area, &clip_box);
- end_implicit = gdk_window_begin_implicit_paint (window, &clip_box);
+ end_implicit = gdk_window_begin_implicit_paint (window, &clip_box, FALSE, 255);
expose_region = cairo_region_copy (update_area);
impl_class = GDK_WINDOW_IMPL_GET_CLASS (window->impl);
if (!end_implicit)
* be to late to anti-expose now. Since this is merely an
* optimization we just avoid doing it at all in that case.
*/
- if (window->implicit_paint != NULL && !window->implicit_paint->flushed)
+ if (window->implicit_paint != NULL && !((GdkWindowPaint *)window->implicit_paint->data)->flushed)
save_region = impl_class->queue_antiexpose (window, update_area);
gdk_window_end_implicit_paint (window);
g_slist_free (displays);
}
+/**
+ * gdk_window_process_all_updates:
+ *
+ * Calls gdk_window_process_updates() for all windows (see #GdkWindow)
+ * in the application.
+ *
+ **/
+void
+gdk_window_process_all_updates (void)
+{
+ gdk_window_process_all_updates_internal (FALSE);
+}
+
/* Currently it is not possible to override
* gdk_window_process_all_updates in the same manner as
* gdk_window_process_updates and gdk_window_invalidate_maybe_recurse
* displays and call the mehod.
*/
-/**
- * gdk_window_process_all_updates:
- *
- * Calls gdk_window_process_updates() for all windows (see #GdkWindow)
- * in the application.
- *
- **/
-void
-gdk_window_process_all_updates (void)
+static void
+gdk_window_process_all_updates_internal (gboolean default_clock_only)
{
GSList *old_update_windows = update_windows;
GSList *tmp_list = update_windows;
/* We can't do this now since that would recurse, so
delay it until after the recursion is done. */
got_recursive_update = TRUE;
- update_idle = 0;
return;
}
in_process_all_updates = TRUE;
got_recursive_update = FALSE;
- if (update_idle)
- g_source_remove (update_idle);
-
update_windows = NULL;
- update_idle = 0;
before_process_all_updates ();
if (!GDK_WINDOW_DESTROYED (window))
{
if (window->update_freeze_count ||
- gdk_window_is_toplevel_frozen (window))
+ gdk_window_is_toplevel_frozen (window) ||
+ (default_clock_only && window->frame_clock != NULL))
gdk_window_add_update_window (window);
else
gdk_window_process_updates_internal (window);
redraw now so that it eventually happens,
otherwise we could miss an update if nothing
else schedules an update. */
- if (got_recursive_update && !update_idle)
- update_idle =
- gdk_threads_add_idle_full (GDK_PRIORITY_REDRAW,
- gdk_window_update_idle,
- NULL, NULL);
+ if (got_recursive_update)
+ gdk_window_schedule_update (NULL);
}
-/**
- * gdk_window_process_updates:
- * @window: a #GdkWindow
- * @update_children: whether to also process updates for child windows
- *
- * Sends one or more expose events to @window. The areas in each
- * expose event will cover the entire update area for the window (see
- * gdk_window_invalidate_region() for details). Normally GDK calls
- * gdk_window_process_all_updates() on your behalf, so there's no
- * need to call this function unless you want to force expose events
- * to be delivered immediately and synchronously (vs. the usual
- * case, where GDK delivers them in an idle handler). Occasionally
- * this is useful to produce nicer scrolling behavior, for example.
- *
- **/
-void
-gdk_window_process_updates (GdkWindow *window,
- gboolean update_children)
+
+enum {
+ PROCESS_UPDATES_NO_RECURSE,
+ PROCESS_UPDATES_WITH_ALL_CHILDREN,
+ PROCESS_UPDATES_WITH_SAME_CLOCK_CHILDREN
+};
+
+static void
+gdk_window_process_updates_with_mode (GdkWindow *window,
+ int recurse_mode)
{
GdkWindow *impl_window;
gdk_window_remove_update_window ((GdkWindow *)impl_window);
}
- if (update_children)
+ if (recurse_mode != PROCESS_UPDATES_NO_RECURSE)
{
/* process updates in reverse stacking order so composition or
* painting over achieves the desired effect for offscreen windows
for (node = g_list_last (children); node; node = node->prev)
{
- gdk_window_process_updates (node->data, TRUE);
- g_object_unref (node->data);
+ GdkWindow *child = node->data;
+ if (recurse_mode == PROCESS_UPDATES_WITH_ALL_CHILDREN ||
+ (recurse_mode == PROCESS_UPDATES_WITH_SAME_CLOCK_CHILDREN &&
+ child->frame_clock == NULL))
+ {
+ gdk_window_process_updates (child, TRUE);
+ }
+ g_object_unref (child);
}
g_list_free (children);
g_object_unref (window);
}
+/**
+ * gdk_window_process_updates:
+ * @window: a #GdkWindow
+ * @update_children: whether to also process updates for child windows
+ *
+ * Sends one or more expose events to @window. The areas in each
+ * expose event will cover the entire update area for the window (see
+ * gdk_window_invalidate_region() for details). Normally GDK calls
+ * gdk_window_process_all_updates() on your behalf, so there's no
+ * need to call this function unless you want to force expose events
+ * to be delivered immediately and synchronously (vs. the usual
+ * case, where GDK delivers them in an idle handler). Occasionally
+ * this is useful to produce nicer scrolling behavior, for example.
+ *
+ **/
+void
+gdk_window_process_updates (GdkWindow *window,
+ gboolean update_children)
+{
+ g_return_if_fail (GDK_IS_WINDOW (window));
+
+ return gdk_window_process_updates_with_mode (window,
+ update_children ?
+ PROCESS_UPDATES_WITH_ALL_CHILDREN :
+ PROCESS_UPDATES_NO_RECURSE);
+}
+
static void
gdk_window_invalidate_rect_full (GdkWindow *window,
const GdkRectangle *rect,
if (window->destroyed)
return;
- gdk_window_flush_if_exposing (window);
-
/* Keep children in (reverse) stacking order */
gdk_window_raise_internal (window);
if (window->destroyed)
return;
- gdk_window_flush_if_exposing (window);
-
/* Keep children in (reverse) stacking order */
gdk_window_lower_internal (window);
return;
}
- gdk_window_flush_if_exposing (window);
-
if (gdk_window_is_toplevel (window))
{
g_return_if_fail (gdk_window_is_toplevel (sibling));
window->y == y)))
return;
- gdk_window_flush_if_exposing (window);
-
/* Handle child windows */
expose = FALSE;
* Everything in the old and new regions that is not copied must be
* invalidated (including children) as this is newly exposed
*/
- if (window->has_alpha_background)
+ if (gdk_window_has_alpha (window))
copy_area = cairo_region_create (); /* Copy nothing for alpha windows */
else
copy_area = cairo_region_copy (new_region);
if (window->destroyed)
return;
- gdk_window_flush_if_exposing (window);
-
old_layered_area = cairo_region_copy (window->layered_region);
old_native_child_region = collect_native_child_region (window, FALSE);
if (old_native_child_region)
impl_window = gdk_window_get_impl_window (window);
/* Calculate the area that can be gotten by copying the old area */
- if (window->has_alpha_background)
+ if (gdk_window_has_alpha (window))
copy_area = cairo_region_create (); /* Copy nothing for alpha windows */
else
copy_area = cairo_region_copy (window->clip_region);
impl_window = gdk_window_get_impl_window (window);
/* compute source regions */
- if (window->has_alpha_background)
+ if (gdk_window_has_alpha (window))
copy_area = cairo_region_create (); /* Copy nothing for alpha windows */
else
copy_area = cairo_region_copy (region);
* See also gdk_window_set_background_pattern().
**/
void
-gdk_window_set_background_rgba (GdkWindow *window,
- GdkRGBA *rgba)
+gdk_window_set_background_rgba (GdkWindow *window,
+ const GdkRGBA *rgba)
{
cairo_pattern_t *pattern;
GDK_ALL_EVENTS_MASK, /* GDK_CLIENT_EVENT = 28 */
GDK_VISIBILITY_NOTIFY_MASK, /* GDK_VISIBILITY_NOTIFY = 29 */
GDK_EXPOSURE_MASK, /* GDK_NO_EXPOSE = 30 */
- GDK_SCROLL_MASK | GDK_BUTTON_PRESS_MASK,/* GDK_SCROLL= 31 */
+ GDK_SCROLL_MASK | GDK_SMOOTH_SCROLL_MASK,/* GDK_SCROLL= 31 */
0, /* GDK_WINDOW_STATE = 32 */
0, /* GDK_SETTING = 33 */
0, /* GDK_OWNER_CHANGE = 34 */
0, /* GDK_GRAB_BROKEN = 35 */
0, /* GDK_DAMAGE = 36 */
+ GDK_TOUCH_MASK, /* GDK_TOUCH_BEGIN = 37 */
+ GDK_TOUCH_MASK, /* GDK_TOUCH_UPDATE = 38 */
+ GDK_TOUCH_MASK, /* GDK_TOUCH_END = 39 */
+ GDK_TOUCH_MASK /* GDK_TOUCH_CANCEL = 40 */
};
G_STATIC_ASSERT (G_N_ELEMENTS (type_masks) == GDK_EVENT_LAST);
type == GDK_2BUTTON_PRESS ||
type == GDK_3BUTTON_PRESS ||
type == GDK_BUTTON_RELEASE ||
+ type == GDK_TOUCH_BEGIN ||
+ type == GDK_TOUCH_END ||
type == GDK_SCROLL;
}
is_motion_type (GdkEventType type)
{
return type == GDK_MOTION_NOTIFY ||
+ type == GDK_TOUCH_UPDATE ||
type == GDK_ENTER_NOTIFY ||
type == GDK_LEAVE_NOTIFY;
}
+static gboolean
+is_touch_type (GdkEventType type)
+{
+ return type == GDK_TOUCH_BEGIN ||
+ type == GDK_TOUCH_UPDATE ||
+ type == GDK_TOUCH_END ||
+ type == GDK_TOUCH_CANCEL;
+}
+
static GdkWindow *
find_common_ancestor (GdkWindow *win1,
GdkWindow *win2)
event->button.state = the_state;
break;
+ case GDK_TOUCH_BEGIN:
+ case GDK_TOUCH_UPDATE:
+ case GDK_TOUCH_END:
+ case GDK_TOUCH_CANCEL:
+ event->touch.time = the_time;
+ event->touch.axes = NULL;
+ event->touch.state = the_state;
+ break;
+
case GDK_SCROLL:
event->scroll.time = the_time;
event->scroll.state = the_state;
GdkEvent *event;
guint32 window_event_mask, type_event_mask;
GdkDeviceGrabInfo *grab;
+ GdkTouchGrabInfo *touch_grab = NULL;
+ GdkPointerWindowInfo *pointer_info;
gboolean block_event = FALSE;
+ GdkEventSequence *sequence;
grab = _gdk_display_has_device_grab (display, device, serial);
+ pointer_info = _gdk_display_get_pointer_info (display, device);
- if (grab != NULL &&
- !grab->owner_events)
+ sequence = gdk_event_get_event_sequence (event_in_queue);
+ if (sequence)
+ touch_grab = _gdk_display_has_touch_grab (display, device, sequence, serial);
+
+ if (touch_grab)
+ {
+ if (window != touch_grab->window)
+ return;
+
+ window_event_mask = touch_grab->event_mask;
+ }
+ else if (grab != NULL &&
+ !grab->owner_events)
{
/* !owner_event => only report events wrt grab window, ignore rest */
if ((GdkWindow *)window != grab->window)
else
window_event_mask = window->event_mask;
- if (type == GDK_LEAVE_NOTIFY)
+ if (type == GDK_ENTER_NOTIFY &&
+ (pointer_info->need_touch_press_enter ||
+ gdk_device_get_source (source_device) == GDK_SOURCE_TOUCHSCREEN) &&
+ mode != GDK_CROSSING_TOUCH_BEGIN &&
+ mode != GDK_CROSSING_TOUCH_END)
+ {
+ pointer_info->need_touch_press_enter = TRUE;
+ block_event = TRUE;
+ }
+ else if (type == GDK_LEAVE_NOTIFY)
{
type_event_mask = GDK_LEAVE_NOTIFY_MASK;
window->devices_inside = g_list_remove (window->devices_inside, device);
serial);
if (new_window_under_pointer != pointer_info->window_under_pointer)
{
+ GdkDevice *source_device;
+
+ if (pointer_info->last_slave)
+ source_device = pointer_info->last_slave;
+ else
+ source_device = device;
+
_gdk_synthesize_crossing_events (display,
pointer_info->window_under_pointer,
new_window_under_pointer,
- device, NULL,
+ device, source_device,
GDK_CROSSING_NORMAL,
pointer_info->toplevel_x,
pointer_info->toplevel_y,
static GdkWindow *
get_event_window (GdkDisplay *display,
GdkDevice *device,
- GdkWindow *pointer_window,
- GdkEventType type,
- GdkModifierType mask,
- guint *evmask_out,
- gulong serial)
-{
- guint evmask;
+ GdkEventSequence *sequence,
+ GdkWindow *pointer_window,
+ GdkEventType type,
+ GdkModifierType mask,
+ guint *evmask_out,
+ gboolean pointer_emulated,
+ gulong serial)
+{
+ guint evmask, emulated_mask = 0;
GdkWindow *grab_window;
GdkDeviceGrabInfo *grab;
+ GdkTouchGrabInfo *touch_grab;
- grab = _gdk_display_has_device_grab (display, device, serial);
+ touch_grab = _gdk_display_has_touch_grab (display, device, sequence, serial);
+ grab = _gdk_display_get_last_device_grab (display, device);
+
+ if (is_touch_type (type) && pointer_emulated)
+ {
+ switch (type)
+ {
+ case GDK_TOUCH_BEGIN:
+ emulated_mask |= GDK_BUTTON_PRESS_MASK;
+ break;
+ case GDK_TOUCH_UPDATE:
+ emulated_mask |= GDK_POINTER_MOTION_MASK;
+ break;
+ case GDK_TOUCH_END:
+ emulated_mask |= GDK_BUTTON_RELEASE_MASK;
+ default:
+ break;
+ }
+ }
+
+ if (touch_grab != NULL &&
+ (!grab || grab->implicit || touch_grab->serial >= grab->serial_start))
+ {
+ evmask = touch_grab->event_mask;
+ evmask = update_evmask_for_button_motion (evmask, mask);
+
+ if (evmask & (type_masks[type] | emulated_mask))
+ {
+ if (evmask_out)
+ *evmask_out = evmask;
+ return touch_grab->window;
+ }
+ else
+ return NULL;
+ }
if (grab != NULL && !grab->owner_events)
{
grab_window = grab->window;
- if (evmask & type_masks[type])
+ if (evmask & (type_masks[type] | emulated_mask))
{
if (evmask_out)
*evmask_out = evmask;
evmask = pointer_window->event_mask;
evmask = update_evmask_for_button_motion (evmask, mask);
- if (evmask & type_masks[type])
+ if (evmask & (type_masks[type] | emulated_mask))
{
if (evmask_out)
*evmask_out = evmask;
evmask = grab->event_mask;
evmask = update_evmask_for_button_motion (evmask, mask);
- if (evmask & type_masks[type])
+ if (evmask & (type_masks[type] | emulated_mask))
{
if (evmask_out)
*evmask_out = evmask;
guint state;
gdouble toplevel_x, toplevel_y;
guint32 time_;
- gboolean non_linear;
+ gboolean non_linear, need_synthetic_enter = FALSE;
+ gint event_type;
+ event_type = source_event->type;
event_window = source_event->any.window;
gdk_event_get_coords (source_event, &toplevel_x, &toplevel_y);
gdk_event_get_state (source_event, &state);
source_event->crossing.detail == GDK_NOTIFY_NONLINEAR_VIRTUAL))
non_linear = TRUE;
+ if (pointer_info->need_touch_press_enter &&
+ gdk_device_get_source (pointer_info->last_slave) != GDK_SOURCE_TOUCHSCREEN &&
+ (source_event->type != GDK_TOUCH_UPDATE ||
+ _gdk_event_get_pointer_emulated (source_event)))
+ {
+ pointer_info->need_touch_press_enter = FALSE;
+ need_synthetic_enter = TRUE;
+ }
+
/* If we get crossing events with subwindow unexpectedly being NULL
that means there is a native subwindow that gdk doesn't know about.
We track these and forward them, with the correct virtual window
return TRUE;
}
- if (pointer_info->window_under_pointer != pointer_window)
+ if ((source_event->type != GDK_TOUCH_UPDATE ||
+ _gdk_event_get_pointer_emulated (source_event)) &&
+ pointer_info->window_under_pointer != pointer_window)
{
/* Either a toplevel crossing notify that ended up inside a child window,
or a motion notify that got into another child window */
serial, non_linear);
_gdk_display_set_window_under_pointer (display, device, pointer_window);
}
- else if (source_event->type == GDK_MOTION_NOTIFY)
+ else if (source_event->type == GDK_MOTION_NOTIFY ||
+ source_event->type == GDK_TOUCH_UPDATE)
{
GdkWindow *event_win;
guint evmask;
gboolean is_hint;
+ GdkEventSequence *sequence;
+
+ sequence = gdk_event_get_event_sequence (source_event);
event_win = get_event_window (display,
device,
+ sequence,
pointer_window,
source_event->type,
state,
&evmask,
+ _gdk_event_get_pointer_emulated (source_event),
serial);
+ if (event_type == GDK_TOUCH_UPDATE)
+ {
+ if (_gdk_event_get_pointer_emulated (source_event))
+ {
+ /* Touch events emulating pointer events are transformed back
+ * to pointer events if:
+ * 1 - The event window doesn't select for touch events
+ * 2 - There's no touch grab for this sequence, which means
+ * it was started as a pointer sequence, but a device
+ * grab added touch events afterwards, the sequence must
+ * not mutate in this case.
+ */
+ if ((evmask & GDK_TOUCH_MASK) == 0 ||
+ !_gdk_display_has_touch_grab (display, device, sequence, serial))
+ event_type = GDK_MOTION_NOTIFY;
+ }
+ else if ((evmask & GDK_TOUCH_MASK) == 0)
+ return TRUE;
+ }
+
+ if (is_touch_type (source_event->type) && !is_touch_type (event_type))
+ state |= GDK_BUTTON1_MASK;
+
if (event_win &&
gdk_device_get_device_type (device) != GDK_DEVICE_TYPE_MASTER &&
gdk_window_get_device_events (event_win, device) == 0)
return TRUE;
+ /* The last device to interact with the window was a touch device,
+ * which synthesized a leave notify event, so synthesize another enter
+ * notify to tell the pointer is on the window.
+ */
+ if (need_synthetic_enter)
+ _gdk_synthesize_crossing_events (display,
+ NULL, pointer_window,
+ device, source_device,
+ GDK_CROSSING_DEVICE_SWITCH,
+ toplevel_x, toplevel_y,
+ state, time_, NULL,
+ serial, FALSE);
+
is_hint = FALSE;
if (event_win &&
+ event_type == GDK_MOTION_NOTIFY &&
(evmask & GDK_POINTER_MOTION_HINT_MASK))
{
gulong *device_serial;
}
}
- if (event_win && !display->ignore_core_events)
+ if (!event_win)
+ return TRUE;
+
+ event = gdk_event_new (event_type);
+ event->any.window = g_object_ref (event_win);
+ event->any.send_event = source_event->any.send_event;
+
+ gdk_event_set_device (event, gdk_event_get_device (source_event));
+ gdk_event_set_source_device (event, source_device);
+
+ if (event_type == GDK_TOUCH_UPDATE)
{
- event = _gdk_make_event (event_win, GDK_MOTION_NOTIFY, source_event, FALSE);
- event->motion.time = time_;
+ event->touch.time = time_;
+ event->touch.state = state | GDK_BUTTON1_MASK;
+ event->touch.sequence = source_event->touch.sequence;
+ event->touch.emulating_pointer = source_event->touch.emulating_pointer;
convert_toplevel_coords_to_window (event_win,
toplevel_x, toplevel_y,
- &event->motion.x, &event->motion.y);
- event->motion.x_root = source_event->motion.x_root;
- event->motion.y_root = source_event->motion.y_root;
+ &event->touch.x, &event->touch.y);
+ gdk_event_get_root_coords (source_event,
+ &event->touch.x_root,
+ &event->touch.y_root);
+
+ event->touch.axes = g_memdup (source_event->touch.axes,
+ sizeof (gdouble) * gdk_device_get_n_axes (source_event->touch.device));
+ }
+ else
+ {
+ event->motion.time = time_;
event->motion.state = state;
event->motion.is_hint = is_hint;
- event->motion.device = source_event->motion.device;
- event->motion.axes = g_memdup (source_event->motion.axes,
- sizeof (gdouble) * gdk_device_get_n_axes (source_event->motion.device));
- gdk_event_set_source_device (event, source_device);
+
+ convert_toplevel_coords_to_window (event_win,
+ toplevel_x, toplevel_y,
+ &event->motion.x, &event->motion.y);
+ gdk_event_get_root_coords (source_event,
+ &event->motion.x_root,
+ &event->motion.y_root);
+
+ if (is_touch_type (source_event->type))
+ event->motion.axes = g_memdup (source_event->touch.axes,
+ sizeof (gdouble) * gdk_device_get_n_axes (source_event->touch.device));
+ else
+ event->motion.axes = g_memdup (source_event->motion.axes,
+ sizeof (gdouble) * gdk_device_get_n_axes (source_event->motion.device));
}
+
+ /* Just insert the event */
+ _gdk_event_queue_insert_after (gdk_window_get_display (event_win),
+ source_event, event);
}
/* unlink all move events from queue.
GdkWindow *pointer_window;
GdkWindow *parent;
GdkEvent *event;
+ GdkPointerWindowInfo *pointer_info;
+ GdkDeviceGrabInfo *pointer_grab;
guint state;
guint32 time_;
GdkEventType type;
GdkDisplay *display;
GdkWindow *w;
GdkDevice *device, *source_device;
+ GdkEventMask evmask;
+ GdkEventSequence *sequence;
type = source_event->any.type;
event_window = source_event->any.window;
toplevel_x, toplevel_y,
&toplevel_x, &toplevel_y);
- if (type == GDK_BUTTON_PRESS &&
+ sequence = gdk_event_get_event_sequence (source_event);
+
+ pointer_info = _gdk_display_get_pointer_info (display, device);
+ pointer_grab = _gdk_display_has_device_grab (display, device, serial);
+
+ if ((type == GDK_BUTTON_PRESS ||
+ type == GDK_TOUCH_BEGIN) &&
!source_event->any.send_event &&
- _gdk_display_has_device_grab (display, device, serial) == NULL)
+ (!pointer_grab ||
+ (type == GDK_TOUCH_BEGIN && pointer_grab->implicit &&
+ !_gdk_event_get_pointer_emulated (source_event))))
{
pointer_window =
_gdk_window_find_descendant_at (toplevel_window,
(parent = get_event_parent (w)) != NULL &&
parent->window_type != GDK_WINDOW_ROOT)
{
- if (w->event_mask & GDK_BUTTON_PRESS_MASK)
+ if (w->event_mask & GDK_BUTTON_PRESS_MASK &&
+ (type == GDK_BUTTON_PRESS ||
+ _gdk_event_get_pointer_emulated (source_event)))
break;
+
+ if (type == GDK_TOUCH_BEGIN &&
+ w->event_mask & GDK_TOUCH_MASK)
+ break;
+
w = parent;
}
- pointer_window = (GdkWindow *)w;
-
- _gdk_display_add_device_grab (display,
- device,
- pointer_window,
- event_window,
- GDK_OWNERSHIP_NONE,
- FALSE,
- gdk_window_get_events (pointer_window),
- serial,
- time_,
- TRUE);
- _gdk_display_device_grab_update (display, device, source_device, serial);
+ pointer_window = w;
+
+ if (pointer_window)
+ {
+ if (type == GDK_TOUCH_BEGIN &&
+ pointer_window->event_mask & GDK_TOUCH_MASK)
+ {
+ _gdk_display_add_touch_grab (display, device, sequence,
+ pointer_window, event_window,
+ gdk_window_get_events (pointer_window),
+ serial, time_);
+ }
+ else if (type == GDK_BUTTON_PRESS ||
+ _gdk_event_get_pointer_emulated (source_event))
+ {
+ _gdk_display_add_device_grab (display,
+ device,
+ pointer_window,
+ event_window,
+ GDK_OWNERSHIP_NONE,
+ FALSE,
+ gdk_window_get_events (pointer_window),
+ serial,
+ time_,
+ TRUE);
+ _gdk_display_device_grab_update (display, device,
+ source_device, serial);
+ }
+ }
}
pointer_window = get_pointer_window (display, toplevel_window, device,
event_win = get_event_window (display,
device,
- pointer_window,
- type, state,
- NULL, serial);
+ sequence,
+ pointer_window,
+ type, state,
+ &evmask,
+ _gdk_event_get_pointer_emulated (source_event),
+ serial);
+
+ if (type == GDK_TOUCH_BEGIN || type == GDK_TOUCH_END)
+ {
+ if (_gdk_event_get_pointer_emulated (source_event))
+ {
+ if ((evmask & GDK_TOUCH_MASK) == 0 ||
+ !_gdk_display_has_touch_grab (display, device, sequence, serial))
+ {
+ if (type == GDK_TOUCH_BEGIN)
+ type = GDK_BUTTON_PRESS;
+ else if (type == GDK_TOUCH_END)
+ type = GDK_BUTTON_RELEASE;
+ }
+ }
+ else if ((evmask & GDK_TOUCH_MASK) == 0)
+ return TRUE;
+ }
+
+ if (source_event->type == GDK_TOUCH_END && !is_touch_type (type))
+ state |= GDK_BUTTON1_MASK;
- if (event_win == NULL || display->ignore_core_events)
+ if (event_win == NULL)
return TRUE;
if (gdk_device_get_device_type (device) != GDK_DEVICE_TYPE_MASTER &&
gdk_window_get_device_events (event_win, device) == 0)
return TRUE;
+ if ((type == GDK_BUTTON_PRESS ||
+ (type == GDK_TOUCH_BEGIN &&
+ _gdk_event_get_pointer_emulated (source_event))) &&
+ pointer_info->need_touch_press_enter)
+ {
+ GdkCrossingMode mode;
+
+ /* The last device to interact with the window was a touch device,
+ * which synthesized a leave notify event, so synthesize another enter
+ * notify to tell the pointer is on the window.
+ */
+ if (gdk_device_get_source (source_device) == GDK_SOURCE_TOUCHSCREEN)
+ mode = GDK_CROSSING_TOUCH_BEGIN;
+ else
+ mode = GDK_CROSSING_DEVICE_SWITCH;
+
+ pointer_info->need_touch_press_enter = FALSE;
+ _gdk_synthesize_crossing_events (display,
+ NULL,
+ pointer_info->window_under_pointer,
+ device, source_device, mode,
+ toplevel_x, toplevel_y,
+ state, time_, source_event,
+ serial, FALSE);
+ }
+ else if (type == GDK_SCROLL &&
+ (((evmask & GDK_SMOOTH_SCROLL_MASK) == 0 &&
+ source_event->scroll.direction == GDK_SCROLL_SMOOTH) ||
+ ((evmask & GDK_SMOOTH_SCROLL_MASK) != 0 &&
+ source_event->scroll.direction != GDK_SCROLL_SMOOTH &&
+ _gdk_event_get_pointer_emulated (source_event))))
+ return FALSE;
+
event = _gdk_make_event (event_win, type, source_event, FALSE);
switch (type)
convert_toplevel_coords_to_window (event_win,
toplevel_x, toplevel_y,
&event->button.x, &event->button.y);
- event->button.x_root = source_event->button.x_root;
- event->button.y_root = source_event->button.y_root;
- event->button.state = state;
- event->button.device = source_event->button.device;
- event->button.axes = g_memdup (source_event->button.axes,
- sizeof (gdouble) * gdk_device_get_n_axes (source_event->button.device));
-
+ gdk_event_get_root_coords (source_event,
+ &event->button.x_root,
+ &event->button.y_root);
+ gdk_event_set_device (event, gdk_event_get_device (source_event));
gdk_event_set_source_device (event, source_device);
+ if (is_touch_type (source_event->type))
+ {
+ if (type == GDK_BUTTON_RELEASE)
+ event->button.state |= GDK_BUTTON1_MASK;
+ event->button.button = 1;
+ event->button.axes = g_memdup (source_event->touch.axes,
+ sizeof (gdouble) * gdk_device_get_n_axes (source_event->touch.device));
+ }
+ else
+ {
+ event->button.button = source_event->button.button;
+ event->button.axes = g_memdup (source_event->button.axes,
+ sizeof (gdouble) * gdk_device_get_n_axes (source_event->button.device));
+ }
+
if (type == GDK_BUTTON_PRESS)
- _gdk_event_button_generate (display, event);
+ _gdk_event_button_generate (display, event);
+ else if ((type == GDK_BUTTON_RELEASE ||
+ (type == GDK_TOUCH_END &&
+ _gdk_event_get_pointer_emulated (source_event))) &&
+ pointer_window == pointer_info->window_under_pointer &&
+ gdk_device_get_source (source_device) == GDK_SOURCE_TOUCHSCREEN)
+ {
+ /* Synthesize a leave notify event
+ * whenever a touch device is released
+ */
+ pointer_info->need_touch_press_enter = TRUE;
+ _gdk_synthesize_crossing_events (display,
+ pointer_window, NULL,
+ device, source_device,
+ GDK_CROSSING_TOUCH_END,
+ toplevel_x, toplevel_y,
+ state, time_, NULL,
+ serial, FALSE);
+ }
+ return TRUE;
+
+ case GDK_TOUCH_BEGIN:
+ case GDK_TOUCH_END:
+ convert_toplevel_coords_to_window (event_win,
+ toplevel_x, toplevel_y,
+ &event->button.x, &event->button.y);
+ gdk_event_get_root_coords (source_event,
+ &event->touch.x_root,
+ &event->touch.y_root);
+ event->touch.state = state;
+ event->touch.device = source_event->touch.device;
+ event->touch.axes = g_memdup (source_event->touch.axes,
+ sizeof (gdouble) * gdk_device_get_n_axes (source_event->touch.device));
+ event->touch.sequence = source_event->touch.sequence;
+ event->touch.emulating_pointer = source_event->touch.emulating_pointer;
+
+ gdk_event_set_source_device (event, source_device);
+
+ if ((type == GDK_TOUCH_END &&
+ _gdk_event_get_pointer_emulated (source_event)) &&
+ pointer_window == pointer_info->window_under_pointer &&
+ gdk_device_get_source (source_device) == GDK_SOURCE_TOUCHSCREEN)
+ {
+ /* Synthesize a leave notify event
+ * whenever a touch device is released
+ */
+ pointer_info->need_touch_press_enter = TRUE;
+ _gdk_synthesize_crossing_events (display,
+ pointer_window, NULL,
+ device, source_device,
+ GDK_CROSSING_TOUCH_END,
+ toplevel_x, toplevel_y,
+ state, time_, NULL,
+ serial, FALSE);
+ }
return TRUE;
case GDK_SCROLL:
event->scroll.y_root = source_event->scroll.y_root;
event->scroll.state = state;
event->scroll.device = source_event->scroll.device;
+ event->scroll.delta_x = source_event->scroll.delta_x;
+ event->scroll.delta_y = source_event->scroll.delta_y;
gdk_event_set_source_device (event, source_device);
return TRUE;
}
#ifdef DEBUG_WINDOW_PRINTING
+
+#ifdef GDK_WINDOWING_X11
+#include "x11/gdkx.h"
+#endif
+
static void
gdk_window_print (GdkWindow *window,
int indent)
{
- GdkRectangle r;
+ char *s;
const char *window_types[] = {
"root",
"toplevel",
g_print (" abs[%d,%d]",
window->abs_x, window->abs_y);
- cairo_region_get_extents (window->clip_region, &r);
- if (cairo_region_is_empty (window->clip_region))
- g_print (" clipbox[empty]");
- else
- g_print (" clipbox[%d,%d %dx%d]", r.x, r.y, r.width, r.height);
+ s = print_region (window->clip_region);
+ g_print (" clipbox[%s]", s);
g_print ("\n");
}
void
_gdk_windowing_got_event (GdkDisplay *display,
- GList *event_link,
- GdkEvent *event,
- gulong serial)
+ GList *event_link,
+ GdkEvent *event,
+ gulong serial)
{
GdkWindow *event_window;
gdouble x, y;
gboolean unlink_event;
- guint old_state, old_button;
GdkDeviceGrabInfo *button_release_grab;
- GdkPointerWindowInfo *pointer_info;
+ GdkPointerWindowInfo *pointer_info = NULL;
GdkDevice *device, *source_device;
gboolean is_toplevel;
{
GdkInputMode mode;
+ if (gdk_device_get_source (device) != GDK_SOURCE_KEYBOARD)
+ {
+ pointer_info = _gdk_display_get_pointer_info (display, device);
+
+ if (source_device != pointer_info->last_slave &&
+ gdk_device_get_device_type (source_device) == GDK_DEVICE_TYPE_SLAVE)
+ pointer_info->last_slave = source_device;
+ else if (pointer_info->last_slave)
+ source_device = pointer_info->last_slave;
+ }
+
g_object_get (device, "input-mode", &mode, NULL);
_gdk_display_device_grab_update (display, device, source_device, serial);
if (!event_window)
return;
- pointer_info = _gdk_display_get_pointer_info (display, device);
-
#ifdef DEBUG_WINDOW_PRINTING
if (event->type == GDK_KEY_PRESS &&
(event->key.keyval == 0xa7 ||
event->key.keyval == 0xbd))
{
- gdk_window_print_tree (event_window, 0,
- event->key.keyval == 0xbd);
+ gdk_window_print_tree (event_window, 0, event->key.keyval == 0xbd);
}
#endif
if (event->type == GDK_VISIBILITY_NOTIFY)
{
event_window->native_visibility = event->visibility.state;
- gdk_window_update_visibility_recursively (event_window,
- event_window);
+ gdk_window_update_visibility_recursively (event_window, event_window);
return;
}
if (!(is_button_type (event->type) ||
- is_motion_type (event->type)) ||
+ is_motion_type (event->type)) ||
event_window->window_type == GDK_WINDOW_ROOT)
return;
*/
/* We ended up in this window after some (perhaps other clients)
- grab, so update the toplevel_under_window state */
+ * grab, so update the toplevel_under_window state
+ */
if (is_toplevel &&
- event->type == GDK_ENTER_NOTIFY &&
- event->crossing.mode == GDK_CROSSING_UNGRAB)
- {
- if (pointer_info->toplevel_under_pointer)
- g_object_unref (pointer_info->toplevel_under_pointer);
- pointer_info->toplevel_under_pointer = g_object_ref (event_window);
- }
+ event->type == GDK_ENTER_NOTIFY &&
+ event->crossing.mode == GDK_CROSSING_UNGRAB)
+ {
+ if (pointer_info->toplevel_under_pointer)
+ g_object_unref (pointer_info->toplevel_under_pointer);
+ pointer_info->toplevel_under_pointer = g_object_ref (event_window);
+ }
unlink_event = TRUE;
goto out;
if (is_toplevel)
{
if (event->type == GDK_ENTER_NOTIFY &&
- event->crossing.detail != GDK_NOTIFY_INFERIOR)
- {
- if (pointer_info->toplevel_under_pointer)
- g_object_unref (pointer_info->toplevel_under_pointer);
- pointer_info->toplevel_under_pointer = g_object_ref (event_window);
- }
+ event->crossing.detail != GDK_NOTIFY_INFERIOR)
+ {
+ if (pointer_info->toplevel_under_pointer)
+ g_object_unref (pointer_info->toplevel_under_pointer);
+ pointer_info->toplevel_under_pointer = g_object_ref (event_window);
+ }
else if (event->type == GDK_LEAVE_NOTIFY &&
- event->crossing.detail != GDK_NOTIFY_INFERIOR &&
- pointer_info->toplevel_under_pointer == event_window)
- {
- if (pointer_info->toplevel_under_pointer)
- g_object_unref (pointer_info->toplevel_under_pointer);
- pointer_info->toplevel_under_pointer = NULL;
- }
+ event->crossing.detail != GDK_NOTIFY_INFERIOR &&
+ pointer_info->toplevel_under_pointer == event_window)
+ {
+ if (pointer_info->toplevel_under_pointer)
+ g_object_unref (pointer_info->toplevel_under_pointer);
+ pointer_info->toplevel_under_pointer = NULL;
+ }
}
- /* Store last pointer window and position/state */
- old_state = pointer_info->state;
- old_button = pointer_info->button;
+ if (pointer_info &&
+ (!is_touch_type (event->type) ||
+ _gdk_event_get_pointer_emulated (event)))
+ {
+ guint old_state, old_button;
- gdk_event_get_coords (event, &x, &y);
- convert_native_coords_to_toplevel (event_window, x, y, &x, &y);
- pointer_info->toplevel_x = x;
- pointer_info->toplevel_y = y;
- gdk_event_get_state (event, &pointer_info->state);
- if (event->type == GDK_BUTTON_PRESS ||
- event->type == GDK_BUTTON_RELEASE)
- pointer_info->button = event->button.button;
+ /* Store last pointer window and position/state */
+ old_state = pointer_info->state;
+ old_button = pointer_info->button;
- if (device &&
- (pointer_info->state != old_state ||
- pointer_info->button != old_button))
- _gdk_display_enable_motion_hints (display, device);
+ gdk_event_get_coords (event, &x, &y);
+ convert_native_coords_to_toplevel (event_window, x, y, &x, &y);
+ pointer_info->toplevel_x = x;
+ pointer_info->toplevel_y = y;
+ gdk_event_get_state (event, &pointer_info->state);
+
+ if (event->type == GDK_BUTTON_PRESS ||
+ event->type == GDK_BUTTON_RELEASE)
+ pointer_info->button = event->button.button;
+ else if (event->type == GDK_TOUCH_BEGIN ||
+ event->type == GDK_TOUCH_END)
+ pointer_info->button = 1;
+
+ if (device &&
+ (pointer_info->state != old_state ||
+ pointer_info->button != old_button))
+ _gdk_display_enable_motion_hints (display, device);
+ }
unlink_event = FALSE;
if (is_motion_type (event->type))
- unlink_event = proxy_pointer_event (display,
- event,
- serial);
+ unlink_event = proxy_pointer_event (display, event, serial);
else if (is_button_type (event->type))
- unlink_event = proxy_button_event (event,
- serial);
+ unlink_event = proxy_button_event (event, serial);
- if (event->type == GDK_BUTTON_RELEASE &&
+ if ((event->type == GDK_BUTTON_RELEASE ||
+ event->type == GDK_TOUCH_END) &&
!event->any.send_event)
{
- button_release_grab =
- _gdk_display_has_device_grab (display, device, serial);
- if (button_release_grab &&
- button_release_grab->implicit &&
- (event->button.state & GDK_ANY_BUTTON_MASK & ~(GDK_BUTTON1_MASK << (event->button.button - 1))) == 0)
- {
- button_release_grab->serial_end = serial;
- button_release_grab->implicit_ungrab = FALSE;
- _gdk_display_device_grab_update (display, device, source_device, serial);
- }
+ GdkEventSequence *sequence;
+
+ sequence = gdk_event_get_event_sequence (event);
+ if (event->type == GDK_TOUCH_END && sequence)
+ {
+ _gdk_display_end_touch_grab (display, device, sequence);
+ }
+
+ if (event->type == GDK_BUTTON_RELEASE ||
+ _gdk_event_get_pointer_emulated (event))
+ {
+ button_release_grab =
+ _gdk_display_has_device_grab (display, device, serial);
+
+ if (button_release_grab &&
+ button_release_grab->implicit &&
+ (event->button.state & GDK_ANY_BUTTON_MASK & ~(GDK_BUTTON1_MASK << (event->button.button - 1))) == 0)
+ {
+ button_release_grab->serial_end = serial;
+ button_release_grab->implicit_ungrab = FALSE;
+ _gdk_display_device_grab_update (display, device, source_device, serial);
+ }
+ }
}
out:
g_return_val_if_fail (GDK_IS_WINDOW (window), NULL);
- window_surface = _gdk_window_ref_cairo_surface (window);
+ window_surface = gdk_window_ref_impl_surface (window);
switch (_gdk_rendering_mode)
{
/**
* gdk_window_get_frame_extents:
* @window: a toplevel #GdkWindow
- * @rect: rectangle to fill with bounding box of the window frame
+ * @rect: (out): rectangle to fill with bounding box of the window frame
*
* Obtains the bounding box of the window, including window manager
* titlebar/borders if any. The frame position is given in root window
GDK_WINDOW_IMPL_GET_CLASS (window->impl)->fullscreen (window);
}
+/**
+ * gdk_window_set_fullscreen_mode:
+ * @window: a toplevel #GdkWindow
+ * @mode: fullscreen mode
+ *
+ * Specifies whether the @window should span over all monitors (in a multi-head
+ * setup) or only the current monitor when in fullscreen mode.
+ *
+ * The @mode argument is from the #GdkFullscreenMode enumeration.
+ * If #GDK_FULLSCREEN_ON_ALL_MONITORS is specified, the fullscreen @window will
+ * span over all monitors from the #GdkScreen.
+ *
+ * On X11, searches through the list of monitors from the #GdkScreen the ones
+ * which delimit the 4 edges of the entire #GdkScreen and will ask the window
+ * manager to span the @window over these monitors.
+ *
+ * If the XINERAMA extension is not available or not usable, this function
+ * has no effect.
+ *
+ * Not all window managers support this, so you can't rely on the fullscreen
+ * window to span over the multiple monitors when #GDK_FULLSCREEN_ON_ALL_MONITORS
+ * is specified.
+ *
+ * Since: 3.8
+ **/
+void
+gdk_window_set_fullscreen_mode (GdkWindow *window,
+ GdkFullscreenMode mode)
+{
+ GdkWindowImplClass *impl_class;
+
+ g_return_if_fail (GDK_IS_WINDOW (window));
+
+ if (window->fullscreen_mode != mode)
+ {
+ window->fullscreen_mode = mode;
+
+ impl_class = GDK_WINDOW_IMPL_GET_CLASS (window->impl);
+ if (impl_class->apply_fullscreen_mode != NULL)
+ impl_class->apply_fullscreen_mode (window);
+ }
+}
+
+/**
+ * gdk_window_get_fullscreen_mode:
+ * @window: a toplevel #GdkWindow
+ *
+ * Obtains the #GdkFullscreenMode of the @window.
+ *
+ * Returns: The #GdkFullscreenMode applied to the window when fullscreen.
+ *
+ * Since: 3.8
+ **/
+GdkFullscreenMode
+gdk_window_get_fullscreen_mode (GdkWindow *window)
+{
+ g_return_val_if_fail (GDK_IS_WINDOW (window), GDK_FULLSCREEN_ON_CURRENT_MONITOR);
+
+ return window->fullscreen_mode;
+}
+
/**
* gdk_window_unfullscreen:
* @window: a toplevel #GdkWindow
/**
* gdk_window_set_opacity:
- * @window: a top-level #GdkWindow
+ * @window: a top-level or non-native #GdkWindow
* @opacity: opacity
*
- * Request the windowing system to make @window partially transparent,
+ * Set @window to render as partially transparent,
* with opacity 0 being fully transparent and 1 fully opaque. (Values
* of the opacity parameter are clamped to the [0,1] range.)
*
- * On X11, this works only on X screens with a compositing manager
- * running.
+ * For toplevel windows this depends on support from the windowing system
+ * that may not always be there. For instance, On X11, this works only on
+ * X screens with a compositing manager running.
+ *
+ * For child windows this function only works for non-native windows.
*
- * For setting up per-pixel alpha, see gdk_screen_get_rgba_visual().
- * For making non-toplevel windows translucent, see
- * gdk_window_set_composited().
+ * For setting up per-pixel alpha topelevels, see gdk_screen_get_rgba_visual(),
+ * and for non-toplevels, see gdk_window_set_composited().
+ *
+ * Support for non-toplevel windows was added in 3.8.
*
* Since: 2.12
*/
gdk_window_set_opacity (GdkWindow *window,
gdouble opacity)
{
- GDK_WINDOW_IMPL_GET_CLASS (window->impl)->set_opacity (window, opacity);
+ if (opacity < 0)
+ opacity = 0;
+ else if (opacity > 1)
+ opacity = 1;
+
+ window->alpha = round (opacity * 255);
+
+ if (window->destroyed)
+ return;
+
+ if (gdk_window_has_impl (window))
+ GDK_WINDOW_IMPL_GET_CLASS (window->impl)->set_opacity (window, opacity);
+ else
+ {
+ recompute_visible_regions (window, TRUE, FALSE);
+ gdk_window_invalidate_rect_full (window, NULL, TRUE, CLEAR_BG_ALL);
+ }
}
/* This function is called when the XWindow is really gone.
{
GDK_WINDOW_IMPL_GET_CLASS (window->impl)->delete_property (window, property);
}
+
+static void
+gdk_window_paint_on_clock (GdkFrameClock *clock,
+ void *data)
+{
+ GdkWindow *window;
+
+ window = GDK_WINDOW (data);
+
+ /* Update window and any children on the same clock.
+ */
+ gdk_window_process_updates_with_mode (window, PROCESS_UPDATES_WITH_SAME_CLOCK_CHILDREN);
+}
+
+/**
+ * gdk_window_set_frame_clock:
+ * @window: window to set frame clock on
+ * @clock: the clock
+ *
+ * Sets the frame clock for the window. The frame clock for a window
+ * cannot be changed while the window is mapped. Set the frame
+ * clock to #NULL to use the default frame clock. (By default the
+ * frame clock comes from the window's parent or is a global default
+ * frame clock.)
+ *
+ * Since: 3.0
+ */
+void
+gdk_window_set_frame_clock (GdkWindow *window,
+ GdkFrameClock *clock)
+{
+ g_return_if_fail (GDK_IS_WINDOW (window));
+ g_return_if_fail (clock == NULL || GDK_IS_FRAME_CLOCK (clock));
+ g_return_if_fail (!GDK_WINDOW_IS_MAPPED (window));
+
+ if (clock == window->frame_clock)
+ return;
+
+ /* If we are using our parent's clock, then the parent will repaint
+ * us when that clock fires. If we are using the default clock, then
+ * it does a gdk_window_process_all_updates() which will repaint us
+ * when the clock fires. If we are using our own clock, then we have
+ * to connect to "paint" on it ourselves and paint ourselves and
+ * any child windows.
+ */
+
+ if (clock)
+ {
+ g_object_ref (clock);
+ g_signal_connect (G_OBJECT (clock),
+ "paint",
+ G_CALLBACK (gdk_window_paint_on_clock),
+ window);
+ }
+
+ if (window->frame_clock)
+ {
+ g_signal_handlers_disconnect_by_func (G_OBJECT (window->frame_clock),
+ G_CALLBACK (gdk_window_paint_on_clock),
+ window);
+ g_object_unref (window->frame_clock);
+ }
+
+ window->frame_clock = clock;
+ g_object_notify (G_OBJECT (window), "paint-clock");
+
+ /* We probably should recurse child windows and emit notify on their
+ * paint-clock properties also, and we should emit notify when a
+ * window is first parented.
+ */
+}
+
+/**
+ * gdk_window_get_frame_clock:
+ * @window: window to get frame clock for
+ *
+ * Gets the frame clock for the window. The frame clock for a window
+ * never changes while the window is mapped. It may be changed at
+ * other times.
+ *
+ * Since: 3.0
+ * Return value: (transfer none): the frame clock
+ */
+GdkFrameClock*
+gdk_window_get_frame_clock (GdkWindow *window)
+{
+ g_return_val_if_fail (GDK_IS_WINDOW (window), NULL);
+
+ if (window->frame_clock != NULL)
+ {
+ /* Frame clock set explicitly on this window */
+ return window->frame_clock;
+ }
+ else
+ {
+ GdkWindow *parent;
+
+ /* parent's frame clock or default */
+ parent = gdk_window_get_effective_parent (window);
+ if (parent != NULL)
+ {
+ return gdk_window_get_frame_clock (parent);
+ }
+ else
+ {
+ gdk_ensure_default_frame_clock ();
+ return _gdk_default_frame_clock;
+ }
+ }
+}