From: Alexander Larsson Date: Tue, 20 Mar 2018 10:46:11 +0000 (+0100) Subject: GdkWindow -> GdkSurface: File renames X-Git-Tag: archive/raspbian/4.4.1+ds1-2+rpi1^2~18^2~22^2~856^2~5 X-Git-Url: https://dgit.raspbian.org/?a=commitdiff_plain;h=890080ebf7a12199d5500fbbf6a0daf06cc2d35f;p=gtk4.git GdkWindow -> GdkSurface: File renames Rename all *window.[ch] source files. This is an automatic operation, done by the following commands: for i in $(git ls-files gdk | grep window); do git mv $i $(echo $i | sed s/window/surface/); git sed -f g $(basename $i) $(basename $i | sed s/window/surface/) ; done git checkout NEWS* po-properties po --- diff --git a/demos/gtk-demo/list_store.c b/demos/gtk-demo/list_store.c index 0662fd1979..53c30a2b72 100644 --- a/demos/gtk-demo/list_store.c +++ b/demos/gtk-demo/list_store.c @@ -37,7 +37,7 @@ enum static Bug data[] = { { FALSE, 60482, "Normal", "scrollable notebooks and hidden tabs" }, - { FALSE, 60620, "Critical", "gdk_surface_clear_area (gdkwindow-win32.c) is not thread-safe" }, + { FALSE, 60620, "Critical", "gdk_surface_clear_area (gdksurface-win32.c) is not thread-safe" }, { FALSE, 50214, "Major", "Xft support does not clean up correctly" }, { TRUE, 52877, "Major", "GtkFileSelection needs a refresh method. " }, { FALSE, 56070, "Normal", "Can't click button after setting in sensitive" }, diff --git a/gdk/broadway/gdkbroadway.h b/gdk/broadway/gdkbroadway.h index 3e72be0f3b..33a7bff89d 100644 --- a/gdk/broadway/gdkbroadway.h +++ b/gdk/broadway/gdkbroadway.h @@ -30,7 +30,7 @@ #define __GDKBROADWAY_H_INSIDE__ #include -#include +#include #include #include diff --git a/gdk/broadway/gdkbroadwaysurface.h b/gdk/broadway/gdkbroadwaysurface.h new file mode 100644 index 0000000000..12ce448c51 --- /dev/null +++ b/gdk/broadway/gdkbroadwaysurface.h @@ -0,0 +1,54 @@ +/* GDK - The GIMP Drawing Kit + * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * 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, see . + */ + +/* + * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS + * file for a list of people on the GTK+ Team. See the ChangeLog + * files for a list of changes. These files are distributed with + * GTK+ at ftp://ftp.gtk.org/pub/gtk/. + */ + +#ifndef __GDK_BROADWAY_SURFACE_H__ +#define __GDK_BROADWAY_SURFACE_H__ + +#include + +G_BEGIN_DECLS + +#define GDK_TYPE_BROADWAY_SURFACE (gdk_broadway_surface_get_type ()) +#define GDK_BROADWAY_SURFACE(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), GDK_TYPE_BROADWAY_SURFACE, GdkBroadwaySurface)) +#define GDK_BROADWAY_SURFACE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GDK_TYPE_BROADWAY_SURFACE, GdkBroadwaySurfaceClass)) +#define GDK_IS_BROADWAY_SURFACE(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), GDK_TYPE_BROADWAY_SURFACE)) +#define GDK_IS_BROADWAY_SURFACE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GDK_TYPE_BROADWAY_SURFACE)) +#define GDK_BROADWAY_SURFACE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GDK_TYPE_BROADWAY_SURFACE, GdkBroadwaySurfaceClass)) + +#ifdef GDK_COMPILATION +typedef struct _GdkBroadwaySurface GdkBroadwaySurface; +#else +typedef GdkSurface GdkBroadwaySurface; +#endif +typedef struct _GdkBroadwaySurfaceClass GdkBroadwaySurfaceClass; + +GDK_AVAILABLE_IN_ALL +GType gdk_broadway_surface_get_type (void); + +GDK_AVAILABLE_IN_ALL +guint32 gdk_broadway_get_last_seen_time (GdkSurface *window); + +G_END_DECLS + +#endif /* __GDK_BROADWAY_SURFACE_H__ */ diff --git a/gdk/broadway/gdkbroadwaywindow.h b/gdk/broadway/gdkbroadwaywindow.h deleted file mode 100644 index 12ce448c51..0000000000 --- a/gdk/broadway/gdkbroadwaywindow.h +++ /dev/null @@ -1,54 +0,0 @@ -/* GDK - The GIMP Drawing Kit - * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * 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, see . - */ - -/* - * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS - * file for a list of people on the GTK+ Team. See the ChangeLog - * files for a list of changes. These files are distributed with - * GTK+ at ftp://ftp.gtk.org/pub/gtk/. - */ - -#ifndef __GDK_BROADWAY_SURFACE_H__ -#define __GDK_BROADWAY_SURFACE_H__ - -#include - -G_BEGIN_DECLS - -#define GDK_TYPE_BROADWAY_SURFACE (gdk_broadway_surface_get_type ()) -#define GDK_BROADWAY_SURFACE(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), GDK_TYPE_BROADWAY_SURFACE, GdkBroadwaySurface)) -#define GDK_BROADWAY_SURFACE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GDK_TYPE_BROADWAY_SURFACE, GdkBroadwaySurfaceClass)) -#define GDK_IS_BROADWAY_SURFACE(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), GDK_TYPE_BROADWAY_SURFACE)) -#define GDK_IS_BROADWAY_SURFACE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GDK_TYPE_BROADWAY_SURFACE)) -#define GDK_BROADWAY_SURFACE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GDK_TYPE_BROADWAY_SURFACE, GdkBroadwaySurfaceClass)) - -#ifdef GDK_COMPILATION -typedef struct _GdkBroadwaySurface GdkBroadwaySurface; -#else -typedef GdkSurface GdkBroadwaySurface; -#endif -typedef struct _GdkBroadwaySurfaceClass GdkBroadwaySurfaceClass; - -GDK_AVAILABLE_IN_ALL -GType gdk_broadway_surface_get_type (void); - -GDK_AVAILABLE_IN_ALL -guint32 gdk_broadway_get_last_seen_time (GdkSurface *window); - -G_END_DECLS - -#endif /* __GDK_BROADWAY_SURFACE_H__ */ diff --git a/gdk/broadway/gdkdevice-broadway.c b/gdk/broadway/gdkdevice-broadway.c index fda10591ac..8158871b64 100644 --- a/gdk/broadway/gdkdevice-broadway.c +++ b/gdk/broadway/gdkdevice-broadway.c @@ -20,7 +20,7 @@ #include "gdkdevice-broadway.h" -#include "gdkwindow.h" +#include "gdksurface.h" #include "gdkprivate-broadway.h" static gboolean gdk_broadway_device_get_history (GdkDevice *device, diff --git a/gdk/broadway/gdkdisplay-broadway.h b/gdk/broadway/gdkdisplay-broadway.h index 51d59ae077..25a962ac66 100644 --- a/gdk/broadway/gdkdisplay-broadway.h +++ b/gdk/broadway/gdkdisplay-broadway.h @@ -26,7 +26,7 @@ #include "gdkdisplayprivate.h" #include "gdkkeys.h" -#include "gdkwindow.h" +#include "gdksurface.h" #include "gdkinternals.h" #include "gdkbroadway-server.h" #include "gdkmonitorprivate.h" diff --git a/gdk/broadway/gdkprivate-broadway.h b/gdk/broadway/gdkprivate-broadway.h index abbdaadce9..8f1a9237eb 100644 --- a/gdk/broadway/gdkprivate-broadway.h +++ b/gdk/broadway/gdkprivate-broadway.h @@ -31,11 +31,11 @@ #include #include -#include "gdkwindow-broadway.h" +#include "gdksurface-broadway.h" #include "gdkdisplay-broadway.h" #include "gdkbroadwaycursor.h" -#include "gdkbroadwaywindow.h" +#include "gdkbroadwaysurface.h" void _gdk_broadway_resync_windows (void); diff --git a/gdk/broadway/gdksurface-broadway.c b/gdk/broadway/gdksurface-broadway.c new file mode 100644 index 0000000000..30777d8756 --- /dev/null +++ b/gdk/broadway/gdksurface-broadway.c @@ -0,0 +1,1428 @@ +/* GDK - The GIMP Drawing Kit + * Copyright (C) 1995-2007 Peter Mattis, Spencer Kimball, + * Josh MacDonald, Ryan Lortie + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * 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, see . + */ + +/* + * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS + * file for a list of people on the GTK+ Team. See the ChangeLog + * files for a list of changes. These files are distributed with + * GTK+ at ftp://ftp.gtk.org/pub/gtk/. + */ + +#include "config.h" + +#include "gdksurface-broadway.h" + +#include "gdkbroadwaydisplay.h" +#include "gdkdisplay.h" +#include "gdksurface.h" +#include "gdksurfaceimpl.h" +#include "gdkdisplay-broadway.h" +#include "gdkprivate-broadway.h" +#include "gdkinternals.h" +#include "gdkdeviceprivate.h" +#include "gdkeventsource.h" +#include +#include + +#include +#include +#include + +/* Forward declarations */ +static void gdk_surface_impl_broadway_finalize (GObject *object); + +#define WINDOW_IS_TOPLEVEL(window) \ + (GDK_SURFACE_TYPE (window) != GDK_SURFACE_CHILD) + +struct _GdkBroadwaySurface { + GdkSurface parent; +}; + +struct _GdkBroadwaySurfaceClass { + GdkSurfaceClass parent_class; +}; + +G_DEFINE_TYPE (GdkBroadwaySurface, gdk_broadway_surface, GDK_TYPE_SURFACE) + +static void +gdk_broadway_surface_class_init (GdkBroadwaySurfaceClass *broadway_surface_class) +{ +} + +static void +gdk_broadway_surface_init (GdkBroadwaySurface *broadway_surface) +{ +} + +G_DEFINE_TYPE (GdkSurfaceImplBroadway, + gdk_surface_impl_broadway, + GDK_TYPE_SURFACE_IMPL) + +static GdkDisplay * +find_broadway_display (void) +{ + GdkDisplay *display; + GSList *list, *l; + + display = NULL; + + list = gdk_display_manager_list_displays (gdk_display_manager_get ()); + for (l = list; l; l = l->next) + { + if (GDK_IS_BROADWAY_DISPLAY (l->data)) + { + display = l->data; + break; + } + } + g_slist_free (list); + + return display; +} + +static guint flush_id = 0; + +static gboolean +flush_idle (gpointer data) +{ + flush_id = 0; + + gdk_display_flush (find_broadway_display ()); + + return FALSE; +} + +/* We need to flush in an idle rather than AFTER_PAINT, as the clock + is frozen during e.g. window resizes so the paint will not happen + and the window resize request is never flushed. */ +static void +queue_flush (GdkSurface *window) +{ + if (flush_id == 0) + { + flush_id = g_idle_add (flush_idle, NULL); + g_source_set_name_by_id (flush_id, "[gtk+] flush_idle"); + } +} + +static void +gdk_surface_impl_broadway_init (GdkSurfaceImplBroadway *impl) +{ + impl->toplevel_window_type = -1; +} + +static void +gdk_surface_impl_broadway_finalize (GObject *object) +{ + GdkSurface *wrapper; + GdkSurfaceImplBroadway *impl; + GdkBroadwayDisplay *broadway_display; + + g_return_if_fail (GDK_IS_SURFACE_IMPL_BROADWAY (object)); + + impl = GDK_SURFACE_IMPL_BROADWAY (object); + + wrapper = impl->wrapper; + + _gdk_broadway_surface_grab_check_destroy (wrapper); + + broadway_display = GDK_BROADWAY_DISPLAY (gdk_surface_get_display (impl->wrapper)); + + g_hash_table_remove (broadway_display->id_ht, GINT_TO_POINTER(impl->id)); + + if (impl->cursor) + g_object_unref (impl->cursor); + + broadway_display->toplevels = g_list_remove (broadway_display->toplevels, impl); + + G_OBJECT_CLASS (gdk_surface_impl_broadway_parent_class)->finalize (object); +} + +static gboolean +thaw_clock_cb (GdkFrameClock *clock) +{ + _gdk_frame_clock_thaw (clock); + g_object_unref (clock); + return G_SOURCE_REMOVE; +} + +void +_gdk_broadway_roundtrip_notify (GdkSurface *window, + guint32 tag, + gboolean local_reply) +{ + GdkFrameClock *clock = gdk_surface_get_frame_clock (window); + + /* If there is no remove web client, rate limit update to once a second */ + if (local_reply) + g_timeout_add_seconds (1, (GSourceFunc)thaw_clock_cb, g_object_ref (clock)); + else + _gdk_frame_clock_thaw (clock); +} + +static void +on_frame_clock_after_paint (GdkFrameClock *clock, + GdkSurface *window) +{ + GdkDisplay *display = gdk_surface_get_display (window); + GdkSurfaceImplBroadway *impl = GDK_SURFACE_IMPL_BROADWAY (window->impl); + GdkBroadwayDisplay *broadway_display; + + _gdk_frame_clock_freeze (gdk_surface_get_frame_clock (window)); + + broadway_display = GDK_BROADWAY_DISPLAY (display); + + _gdk_broadway_server_roundtrip (broadway_display->server, impl->id, _gdk_display_get_next_serial (display)); + + gdk_display_flush (display); +} + +static void +connect_frame_clock (GdkSurface *window) +{ + if (WINDOW_IS_TOPLEVEL (window)) + { + GdkFrameClock *frame_clock = gdk_surface_get_frame_clock (window); + + g_signal_connect (frame_clock, "after-paint", + G_CALLBACK (on_frame_clock_after_paint), window); + } +} + +void +_gdk_broadway_display_create_window_impl (GdkDisplay *display, + GdkSurface *window, + GdkSurface *real_parent, + GdkEventMask event_mask, + GdkSurfaceAttr *attributes) +{ + GdkSurfaceImplBroadway *impl; + GdkBroadwayDisplay *broadway_display; + + broadway_display = GDK_BROADWAY_DISPLAY (display); + + impl = g_object_new (GDK_TYPE_SURFACE_IMPL_BROADWAY, NULL); + window->impl = (GdkSurfaceImpl *)impl; + impl->id = _gdk_broadway_server_new_surface (broadway_display->server, + window->x, + window->y, + window->width, + window->height, + window->window_type == GDK_SURFACE_TEMP); + g_hash_table_insert (broadway_display->id_ht, GINT_TO_POINTER(impl->id), window); + impl->wrapper = window; + + g_assert (window->window_type == GDK_SURFACE_TOPLEVEL || + window->window_type == GDK_SURFACE_TEMP); + g_assert (window->parent == NULL); + + broadway_display->toplevels = g_list_prepend (broadway_display->toplevels, impl); + + connect_frame_clock (window); +} + +static cairo_surface_t * +gdk_surface_broadway_ref_cairo_surface (GdkSurface *window) +{ + GdkSurfaceImplBroadway *impl = GDK_SURFACE_IMPL_BROADWAY (window->impl); + + if (GDK_IS_SURFACE_IMPL_BROADWAY (window) && + GDK_SURFACE_DESTROYED (impl->wrapper)) + return NULL; + + return cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 1, 1); +} + +static void +_gdk_broadway_surface_destroy (GdkSurface *window, + gboolean recursing, + gboolean foreign_destroy) +{ + GdkSurfaceImplBroadway *impl; + GdkBroadwayDisplay *broadway_display; + + g_return_if_fail (GDK_IS_SURFACE (window)); + + impl = GDK_SURFACE_IMPL_BROADWAY (window->impl); + + if (impl->node_data) + g_array_unref (impl->node_data); + if (impl->node_data_textures) + g_ptr_array_unref (impl->node_data_textures); + + _gdk_broadway_surface_grab_check_destroy (window); + + broadway_display = GDK_BROADWAY_DISPLAY (gdk_surface_get_display (window)); + g_hash_table_remove (broadway_display->id_ht, GINT_TO_POINTER (impl->id)); + + _gdk_broadway_server_destroy_surface (broadway_display->server, impl->id); + +} + +void +gdk_broadway_surface_set_nodes (GdkSurface *window, + GArray *nodes, + GPtrArray *node_textures) +{ + GdkSurfaceImplBroadway *impl; + GdkBroadwayDisplay *broadway_display; + + g_return_if_fail (GDK_IS_SURFACE (window)); + + impl = GDK_SURFACE_IMPL_BROADWAY (window->impl); + + broadway_display = GDK_BROADWAY_DISPLAY (gdk_surface_get_display (window)); + + if (nodes) + g_array_ref (nodes); + if (impl->node_data) + g_array_unref (impl->node_data); + impl->node_data = nodes; + + if (node_textures) + g_ptr_array_ref (node_textures); + if (impl->node_data_textures) + g_ptr_array_unref (impl->node_data_textures); + impl->node_data_textures = node_textures; + + gdk_broadway_server_surface_set_nodes (broadway_display->server, impl->id, impl->node_data); +} + +/* This function is called when the XWindow is really gone. + */ +static void +gdk_broadway_surface_destroy_notify (GdkSurface *window) +{ + if (!GDK_SURFACE_DESTROYED (window)) + _gdk_surface_destroy (window, TRUE); + + g_object_unref (window); +} + +static void +gdk_surface_broadway_show (GdkSurface *window, gboolean already_mapped) +{ + GdkSurfaceImplBroadway *impl; + GdkBroadwayDisplay *broadway_display; + + impl = GDK_SURFACE_IMPL_BROADWAY (window->impl); + impl->visible = TRUE; + + if (window->event_mask & GDK_STRUCTURE_MASK) + _gdk_make_event (GDK_SURFACE (window), GDK_MAP, NULL, FALSE); + + if (window->parent && window->parent->event_mask & GDK_SUBSTRUCTURE_MASK) + _gdk_make_event (GDK_SURFACE (window), GDK_MAP, NULL, FALSE); + + broadway_display = GDK_BROADWAY_DISPLAY (gdk_surface_get_display (window)); + if (_gdk_broadway_server_surface_show (broadway_display->server, impl->id)) + queue_flush (window); + +} + +static void +gdk_surface_broadway_hide (GdkSurface *window) +{ + GdkSurfaceImplBroadway *impl; + GdkBroadwayDisplay *broadway_display; + + impl = GDK_SURFACE_IMPL_BROADWAY (window->impl); + impl->visible = FALSE; + + if (window->event_mask & GDK_STRUCTURE_MASK) + _gdk_make_event (GDK_SURFACE (window), GDK_UNMAP, NULL, FALSE); + + if (window->parent && window->parent->event_mask & GDK_SUBSTRUCTURE_MASK) + _gdk_make_event (GDK_SURFACE (window), GDK_UNMAP, NULL, FALSE); + + broadway_display = GDK_BROADWAY_DISPLAY (gdk_surface_get_display (window)); + + _gdk_broadway_surface_grab_check_unmap (window, + _gdk_broadway_server_get_next_serial (broadway_display->server)); + + if (_gdk_broadway_server_surface_hide (broadway_display->server, impl->id)) + queue_flush (window); + + _gdk_surface_clear_update_area (window); +} + +static void +gdk_surface_broadway_withdraw (GdkSurface *window) +{ + gdk_surface_broadway_hide (window); +} + +static void +gdk_surface_broadway_move_resize (GdkSurface *window, + gboolean with_move, + gint x, + gint y, + gint width, + gint height) +{ + GdkSurfaceImplBroadway *impl = GDK_SURFACE_IMPL_BROADWAY (window->impl); + GdkBroadwayDisplay *broadway_display; + gboolean size_changed; + + size_changed = FALSE; + + broadway_display = GDK_BROADWAY_DISPLAY (gdk_surface_get_display (window)); + + if (width > 0 || height > 0) + { + if (width < 1) + width = 1; + + if (height < 1) + height = 1; + + if (width != window->width || + height != window->height) + { + size_changed = TRUE; + + /* Resize clears the content */ + impl->dirty = TRUE; + impl->last_synced = FALSE; + + window->width = width; + window->height = height; + } + } + + _gdk_broadway_server_surface_move_resize (broadway_display->server, + impl->id, + with_move, + x, y, + window->width, window->height); + queue_flush (window); + if (size_changed) + window->resize_count++; +} + +static void +gdk_surface_broadway_raise (GdkSurface *window) +{ +} + +static void +gdk_surface_broadway_restack_toplevel (GdkSurface *window, + GdkSurface *sibling, + gboolean above) +{ +} + +static void +gdk_surface_broadway_lower (GdkSurface *window) +{ +} + + +static void +gdk_broadway_surface_focus (GdkSurface *window, + guint32 timestamp) +{ + GdkSurfaceImplBroadway *impl; + GdkBroadwayDisplay *broadway_display; + + g_return_if_fail (GDK_IS_SURFACE (window)); + + if (GDK_SURFACE_DESTROYED (window) || + !window->accept_focus) + return; + + impl = GDK_SURFACE_IMPL_BROADWAY (window->impl); + broadway_display = GDK_BROADWAY_DISPLAY (gdk_surface_get_display (window)); + _gdk_broadway_server_surface_focus (broadway_display->server, + impl->id); +} + +static void +gdk_broadway_surface_set_type_hint (GdkSurface *window, + GdkSurfaceTypeHint hint) +{ +} + +static GdkSurfaceTypeHint +gdk_broadway_surface_get_type_hint (GdkSurface *window) +{ + return GDK_SURFACE_TYPE_HINT_NORMAL; +} + +static void +gdk_broadway_surface_set_modal_hint (GdkSurface *window, + gboolean modal) +{ +} + +static void +gdk_broadway_surface_set_skip_taskbar_hint (GdkSurface *window, + gboolean skips_taskbar) +{ +} + +static void +gdk_broadway_surface_set_skip_pager_hint (GdkSurface *window, + gboolean skips_pager) +{ +} + +static void +gdk_broadway_surface_set_urgency_hint (GdkSurface *window, + gboolean urgent) +{ +} + +static void +gdk_broadway_surface_set_geometry_hints (GdkSurface *window, + const GdkGeometry *geometry, + GdkSurfaceHints geom_mask) +{ + GdkSurfaceImplBroadway *impl; + + impl = GDK_SURFACE_IMPL_BROADWAY (window->impl); + + impl->geometry_hints = *geometry; + impl->geometry_hints_mask = geom_mask; +} + +static void +gdk_broadway_surface_set_title (GdkSurface *window, + const gchar *title) +{ +} + +static void +gdk_broadway_surface_set_role (GdkSurface *window, + const gchar *role) +{ +} + +static void +gdk_broadway_surface_set_startup_id (GdkSurface *window, + const gchar *startup_id) +{ +} + +static void +gdk_broadway_surface_set_transient_for (GdkSurface *window, + GdkSurface *parent) +{ + GdkBroadwayDisplay *display; + GdkSurfaceImplBroadway *impl; + int parent_id; + + impl = GDK_SURFACE_IMPL_BROADWAY (window->impl); + + parent_id = 0; + if (parent) + parent_id = GDK_SURFACE_IMPL_BROADWAY (parent->impl)->id; + + impl->transient_for = parent_id; + + display = GDK_BROADWAY_DISPLAY (gdk_surface_get_display (impl->wrapper)); + _gdk_broadway_server_surface_set_transient_for (display->server, impl->id, impl->transient_for); +} + +static void +gdk_surface_broadway_get_geometry (GdkSurface *window, + gint *x, + gint *y, + gint *width, + gint *height) +{ + GdkSurfaceImplBroadway *impl; + + g_return_if_fail (GDK_IS_SURFACE (window)); + + impl = GDK_SURFACE_IMPL_BROADWAY (window->impl); + + /* TODO: These should really roundtrip to the client to get the current data */ + + if (x) + *x = impl->wrapper->x; + if (y) + *y = impl->wrapper->y; + if (width) + *width = impl->wrapper->width; + if (height) + *height = impl->wrapper->height; + +} + +static void +gdk_surface_broadway_get_root_coords (GdkSurface *window, + gint x, + gint y, + gint *root_x, + gint *root_y) +{ + GdkSurfaceImplBroadway *impl; + + impl = GDK_SURFACE_IMPL_BROADWAY (window->impl); + + if (root_x) + *root_x = x + impl->wrapper->x; + if (root_y) + *root_y = y + impl->wrapper->y; +} + +static void +gdk_broadway_surface_get_frame_extents (GdkSurface *window, + GdkRectangle *rect) +{ + g_return_if_fail (rect != NULL); + + /* TODO: This should take wm frame into account */ + + rect->x = window->x; + rect->y = window->y; + rect->width = window->width; + rect->height = window->height; +} + +static gboolean +gdk_surface_broadway_get_device_state (GdkSurface *window, + GdkDevice *device, + gdouble *x, + gdouble *y, + GdkModifierType *mask) +{ + GdkSurface *child; + + g_return_val_if_fail (window == NULL || GDK_IS_SURFACE (window), FALSE); + + if (GDK_SURFACE_DESTROYED (window)) + return FALSE; + + GDK_DEVICE_GET_CLASS (device)->query_state (device, window, + &child, + NULL, NULL, + x, y, mask); + return child != NULL; +} + +static GdkEventMask +gdk_surface_broadway_get_events (GdkSurface *window) +{ + if (GDK_SURFACE_DESTROYED (window)) + return 0; + + return 0; +} + +static void +gdk_surface_broadway_set_events (GdkSurface *window, + GdkEventMask event_mask) +{ + if (!GDK_SURFACE_DESTROYED (window)) + { + } +} + +static void +gdk_surface_broadway_shape_combine_region (GdkSurface *window, + const cairo_region_t *shape_region, + gint offset_x, + gint offset_y) +{ +} + +static void +gdk_surface_broadway_input_shape_combine_region (GdkSurface *window, + const cairo_region_t *shape_region, + gint offset_x, + gint offset_y) +{ +} + +static void +gdk_broadway_surface_set_accept_focus (GdkSurface *window, + gboolean accept_focus) +{ + accept_focus = accept_focus != FALSE; + + if (window->accept_focus != accept_focus) + { + window->accept_focus = accept_focus; + } +} + +static void +gdk_broadway_surface_set_focus_on_map (GdkSurface *window, + gboolean focus_on_map) +{ + focus_on_map = focus_on_map != FALSE; + + if (window->focus_on_map != focus_on_map) + { + window->focus_on_map = focus_on_map; + } +} + + +static void +gdk_broadway_surface_set_icon_list (GdkSurface *window, + GList *surfaces) +{ +} + +static void +gdk_broadway_surface_set_icon_name (GdkSurface *window, + const gchar *name) +{ + if (GDK_SURFACE_DESTROYED (window) || + !WINDOW_IS_TOPLEVEL (window)) + return; + + g_object_set_qdata (G_OBJECT (window), g_quark_from_static_string ("gdk-icon-name-set"), + GUINT_TO_POINTER (name != NULL)); +} + +static void +gdk_broadway_surface_iconify (GdkSurface *window) +{ + if (GDK_SURFACE_DESTROYED (window) || + !WINDOW_IS_TOPLEVEL (window)) + return; +} + +static void +gdk_broadway_surface_deiconify (GdkSurface *window) +{ + if (GDK_SURFACE_DESTROYED (window) || + !WINDOW_IS_TOPLEVEL (window)) + return; +} + +static void +gdk_broadway_surface_stick (GdkSurface *window) +{ + if (GDK_SURFACE_DESTROYED (window) || + !WINDOW_IS_TOPLEVEL (window)) + return; + +} + +static void +gdk_broadway_surface_unstick (GdkSurface *window) +{ + if (GDK_SURFACE_DESTROYED (window) || + !WINDOW_IS_TOPLEVEL (window)) + return; + +} + +static void +gdk_broadway_surface_maximize (GdkSurface *window) +{ + GdkSurfaceImplBroadway *impl; + GdkDisplay *display; + GdkMonitor *monitor; + GdkRectangle geom; + + if (GDK_SURFACE_DESTROYED (window) || + !WINDOW_IS_TOPLEVEL (window)) + return; + + impl = GDK_SURFACE_IMPL_BROADWAY (window->impl); + + if (impl->maximized) + return; + + impl->maximized = TRUE; + + gdk_synthesize_window_state (window, 0, GDK_SURFACE_STATE_MAXIMIZED); + + impl->pre_maximize_x = window->x; + impl->pre_maximize_y = window->y; + impl->pre_maximize_width = window->width; + impl->pre_maximize_height = window->height; + + display = gdk_surface_get_display (window); + monitor = gdk_display_get_primary_monitor (display); + gdk_monitor_get_geometry (monitor, &geom); + + gdk_surface_move_resize (window, + geom.x, geom.y, + geom.width, geom.height); +} + +static void +gdk_broadway_surface_unmaximize (GdkSurface *window) +{ + GdkSurfaceImplBroadway *impl; + + if (GDK_SURFACE_DESTROYED (window) || + !WINDOW_IS_TOPLEVEL (window)) + return; + + impl = GDK_SURFACE_IMPL_BROADWAY (window->impl); + + if (!impl->maximized) + return; + + impl->maximized = FALSE; + + gdk_synthesize_window_state (window, GDK_SURFACE_STATE_MAXIMIZED, 0); + + gdk_surface_move_resize (window, + impl->pre_maximize_x, + impl->pre_maximize_y, + impl->pre_maximize_width, + impl->pre_maximize_height); +} + +static void +gdk_broadway_surface_fullscreen (GdkSurface *window) +{ + if (GDK_SURFACE_DESTROYED (window) || + !WINDOW_IS_TOPLEVEL (window)) + return; + +} + +static void +gdk_broadway_surface_unfullscreen (GdkSurface *window) +{ + if (GDK_SURFACE_DESTROYED (window) || + !WINDOW_IS_TOPLEVEL (window)) + return; + +} + +static void +gdk_broadway_surface_set_keep_above (GdkSurface *window, + gboolean setting) +{ + g_return_if_fail (GDK_IS_SURFACE (window)); + + if (GDK_SURFACE_DESTROYED (window) || + !WINDOW_IS_TOPLEVEL (window)) + return; + +} + +static void +gdk_broadway_surface_set_keep_below (GdkSurface *window, gboolean setting) +{ + g_return_if_fail (GDK_IS_SURFACE (window)); + + if (GDK_SURFACE_DESTROYED (window) || + !WINDOW_IS_TOPLEVEL (window)) + return; + +} + +static GdkSurface * +gdk_broadway_surface_get_group (GdkSurface *window) +{ + if (GDK_SURFACE_DESTROYED (window) || + !WINDOW_IS_TOPLEVEL (window)) + return NULL; + + return window; +} + +static void +gdk_broadway_surface_set_group (GdkSurface *window, + GdkSurface *leader) +{ +} + +static void +gdk_broadway_surface_set_decorations (GdkSurface *window, + GdkWMDecoration decorations) +{ + if (GDK_SURFACE_DESTROYED (window) || + !WINDOW_IS_TOPLEVEL (window)) + return; + +} + +static gboolean +gdk_broadway_surface_get_decorations (GdkSurface *window, + GdkWMDecoration *decorations) +{ + gboolean result = FALSE; + + if (GDK_SURFACE_DESTROYED (window) || + !WINDOW_IS_TOPLEVEL (window)) + return FALSE; + + return result; +} + +static void +gdk_broadway_surface_set_functions (GdkSurface *window, + GdkWMFunction functions) +{ + g_return_if_fail (GDK_IS_SURFACE (window)); + + if (GDK_SURFACE_DESTROYED (window) || + !WINDOW_IS_TOPLEVEL (window)) + return; +} + +static void +gdk_broadway_surface_end_paint (GdkSurface *window) +{ + GdkSurfaceImplBroadway *impl; + impl = GDK_SURFACE_IMPL_BROADWAY (window->impl); + impl->dirty = TRUE; +} + +typedef struct _MoveResizeData MoveResizeData; + +struct _MoveResizeData +{ + GdkDisplay *display; + + GdkSurface *moveresize_window; + GdkSurface *moveresize_emulation_window; + gboolean is_resize; + GdkSurfaceEdge resize_edge; + gint moveresize_button; + gint moveresize_x; + gint moveresize_y; + gint moveresize_orig_x; + gint moveresize_orig_y; + gint moveresize_orig_width; + gint moveresize_orig_height; + long moveresize_process_time; + GdkSurfaceHints moveresize_geom_mask; + GdkGeometry moveresize_geometry; + BroadwayInputMsg *moveresize_pending_event; +}; + +static MoveResizeData * +get_move_resize_data (GdkDisplay *display, + gboolean create) +{ + GdkBroadwayDisplay *broadway_display; + MoveResizeData *mv_resize; + + broadway_display = GDK_BROADWAY_DISPLAY (display); + + mv_resize = broadway_display->move_resize_data; + + if (!mv_resize && create) + { + mv_resize = g_new0 (MoveResizeData, 1); + mv_resize->display = display; + + broadway_display->move_resize_data = mv_resize; + } + + return mv_resize; +} + +static void +update_pos (MoveResizeData *mv_resize, + gint new_root_x, + gint new_root_y) +{ + gint dx, dy; + + dx = new_root_x - mv_resize->moveresize_x; + dy = new_root_y - mv_resize->moveresize_y; + + if (mv_resize->is_resize) + { + gint x, y, w, h; + + x = mv_resize->moveresize_orig_x; + y = mv_resize->moveresize_orig_y; + + w = mv_resize->moveresize_orig_width; + h = mv_resize->moveresize_orig_height; + + switch (mv_resize->resize_edge) + { + case GDK_SURFACE_EDGE_NORTH_WEST: + x += dx; + y += dy; + w -= dx; + h -= dy; + break; + case GDK_SURFACE_EDGE_NORTH: + y += dy; + h -= dy; + break; + case GDK_SURFACE_EDGE_NORTH_EAST: + y += dy; + h -= dy; + w += dx; + break; + case GDK_SURFACE_EDGE_SOUTH_WEST: + h += dy; + x += dx; + w -= dx; + break; + case GDK_SURFACE_EDGE_SOUTH_EAST: + w += dx; + h += dy; + break; + case GDK_SURFACE_EDGE_SOUTH: + h += dy; + break; + case GDK_SURFACE_EDGE_EAST: + w += dx; + break; + case GDK_SURFACE_EDGE_WEST: + x += dx; + w -= dx; + break; + default: + break; + } + + x = MAX (x, 0); + y = MAX (y, 0); + w = MAX (w, 1); + h = MAX (h, 1); + + if (mv_resize->moveresize_geom_mask) + { + gdk_surface_constrain_size (&mv_resize->moveresize_geometry, + mv_resize->moveresize_geom_mask, + w, h, &w, &h); + } + + gdk_surface_move_resize (mv_resize->moveresize_window, x, y, w, h); + } + else + { + gint x, y; + + x = mv_resize->moveresize_orig_x + dx; + y = mv_resize->moveresize_orig_y + dy; + + gdk_surface_move (mv_resize->moveresize_window, x, y); + } +} + +static void +finish_drag (MoveResizeData *mv_resize) +{ + gdk_surface_destroy (mv_resize->moveresize_emulation_window); + mv_resize->moveresize_emulation_window = NULL; + g_object_unref (mv_resize->moveresize_window); + mv_resize->moveresize_window = NULL; + g_clear_pointer (&mv_resize->moveresize_pending_event, g_free); +} + +static gboolean +moveresize_lookahead (GdkDisplay *display, + MoveResizeData *mv_resize, + BroadwayInputMsg *event) +{ + GdkBroadwayDisplay *broadway_display; + + broadway_display = GDK_BROADWAY_DISPLAY (display); + + return !_gdk_broadway_server_lookahead_event (broadway_display->server, "mb"); +} + +gboolean +_gdk_broadway_moveresize_handle_event (GdkDisplay *display, + BroadwayInputMsg *event) +{ + guint button_mask = 0; + MoveResizeData *mv_resize = get_move_resize_data (display, FALSE); + + if (!mv_resize || !mv_resize->moveresize_window) + return FALSE; + + button_mask = GDK_BUTTON1_MASK << (mv_resize->moveresize_button - 1); + + switch (event->base.type) + { + case BROADWAY_EVENT_TOUCH: + if (event->touch.touch_type == 2) /* END */ + { + update_pos (mv_resize, + event->touch.root_x, + event->touch.root_y); + + finish_drag (mv_resize); + } + else if (event->touch.touch_type == 1) /* UPDATE */ + { + if (mv_resize->moveresize_window->resize_count > 0) + { + if (mv_resize->moveresize_pending_event) + *mv_resize->moveresize_pending_event = *event; + else + mv_resize->moveresize_pending_event = + g_memdup (event, sizeof (BroadwayInputMsg)); + + break; + } + update_pos (mv_resize, + event->touch.root_x, + event->touch.root_y); + } + + break; + + case BROADWAY_EVENT_POINTER_MOVE: + if (mv_resize->moveresize_window->resize_count > 0) + { + if (mv_resize->moveresize_pending_event) + *mv_resize->moveresize_pending_event = *event; + else + mv_resize->moveresize_pending_event = + g_memdup (event, sizeof (BroadwayInputMsg)); + + break; + } + if (!moveresize_lookahead (display, mv_resize, event)) + break; + + update_pos (mv_resize, + event->pointer.root_x, + event->pointer.root_y); + + /* This should never be triggered in normal cases, but in the + * case where the drag started without an implicit grab being + * in effect, we could miss the release if it occurs before + * we grab the pointer; this ensures that we will never + * get a permanently stuck grab. + */ + if ((event->pointer.state & button_mask) == 0) + finish_drag (mv_resize); + break; + + case BROADWAY_EVENT_BUTTON_RELEASE: + update_pos (mv_resize, + event->pointer.root_x, + event->pointer.root_y); + + if (event->button.button == mv_resize->moveresize_button) + finish_drag (mv_resize); + break; + default: + break; + } + return TRUE; +} + +gboolean +_gdk_broadway_moveresize_configure_done (GdkDisplay *display, + GdkSurface *window) +{ + BroadwayInputMsg *tmp_event; + MoveResizeData *mv_resize = get_move_resize_data (display, FALSE); + + if (!mv_resize || window != mv_resize->moveresize_window) + return FALSE; + + if (mv_resize->moveresize_pending_event) + { + tmp_event = mv_resize->moveresize_pending_event; + mv_resize->moveresize_pending_event = NULL; + _gdk_broadway_moveresize_handle_event (display, tmp_event); + g_free (tmp_event); + } + + return TRUE; +} + +static void +create_moveresize_window (MoveResizeData *mv_resize, + guint32 timestamp) +{ + GdkGrabStatus status; + GdkSeat *seat; + GdkDevice *pointer; + + g_assert (mv_resize->moveresize_emulation_window == NULL); + + mv_resize->moveresize_emulation_window = gdk_surface_new_temp (mv_resize->display); + + gdk_surface_show (mv_resize->moveresize_emulation_window); + + seat = gdk_display_get_default_seat (mv_resize->display); + pointer = gdk_seat_get_pointer (seat); + + G_GNUC_BEGIN_IGNORE_DEPRECATIONS; + status = gdk_device_grab (pointer, + mv_resize->moveresize_emulation_window, + GDK_OWNERSHIP_APPLICATION, + FALSE, + GDK_BUTTON_RELEASE_MASK | + GDK_POINTER_MOTION_MASK, + NULL, + timestamp); + G_GNUC_END_IGNORE_DEPRECATIONS; + + if (status != GDK_GRAB_SUCCESS) + { + /* If this fails, some other client has grabbed the window + * already. + */ + finish_drag (mv_resize); + } + + mv_resize->moveresize_process_time = 0; +} + +static void +calculate_unmoving_origin (MoveResizeData *mv_resize) +{ + GdkRectangle rect; + gint width, height; + + if (mv_resize->moveresize_geom_mask & GDK_HINT_WIN_GRAVITY && + mv_resize->moveresize_geometry.win_gravity == GDK_GRAVITY_STATIC) + { + gdk_surface_get_origin (mv_resize->moveresize_window, + &mv_resize->moveresize_orig_x, + &mv_resize->moveresize_orig_y); + } + else + { + gdk_surface_get_frame_extents (mv_resize->moveresize_window, &rect); + gdk_surface_get_geometry (mv_resize->moveresize_window, + NULL, NULL, &width, &height); + + switch (mv_resize->moveresize_geometry.win_gravity) + { + case GDK_GRAVITY_NORTH_WEST: + mv_resize->moveresize_orig_x = rect.x; + mv_resize->moveresize_orig_y = rect.y; + break; + case GDK_GRAVITY_NORTH: + mv_resize->moveresize_orig_x = rect.x + rect.width / 2 - width / 2; + mv_resize->moveresize_orig_y = rect.y; + break; + case GDK_GRAVITY_NORTH_EAST: + mv_resize->moveresize_orig_x = rect.x + rect.width - width; + mv_resize->moveresize_orig_y = rect.y; + break; + case GDK_GRAVITY_WEST: + mv_resize->moveresize_orig_x = rect.x; + mv_resize->moveresize_orig_y = rect.y + rect.height / 2 - height / 2; + break; + case GDK_GRAVITY_CENTER: + mv_resize->moveresize_orig_x = rect.x + rect.width / 2 - width / 2; + mv_resize->moveresize_orig_y = rect.y + rect.height / 2 - height / 2; + break; + case GDK_GRAVITY_EAST: + mv_resize->moveresize_orig_x = rect.x + rect.width - width; + mv_resize->moveresize_orig_y = rect.y + rect.height / 2 - height / 2; + break; + case GDK_GRAVITY_SOUTH_WEST: + mv_resize->moveresize_orig_x = rect.x; + mv_resize->moveresize_orig_y = rect.y + rect.height - height; + break; + case GDK_GRAVITY_SOUTH: + mv_resize->moveresize_orig_x = rect.x + rect.width / 2 - width / 2; + mv_resize->moveresize_orig_y = rect.y + rect.height - height; + break; + case GDK_GRAVITY_SOUTH_EAST: + mv_resize->moveresize_orig_x = rect.x + rect.width - width; + mv_resize->moveresize_orig_y = rect.y + rect.height - height; + break; + case GDK_GRAVITY_STATIC: + default: + mv_resize->moveresize_orig_x = rect.x; + mv_resize->moveresize_orig_y = rect.y; + break; + } + } +} + +static void +gdk_broadway_surface_begin_resize_drag (GdkSurface *window, + GdkSurfaceEdge edge, + GdkDevice *device, + gint button, + gint root_x, + gint root_y, + guint32 timestamp) +{ + MoveResizeData *mv_resize; + GdkSurfaceImplBroadway *impl; + + impl = GDK_SURFACE_IMPL_BROADWAY (window->impl); + + if (GDK_SURFACE_DESTROYED (window) || + !WINDOW_IS_TOPLEVEL (window)) + return; + + if (impl->maximized) + return; + + mv_resize = get_move_resize_data (gdk_surface_get_display (window), TRUE); + + mv_resize->is_resize = TRUE; + mv_resize->moveresize_button = button; + mv_resize->resize_edge = edge; + mv_resize->moveresize_x = root_x; + mv_resize->moveresize_y = root_y; + mv_resize->moveresize_window = g_object_ref (window); + + mv_resize->moveresize_orig_width = gdk_surface_get_width (window); + mv_resize->moveresize_orig_height = gdk_surface_get_height (window); + + mv_resize->moveresize_geom_mask = impl->geometry_hints_mask; + mv_resize->moveresize_geometry = impl->geometry_hints; + + calculate_unmoving_origin (mv_resize); + + create_moveresize_window (mv_resize, timestamp); +} + +static void +gdk_broadway_surface_begin_move_drag (GdkSurface *window, + GdkDevice *device, + gint button, + gint root_x, + gint root_y, + guint32 timestamp) +{ + MoveResizeData *mv_resize; + GdkSurfaceImplBroadway *impl; + + impl = GDK_SURFACE_IMPL_BROADWAY (window->impl); + + if (GDK_SURFACE_DESTROYED (window) || + !WINDOW_IS_TOPLEVEL (window)) + return; + + if (impl->maximized) + return; + + mv_resize = get_move_resize_data (gdk_surface_get_display (window), TRUE); + + mv_resize->is_resize = FALSE; + mv_resize->moveresize_button = button; + mv_resize->moveresize_x = root_x; + mv_resize->moveresize_y = root_y; + mv_resize->moveresize_window = g_object_ref (window); + + mv_resize->moveresize_orig_width = gdk_surface_get_width (window); + mv_resize->moveresize_orig_height = gdk_surface_get_height (window); + + mv_resize->moveresize_geom_mask = impl->geometry_hints_mask; + mv_resize->moveresize_geometry = impl->geometry_hints; + + calculate_unmoving_origin (mv_resize); + + create_moveresize_window (mv_resize, timestamp); +} + +static gboolean +gdk_broadway_surface_beep (GdkSurface *window) +{ + return FALSE; +} + +static void +gdk_broadway_surface_set_opacity (GdkSurface *window, + gdouble opacity) +{ + g_return_if_fail (GDK_IS_SURFACE (window)); + + if (GDK_SURFACE_DESTROYED (window) || + !WINDOW_IS_TOPLEVEL (window)) + return; + + if (opacity < 0) + opacity = 0; + else if (opacity > 1) + opacity = 1; +} + +guint32 +gdk_broadway_get_last_seen_time (GdkSurface *window) +{ + GdkDisplay *display; + + display = gdk_surface_get_display (window); + return _gdk_broadway_server_get_last_seen_time (GDK_BROADWAY_DISPLAY (display)->server); +} + +static void +gdk_surface_impl_broadway_class_init (GdkSurfaceImplBroadwayClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GdkSurfaceImplClass *impl_class = GDK_SURFACE_IMPL_CLASS (klass); + + object_class->finalize = gdk_surface_impl_broadway_finalize; + + impl_class->ref_cairo_surface = gdk_surface_broadway_ref_cairo_surface; + impl_class->show = gdk_surface_broadway_show; + impl_class->hide = gdk_surface_broadway_hide; + impl_class->withdraw = gdk_surface_broadway_withdraw; + impl_class->set_events = gdk_surface_broadway_set_events; + impl_class->get_events = gdk_surface_broadway_get_events; + impl_class->raise = gdk_surface_broadway_raise; + impl_class->lower = gdk_surface_broadway_lower; + impl_class->restack_toplevel = gdk_surface_broadway_restack_toplevel; + impl_class->move_resize = gdk_surface_broadway_move_resize; + impl_class->get_geometry = gdk_surface_broadway_get_geometry; + impl_class->get_root_coords = gdk_surface_broadway_get_root_coords; + impl_class->get_device_state = gdk_surface_broadway_get_device_state; + impl_class->shape_combine_region = gdk_surface_broadway_shape_combine_region; + impl_class->input_shape_combine_region = gdk_surface_broadway_input_shape_combine_region; + impl_class->destroy = _gdk_broadway_surface_destroy; + impl_class->end_paint = gdk_broadway_surface_end_paint; + impl_class->beep = gdk_broadway_surface_beep; + + impl_class->focus = gdk_broadway_surface_focus; + impl_class->set_type_hint = gdk_broadway_surface_set_type_hint; + impl_class->get_type_hint = gdk_broadway_surface_get_type_hint; + impl_class->set_modal_hint = gdk_broadway_surface_set_modal_hint; + impl_class->set_skip_taskbar_hint = gdk_broadway_surface_set_skip_taskbar_hint; + impl_class->set_skip_pager_hint = gdk_broadway_surface_set_skip_pager_hint; + impl_class->set_urgency_hint = gdk_broadway_surface_set_urgency_hint; + impl_class->set_geometry_hints = gdk_broadway_surface_set_geometry_hints; + impl_class->set_title = gdk_broadway_surface_set_title; + impl_class->set_role = gdk_broadway_surface_set_role; + impl_class->set_startup_id = gdk_broadway_surface_set_startup_id; + impl_class->set_transient_for = gdk_broadway_surface_set_transient_for; + impl_class->get_frame_extents = gdk_broadway_surface_get_frame_extents; + impl_class->set_accept_focus = gdk_broadway_surface_set_accept_focus; + impl_class->set_focus_on_map = gdk_broadway_surface_set_focus_on_map; + impl_class->set_icon_list = gdk_broadway_surface_set_icon_list; + impl_class->set_icon_name = gdk_broadway_surface_set_icon_name; + impl_class->iconify = gdk_broadway_surface_iconify; + impl_class->deiconify = gdk_broadway_surface_deiconify; + impl_class->stick = gdk_broadway_surface_stick; + impl_class->unstick = gdk_broadway_surface_unstick; + impl_class->maximize = gdk_broadway_surface_maximize; + impl_class->unmaximize = gdk_broadway_surface_unmaximize; + impl_class->fullscreen = gdk_broadway_surface_fullscreen; + impl_class->unfullscreen = gdk_broadway_surface_unfullscreen; + impl_class->set_keep_above = gdk_broadway_surface_set_keep_above; + impl_class->set_keep_below = gdk_broadway_surface_set_keep_below; + impl_class->get_group = gdk_broadway_surface_get_group; + impl_class->set_group = gdk_broadway_surface_set_group; + impl_class->set_decorations = gdk_broadway_surface_set_decorations; + impl_class->get_decorations = gdk_broadway_surface_get_decorations; + impl_class->set_functions = gdk_broadway_surface_set_functions; + impl_class->begin_resize_drag = gdk_broadway_surface_begin_resize_drag; + impl_class->begin_move_drag = gdk_broadway_surface_begin_move_drag; + impl_class->set_opacity = gdk_broadway_surface_set_opacity; + impl_class->destroy_notify = gdk_broadway_surface_destroy_notify; + impl_class->register_dnd = _gdk_broadway_surface_register_dnd; + impl_class->drag_begin = _gdk_broadway_surface_drag_begin; +} diff --git a/gdk/broadway/gdksurface-broadway.h b/gdk/broadway/gdksurface-broadway.h new file mode 100644 index 0000000000..00fa539645 --- /dev/null +++ b/gdk/broadway/gdksurface-broadway.h @@ -0,0 +1,84 @@ +/* GDK - The GIMP Drawing Kit + * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * 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, see . + */ + +/* + * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS + * file for a list of people on the GTK+ Team. See the ChangeLog + * files for a list of changes. These files are distributed with + * GTK+ at ftp://ftp.gtk.org/pub/gtk/. + */ + +#ifndef __GDK_SURFACE_BROADWAY_H__ +#define __GDK_SURFACE_BROADWAY_H__ + +#include + +G_BEGIN_DECLS + +typedef struct _GdkSurfaceImplBroadway GdkSurfaceImplBroadway; +typedef struct _GdkSurfaceImplBroadwayClass GdkSurfaceImplBroadwayClass; + +/* Window implementation for Broadway + */ + +#define GDK_TYPE_SURFACE_IMPL_BROADWAY (gdk_surface_impl_broadway_get_type ()) +#define GDK_SURFACE_IMPL_BROADWAY(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), GDK_TYPE_SURFACE_IMPL_BROADWAY, GdkSurfaceImplBroadway)) +#define GDK_SURFACE_IMPL_BROADWAY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GDK_TYPE_SURFACE_IMPL_BROADWAY, GdkSurfaceImplBroadwayClass)) +#define GDK_IS_SURFACE_IMPL_BROADWAY(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), GDK_TYPE_SURFACE_IMPL_BROADWAY)) +#define GDK_IS_SURFACE_IMPL_BROADWAY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GDK_TYPE_SURFACE_IMPL_BROADWAY)) +#define GDK_SURFACE_IMPL_BROADWAY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GDK_TYPE_SURFACE_IMPL_BROADWAY, GdkSurfaceImplBroadwayClass)) + +struct _GdkSurfaceImplBroadway +{ + GdkSurfaceImpl parent_instance; + + GdkSurface *wrapper; + + GdkCursor *cursor; + + int id; + + gboolean visible; + gboolean maximized; + int transient_for; + + int pre_maximize_x; + int pre_maximize_y; + int pre_maximize_width; + int pre_maximize_height; + + gint8 toplevel_window_type; + gboolean dirty; + gboolean last_synced; + + GdkGeometry geometry_hints; + GdkSurfaceHints geometry_hints_mask; + + GArray *node_data; + GPtrArray *node_data_textures; +}; + +struct _GdkSurfaceImplBroadwayClass +{ + GdkSurfaceImplClass parent_class; +}; + +GType gdk_surface_impl_broadway_get_type (void); + +G_END_DECLS + +#endif /* __GDK_SURFACE_BROADWAY_H__ */ diff --git a/gdk/broadway/gdkwindow-broadway.c b/gdk/broadway/gdkwindow-broadway.c deleted file mode 100644 index 9b7dbcf60f..0000000000 --- a/gdk/broadway/gdkwindow-broadway.c +++ /dev/null @@ -1,1428 +0,0 @@ -/* GDK - The GIMP Drawing Kit - * Copyright (C) 1995-2007 Peter Mattis, Spencer Kimball, - * Josh MacDonald, Ryan Lortie - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * 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, see . - */ - -/* - * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS - * file for a list of people on the GTK+ Team. See the ChangeLog - * files for a list of changes. These files are distributed with - * GTK+ at ftp://ftp.gtk.org/pub/gtk/. - */ - -#include "config.h" - -#include "gdkwindow-broadway.h" - -#include "gdkbroadwaydisplay.h" -#include "gdkdisplay.h" -#include "gdkwindow.h" -#include "gdkwindowimpl.h" -#include "gdkdisplay-broadway.h" -#include "gdkprivate-broadway.h" -#include "gdkinternals.h" -#include "gdkdeviceprivate.h" -#include "gdkeventsource.h" -#include -#include - -#include -#include -#include - -/* Forward declarations */ -static void gdk_surface_impl_broadway_finalize (GObject *object); - -#define WINDOW_IS_TOPLEVEL(window) \ - (GDK_SURFACE_TYPE (window) != GDK_SURFACE_CHILD) - -struct _GdkBroadwaySurface { - GdkSurface parent; -}; - -struct _GdkBroadwaySurfaceClass { - GdkSurfaceClass parent_class; -}; - -G_DEFINE_TYPE (GdkBroadwaySurface, gdk_broadway_surface, GDK_TYPE_SURFACE) - -static void -gdk_broadway_surface_class_init (GdkBroadwaySurfaceClass *broadway_surface_class) -{ -} - -static void -gdk_broadway_surface_init (GdkBroadwaySurface *broadway_surface) -{ -} - -G_DEFINE_TYPE (GdkSurfaceImplBroadway, - gdk_surface_impl_broadway, - GDK_TYPE_SURFACE_IMPL) - -static GdkDisplay * -find_broadway_display (void) -{ - GdkDisplay *display; - GSList *list, *l; - - display = NULL; - - list = gdk_display_manager_list_displays (gdk_display_manager_get ()); - for (l = list; l; l = l->next) - { - if (GDK_IS_BROADWAY_DISPLAY (l->data)) - { - display = l->data; - break; - } - } - g_slist_free (list); - - return display; -} - -static guint flush_id = 0; - -static gboolean -flush_idle (gpointer data) -{ - flush_id = 0; - - gdk_display_flush (find_broadway_display ()); - - return FALSE; -} - -/* We need to flush in an idle rather than AFTER_PAINT, as the clock - is frozen during e.g. window resizes so the paint will not happen - and the window resize request is never flushed. */ -static void -queue_flush (GdkSurface *window) -{ - if (flush_id == 0) - { - flush_id = g_idle_add (flush_idle, NULL); - g_source_set_name_by_id (flush_id, "[gtk+] flush_idle"); - } -} - -static void -gdk_surface_impl_broadway_init (GdkSurfaceImplBroadway *impl) -{ - impl->toplevel_window_type = -1; -} - -static void -gdk_surface_impl_broadway_finalize (GObject *object) -{ - GdkSurface *wrapper; - GdkSurfaceImplBroadway *impl; - GdkBroadwayDisplay *broadway_display; - - g_return_if_fail (GDK_IS_SURFACE_IMPL_BROADWAY (object)); - - impl = GDK_SURFACE_IMPL_BROADWAY (object); - - wrapper = impl->wrapper; - - _gdk_broadway_surface_grab_check_destroy (wrapper); - - broadway_display = GDK_BROADWAY_DISPLAY (gdk_surface_get_display (impl->wrapper)); - - g_hash_table_remove (broadway_display->id_ht, GINT_TO_POINTER(impl->id)); - - if (impl->cursor) - g_object_unref (impl->cursor); - - broadway_display->toplevels = g_list_remove (broadway_display->toplevels, impl); - - G_OBJECT_CLASS (gdk_surface_impl_broadway_parent_class)->finalize (object); -} - -static gboolean -thaw_clock_cb (GdkFrameClock *clock) -{ - _gdk_frame_clock_thaw (clock); - g_object_unref (clock); - return G_SOURCE_REMOVE; -} - -void -_gdk_broadway_roundtrip_notify (GdkSurface *window, - guint32 tag, - gboolean local_reply) -{ - GdkFrameClock *clock = gdk_surface_get_frame_clock (window); - - /* If there is no remove web client, rate limit update to once a second */ - if (local_reply) - g_timeout_add_seconds (1, (GSourceFunc)thaw_clock_cb, g_object_ref (clock)); - else - _gdk_frame_clock_thaw (clock); -} - -static void -on_frame_clock_after_paint (GdkFrameClock *clock, - GdkSurface *window) -{ - GdkDisplay *display = gdk_surface_get_display (window); - GdkSurfaceImplBroadway *impl = GDK_SURFACE_IMPL_BROADWAY (window->impl); - GdkBroadwayDisplay *broadway_display; - - _gdk_frame_clock_freeze (gdk_surface_get_frame_clock (window)); - - broadway_display = GDK_BROADWAY_DISPLAY (display); - - _gdk_broadway_server_roundtrip (broadway_display->server, impl->id, _gdk_display_get_next_serial (display)); - - gdk_display_flush (display); -} - -static void -connect_frame_clock (GdkSurface *window) -{ - if (WINDOW_IS_TOPLEVEL (window)) - { - GdkFrameClock *frame_clock = gdk_surface_get_frame_clock (window); - - g_signal_connect (frame_clock, "after-paint", - G_CALLBACK (on_frame_clock_after_paint), window); - } -} - -void -_gdk_broadway_display_create_window_impl (GdkDisplay *display, - GdkSurface *window, - GdkSurface *real_parent, - GdkEventMask event_mask, - GdkSurfaceAttr *attributes) -{ - GdkSurfaceImplBroadway *impl; - GdkBroadwayDisplay *broadway_display; - - broadway_display = GDK_BROADWAY_DISPLAY (display); - - impl = g_object_new (GDK_TYPE_SURFACE_IMPL_BROADWAY, NULL); - window->impl = (GdkSurfaceImpl *)impl; - impl->id = _gdk_broadway_server_new_surface (broadway_display->server, - window->x, - window->y, - window->width, - window->height, - window->window_type == GDK_SURFACE_TEMP); - g_hash_table_insert (broadway_display->id_ht, GINT_TO_POINTER(impl->id), window); - impl->wrapper = window; - - g_assert (window->window_type == GDK_SURFACE_TOPLEVEL || - window->window_type == GDK_SURFACE_TEMP); - g_assert (window->parent == NULL); - - broadway_display->toplevels = g_list_prepend (broadway_display->toplevels, impl); - - connect_frame_clock (window); -} - -static cairo_surface_t * -gdk_surface_broadway_ref_cairo_surface (GdkSurface *window) -{ - GdkSurfaceImplBroadway *impl = GDK_SURFACE_IMPL_BROADWAY (window->impl); - - if (GDK_IS_SURFACE_IMPL_BROADWAY (window) && - GDK_SURFACE_DESTROYED (impl->wrapper)) - return NULL; - - return cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 1, 1); -} - -static void -_gdk_broadway_surface_destroy (GdkSurface *window, - gboolean recursing, - gboolean foreign_destroy) -{ - GdkSurfaceImplBroadway *impl; - GdkBroadwayDisplay *broadway_display; - - g_return_if_fail (GDK_IS_SURFACE (window)); - - impl = GDK_SURFACE_IMPL_BROADWAY (window->impl); - - if (impl->node_data) - g_array_unref (impl->node_data); - if (impl->node_data_textures) - g_ptr_array_unref (impl->node_data_textures); - - _gdk_broadway_surface_grab_check_destroy (window); - - broadway_display = GDK_BROADWAY_DISPLAY (gdk_surface_get_display (window)); - g_hash_table_remove (broadway_display->id_ht, GINT_TO_POINTER (impl->id)); - - _gdk_broadway_server_destroy_surface (broadway_display->server, impl->id); - -} - -void -gdk_broadway_surface_set_nodes (GdkSurface *window, - GArray *nodes, - GPtrArray *node_textures) -{ - GdkSurfaceImplBroadway *impl; - GdkBroadwayDisplay *broadway_display; - - g_return_if_fail (GDK_IS_SURFACE (window)); - - impl = GDK_SURFACE_IMPL_BROADWAY (window->impl); - - broadway_display = GDK_BROADWAY_DISPLAY (gdk_surface_get_display (window)); - - if (nodes) - g_array_ref (nodes); - if (impl->node_data) - g_array_unref (impl->node_data); - impl->node_data = nodes; - - if (node_textures) - g_ptr_array_ref (node_textures); - if (impl->node_data_textures) - g_ptr_array_unref (impl->node_data_textures); - impl->node_data_textures = node_textures; - - gdk_broadway_server_surface_set_nodes (broadway_display->server, impl->id, impl->node_data); -} - -/* This function is called when the XWindow is really gone. - */ -static void -gdk_broadway_surface_destroy_notify (GdkSurface *window) -{ - if (!GDK_SURFACE_DESTROYED (window)) - _gdk_surface_destroy (window, TRUE); - - g_object_unref (window); -} - -static void -gdk_surface_broadway_show (GdkSurface *window, gboolean already_mapped) -{ - GdkSurfaceImplBroadway *impl; - GdkBroadwayDisplay *broadway_display; - - impl = GDK_SURFACE_IMPL_BROADWAY (window->impl); - impl->visible = TRUE; - - if (window->event_mask & GDK_STRUCTURE_MASK) - _gdk_make_event (GDK_SURFACE (window), GDK_MAP, NULL, FALSE); - - if (window->parent && window->parent->event_mask & GDK_SUBSTRUCTURE_MASK) - _gdk_make_event (GDK_SURFACE (window), GDK_MAP, NULL, FALSE); - - broadway_display = GDK_BROADWAY_DISPLAY (gdk_surface_get_display (window)); - if (_gdk_broadway_server_surface_show (broadway_display->server, impl->id)) - queue_flush (window); - -} - -static void -gdk_surface_broadway_hide (GdkSurface *window) -{ - GdkSurfaceImplBroadway *impl; - GdkBroadwayDisplay *broadway_display; - - impl = GDK_SURFACE_IMPL_BROADWAY (window->impl); - impl->visible = FALSE; - - if (window->event_mask & GDK_STRUCTURE_MASK) - _gdk_make_event (GDK_SURFACE (window), GDK_UNMAP, NULL, FALSE); - - if (window->parent && window->parent->event_mask & GDK_SUBSTRUCTURE_MASK) - _gdk_make_event (GDK_SURFACE (window), GDK_UNMAP, NULL, FALSE); - - broadway_display = GDK_BROADWAY_DISPLAY (gdk_surface_get_display (window)); - - _gdk_broadway_surface_grab_check_unmap (window, - _gdk_broadway_server_get_next_serial (broadway_display->server)); - - if (_gdk_broadway_server_surface_hide (broadway_display->server, impl->id)) - queue_flush (window); - - _gdk_surface_clear_update_area (window); -} - -static void -gdk_surface_broadway_withdraw (GdkSurface *window) -{ - gdk_surface_broadway_hide (window); -} - -static void -gdk_surface_broadway_move_resize (GdkSurface *window, - gboolean with_move, - gint x, - gint y, - gint width, - gint height) -{ - GdkSurfaceImplBroadway *impl = GDK_SURFACE_IMPL_BROADWAY (window->impl); - GdkBroadwayDisplay *broadway_display; - gboolean size_changed; - - size_changed = FALSE; - - broadway_display = GDK_BROADWAY_DISPLAY (gdk_surface_get_display (window)); - - if (width > 0 || height > 0) - { - if (width < 1) - width = 1; - - if (height < 1) - height = 1; - - if (width != window->width || - height != window->height) - { - size_changed = TRUE; - - /* Resize clears the content */ - impl->dirty = TRUE; - impl->last_synced = FALSE; - - window->width = width; - window->height = height; - } - } - - _gdk_broadway_server_surface_move_resize (broadway_display->server, - impl->id, - with_move, - x, y, - window->width, window->height); - queue_flush (window); - if (size_changed) - window->resize_count++; -} - -static void -gdk_surface_broadway_raise (GdkSurface *window) -{ -} - -static void -gdk_surface_broadway_restack_toplevel (GdkSurface *window, - GdkSurface *sibling, - gboolean above) -{ -} - -static void -gdk_surface_broadway_lower (GdkSurface *window) -{ -} - - -static void -gdk_broadway_surface_focus (GdkSurface *window, - guint32 timestamp) -{ - GdkSurfaceImplBroadway *impl; - GdkBroadwayDisplay *broadway_display; - - g_return_if_fail (GDK_IS_SURFACE (window)); - - if (GDK_SURFACE_DESTROYED (window) || - !window->accept_focus) - return; - - impl = GDK_SURFACE_IMPL_BROADWAY (window->impl); - broadway_display = GDK_BROADWAY_DISPLAY (gdk_surface_get_display (window)); - _gdk_broadway_server_surface_focus (broadway_display->server, - impl->id); -} - -static void -gdk_broadway_surface_set_type_hint (GdkSurface *window, - GdkSurfaceTypeHint hint) -{ -} - -static GdkSurfaceTypeHint -gdk_broadway_surface_get_type_hint (GdkSurface *window) -{ - return GDK_SURFACE_TYPE_HINT_NORMAL; -} - -static void -gdk_broadway_surface_set_modal_hint (GdkSurface *window, - gboolean modal) -{ -} - -static void -gdk_broadway_surface_set_skip_taskbar_hint (GdkSurface *window, - gboolean skips_taskbar) -{ -} - -static void -gdk_broadway_surface_set_skip_pager_hint (GdkSurface *window, - gboolean skips_pager) -{ -} - -static void -gdk_broadway_surface_set_urgency_hint (GdkSurface *window, - gboolean urgent) -{ -} - -static void -gdk_broadway_surface_set_geometry_hints (GdkSurface *window, - const GdkGeometry *geometry, - GdkSurfaceHints geom_mask) -{ - GdkSurfaceImplBroadway *impl; - - impl = GDK_SURFACE_IMPL_BROADWAY (window->impl); - - impl->geometry_hints = *geometry; - impl->geometry_hints_mask = geom_mask; -} - -static void -gdk_broadway_surface_set_title (GdkSurface *window, - const gchar *title) -{ -} - -static void -gdk_broadway_surface_set_role (GdkSurface *window, - const gchar *role) -{ -} - -static void -gdk_broadway_surface_set_startup_id (GdkSurface *window, - const gchar *startup_id) -{ -} - -static void -gdk_broadway_surface_set_transient_for (GdkSurface *window, - GdkSurface *parent) -{ - GdkBroadwayDisplay *display; - GdkSurfaceImplBroadway *impl; - int parent_id; - - impl = GDK_SURFACE_IMPL_BROADWAY (window->impl); - - parent_id = 0; - if (parent) - parent_id = GDK_SURFACE_IMPL_BROADWAY (parent->impl)->id; - - impl->transient_for = parent_id; - - display = GDK_BROADWAY_DISPLAY (gdk_surface_get_display (impl->wrapper)); - _gdk_broadway_server_surface_set_transient_for (display->server, impl->id, impl->transient_for); -} - -static void -gdk_surface_broadway_get_geometry (GdkSurface *window, - gint *x, - gint *y, - gint *width, - gint *height) -{ - GdkSurfaceImplBroadway *impl; - - g_return_if_fail (GDK_IS_SURFACE (window)); - - impl = GDK_SURFACE_IMPL_BROADWAY (window->impl); - - /* TODO: These should really roundtrip to the client to get the current data */ - - if (x) - *x = impl->wrapper->x; - if (y) - *y = impl->wrapper->y; - if (width) - *width = impl->wrapper->width; - if (height) - *height = impl->wrapper->height; - -} - -static void -gdk_surface_broadway_get_root_coords (GdkSurface *window, - gint x, - gint y, - gint *root_x, - gint *root_y) -{ - GdkSurfaceImplBroadway *impl; - - impl = GDK_SURFACE_IMPL_BROADWAY (window->impl); - - if (root_x) - *root_x = x + impl->wrapper->x; - if (root_y) - *root_y = y + impl->wrapper->y; -} - -static void -gdk_broadway_surface_get_frame_extents (GdkSurface *window, - GdkRectangle *rect) -{ - g_return_if_fail (rect != NULL); - - /* TODO: This should take wm frame into account */ - - rect->x = window->x; - rect->y = window->y; - rect->width = window->width; - rect->height = window->height; -} - -static gboolean -gdk_surface_broadway_get_device_state (GdkSurface *window, - GdkDevice *device, - gdouble *x, - gdouble *y, - GdkModifierType *mask) -{ - GdkSurface *child; - - g_return_val_if_fail (window == NULL || GDK_IS_SURFACE (window), FALSE); - - if (GDK_SURFACE_DESTROYED (window)) - return FALSE; - - GDK_DEVICE_GET_CLASS (device)->query_state (device, window, - &child, - NULL, NULL, - x, y, mask); - return child != NULL; -} - -static GdkEventMask -gdk_surface_broadway_get_events (GdkSurface *window) -{ - if (GDK_SURFACE_DESTROYED (window)) - return 0; - - return 0; -} - -static void -gdk_surface_broadway_set_events (GdkSurface *window, - GdkEventMask event_mask) -{ - if (!GDK_SURFACE_DESTROYED (window)) - { - } -} - -static void -gdk_surface_broadway_shape_combine_region (GdkSurface *window, - const cairo_region_t *shape_region, - gint offset_x, - gint offset_y) -{ -} - -static void -gdk_surface_broadway_input_shape_combine_region (GdkSurface *window, - const cairo_region_t *shape_region, - gint offset_x, - gint offset_y) -{ -} - -static void -gdk_broadway_surface_set_accept_focus (GdkSurface *window, - gboolean accept_focus) -{ - accept_focus = accept_focus != FALSE; - - if (window->accept_focus != accept_focus) - { - window->accept_focus = accept_focus; - } -} - -static void -gdk_broadway_surface_set_focus_on_map (GdkSurface *window, - gboolean focus_on_map) -{ - focus_on_map = focus_on_map != FALSE; - - if (window->focus_on_map != focus_on_map) - { - window->focus_on_map = focus_on_map; - } -} - - -static void -gdk_broadway_surface_set_icon_list (GdkSurface *window, - GList *surfaces) -{ -} - -static void -gdk_broadway_surface_set_icon_name (GdkSurface *window, - const gchar *name) -{ - if (GDK_SURFACE_DESTROYED (window) || - !WINDOW_IS_TOPLEVEL (window)) - return; - - g_object_set_qdata (G_OBJECT (window), g_quark_from_static_string ("gdk-icon-name-set"), - GUINT_TO_POINTER (name != NULL)); -} - -static void -gdk_broadway_surface_iconify (GdkSurface *window) -{ - if (GDK_SURFACE_DESTROYED (window) || - !WINDOW_IS_TOPLEVEL (window)) - return; -} - -static void -gdk_broadway_surface_deiconify (GdkSurface *window) -{ - if (GDK_SURFACE_DESTROYED (window) || - !WINDOW_IS_TOPLEVEL (window)) - return; -} - -static void -gdk_broadway_surface_stick (GdkSurface *window) -{ - if (GDK_SURFACE_DESTROYED (window) || - !WINDOW_IS_TOPLEVEL (window)) - return; - -} - -static void -gdk_broadway_surface_unstick (GdkSurface *window) -{ - if (GDK_SURFACE_DESTROYED (window) || - !WINDOW_IS_TOPLEVEL (window)) - return; - -} - -static void -gdk_broadway_surface_maximize (GdkSurface *window) -{ - GdkSurfaceImplBroadway *impl; - GdkDisplay *display; - GdkMonitor *monitor; - GdkRectangle geom; - - if (GDK_SURFACE_DESTROYED (window) || - !WINDOW_IS_TOPLEVEL (window)) - return; - - impl = GDK_SURFACE_IMPL_BROADWAY (window->impl); - - if (impl->maximized) - return; - - impl->maximized = TRUE; - - gdk_synthesize_window_state (window, 0, GDK_SURFACE_STATE_MAXIMIZED); - - impl->pre_maximize_x = window->x; - impl->pre_maximize_y = window->y; - impl->pre_maximize_width = window->width; - impl->pre_maximize_height = window->height; - - display = gdk_surface_get_display (window); - monitor = gdk_display_get_primary_monitor (display); - gdk_monitor_get_geometry (monitor, &geom); - - gdk_surface_move_resize (window, - geom.x, geom.y, - geom.width, geom.height); -} - -static void -gdk_broadway_surface_unmaximize (GdkSurface *window) -{ - GdkSurfaceImplBroadway *impl; - - if (GDK_SURFACE_DESTROYED (window) || - !WINDOW_IS_TOPLEVEL (window)) - return; - - impl = GDK_SURFACE_IMPL_BROADWAY (window->impl); - - if (!impl->maximized) - return; - - impl->maximized = FALSE; - - gdk_synthesize_window_state (window, GDK_SURFACE_STATE_MAXIMIZED, 0); - - gdk_surface_move_resize (window, - impl->pre_maximize_x, - impl->pre_maximize_y, - impl->pre_maximize_width, - impl->pre_maximize_height); -} - -static void -gdk_broadway_surface_fullscreen (GdkSurface *window) -{ - if (GDK_SURFACE_DESTROYED (window) || - !WINDOW_IS_TOPLEVEL (window)) - return; - -} - -static void -gdk_broadway_surface_unfullscreen (GdkSurface *window) -{ - if (GDK_SURFACE_DESTROYED (window) || - !WINDOW_IS_TOPLEVEL (window)) - return; - -} - -static void -gdk_broadway_surface_set_keep_above (GdkSurface *window, - gboolean setting) -{ - g_return_if_fail (GDK_IS_SURFACE (window)); - - if (GDK_SURFACE_DESTROYED (window) || - !WINDOW_IS_TOPLEVEL (window)) - return; - -} - -static void -gdk_broadway_surface_set_keep_below (GdkSurface *window, gboolean setting) -{ - g_return_if_fail (GDK_IS_SURFACE (window)); - - if (GDK_SURFACE_DESTROYED (window) || - !WINDOW_IS_TOPLEVEL (window)) - return; - -} - -static GdkSurface * -gdk_broadway_surface_get_group (GdkSurface *window) -{ - if (GDK_SURFACE_DESTROYED (window) || - !WINDOW_IS_TOPLEVEL (window)) - return NULL; - - return window; -} - -static void -gdk_broadway_surface_set_group (GdkSurface *window, - GdkSurface *leader) -{ -} - -static void -gdk_broadway_surface_set_decorations (GdkSurface *window, - GdkWMDecoration decorations) -{ - if (GDK_SURFACE_DESTROYED (window) || - !WINDOW_IS_TOPLEVEL (window)) - return; - -} - -static gboolean -gdk_broadway_surface_get_decorations (GdkSurface *window, - GdkWMDecoration *decorations) -{ - gboolean result = FALSE; - - if (GDK_SURFACE_DESTROYED (window) || - !WINDOW_IS_TOPLEVEL (window)) - return FALSE; - - return result; -} - -static void -gdk_broadway_surface_set_functions (GdkSurface *window, - GdkWMFunction functions) -{ - g_return_if_fail (GDK_IS_SURFACE (window)); - - if (GDK_SURFACE_DESTROYED (window) || - !WINDOW_IS_TOPLEVEL (window)) - return; -} - -static void -gdk_broadway_surface_end_paint (GdkSurface *window) -{ - GdkSurfaceImplBroadway *impl; - impl = GDK_SURFACE_IMPL_BROADWAY (window->impl); - impl->dirty = TRUE; -} - -typedef struct _MoveResizeData MoveResizeData; - -struct _MoveResizeData -{ - GdkDisplay *display; - - GdkSurface *moveresize_window; - GdkSurface *moveresize_emulation_window; - gboolean is_resize; - GdkSurfaceEdge resize_edge; - gint moveresize_button; - gint moveresize_x; - gint moveresize_y; - gint moveresize_orig_x; - gint moveresize_orig_y; - gint moveresize_orig_width; - gint moveresize_orig_height; - long moveresize_process_time; - GdkSurfaceHints moveresize_geom_mask; - GdkGeometry moveresize_geometry; - BroadwayInputMsg *moveresize_pending_event; -}; - -static MoveResizeData * -get_move_resize_data (GdkDisplay *display, - gboolean create) -{ - GdkBroadwayDisplay *broadway_display; - MoveResizeData *mv_resize; - - broadway_display = GDK_BROADWAY_DISPLAY (display); - - mv_resize = broadway_display->move_resize_data; - - if (!mv_resize && create) - { - mv_resize = g_new0 (MoveResizeData, 1); - mv_resize->display = display; - - broadway_display->move_resize_data = mv_resize; - } - - return mv_resize; -} - -static void -update_pos (MoveResizeData *mv_resize, - gint new_root_x, - gint new_root_y) -{ - gint dx, dy; - - dx = new_root_x - mv_resize->moveresize_x; - dy = new_root_y - mv_resize->moveresize_y; - - if (mv_resize->is_resize) - { - gint x, y, w, h; - - x = mv_resize->moveresize_orig_x; - y = mv_resize->moveresize_orig_y; - - w = mv_resize->moveresize_orig_width; - h = mv_resize->moveresize_orig_height; - - switch (mv_resize->resize_edge) - { - case GDK_SURFACE_EDGE_NORTH_WEST: - x += dx; - y += dy; - w -= dx; - h -= dy; - break; - case GDK_SURFACE_EDGE_NORTH: - y += dy; - h -= dy; - break; - case GDK_SURFACE_EDGE_NORTH_EAST: - y += dy; - h -= dy; - w += dx; - break; - case GDK_SURFACE_EDGE_SOUTH_WEST: - h += dy; - x += dx; - w -= dx; - break; - case GDK_SURFACE_EDGE_SOUTH_EAST: - w += dx; - h += dy; - break; - case GDK_SURFACE_EDGE_SOUTH: - h += dy; - break; - case GDK_SURFACE_EDGE_EAST: - w += dx; - break; - case GDK_SURFACE_EDGE_WEST: - x += dx; - w -= dx; - break; - default: - break; - } - - x = MAX (x, 0); - y = MAX (y, 0); - w = MAX (w, 1); - h = MAX (h, 1); - - if (mv_resize->moveresize_geom_mask) - { - gdk_surface_constrain_size (&mv_resize->moveresize_geometry, - mv_resize->moveresize_geom_mask, - w, h, &w, &h); - } - - gdk_surface_move_resize (mv_resize->moveresize_window, x, y, w, h); - } - else - { - gint x, y; - - x = mv_resize->moveresize_orig_x + dx; - y = mv_resize->moveresize_orig_y + dy; - - gdk_surface_move (mv_resize->moveresize_window, x, y); - } -} - -static void -finish_drag (MoveResizeData *mv_resize) -{ - gdk_surface_destroy (mv_resize->moveresize_emulation_window); - mv_resize->moveresize_emulation_window = NULL; - g_object_unref (mv_resize->moveresize_window); - mv_resize->moveresize_window = NULL; - g_clear_pointer (&mv_resize->moveresize_pending_event, g_free); -} - -static gboolean -moveresize_lookahead (GdkDisplay *display, - MoveResizeData *mv_resize, - BroadwayInputMsg *event) -{ - GdkBroadwayDisplay *broadway_display; - - broadway_display = GDK_BROADWAY_DISPLAY (display); - - return !_gdk_broadway_server_lookahead_event (broadway_display->server, "mb"); -} - -gboolean -_gdk_broadway_moveresize_handle_event (GdkDisplay *display, - BroadwayInputMsg *event) -{ - guint button_mask = 0; - MoveResizeData *mv_resize = get_move_resize_data (display, FALSE); - - if (!mv_resize || !mv_resize->moveresize_window) - return FALSE; - - button_mask = GDK_BUTTON1_MASK << (mv_resize->moveresize_button - 1); - - switch (event->base.type) - { - case BROADWAY_EVENT_TOUCH: - if (event->touch.touch_type == 2) /* END */ - { - update_pos (mv_resize, - event->touch.root_x, - event->touch.root_y); - - finish_drag (mv_resize); - } - else if (event->touch.touch_type == 1) /* UPDATE */ - { - if (mv_resize->moveresize_window->resize_count > 0) - { - if (mv_resize->moveresize_pending_event) - *mv_resize->moveresize_pending_event = *event; - else - mv_resize->moveresize_pending_event = - g_memdup (event, sizeof (BroadwayInputMsg)); - - break; - } - update_pos (mv_resize, - event->touch.root_x, - event->touch.root_y); - } - - break; - - case BROADWAY_EVENT_POINTER_MOVE: - if (mv_resize->moveresize_window->resize_count > 0) - { - if (mv_resize->moveresize_pending_event) - *mv_resize->moveresize_pending_event = *event; - else - mv_resize->moveresize_pending_event = - g_memdup (event, sizeof (BroadwayInputMsg)); - - break; - } - if (!moveresize_lookahead (display, mv_resize, event)) - break; - - update_pos (mv_resize, - event->pointer.root_x, - event->pointer.root_y); - - /* This should never be triggered in normal cases, but in the - * case where the drag started without an implicit grab being - * in effect, we could miss the release if it occurs before - * we grab the pointer; this ensures that we will never - * get a permanently stuck grab. - */ - if ((event->pointer.state & button_mask) == 0) - finish_drag (mv_resize); - break; - - case BROADWAY_EVENT_BUTTON_RELEASE: - update_pos (mv_resize, - event->pointer.root_x, - event->pointer.root_y); - - if (event->button.button == mv_resize->moveresize_button) - finish_drag (mv_resize); - break; - default: - break; - } - return TRUE; -} - -gboolean -_gdk_broadway_moveresize_configure_done (GdkDisplay *display, - GdkSurface *window) -{ - BroadwayInputMsg *tmp_event; - MoveResizeData *mv_resize = get_move_resize_data (display, FALSE); - - if (!mv_resize || window != mv_resize->moveresize_window) - return FALSE; - - if (mv_resize->moveresize_pending_event) - { - tmp_event = mv_resize->moveresize_pending_event; - mv_resize->moveresize_pending_event = NULL; - _gdk_broadway_moveresize_handle_event (display, tmp_event); - g_free (tmp_event); - } - - return TRUE; -} - -static void -create_moveresize_window (MoveResizeData *mv_resize, - guint32 timestamp) -{ - GdkGrabStatus status; - GdkSeat *seat; - GdkDevice *pointer; - - g_assert (mv_resize->moveresize_emulation_window == NULL); - - mv_resize->moveresize_emulation_window = gdk_surface_new_temp (mv_resize->display); - - gdk_surface_show (mv_resize->moveresize_emulation_window); - - seat = gdk_display_get_default_seat (mv_resize->display); - pointer = gdk_seat_get_pointer (seat); - - G_GNUC_BEGIN_IGNORE_DEPRECATIONS; - status = gdk_device_grab (pointer, - mv_resize->moveresize_emulation_window, - GDK_OWNERSHIP_APPLICATION, - FALSE, - GDK_BUTTON_RELEASE_MASK | - GDK_POINTER_MOTION_MASK, - NULL, - timestamp); - G_GNUC_END_IGNORE_DEPRECATIONS; - - if (status != GDK_GRAB_SUCCESS) - { - /* If this fails, some other client has grabbed the window - * already. - */ - finish_drag (mv_resize); - } - - mv_resize->moveresize_process_time = 0; -} - -static void -calculate_unmoving_origin (MoveResizeData *mv_resize) -{ - GdkRectangle rect; - gint width, height; - - if (mv_resize->moveresize_geom_mask & GDK_HINT_WIN_GRAVITY && - mv_resize->moveresize_geometry.win_gravity == GDK_GRAVITY_STATIC) - { - gdk_surface_get_origin (mv_resize->moveresize_window, - &mv_resize->moveresize_orig_x, - &mv_resize->moveresize_orig_y); - } - else - { - gdk_surface_get_frame_extents (mv_resize->moveresize_window, &rect); - gdk_surface_get_geometry (mv_resize->moveresize_window, - NULL, NULL, &width, &height); - - switch (mv_resize->moveresize_geometry.win_gravity) - { - case GDK_GRAVITY_NORTH_WEST: - mv_resize->moveresize_orig_x = rect.x; - mv_resize->moveresize_orig_y = rect.y; - break; - case GDK_GRAVITY_NORTH: - mv_resize->moveresize_orig_x = rect.x + rect.width / 2 - width / 2; - mv_resize->moveresize_orig_y = rect.y; - break; - case GDK_GRAVITY_NORTH_EAST: - mv_resize->moveresize_orig_x = rect.x + rect.width - width; - mv_resize->moveresize_orig_y = rect.y; - break; - case GDK_GRAVITY_WEST: - mv_resize->moveresize_orig_x = rect.x; - mv_resize->moveresize_orig_y = rect.y + rect.height / 2 - height / 2; - break; - case GDK_GRAVITY_CENTER: - mv_resize->moveresize_orig_x = rect.x + rect.width / 2 - width / 2; - mv_resize->moveresize_orig_y = rect.y + rect.height / 2 - height / 2; - break; - case GDK_GRAVITY_EAST: - mv_resize->moveresize_orig_x = rect.x + rect.width - width; - mv_resize->moveresize_orig_y = rect.y + rect.height / 2 - height / 2; - break; - case GDK_GRAVITY_SOUTH_WEST: - mv_resize->moveresize_orig_x = rect.x; - mv_resize->moveresize_orig_y = rect.y + rect.height - height; - break; - case GDK_GRAVITY_SOUTH: - mv_resize->moveresize_orig_x = rect.x + rect.width / 2 - width / 2; - mv_resize->moveresize_orig_y = rect.y + rect.height - height; - break; - case GDK_GRAVITY_SOUTH_EAST: - mv_resize->moveresize_orig_x = rect.x + rect.width - width; - mv_resize->moveresize_orig_y = rect.y + rect.height - height; - break; - case GDK_GRAVITY_STATIC: - default: - mv_resize->moveresize_orig_x = rect.x; - mv_resize->moveresize_orig_y = rect.y; - break; - } - } -} - -static void -gdk_broadway_surface_begin_resize_drag (GdkSurface *window, - GdkSurfaceEdge edge, - GdkDevice *device, - gint button, - gint root_x, - gint root_y, - guint32 timestamp) -{ - MoveResizeData *mv_resize; - GdkSurfaceImplBroadway *impl; - - impl = GDK_SURFACE_IMPL_BROADWAY (window->impl); - - if (GDK_SURFACE_DESTROYED (window) || - !WINDOW_IS_TOPLEVEL (window)) - return; - - if (impl->maximized) - return; - - mv_resize = get_move_resize_data (gdk_surface_get_display (window), TRUE); - - mv_resize->is_resize = TRUE; - mv_resize->moveresize_button = button; - mv_resize->resize_edge = edge; - mv_resize->moveresize_x = root_x; - mv_resize->moveresize_y = root_y; - mv_resize->moveresize_window = g_object_ref (window); - - mv_resize->moveresize_orig_width = gdk_surface_get_width (window); - mv_resize->moveresize_orig_height = gdk_surface_get_height (window); - - mv_resize->moveresize_geom_mask = impl->geometry_hints_mask; - mv_resize->moveresize_geometry = impl->geometry_hints; - - calculate_unmoving_origin (mv_resize); - - create_moveresize_window (mv_resize, timestamp); -} - -static void -gdk_broadway_surface_begin_move_drag (GdkSurface *window, - GdkDevice *device, - gint button, - gint root_x, - gint root_y, - guint32 timestamp) -{ - MoveResizeData *mv_resize; - GdkSurfaceImplBroadway *impl; - - impl = GDK_SURFACE_IMPL_BROADWAY (window->impl); - - if (GDK_SURFACE_DESTROYED (window) || - !WINDOW_IS_TOPLEVEL (window)) - return; - - if (impl->maximized) - return; - - mv_resize = get_move_resize_data (gdk_surface_get_display (window), TRUE); - - mv_resize->is_resize = FALSE; - mv_resize->moveresize_button = button; - mv_resize->moveresize_x = root_x; - mv_resize->moveresize_y = root_y; - mv_resize->moveresize_window = g_object_ref (window); - - mv_resize->moveresize_orig_width = gdk_surface_get_width (window); - mv_resize->moveresize_orig_height = gdk_surface_get_height (window); - - mv_resize->moveresize_geom_mask = impl->geometry_hints_mask; - mv_resize->moveresize_geometry = impl->geometry_hints; - - calculate_unmoving_origin (mv_resize); - - create_moveresize_window (mv_resize, timestamp); -} - -static gboolean -gdk_broadway_surface_beep (GdkSurface *window) -{ - return FALSE; -} - -static void -gdk_broadway_surface_set_opacity (GdkSurface *window, - gdouble opacity) -{ - g_return_if_fail (GDK_IS_SURFACE (window)); - - if (GDK_SURFACE_DESTROYED (window) || - !WINDOW_IS_TOPLEVEL (window)) - return; - - if (opacity < 0) - opacity = 0; - else if (opacity > 1) - opacity = 1; -} - -guint32 -gdk_broadway_get_last_seen_time (GdkSurface *window) -{ - GdkDisplay *display; - - display = gdk_surface_get_display (window); - return _gdk_broadway_server_get_last_seen_time (GDK_BROADWAY_DISPLAY (display)->server); -} - -static void -gdk_surface_impl_broadway_class_init (GdkSurfaceImplBroadwayClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - GdkSurfaceImplClass *impl_class = GDK_SURFACE_IMPL_CLASS (klass); - - object_class->finalize = gdk_surface_impl_broadway_finalize; - - impl_class->ref_cairo_surface = gdk_surface_broadway_ref_cairo_surface; - impl_class->show = gdk_surface_broadway_show; - impl_class->hide = gdk_surface_broadway_hide; - impl_class->withdraw = gdk_surface_broadway_withdraw; - impl_class->set_events = gdk_surface_broadway_set_events; - impl_class->get_events = gdk_surface_broadway_get_events; - impl_class->raise = gdk_surface_broadway_raise; - impl_class->lower = gdk_surface_broadway_lower; - impl_class->restack_toplevel = gdk_surface_broadway_restack_toplevel; - impl_class->move_resize = gdk_surface_broadway_move_resize; - impl_class->get_geometry = gdk_surface_broadway_get_geometry; - impl_class->get_root_coords = gdk_surface_broadway_get_root_coords; - impl_class->get_device_state = gdk_surface_broadway_get_device_state; - impl_class->shape_combine_region = gdk_surface_broadway_shape_combine_region; - impl_class->input_shape_combine_region = gdk_surface_broadway_input_shape_combine_region; - impl_class->destroy = _gdk_broadway_surface_destroy; - impl_class->end_paint = gdk_broadway_surface_end_paint; - impl_class->beep = gdk_broadway_surface_beep; - - impl_class->focus = gdk_broadway_surface_focus; - impl_class->set_type_hint = gdk_broadway_surface_set_type_hint; - impl_class->get_type_hint = gdk_broadway_surface_get_type_hint; - impl_class->set_modal_hint = gdk_broadway_surface_set_modal_hint; - impl_class->set_skip_taskbar_hint = gdk_broadway_surface_set_skip_taskbar_hint; - impl_class->set_skip_pager_hint = gdk_broadway_surface_set_skip_pager_hint; - impl_class->set_urgency_hint = gdk_broadway_surface_set_urgency_hint; - impl_class->set_geometry_hints = gdk_broadway_surface_set_geometry_hints; - impl_class->set_title = gdk_broadway_surface_set_title; - impl_class->set_role = gdk_broadway_surface_set_role; - impl_class->set_startup_id = gdk_broadway_surface_set_startup_id; - impl_class->set_transient_for = gdk_broadway_surface_set_transient_for; - impl_class->get_frame_extents = gdk_broadway_surface_get_frame_extents; - impl_class->set_accept_focus = gdk_broadway_surface_set_accept_focus; - impl_class->set_focus_on_map = gdk_broadway_surface_set_focus_on_map; - impl_class->set_icon_list = gdk_broadway_surface_set_icon_list; - impl_class->set_icon_name = gdk_broadway_surface_set_icon_name; - impl_class->iconify = gdk_broadway_surface_iconify; - impl_class->deiconify = gdk_broadway_surface_deiconify; - impl_class->stick = gdk_broadway_surface_stick; - impl_class->unstick = gdk_broadway_surface_unstick; - impl_class->maximize = gdk_broadway_surface_maximize; - impl_class->unmaximize = gdk_broadway_surface_unmaximize; - impl_class->fullscreen = gdk_broadway_surface_fullscreen; - impl_class->unfullscreen = gdk_broadway_surface_unfullscreen; - impl_class->set_keep_above = gdk_broadway_surface_set_keep_above; - impl_class->set_keep_below = gdk_broadway_surface_set_keep_below; - impl_class->get_group = gdk_broadway_surface_get_group; - impl_class->set_group = gdk_broadway_surface_set_group; - impl_class->set_decorations = gdk_broadway_surface_set_decorations; - impl_class->get_decorations = gdk_broadway_surface_get_decorations; - impl_class->set_functions = gdk_broadway_surface_set_functions; - impl_class->begin_resize_drag = gdk_broadway_surface_begin_resize_drag; - impl_class->begin_move_drag = gdk_broadway_surface_begin_move_drag; - impl_class->set_opacity = gdk_broadway_surface_set_opacity; - impl_class->destroy_notify = gdk_broadway_surface_destroy_notify; - impl_class->register_dnd = _gdk_broadway_surface_register_dnd; - impl_class->drag_begin = _gdk_broadway_surface_drag_begin; -} diff --git a/gdk/broadway/gdkwindow-broadway.h b/gdk/broadway/gdkwindow-broadway.h deleted file mode 100644 index d6239319d2..0000000000 --- a/gdk/broadway/gdkwindow-broadway.h +++ /dev/null @@ -1,84 +0,0 @@ -/* GDK - The GIMP Drawing Kit - * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * 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, see . - */ - -/* - * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS - * file for a list of people on the GTK+ Team. See the ChangeLog - * files for a list of changes. These files are distributed with - * GTK+ at ftp://ftp.gtk.org/pub/gtk/. - */ - -#ifndef __GDK_SURFACE_BROADWAY_H__ -#define __GDK_SURFACE_BROADWAY_H__ - -#include - -G_BEGIN_DECLS - -typedef struct _GdkSurfaceImplBroadway GdkSurfaceImplBroadway; -typedef struct _GdkSurfaceImplBroadwayClass GdkSurfaceImplBroadwayClass; - -/* Window implementation for Broadway - */ - -#define GDK_TYPE_SURFACE_IMPL_BROADWAY (gdk_surface_impl_broadway_get_type ()) -#define GDK_SURFACE_IMPL_BROADWAY(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), GDK_TYPE_SURFACE_IMPL_BROADWAY, GdkSurfaceImplBroadway)) -#define GDK_SURFACE_IMPL_BROADWAY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GDK_TYPE_SURFACE_IMPL_BROADWAY, GdkSurfaceImplBroadwayClass)) -#define GDK_IS_SURFACE_IMPL_BROADWAY(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), GDK_TYPE_SURFACE_IMPL_BROADWAY)) -#define GDK_IS_SURFACE_IMPL_BROADWAY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GDK_TYPE_SURFACE_IMPL_BROADWAY)) -#define GDK_SURFACE_IMPL_BROADWAY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GDK_TYPE_SURFACE_IMPL_BROADWAY, GdkSurfaceImplBroadwayClass)) - -struct _GdkSurfaceImplBroadway -{ - GdkSurfaceImpl parent_instance; - - GdkSurface *wrapper; - - GdkCursor *cursor; - - int id; - - gboolean visible; - gboolean maximized; - int transient_for; - - int pre_maximize_x; - int pre_maximize_y; - int pre_maximize_width; - int pre_maximize_height; - - gint8 toplevel_window_type; - gboolean dirty; - gboolean last_synced; - - GdkGeometry geometry_hints; - GdkSurfaceHints geometry_hints_mask; - - GArray *node_data; - GPtrArray *node_data_textures; -}; - -struct _GdkSurfaceImplBroadwayClass -{ - GdkSurfaceImplClass parent_class; -}; - -GType gdk_surface_impl_broadway_get_type (void); - -G_END_DECLS - -#endif /* __GDK_SURFACE_BROADWAY_H__ */ diff --git a/gdk/broadway/meson.build b/gdk/broadway/meson.build index 970b6c8e6c..81e29cc9c1 100644 --- a/gdk/broadway/meson.build +++ b/gdk/broadway/meson.build @@ -12,12 +12,12 @@ gdk_broadway_sources = files([ 'gdkkeys-broadway.c', 'gdkmonitor-broadway.c', 'gdkselection-broadway.c', - 'gdkwindow-broadway.c', + 'gdksurface-broadway.c', ]) gdk_broadway_public_headers = [ 'gdkbroadwaydisplay.h', - 'gdkbroadwaywindow.h', + 'gdkbroadwaysurface.h', 'gdkbroadwaycursor.h', 'gdkbroadwaymonitor.h', ] diff --git a/gdk/gdk.h b/gdk/gdk.h index 6bb48e004a..921e93690d 100644 --- a/gdk/gdk.h +++ b/gdk/gdk.h @@ -67,7 +67,7 @@ #include #include #include -#include +#include #include diff --git a/gdk/gdkdisplay.c b/gdk/gdkdisplay.c index cfcffcec6a..2e9f0abd23 100644 --- a/gdk/gdkdisplay.c +++ b/gdk/gdkdisplay.c @@ -31,7 +31,7 @@ #include "gdkdeviceprivate.h" #include "gdkdisplaymanagerprivate.h" #include "gdkevents.h" -#include "gdkwindowimpl.h" +#include "gdksurfaceimpl.h" #include "gdkinternals.h" #include "gdkmonitorprivate.h" diff --git a/gdk/gdkdisplayprivate.h b/gdk/gdkdisplayprivate.h index dc206b9070..db52472a3c 100644 --- a/gdk/gdkdisplayprivate.h +++ b/gdk/gdkdisplayprivate.h @@ -19,7 +19,7 @@ #define __GDK_DISPLAY_PRIVATE_H__ #include "gdkdisplay.h" -#include "gdkwindow.h" +#include "gdksurface.h" #include "gdkcursor.h" #include "gdkmonitor.h" #include "gdkinternals.h" diff --git a/gdk/gdkdnd.c b/gdk/gdkdnd.c index f3197e6d75..7d6d453d2f 100644 --- a/gdk/gdkdnd.c +++ b/gdk/gdkdnd.c @@ -26,7 +26,7 @@ #include "gdkdndprivate.h" #include "gdkdisplay.h" -#include "gdkwindow.h" +#include "gdksurface.h" #include "gdkintl.h" #include "gdkcontentformats.h" #include "gdkcontentprovider.h" diff --git a/gdk/gdkdrawingcontext.c b/gdk/gdkdrawingcontext.c index eecf58a23a..b47aaf35a4 100644 --- a/gdk/gdkdrawingcontext.c +++ b/gdk/gdkdrawingcontext.c @@ -49,7 +49,7 @@ #include "gdkinternals.h" #include "gdkintl.h" #include "gdkframeclockidle.h" -#include "gdkwindowimpl.h" +#include "gdksurfaceimpl.h" #include "gdkglcontextprivate.h" #include "gdk-private.h" diff --git a/gdk/gdkinternals.h b/gdk/gdkinternals.h index 97c54b01e2..c55353448a 100644 --- a/gdk/gdkinternals.h +++ b/gdk/gdkinternals.h @@ -28,7 +28,7 @@ #define __GDK_INTERNALS_H__ #include -#include "gdkwindowimpl.h" +#include "gdksurfaceimpl.h" #include "gdkdisplay.h" #include "gdkeventsprivate.h" #include "gdkenumtypes.h" diff --git a/gdk/gdkpixbuf-drawable.c b/gdk/gdkpixbuf-drawable.c index c8a2c20376..fd4fa5a97a 100644 --- a/gdk/gdkpixbuf-drawable.c +++ b/gdk/gdkpixbuf-drawable.c @@ -24,7 +24,7 @@ #include "gdkpixbuf.h" -#include "gdkwindow.h" +#include "gdksurface.h" #include "gdkinternals.h" #include diff --git a/gdk/gdkseat.h b/gdk/gdkseat.h index f5e6a0336e..c9a0a4c220 100644 --- a/gdk/gdkseat.h +++ b/gdk/gdkseat.h @@ -25,7 +25,7 @@ #endif #include -#include +#include #include #include diff --git a/gdk/gdksurface.c b/gdk/gdksurface.c new file mode 100644 index 0000000000..d37a5ac464 --- /dev/null +++ b/gdk/gdksurface.c @@ -0,0 +1,6957 @@ +/* GDK - The GIMP Drawing Kit + * Copyright (C) 1995-2007 Peter Mattis, Spencer Kimball, + * Josh MacDonald, Ryan Lortie + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * 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, see . + */ + +/* + * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS + * file for a list of people on the GTK+ Team. See the ChangeLog + * files for a list of changes. These files are distributed with + * GTK+ at ftp://ftp.gtk.org/pub/gtk/. + */ + +#include "config.h" + +#include + +#include "gdksurface.h" + +#include "gdkrectangle.h" +#include "gdkinternals.h" +#include "gdkintl.h" +#include "gdkdisplayprivate.h" +#include "gdkdeviceprivate.h" +#include "gdkmarshalers.h" +#include "gdkframeclockidle.h" +#include "gdksurfaceimpl.h" +#include "gdkglcontextprivate.h" +#include "gdkdrawingcontextprivate.h" +#include "gdk-private.h" + +#include + +#include + +/* for the use of round() */ +#include "fallback-c89.c" + +#ifdef GDK_WINDOWING_WAYLAND +#include "wayland/gdkwayland.h" +#endif + +#undef DEBUG_WINDOW_PRINTING + + +/** + * SECTION:windows + * @Short_description: Onscreen display areas in the target window system + * @Title: Windows + * + * A #GdkSurface is a (usually) rectangular region on the screen. + * It’s a low-level object, used to implement high-level objects such as + * #GtkWidget and #GtkWindow on the GTK+ level. A #GtkWindow is a toplevel + * window, the thing a user might think of as a “window” with a titlebar + * and so on; a #GtkWindow may contain many sub-GdkSurfaces. + */ + +/** + * GdkSurface: + * + * The GdkSurface struct contains only private fields and + * should not be accessed directly. + */ + +/* Historically a GdkSurface always matches a platform native window, + * be it a toplevel window or a child window. In this setup the + * GdkSurface (and other GdkDrawables) were platform independent classes, + * and the actual platform specific implementation was in a delegate + * object available as “impl” in the window object. + * + * With the addition of client side windows this changes a bit. The + * application-visible GdkSurface object behaves as it did before, but + * such windows now don't a corresponding native window. Instead subwindows + * windows are “client side”, i.e. emulated by the gdk code such + * that clipping, drawing, moving, events etc work as expected. + * + * GdkSurfaces have a pointer to the “impl window” they are in, i.e. + * the topmost GdkSurface which have the same “impl” value. This is stored + * in impl_window, which is different from the window itself only for client + * side windows. + * All GdkSurfaces (native or not) track the position of the window in the parent + * (x, y), the size of the window (width, height), the position of the window + * with respect to the impl window (abs_x, abs_y). We also track the clip + * region of the window wrt parent windows, in window-relative coordinates (clip_region). + */ + +enum { + MOVED_TO_RECT, + LAST_SIGNAL +}; + +enum { + PROP_0, + PROP_CURSOR, + PROP_DISPLAY, + PROP_STATE, + LAST_PROP +}; + +/* Global info */ + +static void gdk_surface_finalize (GObject *object); + +static void gdk_surface_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec); +static void gdk_surface_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec); + +static void gdk_surface_clear_backing_region (GdkSurface *window); + +static void recompute_visible_regions (GdkSurface *private, + gboolean recalculate_children); +static void gdk_surface_invalidate_in_parent (GdkSurface *private); +static void update_cursor (GdkDisplay *display, + GdkDevice *device); +static void impl_window_add_update_area (GdkSurface *impl_window, + cairo_region_t *region); +static void gdk_surface_invalidate_region_full (GdkSurface *window, + const cairo_region_t *region, + gboolean invalidate_children); +static void gdk_surface_invalidate_rect_full (GdkSurface *window, + const GdkRectangle *rect, + gboolean invalidate_children); +static cairo_surface_t *gdk_surface_ref_impl_surface (GdkSurface *window); + +static void gdk_surface_set_frame_clock (GdkSurface *window, + GdkFrameClock *clock); + + +static guint signals[LAST_SIGNAL] = { 0 }; +static GParamSpec *properties[LAST_PROP] = { NULL, }; + +G_DEFINE_ABSTRACT_TYPE (GdkSurface, gdk_surface, 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 + { + int i; + 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 (i = 0; i < num; i++) + { + cairo_region_get_rectangle (region, i, &r); + 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 + +static GList * +list_insert_link_before (GList *list, + GList *sibling, + GList *link) +{ + if (list == NULL || sibling == list) + { + link->prev = NULL; + link->next = list; + if (list) + list->prev = link; + return link; + } + else if (sibling == NULL) + { + GList *last = g_list_last (list); + + last->next = link; + link->prev = last; + link->next = NULL; + + return list; + } + else + { + link->next = sibling; + link->prev = sibling->prev; + sibling->prev = link; + + if (link->prev) + link->prev->next = link; + + return list; + } +} + +static void +gdk_surface_init (GdkSurface *window) +{ + /* 0-initialization is good for all other fields. */ + + window->window_type = GDK_SURFACE_CHILD; + + window->state = GDK_SURFACE_STATE_WITHDRAWN; + window->fullscreen_mode = GDK_FULLSCREEN_ON_CURRENT_MONITOR; + window->width = 1; + window->height = 1; + window->toplevel_window_type = -1; + window->children_list_node.data = window; + + window->device_cursor = g_hash_table_new_full (NULL, NULL, + NULL, g_object_unref); +} + +static void +gdk_surface_class_init (GdkSurfaceClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = gdk_surface_finalize; + object_class->set_property = gdk_surface_set_property; + object_class->get_property = gdk_surface_get_property; + + /* Properties */ + + /** + * GdkSurface:cursor: + * + * The mouse pointer for a #GdkSurface. See gdk_surface_set_cursor() and + * gdk_surface_get_cursor() for details. + */ + properties[PROP_CURSOR] = + g_param_spec_object ("cursor", + P_("Cursor"), + P_("Cursor"), + GDK_TYPE_CURSOR, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + + /** + * GdkSurface:display: + * + * The #GdkDisplay connection of the window. See gdk_surface_get_display() + * for details. + */ + properties[PROP_DISPLAY] = + g_param_spec_object ("display", + P_("Display"), + P_("Display"), + GDK_TYPE_DISPLAY, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); + + properties[PROP_STATE] = + g_param_spec_flags ("state", + P_("State"), + P_("State"), + GDK_TYPE_SURFACE_STATE, GDK_SURFACE_STATE_WITHDRAWN, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + g_object_class_install_properties (object_class, LAST_PROP, properties); + + /** + * GdkSurface::moved-to-rect: + * @window: the #GdkSurface that moved + * @flipped_rect: (nullable): the position of @window after any possible + * flipping or %NULL if the backend can't obtain it + * @final_rect: (nullable): the final position of @window or %NULL if the + * backend can't obtain it + * @flipped_x: %TRUE if the anchors were flipped horizontally + * @flipped_y: %TRUE if the anchors were flipped vertically + * + * Emitted when the position of @window is finalized after being moved to a + * destination rectangle. + * + * @window might be flipped over the destination rectangle in order to keep + * it on-screen, in which case @flipped_x and @flipped_y will be set to %TRUE + * accordingly. + * + * @flipped_rect is the ideal position of @window after any possible + * flipping, but before any possible sliding. @final_rect is @flipped_rect, + * but possibly translated in the case that flipping is still ineffective in + * keeping @window on-screen. + * Stability: Private + */ + signals[MOVED_TO_RECT] = + g_signal_new (g_intern_static_string ("moved-to-rect"), + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + 0, + NULL, + NULL, + _gdk_marshal_VOID__POINTER_POINTER_BOOLEAN_BOOLEAN, + G_TYPE_NONE, + 4, + G_TYPE_POINTER, + G_TYPE_POINTER, + G_TYPE_BOOLEAN, + G_TYPE_BOOLEAN); +} + +static void +seat_removed_cb (GdkDisplay *display, + GdkSeat *seat, + GdkSurface *window) +{ + GdkDevice *device = gdk_seat_get_pointer (seat); + + window->devices_inside = g_list_remove (window->devices_inside, device); + g_hash_table_remove (window->device_cursor, device); + + if (window->device_events) + g_hash_table_remove (window->device_events, device); +} + +static void +gdk_surface_finalize (GObject *object) +{ + GdkSurface *window = GDK_SURFACE (object); + + g_signal_handlers_disconnect_by_func (gdk_surface_get_display (window), + seat_removed_cb, window); + + if (!GDK_SURFACE_DESTROYED (window)) + { + if (GDK_SURFACE_TYPE (window) != GDK_SURFACE_FOREIGN) + { + g_warning ("losing last reference to undestroyed window"); + _gdk_surface_destroy (window, FALSE); + } + else + /* We use TRUE here, to keep us from actually calling + * XDestroyWindow() on the window + */ + _gdk_surface_destroy (window, TRUE); + } + + if (window->impl) + { + g_object_unref (window->impl); + window->impl = NULL; + } + + if (window->impl_window != window) + { + g_object_unref (window->impl_window); + window->impl_window = NULL; + } + + if (window->shape) + cairo_region_destroy (window->shape); + + if (window->input_shape) + cairo_region_destroy (window->input_shape); + + if (window->cursor) + g_object_unref (window->cursor); + + if (window->device_cursor) + g_hash_table_destroy (window->device_cursor); + + if (window->device_events) + g_hash_table_destroy (window->device_events); + + if (window->devices_inside) + g_list_free (window->devices_inside); + + g_clear_object (&window->display); + + if (window->opaque_region) + cairo_region_destroy (window->opaque_region); + + G_OBJECT_CLASS (gdk_surface_parent_class)->finalize (object); +} + +static void +gdk_surface_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GdkSurface *window = GDK_SURFACE (object); + + switch (prop_id) + { + case PROP_CURSOR: + gdk_surface_set_cursor (window, g_value_get_object (value)); + break; + + case PROP_DISPLAY: + window->display = g_value_dup_object (value); + g_assert (window->display != NULL); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gdk_surface_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GdkSurface *window = GDK_SURFACE (object); + + switch (prop_id) + { + case PROP_CURSOR: + g_value_set_object (value, gdk_surface_get_cursor (window)); + break; + + case PROP_DISPLAY: + g_value_set_object (value, window->display); + break; + + case PROP_STATE: + g_value_set_flags (value, window->state); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static gboolean +gdk_surface_is_subsurface (GdkSurface *window) +{ + return window->window_type == GDK_SURFACE_SUBSURFACE; +} + +static GdkSurface * +gdk_surface_get_impl_window (GdkSurface *window) +{ + return window->impl_window; +} + +GdkSurface * +_gdk_surface_get_impl_window (GdkSurface *window) +{ + return gdk_surface_get_impl_window (window); +} + +static gboolean +gdk_surface_has_impl (GdkSurface *window) +{ + return window->impl_window == window; +} + +static gboolean +gdk_surface_is_toplevel (GdkSurface *window) +{ + return + window->parent == NULL || + window->parent->window_type == GDK_SURFACE_ROOT; +} + +gboolean +_gdk_surface_has_impl (GdkSurface *window) +{ + return gdk_surface_has_impl (window); +} + +static gboolean +gdk_surface_has_no_impl (GdkSurface *window) +{ + return window->impl_window != window; +} + +static void +remove_sibling_overlapped_area (GdkSurface *window, + cairo_region_t *region) +{ + GdkSurface *parent; + GdkSurface *sibling; + cairo_region_t *child_region; + GdkRectangle r; + GList *l; + + parent = window->parent; + + if (gdk_surface_is_toplevel (window)) + return; + + /* Convert from from window coords to parent coords */ + cairo_region_translate (region, window->x, window->y); + + for (l = parent->children; l; l = l->next) + { + sibling = l->data; + + if (sibling == window) + break; + + if (!GDK_SURFACE_IS_MAPPED (sibling) || sibling->input_only) + continue; + + r.x = sibling->x; + r.y = sibling->y; + r.width = sibling->width; + r.height = sibling->height; + + child_region = cairo_region_create_rectangle (&r); + + if (sibling->shape) + { + /* Adjust shape region to parent window coords */ + cairo_region_translate (sibling->shape, sibling->x, sibling->y); + cairo_region_intersect (child_region, sibling->shape); + cairo_region_translate (sibling->shape, -sibling->x, -sibling->y); + } + + cairo_region_subtract (region, child_region); + cairo_region_destroy (child_region); + } + + remove_sibling_overlapped_area (parent, region); + + /* Convert back to window coords */ + cairo_region_translate (region, -window->x, -window->y); +} + +static void +remove_child_area (GdkSurface *window, + gboolean for_input, + cairo_region_t *region) +{ + GdkSurface *child; + cairo_region_t *child_region; + GdkRectangle r; + GList *l; + + for (l = window->children; l; l = l->next) + { + child = l->data; + + /* If region is empty already, no need to do + anything potentially costly */ + if (cairo_region_is_empty (region)) + break; + + if (!GDK_SURFACE_IS_MAPPED (child) || child->input_only) + continue; + + r.x = child->x; + r.y = child->y; + r.width = child->width; + r.height = child->height; + + /* Bail early if child totally outside region */ + if (cairo_region_contains_rectangle (region, &r) == CAIRO_REGION_OVERLAP_OUT) + continue; + + child_region = cairo_region_create_rectangle (&r); + + if (child->shape) + { + /* Adjust shape region to parent window coords */ + cairo_region_translate (child->shape, child->x, child->y); + cairo_region_intersect (child_region, child->shape); + cairo_region_translate (child->shape, -child->x, -child->y); + } + + if (for_input) + { + if (child->input_shape) + cairo_region_intersect (child_region, child->input_shape); + } + + cairo_region_subtract (region, child_region); + cairo_region_destroy (child_region); + } +} + +static gboolean +should_apply_clip_as_shape (GdkSurface *window) +{ + return + gdk_surface_has_impl (window) && + /* Not for non-shaped toplevels */ + (window->shape != NULL || window->applied_shape) && + /* or for foreign windows */ + window->window_type != GDK_SURFACE_FOREIGN && + /* or for the root window */ + window->window_type != GDK_SURFACE_ROOT; +} + +static void +apply_shape (GdkSurface *window, + cairo_region_t *region) +{ + GdkSurfaceImplClass *impl_class; + + /* We trash whether we applied a shape so that + we can avoid unsetting it many times, which + could happen in e.g. apply_clip_as_shape as + windows get resized */ + impl_class = GDK_SURFACE_IMPL_GET_CLASS (window->impl); + if (region) + impl_class->shape_combine_region (window, + region, 0, 0); + else if (window->applied_shape) + impl_class->shape_combine_region (window, + NULL, 0, 0); + + window->applied_shape = region != NULL; +} + +static gboolean +region_rect_equal (const cairo_region_t *region, + const GdkRectangle *rect) +{ + GdkRectangle extents; + + if (cairo_region_num_rectangles (region) != 1) + return FALSE; + + cairo_region_get_extents (region, &extents); + + return extents.x == rect->x && + extents.y == rect->y && + extents.width == rect->width && + extents.height == rect->height; +} + +static void +apply_clip_as_shape (GdkSurface *window) +{ + GdkRectangle r; + cairo_region_t *region; + + r.x = r.y = 0; + r.width = window->width; + r.height = window->height; + + region = cairo_region_copy (window->clip_region); + remove_sibling_overlapped_area (window, region); + + /* We only apply the clip region if would differ + from the actual clip region implied by the size + of the window. This is to avoid unneccessarily + adding meaningless shapes to all native subwindows */ + if (!region_rect_equal (region, &r)) + apply_shape (window, region); + else + apply_shape (window, NULL); + + cairo_region_destroy (region); +} + +static void +recompute_visible_regions_internal (GdkSurface *private, + gboolean recalculate_clip, + gboolean recalculate_children) +{ + GdkRectangle r; + GList *l; + GdkSurface *child; + cairo_region_t *new_clip; + gboolean clip_region_changed; + gboolean abs_pos_changed; + int old_abs_x, old_abs_y; + + old_abs_x = private->abs_x; + old_abs_y = private->abs_y; + + /* Update absolute position */ + if ((gdk_surface_has_impl (private) && + private->window_type != GDK_SURFACE_SUBSURFACE) || + (gdk_surface_is_toplevel (private) && + private->window_type == GDK_SURFACE_SUBSURFACE)) + { + /* Native windows and toplevel subsurfaces start here */ + private->abs_x = 0; + private->abs_y = 0; + } + else + { + private->abs_x = private->parent->abs_x + private->x; + private->abs_y = private->parent->abs_y + private->y; + } + + abs_pos_changed = + private->abs_x != old_abs_x || + private->abs_y != old_abs_y; + + /* Update clip region based on: + * parent clip + * window size/position + */ + clip_region_changed = FALSE; + if (recalculate_clip) + { + if (private->viewable) + { + /* Calculate visible region (sans children) in parent window coords */ + r.x = private->x; + r.y = private->y; + r.width = private->width; + r.height = private->height; + new_clip = cairo_region_create_rectangle (&r); + + if (!gdk_surface_is_toplevel (private)) + cairo_region_intersect (new_clip, private->parent->clip_region); + + /* Convert from parent coords to window coords */ + cairo_region_translate (new_clip, -private->x, -private->y); + + if (should_apply_clip_as_shape (private) && private->shape) + cairo_region_intersect (new_clip, private->shape); + } + else + new_clip = cairo_region_create (); + + if (private->clip_region == NULL || + !cairo_region_equal (private->clip_region, new_clip)) + clip_region_changed = TRUE; + + if (private->clip_region) + cairo_region_destroy (private->clip_region); + private->clip_region = new_clip; + } + + /* Update all children, recursively (except for root, where children are not exact). */ + if ((abs_pos_changed || clip_region_changed || recalculate_children) && + private->window_type != GDK_SURFACE_ROOT) + { + for (l = private->children; l; l = l->next) + { + child = l->data; + /* Only recalculate clip if the the clip region changed, otherwise + * there is no way the child clip region could change (its has not e.g. moved) + * Except if recalculate_children is set to force child updates + */ + recompute_visible_regions_internal (child, + recalculate_clip && (clip_region_changed || recalculate_children), + FALSE); + } + } +} + +/* Call this when private has changed in one or more of these ways: + * size changed + * window moved + * new window added + * stacking order of window changed + * child deleted + * + * It will recalculate abs_x/y and the clip regions + * + * Unless the window didn’t change stacking order or size/pos, pass in TRUE + * for recalculate_siblings. (Mostly used internally for the recursion) + * + * If a child window was removed (and you can’t use that child for + * recompute_visible_regions), pass in TRUE for recalculate_children on the parent + */ +static void +recompute_visible_regions (GdkSurface *private, + gboolean recalculate_children) +{ + GdkSurface *toplevel; + + toplevel = gdk_surface_get_toplevel (private); + toplevel->geometry_dirty = TRUE; + + recompute_visible_regions_internal (private, + TRUE, + recalculate_children); +} + +static void +gdk_surface_clear_old_updated_area (GdkSurface *window) +{ + int i; + + for (i = 0; i < 2; i++) + { + if (window->old_updated_area[i]) + { + cairo_region_destroy (window->old_updated_area[i]); + window->old_updated_area[i] = NULL; + } + } +} + +static void +gdk_surface_append_old_updated_area (GdkSurface *window, + cairo_region_t *region) +{ + if (window->old_updated_area[1]) + cairo_region_destroy (window->old_updated_area[1]); + window->old_updated_area[1] = window->old_updated_area[0]; + window->old_updated_area[0] = cairo_region_reference (region); +} + +void +_gdk_surface_update_size (GdkSurface *window) +{ + gdk_surface_clear_old_updated_area (window); + recompute_visible_regions (window, FALSE); +} + +static GdkEventMask +get_native_device_event_mask (GdkSurface *private, + GdkDevice *device) +{ + GdkEventMask event_mask; + + if (device) + event_mask = GPOINTER_TO_INT (g_hash_table_lookup (private->device_events, device)); + else + event_mask = private->event_mask; + + if (private->window_type == GDK_SURFACE_ROOT || + private->window_type == GDK_SURFACE_FOREIGN) + return event_mask; + else + { + GdkEventMask mask; + + mask = private->event_mask; + + /* We need thse for all native windows so we can + emulate events on children: */ + mask |= + GDK_EXPOSURE_MASK | + GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK | + GDK_TOUCH_MASK | + GDK_POINTER_MOTION_MASK | + GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | + GDK_SCROLL_MASK; + + return mask; + } +} + +static GdkEventMask +get_native_event_mask (GdkSurface *private) +{ + return get_native_device_event_mask (private, NULL); +} + +GdkSurface* +gdk_surface_new (GdkDisplay *display, + GdkSurface *parent, + GdkSurfaceAttr *attributes) +{ + GdkSurface *window; + gboolean native; + GdkEventMask event_mask; + + g_return_val_if_fail (attributes != NULL, NULL); + + if (parent != NULL && GDK_SURFACE_DESTROYED (parent)) + { + g_warning ("gdk_surface_new(): parent is destroyed"); + return NULL; + } + + window = _gdk_display_create_window (display); + + window->parent = parent; + + window->accept_focus = TRUE; + window->focus_on_map = TRUE; + + window->x = attributes->x; + window->y = attributes->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) + { + /* Backwards compatiblity - we've always ignored + * attributes->window_type for input-only windows + * before + */ + if (parent == NULL) + window->window_type = GDK_SURFACE_TEMP; + else + window->window_type = GDK_SURFACE_CHILD; + } + else + window->window_type = attributes->window_type; + + /* Sanity checks */ + switch (window->window_type) + { + case GDK_SURFACE_TOPLEVEL: + case GDK_SURFACE_TEMP: + if (parent != NULL && GDK_SURFACE_TYPE (parent) != GDK_SURFACE_ROOT) + g_warning (G_STRLOC "Toplevel windows must be created as children of\n" + "a window of type GDK_SURFACE_ROOT"); + break; + case GDK_SURFACE_SUBSURFACE: +#ifdef GDK_WINDOWING_WAYLAND + if (!GDK_IS_WAYLAND_DISPLAY (display)) + { + g_warning (G_STRLOC "Subsurface windows can only be used on Wayland"); + return NULL; + } +#endif + break; + case GDK_SURFACE_CHILD: + if (GDK_SURFACE_TYPE (parent) == GDK_SURFACE_ROOT || + GDK_SURFACE_TYPE (parent) == GDK_SURFACE_FOREIGN) + { + g_warning (G_STRLOC "Child windows must not be created as children of\n" + "a window of type GDK_SURFACE_ROOT or GDK_SURFACE_FOREIGN"); + return NULL; + } + break; + default: + g_warning (G_STRLOC "cannot make windows of type %d", window->window_type); + return NULL; + } + + window->event_mask = GDK_ALL_EVENTS_MASK; + + if (attributes->wclass == GDK_INPUT_OUTPUT) + { + window->input_only = FALSE; + } + else + { + window->input_only = TRUE; + } + + native = FALSE; + + if (window->parent != NULL) + window->parent->children = g_list_concat (&window->children_list_node, window->parent->children); + else + { + GdkFrameClock *frame_clock = g_object_new (GDK_TYPE_FRAME_CLOCK_IDLE, NULL); + gdk_surface_set_frame_clock (window, frame_clock); + g_object_unref (frame_clock); + + native = TRUE; /* Always use native windows for toplevels */ + } + +#ifdef GDK_WINDOWING_WAYLAND + if (window->window_type == GDK_SURFACE_SUBSURFACE) + native = TRUE; /* Always use native windows for subsurfaces as well */ +#endif + + if (native) + { + event_mask = get_native_event_mask (window); + + /* Create the impl */ + _gdk_display_create_window_impl (display, window, parent, event_mask, attributes); + window->impl_window = window; + } + else + { + window->impl_window = g_object_ref (window->parent->impl_window); + window->impl = g_object_ref (window->impl_window->impl); + } + + recompute_visible_regions (window, FALSE); + + g_signal_connect (display, "seat-removed", G_CALLBACK (seat_removed_cb), window); + + return window; +} + +/** + * gdk_surface_new_toplevel: (constructor) + * @display: the display to create the window on + * @width: width of new window + * @height: height of new window + * + * Creates a new toplevel window. The window will be managed by the window + * manager. + * + * Returns: (transfer full): the new #GdkSurface + **/ +GdkSurface * +gdk_surface_new_toplevel (GdkDisplay *display, + gint width, + gint height) +{ + GdkSurfaceAttr attr; + + g_return_val_if_fail (GDK_IS_DISPLAY (display), NULL); + + attr.wclass = GDK_INPUT_OUTPUT; + attr.x = 0; + attr.y = 0; + attr.width = width; + attr.height = height; + attr.window_type = GDK_SURFACE_TOPLEVEL; + + return gdk_surface_new (display, NULL, &attr); +} + +/** + * gdk_surface_new_popup: (constructor) + * @display: the display to create the window on + * @position: position of the window on screen + * + * Creates a new toplevel popup window. The window will bypass window + * management. + * + * Returns: (transfer full): the new #GdkSurface + **/ +GdkSurface * +gdk_surface_new_popup (GdkDisplay *display, + const GdkRectangle *position) +{ + GdkSurfaceAttr attr; + + g_return_val_if_fail (GDK_IS_DISPLAY (display), NULL); + g_return_val_if_fail (position != NULL, NULL); + + attr.wclass = GDK_INPUT_OUTPUT; + attr.x = position->x; + attr.y = position->y; + attr.width = position->width; + attr.height = position->height; + attr.window_type = GDK_SURFACE_TEMP; + + return gdk_surface_new (display, NULL, &attr); +} + +/** + * gdk_surface_new_temp: (constructor) + * @display: the display to create the window on + * + * Creates a new toplevel temporary window. The window will be + * situated off-screen and not handle output. + * + * You most likely do not want to use this function. + * + * Returns: (transfer full): the new #GdkSurface + **/ +GdkSurface * +gdk_surface_new_temp (GdkDisplay *display) +{ + GdkSurfaceAttr attr; + + g_return_val_if_fail (GDK_IS_DISPLAY (display), NULL); + + attr.wclass = GDK_INPUT_ONLY; + attr.x = -100; + attr.y = -100; + attr.width = 10; + attr.height = 10; + attr.window_type = GDK_SURFACE_TEMP; + + return gdk_surface_new (display, NULL, &attr); +} + +/** + * gdk_surface_new_child: (constructor) + * @parent: the parent window + * @position: placement of the window inside @parent + * + * Creates a new client-side child window. + * + * Returns: (transfer full): the new #GdkSurface + **/ +GdkSurface * +gdk_surface_new_child (GdkSurface *parent, + const GdkRectangle *position) +{ + GdkSurfaceAttr attr; + + g_return_val_if_fail (GDK_IS_SURFACE (parent), NULL); + + attr.wclass = GDK_INPUT_OUTPUT; + attr.x = position->x; + attr.y = position->y; + attr.width = position->width; + attr.height = position->height; + attr.window_type = GDK_SURFACE_CHILD; + + return gdk_surface_new (gdk_surface_get_display (parent), parent, &attr); +} + +static void +update_pointer_info_foreach (GdkDisplay *display, + GdkDevice *device, + GdkPointerSurfaceInfo *pointer_info, + gpointer user_data) +{ + GdkSurface *window = user_data; + + if (pointer_info->window_under_pointer == window) + { + g_object_unref (pointer_info->window_under_pointer); + pointer_info->window_under_pointer = NULL; + } +} + +static void +window_remove_from_pointer_info (GdkSurface *window, + GdkDisplay *display) +{ + _gdk_display_pointer_info_foreach (display, + update_pointer_info_foreach, + window); +} + +static void +gdk_surface_free_current_paint (GdkSurface *window) +{ + cairo_surface_destroy (window->current_paint.surface); + window->current_paint.surface = NULL; + + cairo_region_destroy (window->current_paint.region); + window->current_paint.region = NULL; + + window->current_paint.surface_needs_composite = FALSE; +} + +/** + * _gdk_surface_destroy_hierarchy: + * @window: a #GdkSurface + * @recursing: If %TRUE, then this is being called because a parent + * was destroyed. + * @recursing_native: If %TRUE, then this is being called because a native parent + * was destroyed. This generally means that the call to the + * windowing system to destroy the window can be omitted, since + * it will be destroyed as a result of the parent being destroyed. + * Unless @foreign_destroy. + * @foreign_destroy: If %TRUE, the window or a parent was destroyed by some + * external agency. The window has already been destroyed and no + * windowing system calls should be made. (This may never happen + * for some windowing systems.) + * + * Internal function to destroy a window. Like gdk_surface_destroy(), + * but does not drop the reference count created by gdk_surface_new(). + **/ +static void +_gdk_surface_destroy_hierarchy (GdkSurface *window, + gboolean recursing, + gboolean recursing_native, + gboolean foreign_destroy) +{ + GdkSurfaceImplClass *impl_class; + GdkSurface *temp_window; + GdkDisplay *display; + GList *tmp; + + g_return_if_fail (GDK_IS_SURFACE (window)); + + if (GDK_SURFACE_DESTROYED (window)) + return; + + display = gdk_surface_get_display (window); + + switch (window->window_type) + { + default: + g_assert_not_reached (); + break; + + case GDK_SURFACE_ROOT: + if (!gdk_display_is_closed (display)) + { + g_error ("attempted to destroy root window"); + break; + } + /* else fall thru */ + case GDK_SURFACE_TOPLEVEL: + case GDK_SURFACE_CHILD: + case GDK_SURFACE_TEMP: + case GDK_SURFACE_FOREIGN: + case GDK_SURFACE_SUBSURFACE: + if (window->window_type == GDK_SURFACE_FOREIGN && !foreign_destroy) + { + } + else + { + if (window->parent) + { + if (window->parent->children) + window->parent->children = g_list_remove_link (window->parent->children, &window->children_list_node); + + if (!recursing && + GDK_SURFACE_IS_MAPPED (window)) + { + recompute_visible_regions (window, FALSE); + gdk_surface_invalidate_in_parent (window); + } + } + + if (window->gl_paint_context) + { + /* Make sure to destroy if current */ + g_object_run_dispose (G_OBJECT (window->gl_paint_context)); + g_object_unref (window->gl_paint_context); + window->gl_paint_context = NULL; + } + + if (window->frame_clock) + { + g_object_run_dispose (G_OBJECT (window->frame_clock)); + gdk_surface_set_frame_clock (window, NULL); + } + + gdk_surface_free_current_paint (window); + + if (window->window_type == GDK_SURFACE_FOREIGN) + g_assert (window->children == NULL); + else + { + tmp = window->children; + window->children = NULL; + /* No need to free children list, its all made up of in-struct nodes */ + + while (tmp) + { + temp_window = tmp->data; + tmp = tmp->next; + + if (temp_window) + _gdk_surface_destroy_hierarchy (temp_window, + TRUE, + recursing_native || gdk_surface_has_impl (window), + foreign_destroy); + } + } + + _gdk_surface_clear_update_area (window); + + impl_class = GDK_SURFACE_IMPL_GET_CLASS (window->impl); + + if (gdk_surface_has_impl (window)) + impl_class->destroy (window, recursing_native, foreign_destroy); + else + { + /* hide to make sure we repaint and break grabs */ + gdk_surface_hide (window); + } + + window->state |= GDK_SURFACE_STATE_WITHDRAWN; + window->parent = NULL; + window->destroyed = TRUE; + + window_remove_from_pointer_info (window, display); + + if (window->clip_region) + { + cairo_region_destroy (window->clip_region); + window->clip_region = NULL; + } + + g_object_notify_by_pspec (G_OBJECT (window), properties[PROP_STATE]); + } + break; + } +} + +/** + * _gdk_surface_destroy: + * @window: a #GdkSurface + * @foreign_destroy: If %TRUE, the window or a parent was destroyed by some + * external agency. The window has already been destroyed and no + * windowing system calls should be made. (This may never happen + * for some windowing systems.) + * + * Internal function to destroy a window. Like gdk_surface_destroy(), + * but does not drop the reference count created by gdk_surface_new(). + **/ +void +_gdk_surface_destroy (GdkSurface *window, + gboolean foreign_destroy) +{ + _gdk_surface_destroy_hierarchy (window, FALSE, FALSE, foreign_destroy); +} + +/** + * gdk_surface_destroy: + * @window: a #GdkSurface + * + * Destroys the window system resources associated with @window and decrements @window's + * reference count. The window system resources for all children of @window are also + * destroyed, but the children’s reference counts are not decremented. + * + * Note that a window will not be destroyed automatically when its reference count + * reaches zero. You must call this function yourself before that happens. + * + **/ +void +gdk_surface_destroy (GdkSurface *window) +{ + _gdk_surface_destroy_hierarchy (window, FALSE, FALSE, FALSE); + g_object_unref (window); +} + +/** + * gdk_surface_set_user_data: + * @window: a #GdkSurface + * @user_data: (allow-none) (type GObject.Object): user data + * + * For most purposes this function is deprecated in favor of + * g_object_set_data(). However, for historical reasons GTK+ stores + * the #GtkWidget that owns a #GdkSurface as user data on the + * #GdkSurface. So, custom widget implementations should use + * this function for that. If GTK+ receives an event for a #GdkSurface, + * and the user data for the window is non-%NULL, GTK+ will assume the + * user data is a #GtkWidget, and forward the event to that widget. + * + **/ +void +gdk_surface_set_user_data (GdkSurface *window, + gpointer user_data) +{ + g_return_if_fail (GDK_IS_SURFACE (window)); + + window->user_data = user_data; +} + +/** + * gdk_surface_get_user_data: + * @window: a #GdkSurface + * @data: (out): return location for user data + * + * Retrieves the user data for @window, which is normally the widget + * that @window belongs to. See gdk_surface_set_user_data(). + * + **/ +void +gdk_surface_get_user_data (GdkSurface *window, + gpointer *data) +{ + *data = window->user_data; +} + +/** + * gdk_surface_get_window_type: + * @window: a #GdkSurface + * + * Gets the type of the window. See #GdkSurfaceType. + * + * Returns: type of window + **/ +GdkSurfaceType +gdk_surface_get_window_type (GdkSurface *window) +{ + g_return_val_if_fail (GDK_IS_SURFACE (window), (GdkSurfaceType) -1); + + return GDK_SURFACE_TYPE (window); +} + +/** + * gdk_surface_get_display: + * @window: a #GdkSurface + * + * Gets the #GdkDisplay associated with a #GdkSurface. + * + * Returns: (transfer none): the #GdkDisplay associated with @window + **/ +GdkDisplay * +gdk_surface_get_display (GdkSurface *window) +{ + g_return_val_if_fail (GDK_IS_SURFACE (window), NULL); + + return window->display; +} +/** + * gdk_surface_is_destroyed: + * @window: a #GdkSurface + * + * Check to see if a window is destroyed.. + * + * Returns: %TRUE if the window is destroyed + **/ +gboolean +gdk_surface_is_destroyed (GdkSurface *window) +{ + return GDK_SURFACE_DESTROYED (window); +} + +/** + * gdk_surface_has_native: + * @window: a #GdkSurface + * + * Checks whether the window has a native window or not. + * + * Returns: %TRUE if the @window has a native window, %FALSE otherwise. + */ +gboolean +gdk_surface_has_native (GdkSurface *window) +{ + g_return_val_if_fail (GDK_IS_SURFACE (window), FALSE); + + return window->parent == NULL || window->parent->impl != window->impl; +} + +/** + * gdk_surface_get_position: + * @window: a #GdkSurface + * @x: (out) (allow-none): X coordinate of window + * @y: (out) (allow-none): Y coordinate of window + * + * Obtains the position of the window as reported in the + * most-recently-processed #GdkEventConfigure. Contrast with + * gdk_surface_get_geometry() which queries the X server for the + * current window position, regardless of which events have been + * received or processed. + * + * The position coordinates are relative to the window’s parent window. + * + **/ +void +gdk_surface_get_position (GdkSurface *window, + gint *x, + gint *y) +{ + g_return_if_fail (GDK_IS_SURFACE (window)); + + if (x) + *x = window->x; + if (y) + *y = window->y; +} + +/** + * gdk_surface_get_parent: + * @window: a #GdkSurface + * + * Obtains the parent of @window, as known to GDK. Does not query the + * X server; thus this returns the parent as passed to gdk_surface_new(), + * not the actual parent. This should never matter unless you’re using + * Xlib calls mixed with GDK calls on the X11 platform. It may also + * matter for toplevel windows, because the window manager may choose + * to reparent them. + * + * Returns: (transfer none): parent of @window + **/ +GdkSurface* +gdk_surface_get_parent (GdkSurface *window) +{ + g_return_val_if_fail (GDK_IS_SURFACE (window), NULL); + + if (gdk_surface_is_subsurface (window)) + return window->transient_for; + else + return window->parent; +} + +/** + * gdk_surface_get_toplevel: + * @window: a #GdkSurface + * + * Gets the toplevel window that’s an ancestor of @window. + * + * Any window type but %GDK_SURFACE_CHILD is considered a + * toplevel window, as is a %GDK_SURFACE_CHILD window that + * has a root window as parent. + * + * Returns: (transfer none): the toplevel window containing @window + **/ +GdkSurface * +gdk_surface_get_toplevel (GdkSurface *window) +{ + g_return_val_if_fail (GDK_IS_SURFACE (window), NULL); + + while (window->window_type == GDK_SURFACE_CHILD || + window->window_type == GDK_SURFACE_SUBSURFACE) + { + if (gdk_surface_is_toplevel (window)) + break; + window = window->parent; + } + + return window; +} + +/** + * gdk_surface_get_children: + * @window: a #GdkSurface + * + * Gets the list of children of @window known to GDK. + * This function only returns children created via GDK, + * so for example it’s useless when used with the root window; + * it only returns windows an application created itself. + * + * The returned list must be freed, but the elements in the + * list need not be. + * + * Returns: (transfer container) (element-type GdkSurface): + * list of child windows inside @window + **/ +GList* +gdk_surface_get_children (GdkSurface *window) +{ + g_return_val_if_fail (GDK_IS_SURFACE (window), NULL); + + if (GDK_SURFACE_DESTROYED (window)) + return NULL; + + return g_list_copy (window->children); +} + +/** + * gdk_surface_peek_children: + * @window: a #GdkSurface + * + * Like gdk_surface_get_children(), but does not copy the list of + * children, so the list does not need to be freed. + * + * Returns: (transfer none) (element-type GdkSurface): + * a reference to the list of child windows in @window + **/ +GList * +gdk_surface_peek_children (GdkSurface *window) +{ + g_return_val_if_fail (GDK_IS_SURFACE (window), NULL); + + if (GDK_SURFACE_DESTROYED (window)) + return NULL; + + return window->children; +} + + +/** + * gdk_surface_get_children_with_user_data: + * @window: a #GdkSurface + * @user_data: user data to look for + * + * Gets the list of children of @window known to GDK with a + * particular @user_data set on it. + * + * The returned list must be freed, but the elements in the + * list need not be. + * + * The list is returned in (relative) stacking order, i.e. the + * lowest window is first. + * + * Returns: (transfer container) (element-type GdkSurface): + * list of child windows inside @window + **/ +GList * +gdk_surface_get_children_with_user_data (GdkSurface *window, + gpointer user_data) +{ + GdkSurface *child; + GList *res, *l; + + g_return_val_if_fail (GDK_IS_SURFACE (window), NULL); + + if (GDK_SURFACE_DESTROYED (window)) + return NULL; + + res = NULL; + for (l = window->children; l != NULL; l = l->next) + { + child = l->data; + + if (child->user_data == user_data) + res = g_list_prepend (res, child); + } + + return res; +} + + +/** + * gdk_surface_is_visible: + * @window: a #GdkSurface + * + * Checks whether the window has been mapped (with gdk_surface_show() or + * gdk_surface_show_unraised()). + * + * Returns: %TRUE if the window is mapped + **/ +gboolean +gdk_surface_is_visible (GdkSurface *window) +{ + g_return_val_if_fail (GDK_IS_SURFACE (window), FALSE); + + return GDK_SURFACE_IS_MAPPED (window); +} + +/** + * gdk_surface_is_viewable: + * @window: a #GdkSurface + * + * Check if the window and all ancestors of the window are + * mapped. (This is not necessarily "viewable" in the X sense, since + * we only check as far as we have GDK window parents, not to the root + * window.) + * + * Returns: %TRUE if the window is viewable + **/ +gboolean +gdk_surface_is_viewable (GdkSurface *window) +{ + g_return_val_if_fail (GDK_IS_SURFACE (window), FALSE); + + if (window->destroyed) + return FALSE; + + return window->viewable; +} + +/** + * gdk_surface_get_state: + * @window: a #GdkSurface + * + * Gets the bitwise OR of the currently active window state flags, + * from the #GdkSurfaceState enumeration. + * + * Returns: window state bitfield + **/ +GdkSurfaceState +gdk_surface_get_state (GdkSurface *window) +{ + g_return_val_if_fail (GDK_IS_SURFACE (window), FALSE); + + return window->state; +} + +static cairo_content_t +gdk_surface_get_content (GdkSurface *window) +{ + cairo_surface_t *surface; + cairo_content_t content; + + g_return_val_if_fail (GDK_IS_SURFACE (window), 0); + + surface = gdk_surface_ref_impl_surface (window); + content = cairo_surface_get_content (surface); + cairo_surface_destroy (surface); + + return content; +} + +static cairo_surface_t * +gdk_surface_ref_impl_surface (GdkSurface *window) +{ + return GDK_SURFACE_IMPL_GET_CLASS (window->impl)->ref_cairo_surface (gdk_surface_get_impl_window (window)); +} + +GdkGLContext * +gdk_surface_get_paint_gl_context (GdkSurface *window, + GError **error) +{ + GError *internal_error = NULL; + + if (GDK_DISPLAY_DEBUG_CHECK (window->display, GL_DISABLE)) + { + g_set_error_literal (error, GDK_GL_ERROR, + GDK_GL_ERROR_NOT_AVAILABLE, + _("GL support disabled via GDK_DEBUG")); + return NULL; + } + + if (window->impl_window->gl_paint_context == NULL) + { + GdkSurfaceImplClass *impl_class = GDK_SURFACE_IMPL_GET_CLASS (window->impl); + + if (impl_class->create_gl_context == NULL) + { + g_set_error_literal (error, GDK_GL_ERROR, GDK_GL_ERROR_NOT_AVAILABLE, + _("The current backend does not support OpenGL")); + return NULL; + } + + window->impl_window->gl_paint_context = + impl_class->create_gl_context (window->impl_window, + TRUE, + NULL, + &internal_error); + } + + if (internal_error != NULL) + { + g_propagate_error (error, internal_error); + g_clear_object (&(window->impl_window->gl_paint_context)); + return NULL; + } + + gdk_gl_context_realize (window->impl_window->gl_paint_context, &internal_error); + if (internal_error != NULL) + { + g_propagate_error (error, internal_error); + g_clear_object (&(window->impl_window->gl_paint_context)); + return NULL; + } + + return window->impl_window->gl_paint_context; +} + +/** + * gdk_surface_create_gl_context: + * @window: a #GdkSurface + * @error: return location for an error + * + * Creates a new #GdkGLContext matching the + * framebuffer format to the visual of the #GdkSurface. The context + * is disconnected from any particular window or surface. + * + * If the creation of the #GdkGLContext failed, @error will be set. + * + * Before using the returned #GdkGLContext, you will need to + * call gdk_gl_context_make_current() or gdk_gl_context_realize(). + * + * Returns: (transfer full): the newly created #GdkGLContext, or + * %NULL on error + **/ +GdkGLContext * +gdk_surface_create_gl_context (GdkSurface *window, + GError **error) +{ + GdkGLContext *paint_context; + + g_return_val_if_fail (GDK_IS_SURFACE (window), NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + paint_context = gdk_surface_get_paint_gl_context (window, error); + if (paint_context == NULL) + return NULL; + + return GDK_SURFACE_IMPL_GET_CLASS (window->impl)->create_gl_context (window->impl_window, + FALSE, + paint_context, + error); +} + +/** + * gdk_surface_create_vulkan_context: + * @window: a #GdkSurface + * @error: return location for an error + * + * Creates a new #GdkVulkanContext for rendering on @window. + * + * If the creation of the #GdkVulkanContext failed, @error will be set. + * + * Returns: (transfer full): the newly created #GdkVulkanContext, or + * %NULL on error + **/ +GdkVulkanContext * +gdk_surface_create_vulkan_context (GdkSurface *window, + GError **error) +{ + GdkDisplay *display; + + g_return_val_if_fail (GDK_IS_SURFACE (window), NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + if (GDK_DISPLAY_DEBUG_CHECK (window->display, VULKAN_DISABLE)) + { + g_set_error_literal (error, GDK_VULKAN_ERROR, GDK_VULKAN_ERROR_NOT_AVAILABLE, + _("Vulkan support disabled via GDK_DEBUG")); + return NULL; + } + + display = gdk_surface_get_display (window); + + if (GDK_DISPLAY_GET_CLASS (display)->vk_extension_name == NULL) + { + g_set_error (error, GDK_VULKAN_ERROR, GDK_VULKAN_ERROR_UNSUPPORTED, + "The %s backend has no Vulkan support.", G_OBJECT_TYPE_NAME (display)); + return FALSE; + } + + return g_initable_new (GDK_DISPLAY_GET_CLASS (display)->vk_context_type, + NULL, + error, + "window", window, + NULL); +} + +static void +gdk_surface_begin_paint_internal (GdkSurface *window, + const cairo_region_t *region) +{ + GdkRectangle clip_box; + GdkSurfaceImplClass *impl_class; + double sx, sy; + gboolean needs_surface; + cairo_content_t surface_content; + + if (window->current_paint.surface != NULL) + { + g_warning ("A paint operation on the window is alredy in progress. " + "This is not allowed."); + return; + } + + impl_class = GDK_SURFACE_IMPL_GET_CLASS (window->impl); + + needs_surface = TRUE; + if (impl_class->begin_paint) + needs_surface = impl_class->begin_paint (window); + + window->current_paint.region = cairo_region_copy (region); + cairo_region_intersect (window->current_paint.region, window->clip_region); + cairo_region_get_extents (window->current_paint.region, &clip_box); + + surface_content = gdk_surface_get_content (window); + + if (needs_surface) + { + window->current_paint.surface = gdk_surface_create_similar_surface (window, + surface_content, + MAX (clip_box.width, 1), + MAX (clip_box.height, 1)); + sx = sy = 1; + cairo_surface_get_device_scale (window->current_paint.surface, &sx, &sy); + cairo_surface_set_device_offset (window->current_paint.surface, -clip_box.x*sx, -clip_box.y*sy); + gdk_cairo_surface_mark_as_direct (window->current_paint.surface, window); + + window->current_paint.surface_needs_composite = TRUE; + } + else + { + window->current_paint.surface = gdk_surface_ref_impl_surface (window); + window->current_paint.surface_needs_composite = FALSE; + } + + if (!cairo_region_is_empty (window->current_paint.region)) + gdk_surface_clear_backing_region (window); +} + +static void +gdk_surface_end_paint_internal (GdkSurface *window) +{ + GdkSurfaceImplClass *impl_class; + cairo_t *cr; + + if (window->current_paint.surface == NULL) + { + g_warning (G_STRLOC": no preceding call to gdk_surface_begin_draw_frame(), see documentation"); + return; + } + + impl_class = GDK_SURFACE_IMPL_GET_CLASS (window->impl); + + if (impl_class->end_paint) + impl_class->end_paint (window); + + if (window->current_paint.surface_needs_composite) + { + cairo_surface_t *surface; + + surface = gdk_surface_ref_impl_surface (window); + cr = cairo_create (surface); + + cairo_set_source_surface (cr, window->current_paint.surface, 0, 0); + gdk_cairo_region (cr, window->current_paint.region); + cairo_clip (cr); + + cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE); + cairo_paint (cr); + + cairo_destroy (cr); + + cairo_surface_flush (surface); + cairo_surface_destroy (surface); + } + + gdk_surface_free_current_paint (window); +} + +/** + * gdk_surface_begin_draw_frame: + * @window: a #GdkSurface + * @context: (allow-none): the context used to draw the frame + * @region: a Cairo region + * + * Indicates that you are beginning the process of redrawing @region + * on @window, and provides you with a #GdkDrawingContext. + * + * If @window is a top level #GdkSurface, backed by a native window + * implementation, a backing store (offscreen buffer) large enough to + * contain @region will be created. The backing store will be initialized + * with the background color or background surface for @window. Then, all + * drawing operations performed on @window will be diverted to the + * backing store. When you call gdk_surface_end_frame(), the contents of + * the backing store will be copied to @window, making it visible + * on screen. Only the part of @window contained in @region will be + * modified; that is, drawing operations are clipped to @region. + * + * The net result of all this is to remove flicker, because the user + * sees the finished product appear all at once when you call + * gdk_surface_end_draw_frame(). If you draw to @window directly without + * calling gdk_surface_begin_draw_frame(), the user may see flicker + * as individual drawing operations are performed in sequence. + * + * When using GTK+, the widget system automatically places calls to + * gdk_surface_begin_draw_frame() and gdk_surface_end_draw_frame() around + * emissions of the `GtkWidget::draw` signal. That is, if you’re + * drawing the contents of the widget yourself, you can assume that the + * widget has a cleared background, is already set as the clip region, + * and already has a backing store. Therefore in most cases, application + * code in GTK does not need to call gdk_surface_begin_draw_frame() + * explicitly. + * + * Returns: (transfer none): a #GdkDrawingContext context that should be + * used to draw the contents of the window; the returned context is owned + * by GDK. + */ +GdkDrawingContext * +gdk_surface_begin_draw_frame (GdkSurface *window, + GdkDrawContext *draw_context, + const cairo_region_t *region) +{ + GdkDrawingContext *context; + cairo_region_t *real_region; + + g_return_val_if_fail (GDK_IS_SURFACE (window), NULL); + g_return_val_if_fail (gdk_surface_has_native (window), NULL); + g_return_val_if_fail (gdk_surface_is_toplevel (window), NULL); + g_return_val_if_fail (region != NULL, NULL); + if (draw_context != NULL) + { + g_return_val_if_fail (GDK_IS_DRAW_CONTEXT (draw_context), NULL); + g_return_val_if_fail (gdk_draw_context_get_window (draw_context) == window, NULL); + } + + if (GDK_SURFACE_DESTROYED (window)) + return NULL; + + if (window->drawing_context != NULL) + { + g_critical ("The window %p already has a drawing context. You cannot " + "call gdk_surface_begin_draw_frame() without calling " + "gdk_surface_end_draw_frame() first.", window); + return NULL; + } + + real_region = cairo_region_copy (region); + + if (draw_context) + gdk_draw_context_begin_frame (draw_context, real_region); + else + gdk_surface_begin_paint_internal (window, real_region); + + context = g_object_new (GDK_TYPE_DRAWING_CONTEXT, + "window", window, + "paint-context", draw_context, + "clip", real_region, + NULL); + + /* Do not take a reference, to avoid creating cycles */ + window->drawing_context = context; + + cairo_region_destroy (real_region); + + return context; +} + +/** + * gdk_surface_end_draw_frame: + * @window: a #GdkSurface + * @context: the #GdkDrawingContext created by gdk_surface_begin_draw_frame() + * + * Indicates that the drawing of the contents of @window started with + * gdk_surface_begin_frame() has been completed. + * + * This function will take care of destroying the #GdkDrawingContext. + * + * It is an error to call this function without a matching + * gdk_surface_begin_frame() first. + */ +void +gdk_surface_end_draw_frame (GdkSurface *window, + GdkDrawingContext *context) +{ + GdkDrawContext *paint_context; + + g_return_if_fail (GDK_IS_SURFACE (window)); + g_return_if_fail (GDK_IS_DRAWING_CONTEXT (context)); + + if (GDK_SURFACE_DESTROYED (window)) + return; + + if (window->drawing_context == NULL) + { + g_critical ("The window %p has no drawing context. You must call " + "gdk_surface_begin_draw_frame() before calling " + "gdk_surface_end_draw_frame().", window); + return; + } + g_return_if_fail (window->drawing_context == context); + + paint_context = gdk_drawing_context_get_paint_context (context); + if (paint_context) + { + cairo_region_t *clip = gdk_drawing_context_get_clip (context); + + gdk_draw_context_end_frame (paint_context, + clip, + window->active_update_area); + + cairo_region_destroy (clip); + } + else + { + gdk_surface_end_paint_internal (window); + } + + window->drawing_context = NULL; + + g_object_unref (context); +} + +/*< private > + * gdk_surface_get_current_paint_region: + * @window: a #GdkSurface + * + * Retrieves a copy of the current paint region. + * + * Returns: (transfer full): a Cairo region + */ +cairo_region_t * +gdk_surface_get_current_paint_region (GdkSurface *window) +{ + cairo_region_t *region; + + if (window->impl_window->current_paint.region != NULL) + { + region = cairo_region_copy (window->impl_window->current_paint.region); + cairo_region_translate (region, -window->abs_x, -window->abs_y); + } + else + { + region = cairo_region_copy (window->clip_region); + } + + return region; +} + +/*< private > + * gdk_surface_get_drawing_context: + * @window: a #GdkSurface + * + * Retrieves the #GdkDrawingContext associated to @window by + * gdk_surface_begin_draw_frame(). + * + * Returns: (transfer none) (nullable): a #GdkDrawingContext, if any is set + */ +GdkDrawingContext * +gdk_surface_get_drawing_context (GdkSurface *window) +{ + g_return_val_if_fail (GDK_IS_SURFACE (window), NULL); + + if (GDK_SURFACE_DESTROYED (window)) + return NULL; + + return window->drawing_context; +} + +/** + * gdk_surface_get_clip_region: + * @window: a #GdkSurface + * + * Computes the region of a window that potentially can be written + * to by drawing primitives. This region may not take into account + * other factors such as if the window is obscured by other windows, + * but no area outside of this region will be affected by drawing + * primitives. + * + * Returns: a #cairo_region_t. This must be freed with cairo_region_destroy() + * when you are done. + **/ +cairo_region_t* +gdk_surface_get_clip_region (GdkSurface *window) +{ + cairo_region_t *result; + + g_return_val_if_fail (GDK_SURFACE (window), NULL); + + result = cairo_region_copy (window->clip_region); + + if (window->current_paint.region != NULL) + cairo_region_intersect (result, window->current_paint.region); + + return result; +} + +/** + * gdk_surface_get_visible_region: + * @window: a #GdkSurface + * + * Computes the region of the @window that is potentially visible. + * This does not necessarily take into account if the window is + * obscured by other windows, but no area outside of this region + * is visible. + * + * Returns: a #cairo_region_t. This must be freed with cairo_region_destroy() + * when you are done. + **/ +cairo_region_t * +gdk_surface_get_visible_region (GdkSurface *window) +{ + g_return_val_if_fail (GDK_IS_SURFACE (window), NULL); + + return cairo_region_copy (window->clip_region); +} + +static void +gdk_surface_clear_backing_region (GdkSurface *window) +{ + cairo_t *cr; + + if (GDK_SURFACE_DESTROYED (window)) + return; + + cr = cairo_create (window->current_paint.surface); + + cairo_set_operator (cr, CAIRO_OPERATOR_CLEAR); + gdk_cairo_region (cr, window->current_paint.region); + cairo_fill (cr); + + cairo_destroy (cr); +} + +/* This returns either the current working surface on the paint stack + * or the actual impl surface of the window. This should not be used + * from very many places: be careful! */ +static cairo_surface_t * +ref_window_surface (GdkSurface *window) +{ + if (window->impl_window->current_paint.surface) + return cairo_surface_reference (window->impl_window->current_paint.surface); + else + return gdk_surface_ref_impl_surface (window); +} + +/* This is used in places like gdk_cairo_set_source_window and + * other places to take "screenshots" of windows. Thus, we allow + * it to be used outside of a begin_paint / end_paint. */ +cairo_surface_t * +_gdk_surface_ref_cairo_surface (GdkSurface *window) +{ + cairo_surface_t *surface; + + g_return_val_if_fail (GDK_IS_SURFACE (window), NULL); + + surface = ref_window_surface (window); + + if (gdk_surface_has_impl (window)) + { + return surface; + } + else + { + cairo_surface_t *subsurface; + subsurface = cairo_surface_create_for_rectangle (surface, + window->abs_x, + window->abs_y, + window->width, + window->height); + cairo_surface_destroy (surface); + return subsurface; + } +} + +/* Code for dirty-region queueing + */ +static GSList *update_windows = NULL; + +static inline gboolean +gdk_surface_is_ancestor (GdkSurface *window, + GdkSurface *ancestor) +{ + while (window) + { + GdkSurface *parent = window->parent; + + if (parent == ancestor) + return TRUE; + + window = parent; + } + + return FALSE; +} + +static void +gdk_surface_add_update_window (GdkSurface *window) +{ + GSList *tmp; + GSList *prev = NULL; + gboolean has_ancestor_in_list = FALSE; + + /* Check whether "window" is already in "update_windows" list. + * It could be added during execution of gtk_widget_destroy() when + * setting focus widget to NULL and redrawing old focus widget. + * See bug 711552. + */ + tmp = g_slist_find (update_windows, window); + if (tmp != NULL) + return; + + for (tmp = update_windows; tmp; tmp = tmp->next) + { + GdkSurface *parent = window->parent; + + /* check if tmp is an ancestor of "window"; if it is, set a + * flag indicating that all following windows are either + * children of "window" or from a differen hierarchy + */ + if (!has_ancestor_in_list && gdk_surface_is_ancestor (window, tmp->data)) + has_ancestor_in_list = TRUE; + + /* insert in reverse stacking order when adding around siblings, + * so processing updates properly paints over lower stacked windows + */ + if (parent == GDK_SURFACE (tmp->data)->parent) + { + if (parent != NULL) + { + gint index = g_list_index (parent->children, window); + for (; tmp && parent == GDK_SURFACE (tmp->data)->parent; tmp = tmp->next) + { + gint sibling_index = g_list_index (parent->children, tmp->data); + if (index > sibling_index) + break; + prev = tmp; + } + } + /* here, tmp got advanced past all lower stacked siblings */ + tmp = g_slist_prepend (tmp, g_object_ref (window)); + if (prev) + prev->next = tmp; + else + update_windows = tmp; + return; + } + + /* if "window" has an ancestor in the list and tmp is one of + * "window's" children, insert "window" before tmp + */ + if (has_ancestor_in_list && gdk_surface_is_ancestor (tmp->data, window)) + { + tmp = g_slist_prepend (tmp, g_object_ref (window)); + + if (prev) + prev->next = tmp; + else + update_windows = tmp; + return; + } + + /* if we're at the end of the list and had an ancestor it it, + * append to the list + */ + if (! tmp->next && has_ancestor_in_list) + { + tmp = g_slist_append (tmp, g_object_ref (window)); + return; + } + + prev = tmp; + } + + /* if all above checks failed ("window" is from a different + * hierarchy than what is already in the list) or the list is + * empty, prepend + */ + update_windows = g_slist_prepend (update_windows, g_object_ref (window)); +} + +static void +gdk_surface_remove_update_window (GdkSurface *window) +{ + GSList *link; + + link = g_slist_find (update_windows, window); + if (link != NULL) + { + update_windows = g_slist_delete_link (update_windows, link); + g_object_unref (window); + } +} + +static gboolean +gdk_surface_is_toplevel_frozen (GdkSurface *window) +{ + GdkSurface *toplevel; + + toplevel = gdk_surface_get_toplevel (window); + + return toplevel->update_and_descendants_freeze_count > 0; +} + +static void +gdk_surface_schedule_update (GdkSurface *window) +{ + GdkFrameClock *frame_clock; + + if (window && + (window->update_freeze_count || + gdk_surface_is_toplevel_frozen (window))) + return; + + /* If there's no frame clock (a foreign window), then the invalid + * region will just stick around unless gdk_surface_process_updates() + * is called. */ + frame_clock = gdk_surface_get_frame_clock (window); + if (frame_clock) + gdk_frame_clock_request_phase (gdk_surface_get_frame_clock (window), + GDK_FRAME_CLOCK_PHASE_PAINT); +} + +void +_gdk_surface_process_updates_recurse (GdkSurface *window, + cairo_region_t *expose_region) +{ + cairo_region_t *clipped_expose_region; + GdkEvent *event; + + if (window->destroyed) + return; + + clipped_expose_region = cairo_region_copy (expose_region); + + cairo_region_intersect (clipped_expose_region, window->clip_region); + + if (cairo_region_is_empty (clipped_expose_region)) + goto out; + + /* Paint the window before the children, clipped to the window region */ + + event = gdk_event_new (GDK_EXPOSE); + event->any.window = g_object_ref (window); + event->any.send_event = FALSE; + event->expose.count = 0; + event->expose.region = cairo_region_reference (clipped_expose_region); + cairo_region_get_extents (clipped_expose_region, &event->expose.area); + + _gdk_event_emit (event); + gdk_event_free (event); + + out: + cairo_region_destroy (clipped_expose_region); +} + + +static void +gdk_surface_update_native_shapes (GdkSurface *window) +{ + if (should_apply_clip_as_shape (window)) + apply_clip_as_shape (window); +} + +/* Process and remove any invalid area on the native window by creating + * expose events for the window and all non-native descendants. + */ +static void +gdk_surface_process_updates_internal (GdkSurface *window) +{ + GdkSurfaceImplClass *impl_class; + GdkSurface *toplevel; + + toplevel = gdk_surface_get_toplevel (window); + if (toplevel->geometry_dirty) + { + gdk_surface_update_native_shapes (toplevel); + toplevel->geometry_dirty = FALSE; + } + + /* Ensure the window lives while updating it */ + g_object_ref (window); + + window->in_update = TRUE; + + /* If an update got queued during update processing, we can get a + * window in the update queue that has an empty update_area. + * just ignore it. + */ + if (window->update_area) + { + g_assert (window->active_update_area == NULL); /* No reentrancy */ + + window->active_update_area = window->update_area; + window->update_area = NULL; + + if (gdk_surface_is_viewable (window)) + { + cairo_region_t *expose_region; + + expose_region = cairo_region_copy (window->active_update_area); + + impl_class = GDK_SURFACE_IMPL_GET_CLASS (window->impl); + + /* Clip to part visible in impl window */ + cairo_region_intersect (expose_region, window->clip_region); + + if (impl_class->queue_antiexpose) + impl_class->queue_antiexpose (window, expose_region); + + impl_class->process_updates_recurse (window, expose_region); + + gdk_surface_append_old_updated_area (window, window->active_update_area); + + cairo_region_destroy (expose_region); + } + + cairo_region_destroy (window->active_update_area); + window->active_update_area = NULL; + } + + window->in_update = FALSE; + + g_object_unref (window); +} + +static void +gdk_surface_paint_on_clock (GdkFrameClock *clock, + void *data) +{ + GdkSurface *window; + + window = GDK_SURFACE (data); + + g_return_if_fail (GDK_IS_SURFACE (window)); + g_return_if_fail (window->impl_window == window); + + if (GDK_SURFACE_DESTROYED (window)) + return; + + g_object_ref (window); + + if (window->update_area && + !window->update_freeze_count && + !gdk_surface_is_toplevel_frozen (window) && + + /* Don't recurse into process_updates_internal, we'll + * do the update later when idle instead. */ + !window->in_update) + { + gdk_surface_process_updates_internal (window); + gdk_surface_remove_update_window (window); + } + + g_object_unref (window); +} + +static void +gdk_surface_invalidate_rect_full (GdkSurface *window, + const GdkRectangle *rect, + gboolean invalidate_children) +{ + GdkRectangle window_rect; + cairo_region_t *region; + + g_return_if_fail (GDK_IS_SURFACE (window)); + + if (GDK_SURFACE_DESTROYED (window)) + return; + + if (window->input_only || !window->viewable) + return; + + if (!rect) + { + window_rect.x = 0; + window_rect.y = 0; + window_rect.width = window->width; + window_rect.height = window->height; + rect = &window_rect; + } + + region = cairo_region_create_rectangle (rect); + gdk_surface_invalidate_region_full (window, region, invalidate_children); + cairo_region_destroy (region); +} + +/** + * gdk_surface_invalidate_rect: + * @window: a #GdkSurface + * @rect: (allow-none): rectangle to invalidate or %NULL to invalidate the whole + * window + * @invalidate_children: whether to also invalidate child windows + * + * A convenience wrapper around gdk_surface_invalidate_region() which + * invalidates a rectangular region. See + * gdk_surface_invalidate_region() for details. + **/ +void +gdk_surface_invalidate_rect (GdkSurface *window, + const GdkRectangle *rect, + gboolean invalidate_children) +{ + gdk_surface_invalidate_rect_full (window, rect, invalidate_children); +} + +static void +impl_window_add_update_area (GdkSurface *impl_window, + cairo_region_t *region) +{ + if (impl_window->update_area) + cairo_region_union (impl_window->update_area, region); + else + { + gdk_surface_add_update_window (impl_window); + impl_window->update_area = cairo_region_copy (region); + gdk_surface_schedule_update (impl_window); + } +} + +static void +gdk_surface_invalidate_maybe_recurse_full (GdkSurface *window, + const cairo_region_t *region, + GdkSurfaceChildFunc child_func, + gpointer user_data) +{ + cairo_region_t *visible_region; + cairo_rectangle_int_t r; + + g_return_if_fail (GDK_IS_SURFACE (window)); + + if (GDK_SURFACE_DESTROYED (window)) + return; + + if (window->input_only || + !window->viewable || + cairo_region_is_empty (region) || + window->window_type == GDK_SURFACE_ROOT) + return; + + r.x = 0; + r.y = 0; + + visible_region = cairo_region_copy (region); + + while (window != NULL && + !cairo_region_is_empty (visible_region)) + { + r.width = window->width; + r.height = window->height; + cairo_region_intersect_rectangle (visible_region, &r); + + if (gdk_surface_has_impl (window)) + { + impl_window_add_update_area (window, visible_region); + break; + } + else + { + cairo_region_translate (visible_region, + window->x, window->y); + window = window->parent; + } + } + + cairo_region_destroy (visible_region); +} + +/** + * gdk_surface_invalidate_maybe_recurse: + * @window: a #GdkSurface + * @region: a #cairo_region_t + * @child_func: (scope call) (allow-none): function to use to decide if to + * recurse to a child, %NULL means never recurse. + * @user_data: data passed to @child_func + * + * Adds @region to the update area for @window. The update area is the + * region that needs to be redrawn, or “dirty region.” + * + * GDK will process all updates whenever the frame clock schedules a redraw, + * so there’s no need to do forces redraws manually, you just need to + * invalidate regions that you know should be redrawn. + * + * The @child_func parameter controls whether the region of + * each child window that intersects @region will also be invalidated. + * Only children for which @child_func returns #TRUE will have the area + * invalidated. + **/ +void +gdk_surface_invalidate_maybe_recurse (GdkSurface *window, + const cairo_region_t *region, + GdkSurfaceChildFunc child_func, + gpointer user_data) +{ + gdk_surface_invalidate_maybe_recurse_full (window, region, + child_func, user_data); +} + +static gboolean +true_predicate (GdkSurface *window, + gpointer user_data) +{ + return TRUE; +} + +static void +gdk_surface_invalidate_region_full (GdkSurface *window, + const cairo_region_t *region, + gboolean invalidate_children) +{ + gdk_surface_invalidate_maybe_recurse_full (window, region, + invalidate_children ? + true_predicate : (gboolean (*) (GdkSurface *, gpointer))NULL, + NULL); +} + +/** + * gdk_surface_invalidate_region: + * @window: a #GdkSurface + * @region: a #cairo_region_t + * @invalidate_children: %TRUE to also invalidate child windows + * + * Adds @region to the update area for @window. The update area is the + * region that needs to be redrawn, or “dirty region.” + * + * GDK will process all updates whenever the frame clock schedules a redraw, + * so there’s no need to do forces redraws manually, you just need to + * invalidate regions that you know should be redrawn. + * + * The @invalidate_children parameter controls whether the region of + * each child window that intersects @region will also be invalidated. + * If %FALSE, then the update area for child windows will remain + * unaffected. See gdk_surface_invalidate_maybe_recurse if you need + * fine grained control over which children are invalidated. + **/ +void +gdk_surface_invalidate_region (GdkSurface *window, + const cairo_region_t *region, + gboolean invalidate_children) +{ + gdk_surface_invalidate_maybe_recurse (window, region, + invalidate_children ? + true_predicate : (gboolean (*) (GdkSurface *, gpointer))NULL, + NULL); +} + +/** + * _gdk_surface_invalidate_for_expose: + * @window: a #GdkSurface + * @region: a #cairo_region_t + * + * Adds @region to the update area for @window. + * + * GDK will process all updates whenever the frame clock schedules a redraw, + * so there’s no need to do forces redraws manually, you just need to + * invalidate regions that you know should be redrawn. + * + * This version of invalidation is used when you recieve expose events + * from the native window system. It exposes the native window, plus + * any non-native child windows. + **/ +void +_gdk_surface_invalidate_for_expose (GdkSurface *window, + cairo_region_t *region) +{ + gdk_surface_invalidate_maybe_recurse_full (window, region, + (gboolean (*) (GdkSurface *, gpointer))gdk_surface_has_no_impl, + NULL); +} + + +/** + * gdk_surface_get_update_area: + * @window: a #GdkSurface + * + * Transfers ownership of the update area from @window to the caller + * of the function. That is, after calling this function, @window will + * no longer have an invalid/dirty region; the update area is removed + * from @window and handed to you. If a window has no update area, + * gdk_surface_get_update_area() returns %NULL. You are responsible for + * calling cairo_region_destroy() on the returned region if it’s non-%NULL. + * + * Returns: the update area for @window + **/ +cairo_region_t * +gdk_surface_get_update_area (GdkSurface *window) +{ + GdkSurface *impl_window; + cairo_region_t *tmp_region, *to_remove; + + g_return_val_if_fail (GDK_IS_SURFACE (window), NULL); + + impl_window = gdk_surface_get_impl_window (window); + + if (impl_window->update_area) + { + tmp_region = cairo_region_copy (window->clip_region); + /* Convert to impl coords */ + cairo_region_translate (tmp_region, window->abs_x, window->abs_y); + cairo_region_intersect (tmp_region, impl_window->update_area); + + if (cairo_region_is_empty (tmp_region)) + { + cairo_region_destroy (tmp_region); + return NULL; + } + else + { + /* Convert from impl coords */ + cairo_region_translate (tmp_region, -window->abs_x, -window->abs_y); + + /* Don't remove any update area that is overlapped by sibling windows + or child windows as these really need to be repainted independently of this window. */ + to_remove = cairo_region_copy (tmp_region); + + remove_child_area (window, FALSE, to_remove); + remove_sibling_overlapped_area (window, to_remove); + + /* Remove from update_area */ + cairo_region_translate (to_remove, window->abs_x, window->abs_y); + cairo_region_subtract (impl_window->update_area, to_remove); + + cairo_region_destroy (to_remove); + + if (cairo_region_is_empty (impl_window->update_area)) + { + cairo_region_destroy (impl_window->update_area); + impl_window->update_area = NULL; + + gdk_surface_remove_update_window ((GdkSurface *)impl_window); + } + + return tmp_region; + } + } + else + return NULL; +} + +/** + * _gdk_surface_clear_update_area: + * @window: a #GdkSurface. + * + * Internal function to clear the update area for a window. This + * is called when the window is hidden or destroyed. + **/ +void +_gdk_surface_clear_update_area (GdkSurface *window) +{ + g_return_if_fail (GDK_IS_SURFACE (window)); + + if (window->update_area) + { + gdk_surface_remove_update_window (window); + + cairo_region_destroy (window->update_area); + window->update_area = NULL; + } +} + +/** + * gdk_surface_freeze_updates: + * @window: a #GdkSurface + * + * Temporarily freezes a window such that it won’t receive expose + * events. The window will begin receiving expose events again when + * gdk_surface_thaw_updates() is called. If gdk_surface_freeze_updates() + * has been called more than once, gdk_surface_thaw_updates() must be called + * an equal number of times to begin processing exposes. + **/ +void +gdk_surface_freeze_updates (GdkSurface *window) +{ + GdkSurface *impl_window; + + g_return_if_fail (GDK_IS_SURFACE (window)); + + impl_window = gdk_surface_get_impl_window (window); + impl_window->update_freeze_count++; +} + +/** + * gdk_surface_thaw_updates: + * @window: a #GdkSurface + * + * Thaws a window frozen with gdk_surface_freeze_updates(). + **/ +void +gdk_surface_thaw_updates (GdkSurface *window) +{ + GdkSurface *impl_window; + + g_return_if_fail (GDK_IS_SURFACE (window)); + + impl_window = gdk_surface_get_impl_window (window); + + g_return_if_fail (impl_window->update_freeze_count > 0); + + if (--impl_window->update_freeze_count == 0) + gdk_surface_schedule_update (GDK_SURFACE (impl_window)); +} + +void +gdk_surface_freeze_toplevel_updates (GdkSurface *window) +{ + g_return_if_fail (GDK_IS_SURFACE (window)); + g_return_if_fail (window->window_type != GDK_SURFACE_CHILD); + + window->update_and_descendants_freeze_count++; + _gdk_frame_clock_freeze (gdk_surface_get_frame_clock (window)); +} + +void +gdk_surface_thaw_toplevel_updates (GdkSurface *window) +{ + g_return_if_fail (GDK_IS_SURFACE (window)); + g_return_if_fail (window->window_type != GDK_SURFACE_CHILD); + g_return_if_fail (window->update_and_descendants_freeze_count > 0); + + window->update_and_descendants_freeze_count--; + _gdk_frame_clock_thaw (gdk_surface_get_frame_clock (window)); + + gdk_surface_schedule_update (window); +} + +/** + * gdk_surface_constrain_size: + * @geometry: a #GdkGeometry structure + * @flags: a mask indicating what portions of @geometry are set + * @width: desired width of window + * @height: desired height of the window + * @new_width: (out): location to store resulting width + * @new_height: (out): location to store resulting height + * + * Constrains a desired width and height according to a + * set of geometry hints (such as minimum and maximum size). + */ +void +gdk_surface_constrain_size (GdkGeometry *geometry, + GdkSurfaceHints flags, + gint width, + gint height, + gint *new_width, + gint *new_height) +{ + /* This routine is partially borrowed from fvwm. + * + * Copyright 1993, Robert Nation + * You may use this code for any purpose, as long as the original + * copyright remains in the source code and all documentation + * + * which in turn borrows parts of the algorithm from uwm + */ + gint min_width = 0; + gint min_height = 0; + gint base_width = 0; + gint base_height = 0; + gint xinc = 1; + gint yinc = 1; + gint max_width = G_MAXINT; + gint max_height = G_MAXINT; + +#define FLOOR(value, base) ( ((gint) ((value) / (base))) * (base) ) + + if ((flags & GDK_HINT_BASE_SIZE) && (flags & GDK_HINT_MIN_SIZE)) + { + base_width = geometry->base_width; + base_height = geometry->base_height; + min_width = geometry->min_width; + min_height = geometry->min_height; + } + else if (flags & GDK_HINT_BASE_SIZE) + { + base_width = geometry->base_width; + base_height = geometry->base_height; + min_width = geometry->base_width; + min_height = geometry->base_height; + } + else if (flags & GDK_HINT_MIN_SIZE) + { + base_width = geometry->min_width; + base_height = geometry->min_height; + min_width = geometry->min_width; + min_height = geometry->min_height; + } + + if (flags & GDK_HINT_MAX_SIZE) + { + max_width = geometry->max_width ; + max_height = geometry->max_height; + } + + if (flags & GDK_HINT_RESIZE_INC) + { + xinc = MAX (xinc, geometry->width_inc); + yinc = MAX (yinc, geometry->height_inc); + } + + /* clamp width and height to min and max values + */ + width = CLAMP (width, min_width, max_width); + height = CLAMP (height, min_height, max_height); + + /* shrink to base + N * inc + */ + width = base_width + FLOOR (width - base_width, xinc); + height = base_height + FLOOR (height - base_height, yinc); + + /* constrain aspect ratio, according to: + * + * width + * min_aspect <= -------- <= max_aspect + * height + */ + + if (flags & GDK_HINT_ASPECT && + geometry->min_aspect > 0 && + geometry->max_aspect > 0) + { + gint delta; + + if (geometry->min_aspect * height > width) + { + delta = FLOOR (height - width / geometry->min_aspect, yinc); + if (height - delta >= min_height) + height -= delta; + else + { + delta = FLOOR (height * geometry->min_aspect - width, xinc); + if (width + delta <= max_width) + width += delta; + } + } + + if (geometry->max_aspect * height < width) + { + delta = FLOOR (width - height * geometry->max_aspect, xinc); + if (width - delta >= min_width) + width -= delta; + else + { + delta = FLOOR (width / geometry->max_aspect - height, yinc); + if (height + delta <= max_height) + height += delta; + } + } + } + +#undef FLOOR + + *new_width = width; + *new_height = height; +} + +/** + * gdk_surface_get_device_position_double: + * @window: a #GdkSurface. + * @device: pointer #GdkDevice to query to. + * @x: (out) (allow-none): return location for the X coordinate of @device, or %NULL. + * @y: (out) (allow-none): return location for the Y coordinate of @device, or %NULL. + * @mask: (out) (allow-none): return location for the modifier mask, or %NULL. + * + * Obtains the current device position in doubles and modifier state. + * The position is given in coordinates relative to the upper left + * corner of @window. + * + * Returns: (nullable) (transfer none): The window underneath @device + * (as with gdk_device_get_window_at_position()), or %NULL if the + * window is not known to GDK. + **/ +GdkSurface * +gdk_surface_get_device_position_double (GdkSurface *window, + GdkDevice *device, + double *x, + double *y, + GdkModifierType *mask) +{ + gdouble tmp_x, tmp_y; + GdkModifierType tmp_mask; + gboolean normal_child; + + g_return_val_if_fail (GDK_IS_SURFACE (window), NULL); + g_return_val_if_fail (GDK_IS_DEVICE (device), NULL); + g_return_val_if_fail (gdk_device_get_source (device) != GDK_SOURCE_KEYBOARD, NULL); + + tmp_x = tmp_y = 0; + tmp_mask = 0; + normal_child = GDK_SURFACE_IMPL_GET_CLASS (window->impl)->get_device_state (window, + device, + &tmp_x, &tmp_y, + &tmp_mask); + /* We got the coords on the impl, convert to the window */ + tmp_x -= window->abs_x; + tmp_y -= window->abs_y; + + if (x) + *x = tmp_x; + if (y) + *y = tmp_y; + if (mask) + *mask = tmp_mask; + + if (normal_child) + return _gdk_surface_find_child_at (window, tmp_x, tmp_y); + return NULL; +} + +/** + * gdk_surface_get_device_position: + * @window: a #GdkSurface. + * @device: pointer #GdkDevice to query to. + * @x: (out) (allow-none): return location for the X coordinate of @device, or %NULL. + * @y: (out) (allow-none): return location for the Y coordinate of @device, or %NULL. + * @mask: (out) (allow-none): return location for the modifier mask, or %NULL. + * + * Obtains the current device position and modifier state. + * The position is given in coordinates relative to the upper left + * corner of @window. + * + * Use gdk_surface_get_device_position_double() if you need subpixel precision. + * + * Returns: (nullable) (transfer none): The window underneath @device + * (as with gdk_device_get_window_at_position()), or %NULL if the + * window is not known to GDK. + **/ +GdkSurface * +gdk_surface_get_device_position (GdkSurface *window, + GdkDevice *device, + gint *x, + gint *y, + GdkModifierType *mask) +{ + gdouble tmp_x, tmp_y; + + window = gdk_surface_get_device_position_double (window, device, + &tmp_x, &tmp_y, mask); + if (x) + *x = round (tmp_x); + if (y) + *y = round (tmp_y); + + return window; +} + +static gboolean +gdk_surface_raise_internal (GdkSurface *window) +{ + GdkSurface *parent = window->parent; + GdkSurfaceImplClass *impl_class; + gboolean did_raise = FALSE; + + if (parent && parent->children->data != window) + { + parent->children = g_list_remove_link (parent->children, &window->children_list_node); + parent->children = g_list_concat (&window->children_list_node, parent->children); + did_raise = TRUE; + } + + impl_class = GDK_SURFACE_IMPL_GET_CLASS (window->impl); + + /* Just do native raise for toplevels */ + if (gdk_surface_has_impl (window)) + impl_class->raise (window); + + return did_raise; +} + +/* Returns TRUE If the native window was mapped or unmapped */ +static gboolean +set_viewable (GdkSurface *w, + gboolean val) +{ + GdkSurface *child; + GList *l; + + if (w->viewable == val) + return FALSE; + + w->viewable = val; + + if (val) + recompute_visible_regions (w, FALSE); + + for (l = w->children; l != NULL; l = l->next) + { + child = l->data; + + if (GDK_SURFACE_IS_MAPPED (child)) + set_viewable (child, val); + } + + return FALSE; +} + +/* Returns TRUE If the native window was mapped or unmapped */ +gboolean +_gdk_surface_update_viewable (GdkSurface *window) +{ + gboolean viewable; + + if (window->window_type == GDK_SURFACE_FOREIGN || + window->window_type == GDK_SURFACE_ROOT) + viewable = TRUE; + else if (gdk_surface_is_toplevel (window) || + window->parent->viewable) + viewable = GDK_SURFACE_IS_MAPPED (window); + else + viewable = FALSE; + + return set_viewable (window, viewable); +} + +static void +gdk_surface_show_internal (GdkSurface *window, gboolean raise) +{ + GdkSurfaceImplClass *impl_class; + gboolean was_mapped, was_viewable; + gboolean did_show, did_raise = FALSE; + + g_return_if_fail (GDK_IS_SURFACE (window)); + + if (window->destroyed) + return; + + was_mapped = GDK_SURFACE_IS_MAPPED (window); + was_viewable = window->viewable; + + if (raise) + { + /* Keep children in (reverse) stacking order */ + did_raise = gdk_surface_raise_internal (window); + } + + if (gdk_surface_has_impl (window)) + { + if (!was_mapped) + gdk_synthesize_window_state (window, + GDK_SURFACE_STATE_WITHDRAWN, + 0); + } + else + { + window->state = 0; + g_object_notify_by_pspec (G_OBJECT (window), properties[PROP_STATE]); + } + + did_show = _gdk_surface_update_viewable (window); + + /* If it was already viewable the backend show op won't be called, call it + again to ensure things happen right if the mapped tracking was not right + for e.g. a foreign window. + Dunno if this is strictly needed but its what happened pre-csw. + Also show if not done by gdk_surface_update_viewable. */ + if (gdk_surface_has_impl (window) && (was_viewable || !did_show)) + { + impl_class = GDK_SURFACE_IMPL_GET_CLASS (window->impl); + impl_class->show (window, !did_show ? was_mapped : TRUE); + } + + if (!was_mapped && !gdk_surface_has_impl (window)) + { + if (window->event_mask & GDK_STRUCTURE_MASK) + _gdk_make_event (window, GDK_MAP, NULL, FALSE); + + if (window->parent && window->parent->event_mask & GDK_SUBSTRUCTURE_MASK) + _gdk_make_event (window, GDK_MAP, NULL, FALSE); + } + + if (!was_mapped || did_raise) + { + recompute_visible_regions (window, FALSE); + + if (gdk_surface_is_viewable (window)) + gdk_surface_invalidate_rect_full (window, NULL, TRUE); + } +} + +/** + * gdk_surface_show_unraised: + * @window: a #GdkSurface + * + * Shows a #GdkSurface onscreen, but does not modify its stacking + * order. In contrast, gdk_surface_show() will raise the window + * to the top of the window stack. + * + * On the X11 platform, in Xlib terms, this function calls + * XMapWindow() (it also updates some internal GDK state, which means + * that you can’t really use XMapWindow() directly on a GDK window). + */ +void +gdk_surface_show_unraised (GdkSurface *window) +{ + gdk_surface_show_internal (window, FALSE); +} + +/** + * gdk_surface_raise: + * @window: a #GdkSurface + * + * Raises @window to the top of the Z-order (stacking order), so that + * other windows with the same parent window appear below @window. + * This is true whether or not the windows are visible. + * + * If @window is a toplevel, the window manager may choose to deny the + * request to move the window in the Z-order, gdk_surface_raise() only + * requests the restack, does not guarantee it. + */ +void +gdk_surface_raise (GdkSurface *window) +{ + gboolean did_raise; + + g_return_if_fail (GDK_IS_SURFACE (window)); + + if (window->destroyed) + return; + + /* Keep children in (reverse) stacking order */ + did_raise = gdk_surface_raise_internal (window); + + if (did_raise && + !gdk_surface_is_toplevel (window) && + gdk_surface_is_viewable (window) && + !window->input_only) + gdk_surface_invalidate_region_full (window, window->clip_region, TRUE); +} + +static void +gdk_surface_lower_internal (GdkSurface *window) +{ + GdkSurface *parent = window->parent; + GdkSurfaceImplClass *impl_class; + + if (parent) + { + parent->children = g_list_remove_link (parent->children, &window->children_list_node); + parent->children = g_list_concat (parent->children, &window->children_list_node); + } + + impl_class = GDK_SURFACE_IMPL_GET_CLASS (window->impl); + + /* Just do native lower for toplevels */ + if (gdk_surface_has_impl (window)) + impl_class->lower (window); +} + +static void +gdk_surface_invalidate_in_parent (GdkSurface *private) +{ + GdkRectangle r, child; + + if (gdk_surface_is_toplevel (private)) + return; + + /* get the visible rectangle of the parent */ + r.x = r.y = 0; + r.width = private->parent->width; + r.height = private->parent->height; + + child.x = private->x; + child.y = private->y; + child.width = private->width; + child.height = private->height; + gdk_rectangle_intersect (&r, &child, &r); + + gdk_surface_invalidate_rect_full (private->parent, &r, TRUE); +} + + +/** + * gdk_surface_lower: + * @window: a #GdkSurface + * + * Lowers @window to the bottom of the Z-order (stacking order), so that + * other windows with the same parent window appear above @window. + * This is true whether or not the other windows are visible. + * + * If @window is a toplevel, the window manager may choose to deny the + * request to move the window in the Z-order, gdk_surface_lower() only + * requests the restack, does not guarantee it. + * + * Note that gdk_surface_show() raises the window again, so don’t call this + * function before gdk_surface_show(). (Try gdk_surface_show_unraised().) + */ +void +gdk_surface_lower (GdkSurface *window) +{ + g_return_if_fail (GDK_IS_SURFACE (window)); + + if (window->destroyed) + return; + + /* Keep children in (reverse) stacking order */ + gdk_surface_lower_internal (window); + + gdk_surface_invalidate_in_parent (window); +} + +/** + * gdk_surface_restack: + * @window: a #GdkSurface + * @sibling: (allow-none): a #GdkSurface that is a sibling of @window, or %NULL + * @above: a boolean + * + * Changes the position of @window in the Z-order (stacking order), so that + * it is above @sibling (if @above is %TRUE) or below @sibling (if @above is + * %FALSE). + * + * If @sibling is %NULL, then this either raises (if @above is %TRUE) or + * lowers the window. + * + * If @window is a toplevel, the window manager may choose to deny the + * request to move the window in the Z-order, gdk_surface_restack() only + * requests the restack, does not guarantee it. + */ +void +gdk_surface_restack (GdkSurface *window, + GdkSurface *sibling, + gboolean above) +{ + GdkSurfaceImplClass *impl_class; + GdkSurface *parent; + GList *sibling_link; + + g_return_if_fail (GDK_IS_SURFACE (window)); + g_return_if_fail (sibling == NULL || GDK_IS_SURFACE (sibling)); + + if (window->destroyed) + return; + + if (sibling == NULL) + { + if (above) + gdk_surface_raise (window); + else + gdk_surface_lower (window); + return; + } + + if (gdk_surface_is_toplevel (window)) + { + g_return_if_fail (gdk_surface_is_toplevel (sibling)); + impl_class = GDK_SURFACE_IMPL_GET_CLASS (window->impl); + impl_class->restack_toplevel (window, sibling, above); + return; + } + + parent = window->parent; + if (parent) + { + sibling_link = g_list_find (parent->children, sibling); + g_return_if_fail (sibling_link != NULL); + if (sibling_link == NULL) + return; + + parent->children = g_list_remove_link (parent->children, &window->children_list_node); + if (above) + parent->children = list_insert_link_before (parent->children, + sibling_link, + &window->children_list_node); + else + parent->children = list_insert_link_before (parent->children, + sibling_link->next, + &window->children_list_node); + } + + gdk_surface_invalidate_in_parent (window); +} + + +/** + * gdk_surface_show: + * @window: a #GdkSurface + * + * Like gdk_surface_show_unraised(), but also raises the window to the + * top of the window stack (moves the window to the front of the + * Z-order). + * + * This function maps a window so it’s visible onscreen. Its opposite + * is gdk_surface_hide(). + * + * When implementing a #GtkWidget, you should call this function on the widget's + * #GdkSurface as part of the “map” method. + */ +void +gdk_surface_show (GdkSurface *window) +{ + gdk_surface_show_internal (window, TRUE); +} + +/** + * gdk_surface_hide: + * @window: a #GdkSurface + * + * For toplevel windows, withdraws them, so they will no longer be + * known to the window manager; for all windows, unmaps them, so + * they won’t be displayed. Normally done automatically as + * part of gtk_widget_hide(). + */ +void +gdk_surface_hide (GdkSurface *window) +{ + GdkSurfaceImplClass *impl_class; + gboolean was_mapped, did_hide; + + g_return_if_fail (GDK_IS_SURFACE (window)); + + if (window->destroyed) + return; + + was_mapped = GDK_SURFACE_IS_MAPPED (window); + + if (gdk_surface_has_impl (window)) + { + + if (GDK_SURFACE_IS_MAPPED (window)) + gdk_synthesize_window_state (window, + 0, + GDK_SURFACE_STATE_WITHDRAWN); + } + else if (was_mapped) + { + window->state = GDK_SURFACE_STATE_WITHDRAWN; + g_object_notify_by_pspec (G_OBJECT (window), properties[PROP_STATE]); + } + + if (was_mapped) + { + GdkDisplay *display; + GdkSeat *seat; + GList *devices, *d; + + /* May need to break grabs on children */ + display = gdk_surface_get_display (window); + seat = gdk_display_get_default_seat (display); + + devices = gdk_seat_get_slaves (seat, GDK_SEAT_CAPABILITY_ALL); + devices = g_list_prepend (devices, gdk_seat_get_keyboard (seat)); + devices = g_list_prepend (devices, gdk_seat_get_pointer (seat)); + + for (d = devices; d; d = d->next) + { + GdkDevice *device = d->data; + + if (_gdk_display_end_device_grab (display, + device, + _gdk_display_get_next_serial (display), + window, + TRUE)) + { +G_GNUC_BEGIN_IGNORE_DEPRECATIONS + gdk_device_ungrab (device, GDK_CURRENT_TIME); +G_GNUC_END_IGNORE_DEPRECATIONS + } + } + + g_list_free (devices); + } + + did_hide = _gdk_surface_update_viewable (window); + + /* Hide foreign window as those are not handled by update_viewable. */ + if (gdk_surface_has_impl (window) && (!did_hide)) + { + impl_class = GDK_SURFACE_IMPL_GET_CLASS (window->impl); + impl_class->hide (window); + } + + gdk_surface_clear_old_updated_area (window); + recompute_visible_regions (window, FALSE); + + if (was_mapped && !gdk_surface_has_impl (window)) + { + if (window->event_mask & GDK_STRUCTURE_MASK) + _gdk_make_event (window, GDK_UNMAP, NULL, FALSE); + + if (window->parent && window->parent->event_mask & GDK_SUBSTRUCTURE_MASK) + _gdk_make_event (window, GDK_UNMAP, NULL, FALSE); + } + + /* Invalidate the rect */ + if (was_mapped) + gdk_surface_invalidate_in_parent (window); +} + +/** + * gdk_surface_withdraw: + * @window: a toplevel #GdkSurface + * + * Withdraws a window (unmaps it and asks the window manager to forget about it). + * This function is not really useful as gdk_surface_hide() automatically + * withdraws toplevel windows before hiding them. + **/ +void +gdk_surface_withdraw (GdkSurface *window) +{ + GdkSurfaceImplClass *impl_class; + gboolean was_mapped; + GdkGLContext *current_context; + + g_return_if_fail (GDK_IS_SURFACE (window)); + + if (window->destroyed) + return; + + was_mapped = GDK_SURFACE_IS_MAPPED (window); + + if (gdk_surface_has_impl (window)) + { + impl_class = GDK_SURFACE_IMPL_GET_CLASS (window->impl); + impl_class->withdraw (window); + + if (was_mapped) + { + if (window->event_mask & GDK_STRUCTURE_MASK) + _gdk_make_event (window, GDK_UNMAP, NULL, FALSE); + + if (window->parent && window->parent->event_mask & GDK_SUBSTRUCTURE_MASK) + _gdk_make_event (window, GDK_UNMAP, NULL, FALSE); + } + + current_context = gdk_gl_context_get_current (); + if (current_context != NULL && gdk_gl_context_get_window (current_context) == window) + gdk_gl_context_clear_current (); + + recompute_visible_regions (window, FALSE); + gdk_surface_clear_old_updated_area (window); + } +} + +/** + * gdk_surface_set_events: + * @window: a #GdkSurface + * @event_mask: event mask for @window + * + * The event mask for a window determines which events will be reported + * for that window from all master input devices. For example, an event mask + * including #GDK_BUTTON_PRESS_MASK means the window should report button + * press events. The event mask is the bitwise OR of values from the + * #GdkEventMask enumeration. + * + * See the [input handling overview][event-masks] for details. + **/ +void +gdk_surface_set_events (GdkSurface *window, + GdkEventMask event_mask) +{ + GdkSurfaceImplClass *impl_class; + + g_return_if_fail (GDK_IS_SURFACE (window)); + + if (window->destroyed) + return; + + window->event_mask = event_mask; + + if (gdk_surface_has_impl (window)) + { + impl_class = GDK_SURFACE_IMPL_GET_CLASS (window->impl); + impl_class->set_events (window, + get_native_event_mask (window)); + } + +} + +/** + * gdk_surface_get_events: + * @window: a #GdkSurface + * + * Gets the event mask for @window for all master input devices. See + * gdk_surface_set_events(). + * + * Returns: event mask for @window + **/ +GdkEventMask +gdk_surface_get_events (GdkSurface *window) +{ + g_return_val_if_fail (GDK_IS_SURFACE (window), 0); + + if (window->destroyed) + return 0; + + return window->event_mask; +} + +/** + * gdk_surface_set_device_events: + * @window: a #GdkSurface + * @device: #GdkDevice to enable events for. + * @event_mask: event mask for @window + * + * Sets the event mask for a given device (Normally a floating device, not + * attached to any visible pointer) to @window. For example, an event mask + * including #GDK_BUTTON_PRESS_MASK means the window should report button + * press events. The event mask is the bitwise OR of values from the + * #GdkEventMask enumeration. + * + * See the [input handling overview][event-masks] for details. + **/ +void +gdk_surface_set_device_events (GdkSurface *window, + GdkDevice *device, + GdkEventMask event_mask) +{ + GdkEventMask device_mask; + GdkSurface *native; + + g_return_if_fail (GDK_IS_SURFACE (window)); + g_return_if_fail (GDK_IS_DEVICE (device)); + + if (GDK_SURFACE_DESTROYED (window)) + return; + + if (G_UNLIKELY (!window->device_events)) + window->device_events = g_hash_table_new (NULL, NULL); + + if (event_mask == 0) + { + /* FIXME: unsetting events on a master device + * would restore window->event_mask + */ + g_hash_table_remove (window->device_events, device); + } + else + g_hash_table_insert (window->device_events, device, + GINT_TO_POINTER (event_mask)); + + native = gdk_surface_get_toplevel (window); + + device_mask = get_native_device_event_mask (window, device); + GDK_DEVICE_GET_CLASS (device)->select_window_events (device, native, device_mask); +} + +/** + * gdk_surface_get_device_events: + * @window: a #GdkSurface. + * @device: a #GdkDevice. + * + * Returns the event mask for @window corresponding to an specific device. + * + * Returns: device event mask for @window + **/ +GdkEventMask +gdk_surface_get_device_events (GdkSurface *window, + GdkDevice *device) +{ + GdkEventMask mask; + + g_return_val_if_fail (GDK_IS_SURFACE (window), 0); + g_return_val_if_fail (GDK_IS_DEVICE (device), 0); + + if (GDK_SURFACE_DESTROYED (window)) + return 0; + + if (!window->device_events) + return 0; + + mask = GPOINTER_TO_INT (g_hash_table_lookup (window->device_events, device)); + + /* FIXME: device could be controlled by window->event_mask */ + + return mask; +} + +static void +gdk_surface_move_resize_toplevel (GdkSurface *window, + gboolean with_move, + gint x, + gint y, + gint width, + gint height) +{ + cairo_region_t *old_region, *new_region; + GdkSurfaceImplClass *impl_class; + gboolean expose; + gboolean is_resize; + + expose = FALSE; + old_region = NULL; + + is_resize = (width != -1) || (height != -1); + + if (gdk_surface_is_viewable (window) && + !window->input_only) + { + expose = TRUE; + old_region = cairo_region_copy (window->clip_region); + } + + impl_class = GDK_SURFACE_IMPL_GET_CLASS (window->impl); + impl_class->move_resize (window, with_move, x, y, width, height); + + /* Avoid recomputing for pure toplevel moves, for performance reasons */ + if (is_resize) + recompute_visible_regions (window, FALSE); + + if (expose) + { + new_region = cairo_region_copy (window->clip_region); + + /* This is the newly exposed area (due to any resize), + * X will expose it, but lets do that without the roundtrip + */ + cairo_region_subtract (new_region, old_region); + gdk_surface_invalidate_region_full (window, new_region, TRUE); + + cairo_region_destroy (old_region); + cairo_region_destroy (new_region); + } +} + + +static void +gdk_surface_move_resize_internal (GdkSurface *window, + gboolean with_move, + gint x, + gint y, + gint width, + gint height) +{ + cairo_region_t *old_region, *new_region; + gboolean expose; + + g_return_if_fail (GDK_IS_SURFACE (window)); + + if (window->destroyed) + return; + + if (gdk_surface_is_toplevel (window)) + { + gdk_surface_move_resize_toplevel (window, with_move, x, y, width, height); + return; + } + + if (width == 0) + width = 1; + if (height == 0) + height = 1; + + /* Bail early if no change */ + if (window->width == width && + window->height == height && + (!with_move || + (window->x == x && + window->y == y))) + return; + + /* Handle child windows */ + + expose = FALSE; + old_region = NULL; + + if (gdk_surface_is_viewable (window) && + !window->input_only) + { + GdkRectangle r; + + expose = TRUE; + + r.x = window->x; + r.y = window->y; + r.width = window->width; + r.height = window->height; + + old_region = cairo_region_create_rectangle (&r); + } + + /* Set the new position and size */ + if (with_move) + { + window->x = x; + window->y = y; + } + if (!(width < 0 && height < 0)) + { + window->width = width; + window->height = height; + } + + recompute_visible_regions (window, FALSE); + + if (expose) + { + GdkRectangle r; + + r.x = window->x; + r.y = window->y; + r.width = window->width; + r.height = window->height; + + new_region = cairo_region_create_rectangle (&r); + + cairo_region_union (new_region, old_region); + + gdk_surface_invalidate_region_full (window->parent, new_region, TRUE); + + cairo_region_destroy (old_region); + cairo_region_destroy (new_region); + } +} + + + +/** + * gdk_surface_move: + * @window: a #GdkSurface + * @x: X coordinate relative to window’s parent + * @y: Y coordinate relative to window’s parent + * + * Repositions a window relative to its parent window. + * For toplevel windows, window managers may ignore or modify the move; + * you should probably use gtk_window_move() on a #GtkWindow widget + * anyway, instead of using GDK functions. For child windows, + * the move will reliably succeed. + * + * If you’re also planning to resize the window, use gdk_surface_move_resize() + * to both move and resize simultaneously, for a nicer visual effect. + **/ +void +gdk_surface_move (GdkSurface *window, + gint x, + gint y) +{ + gdk_surface_move_resize_internal (window, TRUE, x, y, -1, -1); +} + +/** + * gdk_surface_resize: + * @window: a #GdkSurface + * @width: new width of the window + * @height: new height of the window + * + * Resizes @window; for toplevel windows, asks the window manager to resize + * the window. The window manager may not allow the resize. When using GTK+, + * use gtk_window_resize() instead of this low-level GDK function. + * + * Windows may not be resized below 1x1. + * + * If you’re also planning to move the window, use gdk_surface_move_resize() + * to both move and resize simultaneously, for a nicer visual effect. + **/ +void +gdk_surface_resize (GdkSurface *window, + gint width, + gint height) +{ + gdk_surface_move_resize_internal (window, FALSE, 0, 0, width, height); +} + + +/** + * gdk_surface_move_resize: + * @window: a #GdkSurface + * @x: new X position relative to window’s parent + * @y: new Y position relative to window’s parent + * @width: new width + * @height: new height + * + * Equivalent to calling gdk_surface_move() and gdk_surface_resize(), + * except that both operations are performed at once, avoiding strange + * visual effects. (i.e. the user may be able to see the window first + * move, then resize, if you don’t use gdk_surface_move_resize().) + **/ +void +gdk_surface_move_resize (GdkSurface *window, + gint x, + gint y, + gint width, + gint height) +{ + gdk_surface_move_resize_internal (window, TRUE, x, y, width, height); +} + +/** + * gdk_surface_move_to_rect: + * @window: the #GdkSurface to move + * @rect: (not nullable): the destination #GdkRectangle to align @window with + * @rect_anchor: the point on @rect to align with @window's anchor point + * @window_anchor: the point on @window to align with @rect's anchor point + * @anchor_hints: positioning hints to use when limited on space + * @rect_anchor_dx: horizontal offset to shift @window, i.e. @rect's anchor + * point + * @rect_anchor_dy: vertical offset to shift @window, i.e. @rect's anchor point + * + * Moves @window to @rect, aligning their anchor points. + * + * @rect is relative to the top-left corner of the window that @window is + * transient for. @rect_anchor and @window_anchor determine anchor points on + * @rect and @window to pin together. @rect's anchor point can optionally be + * offset by @rect_anchor_dx and @rect_anchor_dy, which is equivalent to + * offsetting the position of @window. + * + * @anchor_hints determines how @window will be moved if the anchor points cause + * it to move off-screen. For example, %GDK_ANCHOR_FLIP_X will replace + * %GDK_GRAVITY_NORTH_WEST with %GDK_GRAVITY_NORTH_EAST and vice versa if + * @window extends beyond the left or right edges of the monitor. + * + * Connect to the #GdkSurface::moved-to-rect signal to find out how it was + * actually positioned. + * + * Stability: Private + */ +void +gdk_surface_move_to_rect (GdkSurface *window, + const GdkRectangle *rect, + GdkGravity rect_anchor, + GdkGravity window_anchor, + GdkAnchorHints anchor_hints, + gint rect_anchor_dx, + gint rect_anchor_dy) +{ + GdkSurfaceImplClass *impl_class; + + g_return_if_fail (GDK_IS_SURFACE (window)); + g_return_if_fail (window->transient_for); + g_return_if_fail (rect); + + impl_class = GDK_SURFACE_IMPL_GET_CLASS (window->impl); + impl_class->move_to_rect (window, + rect, + rect_anchor, + window_anchor, + anchor_hints, + rect_anchor_dx, + rect_anchor_dy); +} + +/** + * gdk_surface_scroll: + * @window: a #GdkSurface + * @dx: Amount to scroll in the X direction + * @dy: Amount to scroll in the Y direction + * + * Scroll the contents of @window, both pixels and children, by the + * given amount. @window itself does not move. Portions of the window + * that the scroll operation brings in from offscreen areas are + * invalidated. The invalidated region may be bigger than what would + * strictly be necessary. + * + * For X11, a minimum area will be invalidated if the window has no + * subwindows, or if the edges of the window’s parent do not extend + * beyond the edges of the window. In other cases, a multi-step process + * is used to scroll the window which may produce temporary visual + * artifacts and unnecessary invalidations. + **/ +void +gdk_surface_scroll (GdkSurface *window, + gint dx, + gint dy) +{ + GList *tmp_list; + + g_return_if_fail (GDK_IS_SURFACE (window)); + + if (dx == 0 && dy == 0) + return; + + if (window->destroyed) + return; + + /* First move all child windows, without causing invalidation */ + + tmp_list = window->children; + while (tmp_list) + { + GdkSurface *child = GDK_SURFACE (tmp_list->data); + + /* Just update the positions, the bits will move with the copy */ + child->x += dx; + child->y += dy; + + tmp_list = tmp_list->next; + } + + recompute_visible_regions (window, TRUE); + + gdk_surface_invalidate_rect_full (window, NULL, TRUE); +} + +/** + * gdk_surface_move_region: + * @window: a #GdkSurface + * @region: The #cairo_region_t to move + * @dx: Amount to move in the X direction + * @dy: Amount to move in the Y direction + * + * Move the part of @window indicated by @region by @dy pixels in the Y + * direction and @dx pixels in the X direction. The portions of @region + * that not covered by the new position of @region are invalidated. + * + * Child windows are not moved. + */ +void +gdk_surface_move_region (GdkSurface *window, + const cairo_region_t *region, + gint dx, + gint dy) +{ + cairo_region_t *expose_area; + + g_return_if_fail (GDK_IS_SURFACE (window)); + g_return_if_fail (region != NULL); + + if (dx == 0 && dy == 0) + return; + + if (window->destroyed) + return; + + expose_area = cairo_region_copy (region); + cairo_region_translate (expose_area, dx, dy); + cairo_region_union (expose_area, region); + + gdk_surface_invalidate_region_full (window, expose_area, FALSE); + cairo_region_destroy (expose_area); +} + +static void +gdk_surface_set_cursor_internal (GdkSurface *window, + GdkDevice *device, + GdkCursor *cursor) +{ + if (GDK_SURFACE_DESTROYED (window)) + return; + + g_assert (gdk_surface_get_display (window) == gdk_device_get_display (device)); + + if (window->window_type == GDK_SURFACE_ROOT || + window->window_type == GDK_SURFACE_FOREIGN) + GDK_DEVICE_GET_CLASS (device)->set_window_cursor (device, window, cursor); + else + { + GdkPointerSurfaceInfo *pointer_info; + GdkDisplay *display; + + display = gdk_surface_get_display (window); + pointer_info = _gdk_display_get_pointer_info (display, device); + + if (_gdk_surface_event_parent_of (window, pointer_info->window_under_pointer)) + update_cursor (display, device); + } +} + +/** + * gdk_surface_get_cursor: + * @window: a #GdkSurface + * + * Retrieves a #GdkCursor pointer for the cursor currently set on the + * specified #GdkSurface, or %NULL. If the return value is %NULL then + * there is no custom cursor set on the specified window, and it is + * using the cursor for its parent window. + * + * Returns: (nullable) (transfer none): a #GdkCursor, or %NULL. The + * returned object is owned by the #GdkSurface and should not be + * unreferenced directly. Use gdk_surface_set_cursor() to unset the + * cursor of the window + */ +GdkCursor * +gdk_surface_get_cursor (GdkSurface *window) +{ + g_return_val_if_fail (GDK_IS_SURFACE (window), NULL); + + return window->cursor; +} + +/** + * gdk_surface_set_cursor: + * @window: a #GdkSurface + * @cursor: (allow-none): a cursor + * + * Sets the default mouse pointer for a #GdkSurface. + * + * Note that @cursor must be for the same display as @window. + * + * Use gdk_cursor_new_for_display() or gdk_cursor_new_from_texture() to + * create the cursor. To make the cursor invisible, use %GDK_BLANK_CURSOR. + * Passing %NULL for the @cursor argument to gdk_surface_set_cursor() means + * that @window will use the cursor of its parent window. Most windows + * should use this default. + */ +void +gdk_surface_set_cursor (GdkSurface *window, + GdkCursor *cursor) +{ + GdkDisplay *display; + + g_return_if_fail (GDK_IS_SURFACE (window)); + + display = gdk_surface_get_display (window); + + if (window->cursor) + { + g_object_unref (window->cursor); + window->cursor = NULL; + } + + if (!GDK_SURFACE_DESTROYED (window)) + { + GdkDevice *device; + GList *seats, *s; + + if (cursor) + window->cursor = g_object_ref (cursor); + + seats = gdk_display_list_seats (display); + + for (s = seats; s; s = s->next) + { + GList *devices, *d; + + device = gdk_seat_get_pointer (s->data); + gdk_surface_set_cursor_internal (window, device, window->cursor); + + devices = gdk_seat_get_slaves (s->data, GDK_SEAT_CAPABILITY_TABLET_STYLUS); + for (d = devices; d; d = d->next) + { + device = gdk_device_get_associated_device (d->data); + gdk_surface_set_cursor_internal (window, device, window->cursor); + } + g_list_free (devices); + } + + g_list_free (seats); + g_object_notify_by_pspec (G_OBJECT (window), properties[PROP_CURSOR]); + } +} + +/** + * gdk_surface_get_device_cursor: + * @window: a #GdkSurface. + * @device: a master, pointer #GdkDevice. + * + * Retrieves a #GdkCursor pointer for the @device currently set on the + * specified #GdkSurface, or %NULL. If the return value is %NULL then + * there is no custom cursor set on the specified window, and it is + * using the cursor for its parent window. + * + * Returns: (nullable) (transfer none): a #GdkCursor, or %NULL. The + * returned object is owned by the #GdkSurface and should not be + * unreferenced directly. Use gdk_surface_set_cursor() to unset the + * cursor of the window + **/ +GdkCursor * +gdk_surface_get_device_cursor (GdkSurface *window, + GdkDevice *device) +{ + g_return_val_if_fail (GDK_IS_SURFACE (window), NULL); + g_return_val_if_fail (GDK_IS_DEVICE (device), NULL); + g_return_val_if_fail (gdk_device_get_source (device) != GDK_SOURCE_KEYBOARD, NULL); + g_return_val_if_fail (gdk_device_get_device_type (device) == GDK_DEVICE_TYPE_MASTER, NULL); + + return g_hash_table_lookup (window->device_cursor, device); +} + +/** + * gdk_surface_set_device_cursor: + * @window: a #GdkSurface + * @device: a master, pointer #GdkDevice + * @cursor: a #GdkCursor + * + * Sets a specific #GdkCursor for a given device when it gets inside @window. + * Use gdk_cursor_new_for_display() or gdk_cursor_new_from_texture() to create + * the cursor. To make the cursor invisible, use %GDK_BLANK_CURSOR. Passing + * %NULL for the @cursor argument to gdk_surface_set_cursor() means that + * @window will use the cursor of its parent window. Most windows should + * use this default. + **/ +void +gdk_surface_set_device_cursor (GdkSurface *window, + GdkDevice *device, + GdkCursor *cursor) +{ + g_return_if_fail (GDK_IS_SURFACE (window)); + g_return_if_fail (GDK_IS_DEVICE (device)); + g_return_if_fail (gdk_device_get_source (device) != GDK_SOURCE_KEYBOARD); + g_return_if_fail (gdk_device_get_device_type (device) == GDK_DEVICE_TYPE_MASTER); + + if (!cursor) + g_hash_table_remove (window->device_cursor, device); + else + g_hash_table_replace (window->device_cursor, device, g_object_ref (cursor)); + + gdk_surface_set_cursor_internal (window, device, cursor); +} + +/** + * gdk_surface_get_geometry: + * @window: a #GdkSurface + * @x: (out) (allow-none): return location for X coordinate of window (relative to its parent) + * @y: (out) (allow-none): return location for Y coordinate of window (relative to its parent) + * @width: (out) (allow-none): return location for width of window + * @height: (out) (allow-none): return location for height of window + * + * Any of the return location arguments to this function may be %NULL, + * if you aren’t interested in getting the value of that field. + * + * The X and Y coordinates returned are relative to the parent window + * of @window, which for toplevels usually means relative to the + * window decorations (titlebar, etc.) rather than relative to the + * root window (screen-size background window). + * + * On the X11 platform, the geometry is obtained from the X server, + * so reflects the latest position of @window; this may be out-of-sync + * with the position of @window delivered in the most-recently-processed + * #GdkEventConfigure. gdk_surface_get_position() in contrast gets the + * position from the most recent configure event. + * + * Note: If @window is not a toplevel, it is much better + * to call gdk_surface_get_position(), gdk_surface_get_width() and + * gdk_surface_get_height() instead, because it avoids the roundtrip to + * the X server and because these functions support the full 32-bit + * coordinate space, whereas gdk_surface_get_geometry() is restricted to + * the 16-bit coordinates of X11. + */ +void +gdk_surface_get_geometry (GdkSurface *window, + gint *x, + gint *y, + gint *width, + gint *height) +{ + GdkSurface *parent; + GdkSurfaceImplClass *impl_class; + + g_return_if_fail (GDK_IS_SURFACE (window)); + + if (!GDK_SURFACE_DESTROYED (window)) + { + if (gdk_surface_has_impl (window)) + { + impl_class = GDK_SURFACE_IMPL_GET_CLASS (window->impl); + impl_class->get_geometry (window, x, y, + width, height); + /* This reports the position wrt to the native parent, we need to convert + it to be relative to the client side parent */ + parent = window->parent; + if (parent && !gdk_surface_has_impl (parent)) + { + if (x) + *x -= parent->abs_x; + if (y) + *y -= parent->abs_y; + } + } + else + { + if (x) + *x = window->x; + if (y) + *y = window->y; + if (width) + *width = window->width; + if (height) + *height = window->height; + } + } +} + +/** + * gdk_surface_get_width: + * @window: a #GdkSurface + * + * Returns the width of the given @window. + * + * On the X11 platform the returned size is the size reported in the + * most-recently-processed configure event, rather than the current + * size on the X server. + * + * Returns: The width of @window + */ +int +gdk_surface_get_width (GdkSurface *window) +{ + g_return_val_if_fail (GDK_IS_SURFACE (window), 0); + + return window->width; +} + +/** + * gdk_surface_get_height: + * @window: a #GdkSurface + * + * Returns the height of the given @window. + * + * On the X11 platform the returned size is the size reported in the + * most-recently-processed configure event, rather than the current + * size on the X server. + * + * Returns: The height of @window + */ +int +gdk_surface_get_height (GdkSurface *window) +{ + g_return_val_if_fail (GDK_IS_SURFACE (window), 0); + + return window->height; +} + +/** + * gdk_surface_get_origin: + * @window: a #GdkSurface + * @x: (out) (allow-none): return location for X coordinate + * @y: (out) (allow-none): return location for Y coordinate + * + * Obtains the position of a window in root window coordinates. + * (Compare with gdk_surface_get_position() and + * gdk_surface_get_geometry() which return the position of a window + * relative to its parent window.) + * + * Returns: not meaningful, ignore + */ +gint +gdk_surface_get_origin (GdkSurface *window, + gint *x, + gint *y) +{ + gint dummy_x, dummy_y; + + g_return_val_if_fail (GDK_IS_SURFACE (window), 0); + + gdk_surface_get_root_coords (window, + 0, 0, + x ? x : &dummy_x, + y ? y : &dummy_y); + + return TRUE; +} + +/** + * gdk_surface_get_root_coords: + * @window: a #GdkSurface + * @x: X coordinate in window + * @y: Y coordinate in window + * @root_x: (out): return location for X coordinate + * @root_y: (out): return location for Y coordinate + * + * Obtains the position of a window position in root + * window coordinates. This is similar to + * gdk_surface_get_origin() but allows you to pass + * in any position in the window, not just the origin. + */ +void +gdk_surface_get_root_coords (GdkSurface *window, + gint x, + gint y, + gint *root_x, + gint *root_y) +{ + GdkSurfaceImplClass *impl_class; + + g_return_if_fail (GDK_IS_SURFACE (window)); + + if (GDK_SURFACE_DESTROYED (window)) + { + *root_x = 0; + *root_y = 0; + return; + } + + impl_class = GDK_SURFACE_IMPL_GET_CLASS (window->impl); + impl_class->get_root_coords (window->impl_window, + x + window->abs_x, + y + window->abs_y, + root_x, root_y); +} + +/** + * gdk_surface_coords_to_parent: + * @window: a child window + * @x: X coordinate in child’s coordinate system + * @y: Y coordinate in child’s coordinate system + * @parent_x: (out) (allow-none): return location for X coordinate + * in parent’s coordinate system, or %NULL + * @parent_y: (out) (allow-none): return location for Y coordinate + * in parent’s coordinate system, or %NULL + * + * Transforms window coordinates from a child window to its parent + * window. Calling this function is equivalent to adding the return + * values of gdk_surface_get_position() to the child coordinates. + * + * See also: gdk_surface_coords_from_parent() + **/ +void +gdk_surface_coords_to_parent (GdkSurface *window, + gdouble x, + gdouble y, + gdouble *parent_x, + gdouble *parent_y) +{ + g_return_if_fail (GDK_IS_SURFACE (window)); + + if (parent_x) + *parent_x = x + window->x; + + if (parent_y) + *parent_y = y + window->y; +} + +/** + * gdk_surface_coords_from_parent: + * @window: a child window + * @parent_x: X coordinate in parent’s coordinate system + * @parent_y: Y coordinate in parent’s coordinate system + * @x: (out) (allow-none): return location for X coordinate in child’s coordinate system + * @y: (out) (allow-none): return location for Y coordinate in child’s coordinate system + * + * Transforms window coordinates from a parent window to a child + * window. + * + * Calling this function is equivalent to subtracting the return + * values of gdk_surface_get_position() from the parent coordinates. + * + * See also: gdk_surface_coords_to_parent() + **/ +void +gdk_surface_coords_from_parent (GdkSurface *window, + gdouble parent_x, + gdouble parent_y, + gdouble *x, + gdouble *y) +{ + g_return_if_fail (GDK_IS_SURFACE (window)); + + if (x) + *x = parent_x - window->x; + + if (y) + *y = parent_y - window->y; +} + +/** + * gdk_surface_shape_combine_region: + * @window: a #GdkSurface + * @shape_region: (allow-none): region of window to be non-transparent + * @offset_x: X position of @shape_region in @window coordinates + * @offset_y: Y position of @shape_region in @window coordinates + * + * Makes pixels in @window outside @shape_region be transparent, + * so that the window may be nonrectangular. + * + * If @shape_region is %NULL, the shape will be unset, so the whole + * window will be opaque again. @offset_x and @offset_y are ignored + * if @shape_region is %NULL. + * + * On the X11 platform, this uses an X server extension which is + * widely available on most common platforms, but not available on + * very old X servers, and occasionally the implementation will be + * buggy. On servers without the shape extension, this function + * will do nothing. + * + * This function works on both toplevel and child windows. + */ +void +gdk_surface_shape_combine_region (GdkSurface *window, + const cairo_region_t *shape_region, + gint offset_x, + gint offset_y) +{ + cairo_region_t *old_region, *new_region, *diff; + + g_return_if_fail (GDK_IS_SURFACE (window)); + + if (GDK_SURFACE_DESTROYED (window)) + return; + + if (!window->shape && shape_region == NULL) + return; + + window->shaped = (shape_region != NULL); + + if (window->shape) + cairo_region_destroy (window->shape); + + old_region = NULL; + if (GDK_SURFACE_IS_MAPPED (window)) + old_region = cairo_region_copy (window->clip_region); + + if (shape_region) + { + window->shape = cairo_region_copy (shape_region); + cairo_region_translate (window->shape, offset_x, offset_y); + } + else + window->shape = NULL; + + recompute_visible_regions (window, FALSE); + + if (old_region) + { + new_region = cairo_region_copy (window->clip_region); + + /* New area in the window, needs invalidation */ + diff = cairo_region_copy (new_region); + cairo_region_subtract (diff, old_region); + + gdk_surface_invalidate_region_full (window, diff, TRUE); + + cairo_region_destroy (diff); + + if (!gdk_surface_is_toplevel (window)) + { + /* New area in the non-root parent window, needs invalidation */ + diff = cairo_region_copy (old_region); + cairo_region_subtract (diff, new_region); + + /* Adjust region to parent window coords */ + cairo_region_translate (diff, window->x, window->y); + + gdk_surface_invalidate_region_full (window->parent, diff, TRUE); + + cairo_region_destroy (diff); + } + + cairo_region_destroy (new_region); + cairo_region_destroy (old_region); + } +} + +static void +do_child_shapes (GdkSurface *window, + gboolean merge) +{ + GdkRectangle r; + cairo_region_t *region; + + r.x = 0; + r.y = 0; + r.width = window->width; + r.height = window->height; + + region = cairo_region_create_rectangle (&r); + remove_child_area (window, FALSE, region); + + if (merge && window->shape) + cairo_region_subtract (region, window->shape); + + cairo_region_xor_rectangle (region, &r); + + gdk_surface_shape_combine_region (window, region, 0, 0); + + cairo_region_destroy (region); +} + +/** + * gdk_surface_set_child_shapes: + * @window: a #GdkSurface + * + * Sets the shape mask of @window to the union of shape masks + * for all children of @window, ignoring the shape mask of @window + * itself. Contrast with gdk_surface_merge_child_shapes() which includes + * the shape mask of @window in the masks to be merged. + **/ +void +gdk_surface_set_child_shapes (GdkSurface *window) +{ + g_return_if_fail (GDK_IS_SURFACE (window)); + + do_child_shapes (window, FALSE); +} + +/** + * gdk_surface_merge_child_shapes: + * @window: a #GdkSurface + * + * Merges the shape masks for any child windows into the + * shape mask for @window. i.e. the union of all masks + * for @window and its children will become the new mask + * for @window. See gdk_surface_shape_combine_region(). + * + * This function is distinct from gdk_surface_set_child_shapes() + * because it includes @window’s shape mask in the set of shapes to + * be merged. + */ +void +gdk_surface_merge_child_shapes (GdkSurface *window) +{ + g_return_if_fail (GDK_IS_SURFACE (window)); + + do_child_shapes (window, TRUE); +} + +/** + * gdk_surface_input_shape_combine_region: + * @window: a #GdkSurface + * @shape_region: region of window to be non-transparent + * @offset_x: X position of @shape_region in @window coordinates + * @offset_y: Y position of @shape_region in @window coordinates + * + * Like gdk_surface_shape_combine_region(), but the shape applies + * only to event handling. Mouse events which happen while + * the pointer position corresponds to an unset bit in the + * mask will be passed on the window below @window. + * + * An input shape is typically used with RGBA windows. + * The alpha channel of the window defines which pixels are + * invisible and allows for nicely antialiased borders, + * and the input shape controls where the window is + * “clickable”. + * + * On the X11 platform, this requires version 1.1 of the + * shape extension. + * + * On the Win32 platform, this functionality is not present and the + * function does nothing. + */ +void +gdk_surface_input_shape_combine_region (GdkSurface *window, + const cairo_region_t *shape_region, + gint offset_x, + gint offset_y) +{ + GdkSurfaceImplClass *impl_class; + + g_return_if_fail (GDK_IS_SURFACE (window)); + + if (GDK_SURFACE_DESTROYED (window)) + return; + + if (window->input_shape) + cairo_region_destroy (window->input_shape); + + if (shape_region) + { + window->input_shape = cairo_region_copy (shape_region); + cairo_region_translate (window->input_shape, offset_x, offset_y); + } + else + window->input_shape = NULL; + + if (gdk_surface_has_impl (window)) + { + impl_class = GDK_SURFACE_IMPL_GET_CLASS (window->impl); + impl_class->input_shape_combine_region (window, window->input_shape, 0, 0); + } +} + +static void +do_child_input_shapes (GdkSurface *window, + gboolean merge) +{ + GdkRectangle r; + cairo_region_t *region; + + r.x = 0; + r.y = 0; + r.width = window->width; + r.height = window->height; + + region = cairo_region_create_rectangle (&r); + remove_child_area (window, TRUE, region); + + if (merge && window->shape) + cairo_region_subtract (region, window->shape); + if (merge && window->input_shape) + cairo_region_subtract (region, window->input_shape); + + cairo_region_xor_rectangle (region, &r); + + gdk_surface_input_shape_combine_region (window, region, 0, 0); +} + + +/** + * gdk_surface_set_child_input_shapes: + * @window: a #GdkSurface + * + * Sets the input shape mask of @window to the union of input shape masks + * for all children of @window, ignoring the input shape mask of @window + * itself. Contrast with gdk_surface_merge_child_input_shapes() which includes + * the input shape mask of @window in the masks to be merged. + **/ +void +gdk_surface_set_child_input_shapes (GdkSurface *window) +{ + g_return_if_fail (GDK_IS_SURFACE (window)); + + do_child_input_shapes (window, FALSE); +} + +/** + * gdk_surface_set_pass_through: + * @window: a #GdkSurface + * @pass_through: a boolean + * + * Sets whether input to the window is passed through to the window + * below. + * + * The default value of this is %FALSE, which means that pointer + * events that happen inside the window are send first to the window, + * but if the event is not selected by the event mask then the event + * is sent to the parent window, and so on up the hierarchy. + * + * If @pass_through is %TRUE then such pointer events happen as if the + * window wasn't there at all, and thus will be sent first to any + * windows below @window. This is useful if the window is used in a + * transparent fashion. In the terminology of the web this would be called + * "pointer-events: none". + * + * Note that a window with @pass_through %TRUE can still have a subwindow + * without pass through, so you can get events on a subset of a window. And in + * that cases you would get the in-between related events such as the pointer + * enter/leave events on its way to the destination window. + **/ +void +gdk_surface_set_pass_through (GdkSurface *window, + gboolean pass_through) +{ + g_return_if_fail (GDK_IS_SURFACE (window)); + + window->pass_through = !!pass_through; +} + +/** + * gdk_surface_get_pass_through: + * @window: a #GdkSurface + * + * Returns whether input to the window is passed through to the window + * below. + * + * See gdk_surface_set_pass_through() for details + **/ +gboolean +gdk_surface_get_pass_through (GdkSurface *window) +{ + g_return_val_if_fail (GDK_IS_SURFACE (window), FALSE); + + return window->pass_through; +} + +/** + * gdk_surface_merge_child_input_shapes: + * @window: a #GdkSurface + * + * Merges the input shape masks for any child windows into the + * input shape mask for @window. i.e. the union of all input masks + * for @window and its children will become the new input mask + * for @window. See gdk_surface_input_shape_combine_region(). + * + * This function is distinct from gdk_surface_set_child_input_shapes() + * because it includes @window’s input shape mask in the set of + * shapes to be merged. + **/ +void +gdk_surface_merge_child_input_shapes (GdkSurface *window) +{ + g_return_if_fail (GDK_IS_SURFACE (window)); + + do_child_input_shapes (window, TRUE); +} + + +/** + * gdk_surface_get_modal_hint: + * @window: A toplevel #GdkSurface. + * + * Determines whether or not the window manager is hinted that @window + * has modal behaviour. + * + * Returns: whether or not the window has the modal hint set. + */ +gboolean +gdk_surface_get_modal_hint (GdkSurface *window) +{ + g_return_val_if_fail (GDK_IS_SURFACE (window), FALSE); + + return window->modal_hint; +} + +/** + * gdk_surface_get_accept_focus: + * @window: a toplevel #GdkSurface. + * + * Determines whether or not the desktop environment shuld be hinted that + * the window does not want to receive input focus. + * + * Returns: whether or not the window should receive input focus. + */ +gboolean +gdk_surface_get_accept_focus (GdkSurface *window) +{ + g_return_val_if_fail (GDK_IS_SURFACE (window), FALSE); + + return window->accept_focus; +} + +/** + * gdk_surface_get_focus_on_map: + * @window: a toplevel #GdkSurface. + * + * Determines whether or not the desktop environment should be hinted that the + * window does not want to receive input focus when it is mapped. + * + * Returns: whether or not the window wants to receive input focus when + * it is mapped. + */ +gboolean +gdk_surface_get_focus_on_map (GdkSurface *window) +{ + g_return_val_if_fail (GDK_IS_SURFACE (window), FALSE); + + return window->focus_on_map; +} + +/** + * gdk_surface_is_input_only: + * @window: a toplevel #GdkSurface + * + * Determines whether or not the window is an input only window. + * + * Returns: %TRUE if @window is input only + */ +gboolean +gdk_surface_is_input_only (GdkSurface *window) +{ + g_return_val_if_fail (GDK_IS_SURFACE (window), FALSE); + + return window->input_only; +} + +/** + * gdk_surface_is_shaped: + * @window: a toplevel #GdkSurface + * + * Determines whether or not the window is shaped. + * + * Returns: %TRUE if @window is shaped + */ +gboolean +gdk_surface_is_shaped (GdkSurface *window) +{ + g_return_val_if_fail (GDK_IS_SURFACE (window), FALSE); + + return window->shaped; +} + +/* Gets the toplevel for a window as used for events, + i.e. including offscreen parents going up to the native + toplevel */ +static GdkSurface * +get_event_toplevel (GdkSurface *window) +{ + GdkSurface *parent; + + while ((parent = window->parent) != NULL && + (parent->window_type != GDK_SURFACE_ROOT)) + window = parent; + + return window; +} + +gboolean +_gdk_surface_event_parent_of (GdkSurface *parent, + GdkSurface *child) +{ + GdkSurface *w; + + w = child; + while (w != NULL) + { + if (w == parent) + return TRUE; + + w = w->parent; + } + + return FALSE; +} + +static void +update_cursor (GdkDisplay *display, + GdkDevice *device) +{ + GdkSurface *cursor_window, *parent, *toplevel; + GdkSurface *pointer_window; + GdkPointerSurfaceInfo *pointer_info; + GdkDeviceGrabInfo *grab; + GdkCursor *cursor; + + pointer_info = _gdk_display_get_pointer_info (display, device); + pointer_window = pointer_info->window_under_pointer; + + /* We ignore the serials here and just pick the last grab + we've sent, as that would shortly be used anyway. */ + grab = _gdk_display_get_last_device_grab (display, device); + if (/* have grab */ + grab != NULL && + /* the pointer is not in a descendant of the grab window */ + !_gdk_surface_event_parent_of (grab->window, pointer_window)) + { + /* use the cursor from the grab window */ + cursor_window = grab->window; + } + else + { + /* otherwise use the cursor from the pointer window */ + cursor_window = pointer_window; + } + + /* Find the first window with the cursor actually set, as + the cursor is inherited from the parent */ + while (cursor_window->cursor == NULL && + !g_hash_table_contains (cursor_window->device_cursor, device) && + (parent = cursor_window->parent) != NULL && + parent->window_type != GDK_SURFACE_ROOT) + cursor_window = parent; + + cursor = g_hash_table_lookup (cursor_window->device_cursor, device); + + if (!cursor) + cursor = cursor_window->cursor; + + /* Set all cursors on toplevel, otherwise its tricky to keep track of + * which native window has what cursor set. */ + toplevel = get_event_toplevel (pointer_window); + GDK_DEVICE_GET_CLASS (device)->set_window_cursor (device, toplevel, cursor); +} + +static gboolean +point_in_window (GdkSurface *window, + gdouble x, + gdouble y) +{ + return + x >= 0 && x < window->width && + y >= 0 && y < window->height && + (window->shape == NULL || + cairo_region_contains_point (window->shape, + x, y)) && + (window->input_shape == NULL || + cairo_region_contains_point (window->input_shape, + x, y)); +} + +/* Same as point_in_window, except it also takes pass_through and its + interaction with child windows into account */ +static gboolean +point_in_input_window (GdkSurface *window, + gdouble x, + gdouble y, + GdkSurface **input_window, + gdouble *input_window_x, + gdouble *input_window_y) +{ + GdkSurface *sub; + double child_x, child_y; + GList *l; + + if (!point_in_window (window, x, y)) + return FALSE; + + if (!window->pass_through) + { + if (input_window) + { + *input_window = window; + *input_window_x = x; + *input_window_y = y; + } + return TRUE; + } + + /* For pass-through, must be over a child input window */ + + /* Children is ordered in reverse stack order, i.e. first is topmost */ + for (l = window->children; l != NULL; l = l->next) + { + sub = l->data; + + if (!GDK_SURFACE_IS_MAPPED (sub)) + continue; + + gdk_surface_coords_from_parent ((GdkSurface *)sub, + x, y, + &child_x, &child_y); + if (point_in_input_window (sub, child_x, child_y, + input_window, input_window_x, input_window_y)) + { + if (input_window) + gdk_surface_coords_to_parent (sub, + *input_window_x, + *input_window_y, + input_window_x, + input_window_y); + return TRUE; + } + } + + return FALSE; +} + +GdkSurface * +_gdk_surface_find_child_at (GdkSurface *window, + double x, + double y) +{ + GdkSurface *sub; + double child_x, child_y; + GList *l; + + if (point_in_window (window, x, y)) + { + /* Children is ordered in reverse stack order, i.e. first is topmost */ + for (l = window->children; l != NULL; l = l->next) + { + sub = l->data; + + if (!GDK_SURFACE_IS_MAPPED (sub)) + continue; + + gdk_surface_coords_from_parent ((GdkSurface *)sub, + x, y, + &child_x, &child_y); + if (point_in_input_window (sub, child_x, child_y, + NULL, NULL, NULL)) + return (GdkSurface *)sub; + } + } + + return NULL; +} + +GdkSurface * +_gdk_surface_find_descendant_at (GdkSurface *window, + gdouble x, + gdouble y, + gdouble *found_x, + gdouble *found_y) +{ + GdkSurface *sub, *input_window; + gdouble child_x, child_y; + GList *l; + gboolean found; + + if (point_in_window (window, x, y)) + { + do + { + found = FALSE; + /* Children is ordered in reverse stack order, i.e. first is topmost */ + for (l = window->children; l != NULL; l = l->next) + { + sub = l->data; + + if (!GDK_SURFACE_IS_MAPPED (sub)) + continue; + + gdk_surface_coords_from_parent ((GdkSurface *)sub, + x, y, + &child_x, &child_y); + if (point_in_input_window (sub, child_x, child_y, + &input_window, &child_x, &child_y)) + { + x = child_x; + y = child_y; + window = input_window; + found = TRUE; + break; + } + } + } + while (found); + } + else + { + /* Not in window at all */ + window = NULL; + } + + if (found_x) + *found_x = x; + if (found_y) + *found_y = y; + + return window; +} + +/** + * gdk_surface_beep: + * @window: a toplevel #GdkSurface + * + * Emits a short beep associated to @window in the appropriate + * display, if supported. Otherwise, emits a short beep on + * the display just as gdk_display_beep(). + **/ +void +gdk_surface_beep (GdkSurface *window) +{ + GdkDisplay *display; + GdkSurface *toplevel; + + g_return_if_fail (GDK_IS_SURFACE (window)); + + if (GDK_SURFACE_DESTROYED (window)) + return; + + toplevel = get_event_toplevel (window); + display = gdk_surface_get_display (window); + + if (toplevel) + { + if (GDK_SURFACE_IMPL_GET_CLASS (toplevel->impl)->beep (toplevel)) + return; + } + + /* If windows fail to beep, we beep the display. */ + gdk_display_beep (display); +} + +/** + * gdk_surface_set_support_multidevice: + * @window: a #GdkSurface. + * @support_multidevice: %TRUE to enable multidevice support in @window. + * + * This function will enable multidevice features in @window. + * + * Multidevice aware windows will need to handle properly multiple, + * per device enter/leave events, device grabs and grab ownerships. + **/ +void +gdk_surface_set_support_multidevice (GdkSurface *window, + gboolean support_multidevice) +{ + g_return_if_fail (GDK_IS_SURFACE (window)); + + if (GDK_SURFACE_DESTROYED (window)) + return; + + if (window->support_multidevice == support_multidevice) + return; + + window->support_multidevice = support_multidevice; + + /* FIXME: What to do if called when some pointers are inside the window ? */ +} + +/** + * gdk_surface_get_support_multidevice: + * @window: a #GdkSurface. + * + * Returns %TRUE if the window is aware of the existence of multiple + * devices. + * + * Returns: %TRUE if the window handles multidevice features. + **/ +gboolean +gdk_surface_get_support_multidevice (GdkSurface *window) +{ + g_return_val_if_fail (GDK_IS_SURFACE (window), FALSE); + + if (GDK_SURFACE_DESTROYED (window)) + return FALSE; + + return window->support_multidevice; +} + +/* send motion events if the right buttons are down */ + +GdkEvent * +_gdk_make_event (GdkSurface *window, + GdkEventType type, + GdkEvent *event_in_queue, + gboolean before_event) +{ + GdkEvent *event = gdk_event_new (type); + guint32 the_time; + GdkModifierType the_state; + + the_time = gdk_event_get_time (event_in_queue); + gdk_event_get_state (event_in_queue, &the_state); + + event->any.window = g_object_ref (window); + event->any.send_event = FALSE; + if (event_in_queue && event_in_queue->any.send_event) + event->any.send_event = TRUE; + + switch ((guint) type) + { + case GDK_MOTION_NOTIFY: + event->motion.time = the_time; + event->motion.axes = NULL; + event->motion.state = the_state; + break; + + case GDK_BUTTON_PRESS: + case GDK_BUTTON_RELEASE: + event->button.time = the_time; + event->button.axes = NULL; + 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; + break; + + case GDK_KEY_PRESS: + case GDK_KEY_RELEASE: + event->key.time = the_time; + event->key.state = the_state; + break; + + case GDK_ENTER_NOTIFY: + case GDK_LEAVE_NOTIFY: + event->crossing.time = the_time; + event->crossing.state = the_state; + break; + + case GDK_PROXIMITY_IN: + case GDK_PROXIMITY_OUT: + event->proximity.time = the_time; + break; + + case GDK_DRAG_ENTER: + case GDK_DRAG_LEAVE: + case GDK_DRAG_MOTION: + case GDK_DROP_START: + event->dnd.time = the_time; + break; + + case GDK_TOUCHPAD_SWIPE: + event->touchpad_swipe.time = the_time; + event->touchpad_swipe.state = the_state; + break; + + case GDK_TOUCHPAD_PINCH: + event->touchpad_pinch.time = the_time; + event->touchpad_pinch.state = the_state; + break; + + case GDK_FOCUS_CHANGE: + case GDK_CONFIGURE: + case GDK_MAP: + case GDK_UNMAP: + case GDK_DELETE: + case GDK_DESTROY: + case GDK_EXPOSE: + default: + break; + } + + if (event_in_queue) + { + if (before_event) + _gdk_event_queue_insert_before (gdk_surface_get_display (window), event_in_queue, event); + else + _gdk_event_queue_insert_after (gdk_surface_get_display (window), event_in_queue, event); + } + else + _gdk_event_queue_append (gdk_surface_get_display (window), event); + + return event; +} + +void +_gdk_display_set_window_under_pointer (GdkDisplay *display, + GdkDevice *device, + GdkSurface *window) +{ + GdkPointerSurfaceInfo *device_info; + + device_info = _gdk_display_get_pointer_info (display, device); + + if (device_info->window_under_pointer) + g_object_unref (device_info->window_under_pointer); + device_info->window_under_pointer = window; + + if (window) + { + g_object_ref (window); + update_cursor (display, device); + } +} + +#define GDK_ANY_BUTTON_MASK (GDK_BUTTON1_MASK | \ + GDK_BUTTON2_MASK | \ + GDK_BUTTON3_MASK | \ + GDK_BUTTON4_MASK | \ + GDK_BUTTON5_MASK) + +#ifdef DEBUG_WINDOW_PRINTING + +#ifdef GDK_WINDOWING_X11 +#include "x11/gdkx.h" +#endif + +static void +gdk_surface_print (GdkSurface *window, + int indent) +{ + char *s; + const char *window_types[] = { + "root", + "toplevel", + "child", + "dialog", + "temp", + "foreign", + "subsurface" + }; + + g_print ("%*s%p: [%s] %d,%d %dx%d", indent, "", window, + window->user_data ? g_type_name_from_instance (window->user_data) : "no widget", + window->x, window->y, + window->width, window->height + ); + + if (gdk_surface_has_impl (window)) + { +#ifdef GDK_WINDOWING_X11 + g_print (" impl(0x%lx)", gdk_x11_surface_get_xid (window)); +#endif + } + + if (window->window_type != GDK_SURFACE_CHILD) + g_print (" %s", window_types[window->window_type]); + + if (window->input_only) + g_print (" input-only"); + + if (window->shaped) + g_print (" shaped"); + + if (!gdk_surface_is_visible ((GdkSurface *)window)) + g_print (" hidden"); + + g_print (" abs[%d,%d]", + window->abs_x, window->abs_y); + + if (window->alpha != 255) + g_print (" alpha[%d]", + window->alpha); + + s = print_region (window->clip_region); + g_print (" clipbox[%s]", s); + + g_print ("\n"); +} + + +static void +gdk_surface_print_tree (GdkSurface *window, + int indent, + gboolean include_input_only) +{ + GList *l; + + if (window->input_only && !include_input_only) + return; + + gdk_surface_print (window, indent); + + for (l = window->children; l != NULL; l = l->next) + gdk_surface_print_tree (l->data, indent + 4, include_input_only); +} + +#endif /* DEBUG_WINDOW_PRINTING */ + +void +_gdk_windowing_got_event (GdkDisplay *display, + GList *event_link, + GdkEvent *event, + gulong serial) +{ + GdkSurface *event_window; + gboolean unlink_event = FALSE; + GdkDeviceGrabInfo *button_release_grab; + GdkPointerSurfaceInfo *pointer_info = NULL; + GdkDevice *device, *source_device; + + _gdk_display_update_last_event (display, event); + + device = gdk_event_get_device (event); + source_device = gdk_event_get_source_device (event); + + if (device) + { + if (gdk_device_get_source (device) != GDK_SOURCE_KEYBOARD && + gdk_device_get_source (device) != GDK_SOURCE_TABLET_PAD) + { + 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; + } + + _gdk_display_device_grab_update (display, device, source_device, serial); + + if (gdk_device_get_input_mode (device) == GDK_MODE_DISABLED || + !_gdk_display_check_grab_ownership (display, device, serial)) + { + /* Device events are blocked by another + * device grab, or the device is disabled + */ + unlink_event = TRUE; + goto out; + } + } + + event_window = event->any.window; + if (!event_window) + goto out; + +#ifdef DEBUG_WINDOW_PRINTING + if (event->any.type == GDK_KEY_PRESS && + (event->key.keyval == 0xa7 || + event->key.keyval == 0xbd)) + { + gdk_surface_print_tree (event_window, 0, event->key.keyval == 0xbd); + } +#endif + + if (event_window->window_type == GDK_SURFACE_ROOT) + goto out; + + if (event->any.type == GDK_ENTER_NOTIFY) + _gdk_display_set_window_under_pointer (display, device, event_window); + else if (event->any.type == GDK_LEAVE_NOTIFY) + _gdk_display_set_window_under_pointer (display, device, NULL); + + if ((event->any.type == GDK_BUTTON_RELEASE || + event->any.type == GDK_TOUCH_CANCEL || + event->any.type == GDK_TOUCH_END) && + !event->any.send_event) + { + if (event->any.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: + if (unlink_event) + { + _gdk_event_queue_remove_link (display, event_link); + g_list_free_1 (event_link); + gdk_event_free (event); + } + + /* This does two things - first it sees if there are motions at the + * end of the queue that can be compressed. Second, if there is just + * a single motion that won't be dispatched because it is a compression + * candidate it queues up flushing the event queue. + */ + _gdk_event_queue_handle_motion_compression (display); +} + +/** + * gdk_surface_create_similar_surface: + * @window: window to make new surface similar to + * @content: the content for the new surface + * @width: width of the new surface + * @height: height of the new surface + * + * Create a new surface that is as compatible as possible with the + * given @window. For example the new surface will have the same + * fallback resolution and font options as @window. Generally, the new + * surface will also use the same backend as @window, unless that is + * not possible for some reason. The type of the returned surface may + * be examined with cairo_surface_get_type(). + * + * Initially the surface contents are all 0 (transparent if contents + * have transparency, black otherwise.) + * + * Returns: a pointer to the newly allocated surface. The caller + * owns the surface and should call cairo_surface_destroy() when done + * with it. + * + * This function always returns a valid pointer, but it will return a + * pointer to a “nil” surface if @other is already in an error state + * or any other error occurs. + **/ +cairo_surface_t * +gdk_surface_create_similar_surface (GdkSurface * window, + cairo_content_t content, + int width, + int height) +{ + cairo_surface_t *window_surface, *surface; + double sx, sy; + + g_return_val_if_fail (GDK_IS_SURFACE (window), NULL); + + window_surface = gdk_surface_ref_impl_surface (window); + sx = sy = 1; + cairo_surface_get_device_scale (window_surface, &sx, &sy); + + if (GDK_DISPLAY_DEBUG_CHECK (window->display, CAIRO_IMAGE)) + { + surface = cairo_image_surface_create (content == CAIRO_CONTENT_COLOR ? CAIRO_FORMAT_RGB24 : + content == CAIRO_CONTENT_ALPHA ? CAIRO_FORMAT_A8 : CAIRO_FORMAT_ARGB32, + width * sx, height * sy); + cairo_surface_set_device_scale (surface, sx, sy); + } + else + { + surface = cairo_surface_create_similar (window_surface, + content, + width, height); + } + + cairo_surface_destroy (window_surface); + + return surface; +} + + +/** + * gdk_surface_create_similar_image_surface: + * @window: (nullable): window to make new surface similar to, or + * %NULL if none + * @format: (type int): the format for the new surface + * @width: width of the new surface + * @height: height of the new surface + * @scale: the scale of the new surface, or 0 to use same as @window + * + * Create a new image surface that is efficient to draw on the + * given @window. + * + * Initially the surface contents are all 0 (transparent if contents + * have transparency, black otherwise.) + * + * The @width and @height of the new surface are not affected by + * the scaling factor of the @window, or by the @scale argument; they + * are the size of the surface in device pixels. If you wish to create + * an image surface capable of holding the contents of @window you can + * use: + * + * |[ + * int scale = gdk_surface_get_scale_factor (window); + * int width = gdk_surface_get_width (window) * scale; + * int height = gdk_surface_get_height (window) * scale; + * + * // format is set elsewhere + * cairo_surface_t *surface = + * gdk_surface_create_similar_image_surface (window, + * format, + * width, height, + * scale); + * ]| + * + * Note that unlike cairo_surface_create_similar_image(), the new + * surface's device scale is set to @scale, or to the scale factor of + * @window if @scale is 0. + * + * Returns: a pointer to the newly allocated surface. The caller + * owns the surface and should call cairo_surface_destroy() when done + * with it. + * + * This function always returns a valid pointer, but it will return a + * pointer to a “nil” surface if @other is already in an error state + * or any other error occurs. + **/ +cairo_surface_t * +gdk_surface_create_similar_image_surface (GdkSurface * window, + cairo_format_t format, + int width, + int height, + int scale) +{ + cairo_surface_t *surface; + + g_return_val_if_fail (window == NULL || GDK_IS_SURFACE (window), NULL); + + if (window == NULL) + { + surface = cairo_image_surface_create (format, width, height); + } + else if (GDK_SURFACE_IMPL_GET_CLASS (window->impl)->create_similar_image_surface) + { + surface = + GDK_SURFACE_IMPL_GET_CLASS (window->impl)->create_similar_image_surface (window, format, width, height); + } + else + { + cairo_surface_t *window_surface; + + window_surface = gdk_surface_ref_impl_surface (window); + surface = + cairo_surface_create_similar_image (window_surface, + format, + width, + height); + cairo_surface_destroy (window_surface); + } + + if (scale == 0) + scale = gdk_surface_get_scale_factor (window); + + cairo_surface_set_device_scale (surface, scale, scale); + + return surface; +} + + +/** + * gdk_surface_focus: + * @window: a #GdkSurface + * @timestamp: timestamp of the event triggering the window focus + * + * Sets keyboard focus to @window. In most cases, gtk_window_present() + * should be used on a #GtkWindow, rather than calling this function. + * + **/ +void +gdk_surface_focus (GdkSurface *window, + guint32 timestamp) +{ + GDK_SURFACE_IMPL_GET_CLASS (window->impl)->focus (window, timestamp); +} + +/** + * gdk_surface_set_type_hint: + * @window: A toplevel #GdkSurface + * @hint: A hint of the function this window will have + * + * The application can use this call to provide a hint to the window + * manager about the functionality of a window. The window manager + * can use this information when determining the decoration and behaviour + * of the window. + * + * The hint must be set before the window is mapped. + **/ +void +gdk_surface_set_type_hint (GdkSurface *window, + GdkSurfaceTypeHint hint) +{ + GDK_SURFACE_IMPL_GET_CLASS (window->impl)->set_type_hint (window, hint); +} + +/** + * gdk_surface_get_type_hint: + * @window: A toplevel #GdkSurface + * + * This function returns the type hint set for a window. + * + * Returns: The type hint set for @window + **/ +GdkSurfaceTypeHint +gdk_surface_get_type_hint (GdkSurface *window) +{ + return GDK_SURFACE_IMPL_GET_CLASS (window->impl)->get_type_hint (window); +} + +/** + * gdk_surface_set_modal_hint: + * @window: A toplevel #GdkSurface + * @modal: %TRUE if the window is modal, %FALSE otherwise. + * + * The application can use this hint to tell the window manager + * that a certain window has modal behaviour. The window manager + * can use this information to handle modal windows in a special + * way. + * + * You should only use this on windows for which you have + * previously called gdk_surface_set_transient_for() + **/ +void +gdk_surface_set_modal_hint (GdkSurface *window, + gboolean modal) +{ + GDK_SURFACE_IMPL_GET_CLASS (window->impl)->set_modal_hint (window, modal); +} + +/** + * gdk_surface_set_skip_taskbar_hint: + * @window: a toplevel #GdkSurface + * @skips_taskbar: %TRUE to skip the taskbar + * + * Toggles whether a window should appear in a task list or window + * list. If a window’s semantic type as specified with + * gdk_surface_set_type_hint() already fully describes the window, this + * function should not be called in addition, + * instead you should allow the window to be treated according to + * standard policy for its semantic type. + **/ +void +gdk_surface_set_skip_taskbar_hint (GdkSurface *window, + gboolean skips_taskbar) +{ + GDK_SURFACE_IMPL_GET_CLASS (window->impl)->set_skip_taskbar_hint (window, skips_taskbar); +} + +/** + * gdk_surface_set_skip_pager_hint: + * @window: a toplevel #GdkSurface + * @skips_pager: %TRUE to skip the pager + * + * Toggles whether a window should appear in a pager (workspace + * switcher, or other desktop utility program that displays a small + * thumbnail representation of the windows on the desktop). If a + * window’s semantic type as specified with gdk_surface_set_type_hint() + * already fully describes the window, this function should + * not be called in addition, instead you should + * allow the window to be treated according to standard policy for + * its semantic type. + **/ +void +gdk_surface_set_skip_pager_hint (GdkSurface *window, + gboolean skips_pager) +{ + GDK_SURFACE_IMPL_GET_CLASS (window->impl)->set_skip_pager_hint (window, skips_pager); +} + +/** + * gdk_surface_set_urgency_hint: + * @window: a toplevel #GdkSurface + * @urgent: %TRUE if the window is urgent + * + * Toggles whether a window needs the user's + * urgent attention. + **/ +void +gdk_surface_set_urgency_hint (GdkSurface *window, + gboolean urgent) +{ + GDK_SURFACE_IMPL_GET_CLASS (window->impl)->set_urgency_hint (window, urgent); +} + +/** + * gdk_surface_set_geometry_hints: + * @window: a toplevel #GdkSurface + * @geometry: geometry hints + * @geom_mask: bitmask indicating fields of @geometry to pay attention to + * + * Sets the geometry hints for @window. Hints flagged in @geom_mask + * are set, hints not flagged in @geom_mask are unset. + * To unset all hints, use a @geom_mask of 0 and a @geometry of %NULL. + * + * This function provides hints to the windowing system about + * acceptable sizes for a toplevel window. The purpose of + * this is to constrain user resizing, but the windowing system + * will typically (but is not required to) also constrain the + * current size of the window to the provided values and + * constrain programatic resizing via gdk_surface_resize() or + * gdk_surface_move_resize(). + * + * Note that on X11, this effect has no effect on windows + * of type %GDK_SURFACE_TEMP since these windows are not resizable + * by the user. + * + * Since you can’t count on the windowing system doing the + * constraints for programmatic resizes, you should generally + * call gdk_surface_constrain_size() yourself to determine + * appropriate sizes. + * + **/ +void +gdk_surface_set_geometry_hints (GdkSurface *window, + const GdkGeometry *geometry, + GdkSurfaceHints geom_mask) +{ + g_return_if_fail (geometry != NULL || geom_mask == 0); + + GDK_SURFACE_IMPL_GET_CLASS (window->impl)->set_geometry_hints (window, geometry, geom_mask); +} + +/** + * gdk_surface_set_title: + * @window: a toplevel #GdkSurface + * @title: title of @window + * + * Sets the title of a toplevel window, to be displayed in the titlebar. + * If you haven’t explicitly set the icon name for the window + * (using gdk_surface_set_icon_name()), the icon name will be set to + * @title as well. @title must be in UTF-8 encoding (as with all + * user-readable strings in GDK/GTK+). @title may not be %NULL. + **/ +void +gdk_surface_set_title (GdkSurface *window, + const gchar *title) +{ + GDK_SURFACE_IMPL_GET_CLASS (window->impl)->set_title (window, title); +} + +/** + * gdk_surface_set_role: + * @window: a toplevel #GdkSurface + * @role: a string indicating its role + * + * When using GTK+, typically you should use gtk_window_set_role() instead + * of this low-level function. + * + * The window manager and session manager use a window’s role to + * distinguish it from other kinds of window in the same application. + * When an application is restarted after being saved in a previous + * session, all windows with the same title and role are treated as + * interchangeable. So if you have two windows with the same title + * that should be distinguished for session management purposes, you + * should set the role on those windows. It doesn’t matter what string + * you use for the role, as long as you have a different role for each + * non-interchangeable kind of window. + * + **/ +void +gdk_surface_set_role (GdkSurface *window, + const gchar *role) +{ + GDK_SURFACE_IMPL_GET_CLASS (window->impl)->set_role (window, role); +} + +/** + * gdk_surface_set_startup_id: + * @window: a toplevel #GdkSurface + * @startup_id: a string with startup-notification identifier + * + * When using GTK+, typically you should use gtk_window_set_startup_id() + * instead of this low-level function. + **/ +void +gdk_surface_set_startup_id (GdkSurface *window, + const gchar *startup_id) +{ + GDK_SURFACE_IMPL_GET_CLASS (window->impl)->set_startup_id (window, startup_id); +} + +/** + * gdk_surface_set_transient_for: + * @window: a toplevel #GdkSurface + * @parent: another toplevel #GdkSurface + * + * Indicates to the window manager that @window is a transient dialog + * associated with the application window @parent. This allows the + * window manager to do things like center @window on @parent and + * keep @window above @parent. + * + * See gtk_window_set_transient_for() if you’re using #GtkWindow or + * #GtkDialog. + **/ +void +gdk_surface_set_transient_for (GdkSurface *window, + GdkSurface *parent) +{ + window->transient_for = parent; + + GDK_SURFACE_IMPL_GET_CLASS (window->impl)->set_transient_for (window, parent); +} + +/** + * gdk_surface_get_root_origin: + * @window: a toplevel #GdkSurface + * @x: (out): return location for X position of window frame + * @y: (out): return location for Y position of window frame + * + * Obtains the top-left corner of the window manager frame in root + * window coordinates. + * + **/ +void +gdk_surface_get_root_origin (GdkSurface *window, + gint *x, + gint *y) +{ + GdkRectangle rect; + + gdk_surface_get_frame_extents (window, &rect); + + if (x) + *x = rect.x; + + if (y) + *y = rect.y; +} + +/** + * gdk_surface_get_frame_extents: + * @window: a toplevel #GdkSurface + * @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 + * coordinates. To get the position of the window itself (rather than + * the frame) in root window coordinates, use gdk_surface_get_origin(). + * + **/ +void +gdk_surface_get_frame_extents (GdkSurface *window, + GdkRectangle *rect) +{ + GDK_SURFACE_IMPL_GET_CLASS (window->impl)->get_frame_extents (window, rect); +} + +/** + * gdk_surface_set_accept_focus: + * @window: a toplevel #GdkSurface + * @accept_focus: %TRUE if the window should receive input focus + * + * Setting @accept_focus to %FALSE hints the desktop environment that the + * window doesn’t want to receive input focus. + * + * On X, it is the responsibility of the window manager to interpret this + * hint. ICCCM-compliant window manager usually respect it. + **/ +void +gdk_surface_set_accept_focus (GdkSurface *window, + gboolean accept_focus) +{ + GDK_SURFACE_IMPL_GET_CLASS (window->impl)->set_accept_focus (window, accept_focus); +} + +/** + * gdk_surface_set_focus_on_map: + * @window: a toplevel #GdkSurface + * @focus_on_map: %TRUE if the window should receive input focus when mapped + * + * Setting @focus_on_map to %FALSE hints the desktop environment that the + * window doesn’t want to receive input focus when it is mapped. + * focus_on_map should be turned off for windows that aren’t triggered + * interactively (such as popups from network activity). + * + * On X, it is the responsibility of the window manager to interpret + * this hint. Window managers following the freedesktop.org window + * manager extension specification should respect it. + **/ +void +gdk_surface_set_focus_on_map (GdkSurface *window, + gboolean focus_on_map) +{ + GDK_SURFACE_IMPL_GET_CLASS (window->impl)->set_focus_on_map (window, focus_on_map); +} + +/** + * gdk_surface_set_icon_list: + * @window: The #GdkSurface toplevel window to set the icon of. + * @surfaces: (transfer none) (element-type GdkTexture): + * A list of image surfaces, of different sizes. + * + * Sets a list of icons for the window. One of these will be used + * to represent the window when it has been iconified. The icon is + * usually shown in an icon box or some sort of task bar. Which icon + * size is shown depends on the window manager. The window manager + * can scale the icon but setting several size icons can give better + * image quality since the window manager may only need to scale the + * icon by a small amount or not at all. + * + * Note that some platforms don't support window icons. + */ +void +gdk_surface_set_icon_list (GdkSurface *window, + GList *textures) +{ + GDK_SURFACE_IMPL_GET_CLASS (window->impl)->set_icon_list (window, textures); +} + +/** + * gdk_surface_set_icon_name: + * @window: a toplevel #GdkSurface + * @name: (allow-none): name of window while iconified (minimized) + * + * Windows may have a name used while minimized, distinct from the + * name they display in their titlebar. Most of the time this is a bad + * idea from a user interface standpoint. But you can set such a name + * with this function, if you like. + * + * After calling this with a non-%NULL @name, calls to gdk_surface_set_title() + * will not update the icon title. + * + * Using %NULL for @name unsets the icon title; further calls to + * gdk_surface_set_title() will again update the icon title as well. + * + * Note that some platforms don't support window icons. + **/ +void +gdk_surface_set_icon_name (GdkSurface *window, + const gchar *name) +{ + GDK_SURFACE_IMPL_GET_CLASS (window->impl)->set_icon_name (window, name); +} + +/** + * gdk_surface_iconify: + * @window: a toplevel #GdkSurface + * + * Asks to iconify (minimize) @window. The window manager may choose + * to ignore the request, but normally will honor it. Using + * gtk_window_iconify() is preferred, if you have a #GtkWindow widget. + * + * This function only makes sense when @window is a toplevel window. + * + **/ +void +gdk_surface_iconify (GdkSurface *window) +{ + GDK_SURFACE_IMPL_GET_CLASS (window->impl)->iconify (window); +} + +/** + * gdk_surface_deiconify: + * @window: a toplevel #GdkSurface + * + * Attempt to deiconify (unminimize) @window. On X11 the window manager may + * choose to ignore the request to deiconify. When using GTK+, + * use gtk_window_deiconify() instead of the #GdkSurface variant. Or better yet, + * you probably want to use gtk_window_present(), which raises the window, focuses it, + * unminimizes it, and puts it on the current desktop. + * + **/ +void +gdk_surface_deiconify (GdkSurface *window) +{ + GDK_SURFACE_IMPL_GET_CLASS (window->impl)->deiconify (window); +} + +/** + * gdk_surface_stick: + * @window: a toplevel #GdkSurface + * + * “Pins” a window such that it’s on all workspaces and does not scroll + * with viewports, for window managers that have scrollable viewports. + * (When using #GtkWindow, gtk_window_stick() may be more useful.) + * + * On the X11 platform, this function depends on window manager + * support, so may have no effect with many window managers. However, + * GDK will do the best it can to convince the window manager to stick + * the window. For window managers that don’t support this operation, + * there’s nothing you can do to force it to happen. + * + **/ +void +gdk_surface_stick (GdkSurface *window) +{ + GDK_SURFACE_IMPL_GET_CLASS (window->impl)->stick (window); +} + +/** + * gdk_surface_unstick: + * @window: a toplevel #GdkSurface + * + * Reverse operation for gdk_surface_stick(); see gdk_surface_stick(), + * and gtk_window_unstick(). + * + **/ +void +gdk_surface_unstick (GdkSurface *window) +{ + GDK_SURFACE_IMPL_GET_CLASS (window->impl)->unstick (window); +} + +/** + * gdk_surface_maximize: + * @window: a toplevel #GdkSurface + * + * Maximizes the window. If the window was already maximized, then + * this function does nothing. + * + * On X11, asks the window manager to maximize @window, if the window + * manager supports this operation. Not all window managers support + * this, and some deliberately ignore it or don’t have a concept of + * “maximized”; so you can’t rely on the maximization actually + * happening. But it will happen with most standard window managers, + * and GDK makes a best effort to get it to happen. + * + * On Windows, reliably maximizes the window. + * + **/ +void +gdk_surface_maximize (GdkSurface *window) +{ + GDK_SURFACE_IMPL_GET_CLASS (window->impl)->maximize (window); +} + +/** + * gdk_surface_unmaximize: + * @window: a toplevel #GdkSurface + * + * Unmaximizes the window. If the window wasn’t maximized, then this + * function does nothing. + * + * On X11, asks the window manager to unmaximize @window, if the + * window manager supports this operation. Not all window managers + * support this, and some deliberately ignore it or don’t have a + * concept of “maximized”; so you can’t rely on the unmaximization + * actually happening. But it will happen with most standard window + * managers, and GDK makes a best effort to get it to happen. + * + * On Windows, reliably unmaximizes the window. + * + **/ +void +gdk_surface_unmaximize (GdkSurface *window) +{ + GDK_SURFACE_IMPL_GET_CLASS (window->impl)->unmaximize (window); +} + +/** + * gdk_surface_fullscreen: + * @window: a toplevel #GdkSurface + * + * Moves the window into fullscreen mode. This means the + * window covers the entire screen and is above any panels + * or task bars. + * + * If the window was already fullscreen, then this function does nothing. + * + * On X11, asks the window manager to put @window in a fullscreen + * state, if the window manager supports this operation. Not all + * window managers support this, and some deliberately ignore it or + * don’t have a concept of “fullscreen”; so you can’t rely on the + * fullscreenification actually happening. But it will happen with + * most standard window managers, and GDK makes a best effort to get + * it to happen. + **/ +void +gdk_surface_fullscreen (GdkSurface *window) +{ + GDK_SURFACE_IMPL_GET_CLASS (window->impl)->fullscreen (window); +} + +/** + * gdk_surface_fullscreen_on_monitor: + * @window: a toplevel #GdkSurface + * @monitor: Which monitor to display fullscreen on. + * + * Moves the window into fullscreen mode on the given monitor. This means + * the window covers the entire screen and is above any panels or task bars. + * + * If the window was already fullscreen, then this function does nothing. + **/ +void +gdk_surface_fullscreen_on_monitor (GdkSurface *window, + GdkMonitor *monitor) +{ + g_return_if_fail (GDK_IS_SURFACE (window)); + g_return_if_fail (GDK_IS_MONITOR (monitor)); + g_return_if_fail (gdk_monitor_get_display (monitor) == gdk_surface_get_display (window)); + g_return_if_fail (gdk_monitor_is_valid (monitor)); + + if (GDK_SURFACE_IMPL_GET_CLASS (window->impl)->fullscreen_on_monitor != NULL) + GDK_SURFACE_IMPL_GET_CLASS (window->impl)->fullscreen_on_monitor (window, monitor); + else + GDK_SURFACE_IMPL_GET_CLASS (window->impl)->fullscreen (window); +} + +/** + * gdk_surface_set_fullscreen_mode: + * @window: a toplevel #GdkSurface + * @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 of the display. + * + * On X11, searches through the list of monitors display the ones + * which delimit the 4 edges of the entire display 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. + **/ +void +gdk_surface_set_fullscreen_mode (GdkSurface *window, + GdkFullscreenMode mode) +{ + GdkSurfaceImplClass *impl_class; + + g_return_if_fail (GDK_IS_SURFACE (window)); + + if (window->fullscreen_mode != mode) + { + window->fullscreen_mode = mode; + + impl_class = GDK_SURFACE_IMPL_GET_CLASS (window->impl); + if (impl_class->apply_fullscreen_mode != NULL) + impl_class->apply_fullscreen_mode (window); + } +} + +/** + * gdk_surface_get_fullscreen_mode: + * @window: a toplevel #GdkSurface + * + * Obtains the #GdkFullscreenMode of the @window. + * + * Returns: The #GdkFullscreenMode applied to the window when fullscreen. + **/ +GdkFullscreenMode +gdk_surface_get_fullscreen_mode (GdkSurface *window) +{ + g_return_val_if_fail (GDK_IS_SURFACE (window), GDK_FULLSCREEN_ON_CURRENT_MONITOR); + + return window->fullscreen_mode; +} + +/** + * gdk_surface_unfullscreen: + * @window: a toplevel #GdkSurface + * + * Moves the window out of fullscreen mode. If the window was not + * fullscreen, does nothing. + * + * On X11, asks the window manager to move @window out of the fullscreen + * state, if the window manager supports this operation. Not all + * window managers support this, and some deliberately ignore it or + * don’t have a concept of “fullscreen”; so you can’t rely on the + * unfullscreenification actually happening. But it will happen with + * most standard window managers, and GDK makes a best effort to get + * it to happen. + **/ +void +gdk_surface_unfullscreen (GdkSurface *window) +{ + GDK_SURFACE_IMPL_GET_CLASS (window->impl)->unfullscreen (window); +} + +/** + * gdk_surface_set_keep_above: + * @window: a toplevel #GdkSurface + * @setting: whether to keep @window above other windows + * + * Set if @window must be kept above other windows. If the + * window was already above, then this function does nothing. + * + * On X11, asks the window manager to keep @window above, if the window + * manager supports this operation. Not all window managers support + * this, and some deliberately ignore it or don’t have a concept of + * “keep above”; so you can’t rely on the window being kept above. + * But it will happen with most standard window managers, + * and GDK makes a best effort to get it to happen. + **/ +void +gdk_surface_set_keep_above (GdkSurface *window, + gboolean setting) +{ + GDK_SURFACE_IMPL_GET_CLASS (window->impl)->set_keep_above (window, setting); +} + +/** + * gdk_surface_set_keep_below: + * @window: a toplevel #GdkSurface + * @setting: whether to keep @window below other windows + * + * Set if @window must be kept below other windows. If the + * window was already below, then this function does nothing. + * + * On X11, asks the window manager to keep @window below, if the window + * manager supports this operation. Not all window managers support + * this, and some deliberately ignore it or don’t have a concept of + * “keep below”; so you can’t rely on the window being kept below. + * But it will happen with most standard window managers, + * and GDK makes a best effort to get it to happen. + **/ +void +gdk_surface_set_keep_below (GdkSurface *window, gboolean setting) +{ + GDK_SURFACE_IMPL_GET_CLASS (window->impl)->set_keep_below (window, setting); +} + +/** + * gdk_surface_get_group: + * @window: a toplevel #GdkSurface + * + * Returns the group leader window for @window. See gdk_surface_set_group(). + * + * Returns: (transfer none): the group leader window for @window + **/ +GdkSurface * +gdk_surface_get_group (GdkSurface *window) +{ + return GDK_SURFACE_IMPL_GET_CLASS (window->impl)->get_group (window); +} + +/** + * gdk_surface_set_group: + * @window: a toplevel #GdkSurface + * @leader: (allow-none): group leader window, or %NULL to restore the default group leader window + * + * Sets the group leader window for @window. By default, + * GDK sets the group leader for all toplevel windows + * to a global window implicitly created by GDK. With this function + * you can override this default. + * + * The group leader window allows the window manager to distinguish + * all windows that belong to a single application. It may for example + * allow users to minimize/unminimize all windows belonging to an + * application at once. You should only set a non-default group window + * if your application pretends to be multiple applications. + **/ +void +gdk_surface_set_group (GdkSurface *window, + GdkSurface *leader) +{ + GDK_SURFACE_IMPL_GET_CLASS (window->impl)->set_group (window, leader); +} + +/** + * gdk_surface_set_decorations: + * @window: a toplevel #GdkSurface + * @decorations: decoration hint mask + * + * “Decorations” are the features the window manager adds to a toplevel #GdkSurface. + * This function sets the traditional Motif window manager hints that tell the + * window manager which decorations you would like your window to have. + * Usually you should use gtk_window_set_decorated() on a #GtkWindow instead of + * using the GDK function directly. + * + * The @decorations argument is the logical OR of the fields in + * the #GdkWMDecoration enumeration. If #GDK_DECOR_ALL is included in the + * mask, the other bits indicate which decorations should be turned off. + * If #GDK_DECOR_ALL is not included, then the other bits indicate + * which decorations should be turned on. + * + * Most window managers honor a decorations hint of 0 to disable all decorations, + * but very few honor all possible combinations of bits. + * + **/ +void +gdk_surface_set_decorations (GdkSurface *window, + GdkWMDecoration decorations) +{ + GDK_SURFACE_IMPL_GET_CLASS (window->impl)->set_decorations (window, decorations); +} + +/** + * gdk_surface_get_decorations: + * @window: The toplevel #GdkSurface to get the decorations from + * @decorations: (out): The window decorations will be written here + * + * Returns the decorations set on the GdkSurface with + * gdk_surface_set_decorations(). + * + * Returns: %TRUE if the window has decorations set, %FALSE otherwise. + **/ +gboolean +gdk_surface_get_decorations(GdkSurface *window, + GdkWMDecoration *decorations) +{ + return GDK_SURFACE_IMPL_GET_CLASS (window->impl)->get_decorations (window, decorations); +} + +/** + * gdk_surface_set_functions: + * @window: a toplevel #GdkSurface + * @functions: bitmask of operations to allow on @window + * + * Sets hints about the window management functions to make available + * via buttons on the window frame. + * + * On the X backend, this function sets the traditional Motif window + * manager hint for this purpose. However, few window managers do + * anything reliable or interesting with this hint. Many ignore it + * entirely. + * + * The @functions argument is the logical OR of values from the + * #GdkWMFunction enumeration. If the bitmask includes #GDK_FUNC_ALL, + * then the other bits indicate which functions to disable; if + * it doesn’t include #GDK_FUNC_ALL, it indicates which functions to + * enable. + * + **/ +void +gdk_surface_set_functions (GdkSurface *window, + GdkWMFunction functions) +{ + GDK_SURFACE_IMPL_GET_CLASS (window->impl)->set_functions (window, functions); +} + +/** + * gdk_surface_begin_resize_drag_for_device: + * @window: a toplevel #GdkSurface + * @edge: the edge or corner from which the drag is started + * @device: the device used for the operation + * @button: the button being used to drag, or 0 for a keyboard-initiated drag + * @root_x: root window X coordinate of mouse click that began the drag + * @root_y: root window Y coordinate of mouse click that began the drag + * @timestamp: timestamp of mouse click that began the drag (use gdk_event_get_time()) + * + * Begins a window resize operation (for a toplevel window). + * You might use this function to implement a “window resize grip,” for + * example; in fact #GtkStatusbar uses it. The function works best + * with window managers that support the + * [Extended Window Manager Hints](http://www.freedesktop.org/Standards/wm-spec) + * but has a fallback implementation for other window managers. + */ +void +gdk_surface_begin_resize_drag_for_device (GdkSurface *window, + GdkSurfaceEdge edge, + GdkDevice *device, + gint button, + gint root_x, + gint root_y, + guint32 timestamp) +{ + GDK_SURFACE_IMPL_GET_CLASS (window->impl)->begin_resize_drag (window, edge, device, button, root_x, root_y, timestamp); +} + +/** + * gdk_surface_begin_resize_drag: + * @window: a toplevel #GdkSurface + * @edge: the edge or corner from which the drag is started + * @button: the button being used to drag, or 0 for a keyboard-initiated drag + * @root_x: root window X coordinate of mouse click that began the drag + * @root_y: root window Y coordinate of mouse click that began the drag + * @timestamp: timestamp of mouse click that began the drag (use gdk_event_get_time()) + * + * Begins a window resize operation (for a toplevel window). + * + * This function assumes that the drag is controlled by the + * client pointer device, use gdk_surface_begin_resize_drag_for_device() + * to begin a drag with a different device. + */ +void +gdk_surface_begin_resize_drag (GdkSurface *window, + GdkSurfaceEdge edge, + gint button, + gint root_x, + gint root_y, + guint32 timestamp) +{ + GdkDisplay *display; + GdkDevice *device; + + display = gdk_surface_get_display (window); + device = gdk_seat_get_pointer (gdk_display_get_default_seat (display)); + gdk_surface_begin_resize_drag_for_device (window, edge, + device, button, root_x, root_y, timestamp); +} + +/** + * gdk_surface_begin_move_drag_for_device: + * @window: a toplevel #GdkSurface + * @device: the device used for the operation + * @button: the button being used to drag, or 0 for a keyboard-initiated drag + * @root_x: root window X coordinate of mouse click that began the drag + * @root_y: root window Y coordinate of mouse click that began the drag + * @timestamp: timestamp of mouse click that began the drag + * + * Begins a window move operation (for a toplevel window). + * You might use this function to implement a “window move grip,” for + * example. The function works best with window managers that support the + * [Extended Window Manager Hints](http://www.freedesktop.org/Standards/wm-spec) + * but has a fallback implementation for other window managers. + */ +void +gdk_surface_begin_move_drag_for_device (GdkSurface *window, + GdkDevice *device, + gint button, + gint root_x, + gint root_y, + guint32 timestamp) +{ + GDK_SURFACE_IMPL_GET_CLASS (window->impl)->begin_move_drag (window, + device, button, root_x, root_y, timestamp); +} + +/** + * gdk_surface_begin_move_drag: + * @window: a toplevel #GdkSurface + * @button: the button being used to drag, or 0 for a keyboard-initiated drag + * @root_x: root window X coordinate of mouse click that began the drag + * @root_y: root window Y coordinate of mouse click that began the drag + * @timestamp: timestamp of mouse click that began the drag + * + * Begins a window move operation (for a toplevel window). + * + * This function assumes that the drag is controlled by the + * client pointer device, use gdk_surface_begin_move_drag_for_device() + * to begin a drag with a different device. + */ +void +gdk_surface_begin_move_drag (GdkSurface *window, + gint button, + gint root_x, + gint root_y, + guint32 timestamp) +{ + GdkDisplay *display; + GdkDevice *device; + + display = gdk_surface_get_display (window); + device = gdk_seat_get_pointer (gdk_display_get_default_seat (display)); + gdk_surface_begin_move_drag_for_device (window, device, button, root_x, root_y, timestamp); +} + +/** + * gdk_surface_set_opacity: + * @window: a top-level or non-native #GdkSurface + * @opacity: opacity + * + * 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.) + * + * 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. On Wayland, there is no + * per-window opacity value that the compositor would apply. Instead, use + * `gdk_surface_set_opaque_region (window, NULL)` to tell the compositor + * that the entire window is (potentially) non-opaque, and draw your content + * with alpha, or use gtk_widget_set_opacity() to set an overall opacity + * for your widgets. + * + * Support for non-toplevel windows was added in 3.8. + */ +void +gdk_surface_set_opacity (GdkSurface *window, + gdouble opacity) +{ + if (opacity < 0) + opacity = 0; + else if (opacity > 1) + opacity = 1; + + window->alpha = round (opacity * 255); + + if (window->destroyed) + return; + + if (gdk_surface_has_impl (window)) + GDK_SURFACE_IMPL_GET_CLASS (window->impl)->set_opacity (window, opacity); + else + { + recompute_visible_regions (window, FALSE); + gdk_surface_invalidate_rect_full (window, NULL, TRUE); + } +} + +/* This function is called when the XWindow is really gone. + */ +void +gdk_surface_destroy_notify (GdkSurface *window) +{ + GDK_SURFACE_IMPL_GET_CLASS (window->impl)->destroy_notify (window); +} + +/** + * gdk_surface_register_dnd: + * @window: a #GdkSurface. + * + * Registers a window as a potential drop destination. + */ +void +gdk_surface_register_dnd (GdkSurface *window) +{ + GDK_SURFACE_IMPL_GET_CLASS (window->impl)->register_dnd (window); +} + +/** + * gdk_drag_begin: + * @window: the source window for this drag + * @device: the device that controls this drag + * @content: (transfer none): the offered content + * @actions: the actions supported by this drag + * @dx: the x offset to @device's position where the drag nominally started + * @dy: the y offset to @device's position where the drag nominally started + * + * Starts a drag and creates a new drag context for it. + * + * This function is called by the drag source. + * + * Returns: (transfer full) (nullable): a newly created #GdkDragContext or + * %NULL on error. + */ +GdkDragContext * +gdk_drag_begin (GdkSurface *window, + GdkDevice *device, + GdkContentProvider *content, + GdkDragAction actions, + gint dx, + gint dy) +{ + g_return_val_if_fail (GDK_IS_SURFACE (window), NULL); + g_return_val_if_fail (GDK_IS_DEVICE (device), NULL); + g_return_val_if_fail (gdk_surface_get_display (window) == gdk_device_get_display (device), NULL); + g_return_val_if_fail (GDK_IS_CONTENT_PROVIDER (content), NULL); + + return GDK_SURFACE_IMPL_GET_CLASS (window->impl)->drag_begin (window, device, content, actions, dx, dy); +} + +static void +gdk_surface_flush_events (GdkFrameClock *clock, + void *data) +{ + GdkSurface *window; + GdkDisplay *display; + + window = GDK_SURFACE (data); + + display = gdk_surface_get_display (window); + _gdk_event_queue_flush (display); + _gdk_display_pause_events (display); + + gdk_frame_clock_request_phase (clock, GDK_FRAME_CLOCK_PHASE_RESUME_EVENTS); + + window->frame_clock_events_paused = TRUE; +} + +static void +gdk_surface_resume_events (GdkFrameClock *clock, + void *data) +{ + GdkSurface *window; + GdkDisplay *display; + + window = GDK_SURFACE (data); + + display = gdk_surface_get_display (window); + _gdk_display_unpause_events (display); + + window->frame_clock_events_paused = FALSE; +} + +static void +gdk_surface_set_frame_clock (GdkSurface *window, + GdkFrameClock *clock) +{ + g_return_if_fail (GDK_IS_SURFACE (window)); + g_return_if_fail (clock == NULL || GDK_IS_FRAME_CLOCK (clock)); + g_return_if_fail (clock == NULL || gdk_surface_is_toplevel (window)); + + if (clock == window->frame_clock) + return; + + if (clock) + { + g_object_ref (clock); + g_signal_connect (G_OBJECT (clock), + "flush-events", + G_CALLBACK (gdk_surface_flush_events), + window); + g_signal_connect (G_OBJECT (clock), + "paint", + G_CALLBACK (gdk_surface_paint_on_clock), + window); + g_signal_connect (G_OBJECT (clock), + "resume-events", + G_CALLBACK (gdk_surface_resume_events), + window); + } + + if (window->frame_clock) + { + if (window->frame_clock_events_paused) + gdk_surface_resume_events (window->frame_clock, G_OBJECT (window)); + + g_signal_handlers_disconnect_by_func (G_OBJECT (window->frame_clock), + G_CALLBACK (gdk_surface_flush_events), + window); + g_signal_handlers_disconnect_by_func (G_OBJECT (window->frame_clock), + G_CALLBACK (gdk_surface_paint_on_clock), + window); + g_signal_handlers_disconnect_by_func (G_OBJECT (window->frame_clock), + G_CALLBACK (gdk_surface_resume_events), + window); + g_object_unref (window->frame_clock); + } + + window->frame_clock = clock; +} + +/** + * gdk_surface_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 unless the window is reparented to a new toplevel + * window. + * + * Returns: (transfer none): the frame clock + */ +GdkFrameClock* +gdk_surface_get_frame_clock (GdkSurface *window) +{ + GdkSurface *toplevel; + + g_return_val_if_fail (GDK_IS_SURFACE (window), NULL); + + toplevel = gdk_surface_get_toplevel (window); + + return toplevel->frame_clock; +} + +/** + * gdk_surface_get_scale_factor: + * @window: window to get scale factor for + * + * Returns the internal scale factor that maps from window coordiantes + * to the actual device pixels. On traditional systems this is 1, but + * on very high density outputs this can be a higher value (often 2). + * + * A higher value means that drawing is automatically scaled up to + * a higher resolution, so any code doing drawing will automatically look + * nicer. However, if you are supplying pixel-based data the scale + * value can be used to determine whether to use a pixel resource + * with higher resolution data. + * + * The scale of a window may change during runtime, if this happens + * a configure event will be sent to the toplevel window. + * + * Returns: the scale factor + */ +gint +gdk_surface_get_scale_factor (GdkSurface *window) +{ + GdkSurfaceImplClass *impl_class; + + g_return_val_if_fail (GDK_IS_SURFACE (window), 1); + + if (GDK_SURFACE_DESTROYED (window)) + return 1; + + impl_class = GDK_SURFACE_IMPL_GET_CLASS (window->impl); + + if (impl_class->get_scale_factor) + return impl_class->get_scale_factor (window); + + return 1; +} + +/* Returns the *real* unscaled size, which may be a fractional size + in window scale coordinates. We need this to properly handle GL + coordinates which are y-flipped in the real coordinates. */ +void +gdk_surface_get_unscaled_size (GdkSurface *window, + int *unscaled_width, + int *unscaled_height) +{ + GdkSurfaceImplClass *impl_class; + gint scale; + + g_return_if_fail (GDK_IS_SURFACE (window)); + + if (window->impl_window == window) + { + impl_class = GDK_SURFACE_IMPL_GET_CLASS (window->impl); + + if (impl_class->get_unscaled_size) + { + impl_class->get_unscaled_size (window, unscaled_width, unscaled_height); + return; + } + } + + scale = gdk_surface_get_scale_factor (window); + + if (unscaled_width) + *unscaled_width = window->width * scale; + + if (unscaled_height) + *unscaled_height = window->height * scale; +} + + +/** + * gdk_surface_set_opaque_region: + * @window: a top-level or non-native #GdkSurface + * @region: (allow-none): a region, or %NULL + * + * For optimisation purposes, compositing window managers may + * like to not draw obscured regions of windows, or turn off blending + * during for these regions. With RGB windows with no transparency, + * this is just the shape of the window, but with ARGB32 windows, the + * compositor does not know what regions of the window are transparent + * or not. + * + * This function only works for toplevel windows. + * + * GTK+ will update this property automatically if + * the @window background is opaque, as we know where the opaque regions + * are. If your window background is not opaque, please update this + * property in your #GtkWidget::style-updated handler. + */ +void +gdk_surface_set_opaque_region (GdkSurface *window, + cairo_region_t *region) +{ + GdkSurfaceImplClass *impl_class; + + g_return_if_fail (GDK_IS_SURFACE (window)); + g_return_if_fail (!GDK_SURFACE_DESTROYED (window)); + + if (cairo_region_equal (window->opaque_region, region)) + return; + + g_clear_pointer (&window->opaque_region, cairo_region_destroy); + + if (region != NULL) + window->opaque_region = cairo_region_reference (region); + + impl_class = GDK_SURFACE_IMPL_GET_CLASS (window->impl); + + if (impl_class->set_opaque_region) + impl_class->set_opaque_region (window, region); +} + +/** + * gdk_surface_set_shadow_width: + * @window: a #GdkSurface + * @left: The left extent + * @right: The right extent + * @top: The top extent + * @bottom: The bottom extent + * + * Newer GTK+ windows using client-side decorations use extra geometry + * around their frames for effects like shadows and invisible borders. + * Window managers that want to maximize windows or snap to edges need + * to know where the extents of the actual frame lie, so that users + * don’t feel like windows are snapping against random invisible edges. + * + * Note that this property is automatically updated by GTK+, so this + * function should only be used by applications which do not use GTK+ + * to create toplevel windows. + */ +void +gdk_surface_set_shadow_width (GdkSurface *window, + gint left, + gint right, + gint top, + gint bottom) +{ + GdkSurfaceImplClass *impl_class; + + g_return_if_fail (GDK_IS_SURFACE (window)); + g_return_if_fail (!GDK_SURFACE_DESTROYED (window)); + g_return_if_fail (left >= 0 && right >= 0 && top >= 0 && bottom >= 0); + + window->shadow_top = top; + window->shadow_left = left; + window->shadow_right = right; + window->shadow_bottom = bottom; + + impl_class = GDK_SURFACE_IMPL_GET_CLASS (window->impl); + + if (impl_class->set_shadow_width) + impl_class->set_shadow_width (window, left, right, top, bottom); +} + +/** + * gdk_surface_show_window_menu: + * @window: a #GdkSurface + * @event: a #GdkEvent to show the menu for + * + * Asks the windowing system to show the window menu. The window menu + * is the menu shown when right-clicking the titlebar on traditional + * windows managed by the window manager. This is useful for windows + * using client-side decorations, activating it with a right-click + * on the window decorations. + * + * Returns: %TRUE if the window menu was shown and %FALSE otherwise. + */ +gboolean +gdk_surface_show_window_menu (GdkSurface *window, + GdkEvent *event) +{ + GdkSurfaceImplClass *impl_class; + + g_return_val_if_fail (GDK_IS_SURFACE (window), FALSE); + g_return_val_if_fail (!GDK_SURFACE_DESTROYED (window), FALSE); + + impl_class = GDK_SURFACE_IMPL_GET_CLASS (window->impl); + + if (impl_class->show_window_menu) + return impl_class->show_window_menu (window, event); + else + return FALSE; +} + +gboolean +gdk_surface_supports_edge_constraints (GdkSurface *window) +{ + GdkSurfaceImplClass *impl_class; + + g_return_val_if_fail (GDK_IS_SURFACE (window), FALSE); + g_return_val_if_fail (!GDK_SURFACE_DESTROYED (window), FALSE); + + impl_class = GDK_SURFACE_IMPL_GET_CLASS (window->impl); + + if (impl_class->supports_edge_constraints) + return impl_class->supports_edge_constraints (window); + else + return FALSE; +} + +void +gdk_surface_set_state (GdkSurface *window, + GdkSurfaceState new_state) +{ + g_return_if_fail (GDK_IS_SURFACE (window)); + + if (new_state == window->state) + return; /* No actual work to do, nothing changed. */ + + /* Actually update the field in GdkSurface, this is sort of an odd + * place to do it, but seems like the safest since it ensures we expose no + * inconsistent state to the user. + */ + + window->state = new_state; + + _gdk_surface_update_viewable (window); + + /* We only really send the event to toplevels, since + * all the window states don't apply to non-toplevels. + * Non-toplevels do use the GDK_SURFACE_STATE_WITHDRAWN flag + * internally so we needed to update window->state. + */ + switch (window->window_type) + { + case GDK_SURFACE_TOPLEVEL: + case GDK_SURFACE_TEMP: /* ? */ + g_object_notify (G_OBJECT (window), "state"); + break; + case GDK_SURFACE_FOREIGN: + case GDK_SURFACE_ROOT: + case GDK_SURFACE_CHILD: + default: + break; + } +} + +void +gdk_synthesize_window_state (GdkSurface *window, + GdkSurfaceState unset_flags, + GdkSurfaceState set_flags) +{ + gdk_surface_set_state (window, (window->state | set_flags) & ~unset_flags); +} diff --git a/gdk/gdksurface.h b/gdk/gdksurface.h new file mode 100644 index 0000000000..9cc1b57030 --- /dev/null +++ b/gdk/gdksurface.h @@ -0,0 +1,933 @@ +/* GDK - The GIMP Drawing Kit + * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * 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, see . + */ + +/* + * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS + * file for a list of people on the GTK+ Team. See the ChangeLog + * files for a list of changes. These files are distributed with + * GTK+ at ftp://ftp.gtk.org/pub/gtk/. + */ + +#ifndef __GDK_SURFACE_H__ +#define __GDK_SURFACE_H__ + +#if !defined (__GDK_H_INSIDE__) && !defined (GDK_COMPILATION) +#error "Only can be included directly." +#endif + +#include +#include +#include +#include +#include +#include + +G_BEGIN_DECLS + +typedef struct _GdkGeometry GdkGeometry; + +/** + * GdkSurfaceType: + * @GDK_SURFACE_ROOT: root window; this window has no parent, covers the entire + * screen, and is created by the window system + * @GDK_SURFACE_TOPLEVEL: toplevel window (used to implement #GtkWindow) + * @GDK_SURFACE_CHILD: child window (used to implement e.g. #GtkEntry) + * @GDK_SURFACE_TEMP: override redirect temporary window (used to implement + * #GtkMenu) + * @GDK_SURFACE_FOREIGN: foreign window (see gdk_surface_foreign_new()) + * @GDK_SURFACE_SUBSURFACE: subsurface-based window; This window is visually + * tied to a toplevel, and is moved/stacked with it. Currently this window + * type is only implemented in Wayland. Since 3.14 + * + * Describes the kind of window. + */ +typedef enum +{ + GDK_SURFACE_ROOT, + GDK_SURFACE_TOPLEVEL, + GDK_SURFACE_CHILD, + GDK_SURFACE_TEMP, + GDK_SURFACE_FOREIGN, + GDK_SURFACE_SUBSURFACE +} GdkSurfaceType; + +/* Size restriction enumeration. + */ +/** + * GdkSurfaceHints: + * @GDK_HINT_POS: indicates that the program has positioned the window + * @GDK_HINT_MIN_SIZE: min size fields are set + * @GDK_HINT_MAX_SIZE: max size fields are set + * @GDK_HINT_BASE_SIZE: base size fields are set + * @GDK_HINT_ASPECT: aspect ratio fields are set + * @GDK_HINT_RESIZE_INC: resize increment fields are set + * @GDK_HINT_WIN_GRAVITY: window gravity field is set + * @GDK_HINT_USER_POS: indicates that the window’s position was explicitly set + * by the user + * @GDK_HINT_USER_SIZE: indicates that the window’s size was explicitly set by + * the user + * + * Used to indicate which fields of a #GdkGeometry struct should be paid + * attention to. Also, the presence/absence of @GDK_HINT_POS, + * @GDK_HINT_USER_POS, and @GDK_HINT_USER_SIZE is significant, though they don't + * directly refer to #GdkGeometry fields. @GDK_HINT_USER_POS will be set + * automatically by #GtkWindow if you call gtk_window_move(). + * @GDK_HINT_USER_POS and @GDK_HINT_USER_SIZE should be set if the user + * specified a size/position using a --geometry command-line argument; + * gtk_window_parse_geometry() automatically sets these flags. + */ +typedef enum +{ + GDK_HINT_POS = 1 << 0, + GDK_HINT_MIN_SIZE = 1 << 1, + GDK_HINT_MAX_SIZE = 1 << 2, + GDK_HINT_BASE_SIZE = 1 << 3, + GDK_HINT_ASPECT = 1 << 4, + GDK_HINT_RESIZE_INC = 1 << 5, + GDK_HINT_WIN_GRAVITY = 1 << 6, + GDK_HINT_USER_POS = 1 << 7, + GDK_HINT_USER_SIZE = 1 << 8 +} GdkSurfaceHints; + +/* The next two enumeration values current match the + * Motif constants. If this is changed, the implementation + * of gdk_surface_set_decorations/gdk_surface_set_functions + * will need to change as well. + */ +/** + * GdkWMDecoration: + * @GDK_DECOR_ALL: all decorations should be applied. + * @GDK_DECOR_BORDER: a frame should be drawn around the window. + * @GDK_DECOR_RESIZEH: the frame should have resize handles. + * @GDK_DECOR_TITLE: a titlebar should be placed above the window. + * @GDK_DECOR_MENU: a button for opening a menu should be included. + * @GDK_DECOR_MINIMIZE: a minimize button should be included. + * @GDK_DECOR_MAXIMIZE: a maximize button should be included. + * + * These are hints originally defined by the Motif toolkit. + * The window manager can use them when determining how to decorate + * the window. The hint must be set before mapping the window. + */ +typedef enum +{ + GDK_DECOR_ALL = 1 << 0, + GDK_DECOR_BORDER = 1 << 1, + GDK_DECOR_RESIZEH = 1 << 2, + GDK_DECOR_TITLE = 1 << 3, + GDK_DECOR_MENU = 1 << 4, + GDK_DECOR_MINIMIZE = 1 << 5, + GDK_DECOR_MAXIMIZE = 1 << 6 +} GdkWMDecoration; + +/** + * GdkWMFunction: + * @GDK_FUNC_ALL: all functions should be offered. + * @GDK_FUNC_RESIZE: the window should be resizable. + * @GDK_FUNC_MOVE: the window should be movable. + * @GDK_FUNC_MINIMIZE: the window should be minimizable. + * @GDK_FUNC_MAXIMIZE: the window should be maximizable. + * @GDK_FUNC_CLOSE: the window should be closable. + * + * These are hints originally defined by the Motif toolkit. The window manager + * can use them when determining the functions to offer for the window. The + * hint must be set before mapping the window. + */ +typedef enum +{ + GDK_FUNC_ALL = 1 << 0, + GDK_FUNC_RESIZE = 1 << 1, + GDK_FUNC_MOVE = 1 << 2, + GDK_FUNC_MINIMIZE = 1 << 3, + GDK_FUNC_MAXIMIZE = 1 << 4, + GDK_FUNC_CLOSE = 1 << 5 +} GdkWMFunction; + +/* Currently, these are the same values numerically as in the + * X protocol. If you change that, gdksurface-x11.c/gdk_surface_set_geometry_hints() + * will need fixing. + */ +/** + * GdkGravity: + * @GDK_GRAVITY_NORTH_WEST: the reference point is at the top left corner. + * @GDK_GRAVITY_NORTH: the reference point is in the middle of the top edge. + * @GDK_GRAVITY_NORTH_EAST: the reference point is at the top right corner. + * @GDK_GRAVITY_WEST: the reference point is at the middle of the left edge. + * @GDK_GRAVITY_CENTER: the reference point is at the center of the window. + * @GDK_GRAVITY_EAST: the reference point is at the middle of the right edge. + * @GDK_GRAVITY_SOUTH_WEST: the reference point is at the lower left corner. + * @GDK_GRAVITY_SOUTH: the reference point is at the middle of the lower edge. + * @GDK_GRAVITY_SOUTH_EAST: the reference point is at the lower right corner. + * @GDK_GRAVITY_STATIC: the reference point is at the top left corner of the + * window itself, ignoring window manager decorations. + * + * Defines the reference point of a window and the meaning of coordinates + * passed to gtk_window_move(). See gtk_window_move() and the "implementation + * notes" section of the + * [Extended Window Manager Hints](http://www.freedesktop.org/Standards/wm-spec) + * specification for more details. + */ +typedef enum +{ + GDK_GRAVITY_NORTH_WEST = 1, + GDK_GRAVITY_NORTH, + GDK_GRAVITY_NORTH_EAST, + GDK_GRAVITY_WEST, + GDK_GRAVITY_CENTER, + GDK_GRAVITY_EAST, + GDK_GRAVITY_SOUTH_WEST, + GDK_GRAVITY_SOUTH, + GDK_GRAVITY_SOUTH_EAST, + GDK_GRAVITY_STATIC +} GdkGravity; + +/** + * GdkAnchorHints: + * @GDK_ANCHOR_FLIP_X: allow flipping anchors horizontally + * @GDK_ANCHOR_FLIP_Y: allow flipping anchors vertically + * @GDK_ANCHOR_SLIDE_X: allow sliding window horizontally + * @GDK_ANCHOR_SLIDE_Y: allow sliding window vertically + * @GDK_ANCHOR_RESIZE_X: allow resizing window horizontally + * @GDK_ANCHOR_RESIZE_Y: allow resizing window vertically + * @GDK_ANCHOR_FLIP: allow flipping anchors on both axes + * @GDK_ANCHOR_SLIDE: allow sliding window on both axes + * @GDK_ANCHOR_RESIZE: allow resizing window on both axes + * + * Positioning hints for aligning a window relative to a rectangle. + * + * These hints determine how the window should be positioned in the case that + * the window would fall off-screen if placed in its ideal position. + * + * For example, %GDK_ANCHOR_FLIP_X will replace %GDK_GRAVITY_NORTH_WEST with + * %GDK_GRAVITY_NORTH_EAST and vice versa if the window extends beyond the left + * or right edges of the monitor. + * + * If %GDK_ANCHOR_SLIDE_X is set, the window can be shifted horizontally to fit + * on-screen. If %GDK_ANCHOR_RESIZE_X is set, the window can be shrunken + * horizontally to fit. + * + * In general, when multiple flags are set, flipping should take precedence over + * sliding, which should take precedence over resizing. + * + * Since: 3.22 + * Stability: Unstable + */ +typedef enum +{ + GDK_ANCHOR_FLIP_X = 1 << 0, + GDK_ANCHOR_FLIP_Y = 1 << 1, + GDK_ANCHOR_SLIDE_X = 1 << 2, + GDK_ANCHOR_SLIDE_Y = 1 << 3, + GDK_ANCHOR_RESIZE_X = 1 << 4, + GDK_ANCHOR_RESIZE_Y = 1 << 5, + GDK_ANCHOR_FLIP = GDK_ANCHOR_FLIP_X | GDK_ANCHOR_FLIP_Y, + GDK_ANCHOR_SLIDE = GDK_ANCHOR_SLIDE_X | GDK_ANCHOR_SLIDE_Y, + GDK_ANCHOR_RESIZE = GDK_ANCHOR_RESIZE_X | GDK_ANCHOR_RESIZE_Y +} GdkAnchorHints; + +/** + * GdkSurfaceEdge: + * @GDK_SURFACE_EDGE_NORTH_WEST: the top left corner. + * @GDK_SURFACE_EDGE_NORTH: the top edge. + * @GDK_SURFACE_EDGE_NORTH_EAST: the top right corner. + * @GDK_SURFACE_EDGE_WEST: the left edge. + * @GDK_SURFACE_EDGE_EAST: the right edge. + * @GDK_SURFACE_EDGE_SOUTH_WEST: the lower left corner. + * @GDK_SURFACE_EDGE_SOUTH: the lower edge. + * @GDK_SURFACE_EDGE_SOUTH_EAST: the lower right corner. + * + * Determines a window edge or corner. + */ +typedef enum +{ + GDK_SURFACE_EDGE_NORTH_WEST, + GDK_SURFACE_EDGE_NORTH, + GDK_SURFACE_EDGE_NORTH_EAST, + GDK_SURFACE_EDGE_WEST, + GDK_SURFACE_EDGE_EAST, + GDK_SURFACE_EDGE_SOUTH_WEST, + GDK_SURFACE_EDGE_SOUTH, + GDK_SURFACE_EDGE_SOUTH_EAST +} GdkSurfaceEdge; + +/** + * GdkFullscreenMode: + * @GDK_FULLSCREEN_ON_CURRENT_MONITOR: Fullscreen on current monitor only. + * @GDK_FULLSCREEN_ON_ALL_MONITORS: Span across all monitors when fullscreen. + * + * Indicates which monitor (in a multi-head setup) a window should span over + * when in fullscreen mode. + * + * Since: 3.8 + **/ +typedef enum +{ + GDK_FULLSCREEN_ON_CURRENT_MONITOR, + GDK_FULLSCREEN_ON_ALL_MONITORS +} GdkFullscreenMode; + +/** + * GdkGeometry: + * @min_width: minimum width of window (or -1 to use requisition, with + * #GtkWindow only) + * @min_height: minimum height of window (or -1 to use requisition, with + * #GtkWindow only) + * @max_width: maximum width of window (or -1 to use requisition, with + * #GtkWindow only) + * @max_height: maximum height of window (or -1 to use requisition, with + * #GtkWindow only) + * @base_width: allowed window widths are @base_width + @width_inc * N where N + * is any integer (-1 allowed with #GtkWindow) + * @base_height: allowed window widths are @base_height + @height_inc * N where + * N is any integer (-1 allowed with #GtkWindow) + * @width_inc: width resize increment + * @height_inc: height resize increment + * @min_aspect: minimum width/height ratio + * @max_aspect: maximum width/height ratio + * @win_gravity: window gravity, see gtk_window_set_gravity() + * + * The #GdkGeometry struct gives the window manager information about + * a window’s geometry constraints. Normally you would set these on + * the GTK+ level using gtk_window_set_geometry_hints(). #GtkWindow + * then sets the hints on the #GdkSurface it creates. + * + * gdk_surface_set_geometry_hints() expects the hints to be fully valid already + * and simply passes them to the window manager; in contrast, + * gtk_window_set_geometry_hints() performs some interpretation. For example, + * #GtkWindow will apply the hints to the geometry widget instead of the + * toplevel window, if you set a geometry widget. Also, the + * @min_width/@min_height/@max_width/@max_height fields may be set to -1, and + * #GtkWindow will substitute the size request of the window or geometry widget. + * If the minimum size hint is not provided, #GtkWindow will use its requisition + * as the minimum size. If the minimum size is provided and a geometry widget is + * set, #GtkWindow will take the minimum size as the minimum size of the + * geometry widget rather than the entire window. The base size is treated + * similarly. + * + * The canonical use-case for gtk_window_set_geometry_hints() is to get a + * terminal widget to resize properly. Here, the terminal text area should be + * the geometry widget; #GtkWindow will then automatically set the base size to + * the size of other widgets in the terminal window, such as the menubar and + * scrollbar. Then, the @width_inc and @height_inc fields should be set to the + * size of one character in the terminal. Finally, the base size should be set + * to the size of one character. The net effect is that the minimum size of the + * terminal will have a 1x1 character terminal area, and only terminal sizes on + * the “character grid” will be allowed. + * + * Here’s an example of how the terminal example would be implemented, assuming + * a terminal area widget called “terminal” and a toplevel window “toplevel”: + * + * |[ + * GdkGeometry hints; + * + * hints.base_width = terminal->char_width; + * hints.base_height = terminal->char_height; + * hints.min_width = terminal->char_width; + * hints.min_height = terminal->char_height; + * hints.width_inc = terminal->char_width; + * hints.height_inc = terminal->char_height; + * + * gtk_window_set_geometry_hints (GTK_WINDOW (toplevel), + * GTK_WIDGET (terminal), + * &hints, + * GDK_HINT_RESIZE_INC | + * GDK_HINT_MIN_SIZE | + * GDK_HINT_BASE_SIZE); + * ]| + * + * The other useful fields are the @min_aspect and @max_aspect fields; these + * contain a width/height ratio as a floating point number. If a geometry widget + * is set, the aspect applies to the geometry widget rather than the entire + * window. The most common use of these hints is probably to set @min_aspect and + * @max_aspect to the same value, thus forcing the window to keep a constant + * aspect ratio. + */ +struct _GdkGeometry +{ + gint min_width; + gint min_height; + gint max_width; + gint max_height; + gint base_width; + gint base_height; + gint width_inc; + gint height_inc; + gdouble min_aspect; + gdouble max_aspect; + GdkGravity win_gravity; +}; + +/** + * GdkSurfaceState: + * @GDK_SURFACE_STATE_WITHDRAWN: the window is not shown. + * @GDK_SURFACE_STATE_ICONIFIED: the window is minimized. + * @GDK_SURFACE_STATE_MAXIMIZED: the window is maximized. + * @GDK_SURFACE_STATE_STICKY: the window is sticky. + * @GDK_SURFACE_STATE_FULLSCREEN: the window is maximized without + * decorations. + * @GDK_SURFACE_STATE_ABOVE: the window is kept above other windows. + * @GDK_SURFACE_STATE_BELOW: the window is kept below other windows. + * @GDK_SURFACE_STATE_FOCUSED: the window is presented as focused (with active decorations). + * @GDK_SURFACE_STATE_TILED: the window is in a tiled state, Since 3.10. Since 3.91.2, this + * is deprecated in favor of per-edge information. + * @GDK_SURFACE_STATE_TOP_TILED: whether the top edge is tiled, Since 3.91.2 + * @GDK_SURFACE_STATE_TOP_RESIZABLE: whether the top edge is resizable, Since 3.91.2 + * @GDK_SURFACE_STATE_RIGHT_TILED: whether the right edge is tiled, Since 3.91.2 + * @GDK_SURFACE_STATE_RIGHT_RESIZABLE: whether the right edge is resizable, Since 3.91.2 + * @GDK_SURFACE_STATE_BOTTOM_TILED: whether the bottom edge is tiled, Since 3.91.2 + * @GDK_SURFACE_STATE_BOTTOM_RESIZABLE: whether the bottom edge is resizable, Since 3.91.2 + * @GDK_SURFACE_STATE_LEFT_TILED: whether the left edge is tiled, Since 3.91.2 + * @GDK_SURFACE_STATE_LEFT_RESIZABLE: whether the left edge is resizable, Since 3.91.2 + * + * Specifies the state of a toplevel window. + */ +typedef enum +{ + GDK_SURFACE_STATE_WITHDRAWN = 1 << 0, + GDK_SURFACE_STATE_ICONIFIED = 1 << 1, + GDK_SURFACE_STATE_MAXIMIZED = 1 << 2, + GDK_SURFACE_STATE_STICKY = 1 << 3, + GDK_SURFACE_STATE_FULLSCREEN = 1 << 4, + GDK_SURFACE_STATE_ABOVE = 1 << 5, + GDK_SURFACE_STATE_BELOW = 1 << 6, + GDK_SURFACE_STATE_FOCUSED = 1 << 7, + GDK_SURFACE_STATE_TILED = 1 << 8, + GDK_SURFACE_STATE_TOP_TILED = 1 << 9, + GDK_SURFACE_STATE_TOP_RESIZABLE = 1 << 10, + GDK_SURFACE_STATE_RIGHT_TILED = 1 << 11, + GDK_SURFACE_STATE_RIGHT_RESIZABLE = 1 << 12, + GDK_SURFACE_STATE_BOTTOM_TILED = 1 << 13, + GDK_SURFACE_STATE_BOTTOM_RESIZABLE = 1 << 14, + GDK_SURFACE_STATE_LEFT_TILED = 1 << 15, + GDK_SURFACE_STATE_LEFT_RESIZABLE = 1 << 16 +} GdkSurfaceState; + + +typedef struct _GdkSurfaceClass GdkSurfaceClass; + +#define GDK_TYPE_SURFACE (gdk_surface_get_type ()) +#define GDK_SURFACE(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), GDK_TYPE_SURFACE, GdkSurface)) +#define GDK_SURFACE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GDK_TYPE_SURFACE, GdkSurfaceClass)) +#define GDK_IS_SURFACE(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), GDK_TYPE_SURFACE)) +#define GDK_IS_SURFACE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GDK_TYPE_SURFACE)) +#define GDK_SURFACE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GDK_TYPE_SURFACE, GdkSurfaceClass)) + + +struct _GdkSurfaceClass +{ + GObjectClass parent_class; + + /* Padding for future expansion */ + void (*_gdk_reserved1) (void); + void (*_gdk_reserved2) (void); + void (*_gdk_reserved3) (void); + void (*_gdk_reserved4) (void); + void (*_gdk_reserved5) (void); + void (*_gdk_reserved6) (void); + void (*_gdk_reserved7) (void); + void (*_gdk_reserved8) (void); +}; + +/* Windows + */ +GDK_AVAILABLE_IN_ALL +GType gdk_surface_get_type (void) G_GNUC_CONST; +GDK_AVAILABLE_IN_ALL +GdkSurface * gdk_surface_new_toplevel (GdkDisplay *display, + int width, + int height); +GDK_AVAILABLE_IN_ALL +GdkSurface * gdk_surface_new_popup (GdkDisplay *display, + const GdkRectangle *position); +GDK_AVAILABLE_IN_ALL +GdkSurface * gdk_surface_new_temp (GdkDisplay *display); +GDK_AVAILABLE_IN_ALL +GdkSurface * gdk_surface_new_child (GdkSurface *parent, + const GdkRectangle *position); + +GDK_AVAILABLE_IN_ALL +void gdk_surface_destroy (GdkSurface *window); +GDK_AVAILABLE_IN_ALL +GdkSurfaceType gdk_surface_get_window_type (GdkSurface *window); +GDK_AVAILABLE_IN_ALL +gboolean gdk_surface_is_destroyed (GdkSurface *window); + +GDK_AVAILABLE_IN_ALL +GdkDisplay * gdk_surface_get_display (GdkSurface *window); +GDK_AVAILABLE_IN_ALL +void gdk_surface_show (GdkSurface *window); +GDK_AVAILABLE_IN_ALL +void gdk_surface_hide (GdkSurface *window); +GDK_AVAILABLE_IN_ALL +void gdk_surface_withdraw (GdkSurface *window); +GDK_AVAILABLE_IN_ALL +void gdk_surface_show_unraised (GdkSurface *window); +GDK_AVAILABLE_IN_ALL +void gdk_surface_move (GdkSurface *window, + gint x, + gint y); +GDK_AVAILABLE_IN_ALL +void gdk_surface_resize (GdkSurface *window, + gint width, + gint height); +GDK_AVAILABLE_IN_ALL +void gdk_surface_move_resize (GdkSurface *window, + gint x, + gint y, + gint width, + gint height); +GDK_AVAILABLE_IN_ALL +void gdk_surface_raise (GdkSurface *window); +GDK_AVAILABLE_IN_ALL +void gdk_surface_lower (GdkSurface *window); +GDK_AVAILABLE_IN_ALL +void gdk_surface_restack (GdkSurface *window, + GdkSurface *sibling, + gboolean above); +GDK_AVAILABLE_IN_ALL +void gdk_surface_focus (GdkSurface *window, + guint32 timestamp); +GDK_AVAILABLE_IN_ALL +void gdk_surface_set_user_data (GdkSurface *window, + gpointer user_data); +GDK_AVAILABLE_IN_ALL +gboolean gdk_surface_get_accept_focus (GdkSurface *window); +GDK_AVAILABLE_IN_ALL +void gdk_surface_set_accept_focus (GdkSurface *window, + gboolean accept_focus); +GDK_AVAILABLE_IN_ALL +gboolean gdk_surface_get_focus_on_map (GdkSurface *window); +GDK_AVAILABLE_IN_ALL +void gdk_surface_set_focus_on_map (GdkSurface *window, + gboolean focus_on_map); +GDK_AVAILABLE_IN_ALL +void gdk_surface_scroll (GdkSurface *window, + gint dx, + gint dy); +GDK_AVAILABLE_IN_ALL +void gdk_surface_move_region (GdkSurface *window, + const cairo_region_t *region, + gint dx, + gint dy); + +/* + * This allows for making shaped (partially transparent) windows + * - cool feature, needed for Drag and Drag for example. + */ +GDK_AVAILABLE_IN_ALL +void gdk_surface_shape_combine_region (GdkSurface *window, + const cairo_region_t *shape_region, + gint offset_x, + gint offset_y); + +/* + * This routine allows you to quickly take the shapes of all the child windows + * of a window and use their shapes as the shape mask for this window - useful + * for container windows that dont want to look like a big box + * + * - Raster + */ +GDK_AVAILABLE_IN_ALL +void gdk_surface_set_child_shapes (GdkSurface *window); + +/* + * This routine allows you to merge (ie ADD) child shapes to your + * own window’s shape keeping its current shape and ADDING the child + * shapes to it. + * + * - Raster + */ +GDK_AVAILABLE_IN_ALL +void gdk_surface_merge_child_shapes (GdkSurface *window); + +GDK_AVAILABLE_IN_ALL +void gdk_surface_input_shape_combine_region (GdkSurface *window, + const cairo_region_t *shape_region, + gint offset_x, + gint offset_y); +GDK_AVAILABLE_IN_ALL +void gdk_surface_set_child_input_shapes (GdkSurface *window); +GDK_AVAILABLE_IN_ALL +void gdk_surface_merge_child_input_shapes (GdkSurface *window); + + +GDK_AVAILABLE_IN_ALL +void gdk_surface_set_pass_through (GdkSurface *window, + gboolean pass_through); +GDK_AVAILABLE_IN_ALL +gboolean gdk_surface_get_pass_through (GdkSurface *window); + +/* + * Check if a window has been shown, and whether all its + * parents up to a toplevel have been shown, respectively. + * Note that a window that is_viewable below is not necessarily + * viewable in the X sense. + */ +GDK_AVAILABLE_IN_ALL +gboolean gdk_surface_is_visible (GdkSurface *window); +GDK_AVAILABLE_IN_ALL +gboolean gdk_surface_is_viewable (GdkSurface *window); +GDK_AVAILABLE_IN_ALL +gboolean gdk_surface_is_input_only (GdkSurface *window); +GDK_AVAILABLE_IN_ALL +gboolean gdk_surface_is_shaped (GdkSurface *window); + +GDK_AVAILABLE_IN_ALL +GdkSurfaceState gdk_surface_get_state (GdkSurface *window); + + +/* GdkSurface */ + +GDK_AVAILABLE_IN_ALL +gboolean gdk_surface_has_native (GdkSurface *window); +GDK_AVAILABLE_IN_ALL +void gdk_surface_set_type_hint (GdkSurface *window, + GdkSurfaceTypeHint hint); +GDK_AVAILABLE_IN_ALL +GdkSurfaceTypeHint gdk_surface_get_type_hint (GdkSurface *window); + +GDK_AVAILABLE_IN_ALL +gboolean gdk_surface_get_modal_hint (GdkSurface *window); +GDK_AVAILABLE_IN_ALL +void gdk_surface_set_modal_hint (GdkSurface *window, + gboolean modal); + +GDK_AVAILABLE_IN_ALL +void gdk_surface_set_skip_taskbar_hint (GdkSurface *window, + gboolean skips_taskbar); +GDK_AVAILABLE_IN_ALL +void gdk_surface_set_skip_pager_hint (GdkSurface *window, + gboolean skips_pager); +GDK_AVAILABLE_IN_ALL +void gdk_surface_set_urgency_hint (GdkSurface *window, + gboolean urgent); + +GDK_AVAILABLE_IN_ALL +void gdk_surface_set_geometry_hints (GdkSurface *window, + const GdkGeometry *geometry, + GdkSurfaceHints geom_mask); + +GDK_AVAILABLE_IN_ALL +cairo_region_t *gdk_surface_get_clip_region (GdkSurface *window); +GDK_AVAILABLE_IN_ALL +cairo_region_t *gdk_surface_get_visible_region(GdkSurface *window); + +GDK_AVAILABLE_IN_ALL +GdkDrawingContext *gdk_surface_begin_draw_frame (GdkSurface *window, + GdkDrawContext *context, + const cairo_region_t *region); +GDK_AVAILABLE_IN_ALL +void gdk_surface_end_draw_frame (GdkSurface *window, + GdkDrawingContext *context); + +GDK_AVAILABLE_IN_ALL +void gdk_surface_set_title (GdkSurface *window, + const gchar *title); +GDK_AVAILABLE_IN_ALL +void gdk_surface_set_role (GdkSurface *window, + const gchar *role); +GDK_AVAILABLE_IN_ALL +void gdk_surface_set_startup_id (GdkSurface *window, + const gchar *startup_id); +GDK_AVAILABLE_IN_ALL +void gdk_surface_set_transient_for (GdkSurface *window, + GdkSurface *parent); + +GDK_AVAILABLE_IN_ALL +void gdk_surface_set_cursor (GdkSurface *window, + GdkCursor *cursor); +GDK_AVAILABLE_IN_ALL +GdkCursor *gdk_surface_get_cursor (GdkSurface *window); +GDK_AVAILABLE_IN_ALL +void gdk_surface_set_device_cursor (GdkSurface *window, + GdkDevice *device, + GdkCursor *cursor); +GDK_AVAILABLE_IN_ALL +GdkCursor *gdk_surface_get_device_cursor (GdkSurface *window, + GdkDevice *device); +GDK_AVAILABLE_IN_ALL +void gdk_surface_get_user_data (GdkSurface *window, + gpointer *data); +GDK_AVAILABLE_IN_ALL +void gdk_surface_get_geometry (GdkSurface *window, + gint *x, + gint *y, + gint *width, + gint *height); +GDK_AVAILABLE_IN_ALL +int gdk_surface_get_width (GdkSurface *window); +GDK_AVAILABLE_IN_ALL +int gdk_surface_get_height (GdkSurface *window); +GDK_AVAILABLE_IN_ALL +void gdk_surface_get_position (GdkSurface *window, + gint *x, + gint *y); +GDK_AVAILABLE_IN_ALL +gint gdk_surface_get_origin (GdkSurface *window, + gint *x, + gint *y); +GDK_AVAILABLE_IN_ALL +void gdk_surface_get_root_coords (GdkSurface *window, + gint x, + gint y, + gint *root_x, + gint *root_y); +GDK_AVAILABLE_IN_ALL +void gdk_surface_coords_to_parent (GdkSurface *window, + gdouble x, + gdouble y, + gdouble *parent_x, + gdouble *parent_y); +GDK_AVAILABLE_IN_ALL +void gdk_surface_coords_from_parent (GdkSurface *window, + gdouble parent_x, + gdouble parent_y, + gdouble *x, + gdouble *y); + +GDK_AVAILABLE_IN_ALL +void gdk_surface_get_root_origin (GdkSurface *window, + gint *x, + gint *y); +GDK_AVAILABLE_IN_ALL +void gdk_surface_get_frame_extents (GdkSurface *window, + GdkRectangle *rect); + +GDK_AVAILABLE_IN_ALL +gint gdk_surface_get_scale_factor (GdkSurface *window); + +GDK_AVAILABLE_IN_ALL +GdkSurface * gdk_surface_get_device_position (GdkSurface *window, + GdkDevice *device, + gint *x, + gint *y, + GdkModifierType *mask); +GDK_AVAILABLE_IN_ALL +GdkSurface * gdk_surface_get_device_position_double (GdkSurface *window, + GdkDevice *device, + gdouble *x, + gdouble *y, + GdkModifierType *mask); +GDK_AVAILABLE_IN_ALL +GdkSurface * gdk_surface_get_parent (GdkSurface *window); +GDK_AVAILABLE_IN_ALL +GdkSurface * gdk_surface_get_toplevel (GdkSurface *window); + +GDK_AVAILABLE_IN_ALL +GList * gdk_surface_get_children (GdkSurface *window); +GDK_AVAILABLE_IN_ALL +GList * gdk_surface_peek_children (GdkSurface *window); +GDK_AVAILABLE_IN_ALL +GList * gdk_surface_get_children_with_user_data (GdkSurface *window, + gpointer user_data); + +GDK_AVAILABLE_IN_ALL +GdkEventMask gdk_surface_get_events (GdkSurface *window); +GDK_AVAILABLE_IN_ALL +void gdk_surface_set_events (GdkSurface *window, + GdkEventMask event_mask); +GDK_AVAILABLE_IN_ALL +void gdk_surface_set_device_events (GdkSurface *window, + GdkDevice *device, + GdkEventMask event_mask); +GDK_AVAILABLE_IN_ALL +GdkEventMask gdk_surface_get_device_events (GdkSurface *window, + GdkDevice *device); + +GDK_AVAILABLE_IN_ALL +void gdk_surface_set_icon_list (GdkSurface *window, + GList *surfaces); +GDK_AVAILABLE_IN_ALL +void gdk_surface_set_icon_name (GdkSurface *window, + const gchar *name); +GDK_AVAILABLE_IN_ALL +void gdk_surface_set_group (GdkSurface *window, + GdkSurface *leader); +GDK_AVAILABLE_IN_ALL +GdkSurface* gdk_surface_get_group (GdkSurface *window); +GDK_AVAILABLE_IN_ALL +void gdk_surface_set_decorations (GdkSurface *window, + GdkWMDecoration decorations); +GDK_AVAILABLE_IN_ALL +gboolean gdk_surface_get_decorations (GdkSurface *window, + GdkWMDecoration *decorations); +GDK_AVAILABLE_IN_ALL +void gdk_surface_set_functions (GdkSurface *window, + GdkWMFunction functions); + +GDK_AVAILABLE_IN_ALL +cairo_surface_t * + gdk_surface_create_similar_surface (GdkSurface *window, + cairo_content_t content, + int width, + int height); +GDK_AVAILABLE_IN_ALL +cairo_surface_t * + gdk_surface_create_similar_image_surface (GdkSurface *window, + cairo_format_t format, + int width, + int height, + int scale); + +GDK_AVAILABLE_IN_ALL +void gdk_surface_beep (GdkSurface *window); +GDK_AVAILABLE_IN_ALL +void gdk_surface_iconify (GdkSurface *window); +GDK_AVAILABLE_IN_ALL +void gdk_surface_deiconify (GdkSurface *window); +GDK_AVAILABLE_IN_ALL +void gdk_surface_stick (GdkSurface *window); +GDK_AVAILABLE_IN_ALL +void gdk_surface_unstick (GdkSurface *window); +GDK_AVAILABLE_IN_ALL +void gdk_surface_maximize (GdkSurface *window); +GDK_AVAILABLE_IN_ALL +void gdk_surface_unmaximize (GdkSurface *window); +GDK_AVAILABLE_IN_ALL +void gdk_surface_fullscreen (GdkSurface *window); +GDK_AVAILABLE_IN_ALL +void gdk_surface_fullscreen_on_monitor (GdkSurface *window, + GdkMonitor *monitor); +GDK_AVAILABLE_IN_ALL +void gdk_surface_set_fullscreen_mode (GdkSurface *window, + GdkFullscreenMode mode); +GDK_AVAILABLE_IN_ALL +GdkFullscreenMode + gdk_surface_get_fullscreen_mode (GdkSurface *window); +GDK_AVAILABLE_IN_ALL +void gdk_surface_unfullscreen (GdkSurface *window); +GDK_AVAILABLE_IN_ALL +void gdk_surface_set_keep_above (GdkSurface *window, + gboolean setting); +GDK_AVAILABLE_IN_ALL +void gdk_surface_set_keep_below (GdkSurface *window, + gboolean setting); +GDK_AVAILABLE_IN_ALL +void gdk_surface_set_opacity (GdkSurface *window, + gdouble opacity); +GDK_AVAILABLE_IN_ALL +void gdk_surface_register_dnd (GdkSurface *window); + +GDK_AVAILABLE_IN_ALL +void gdk_surface_begin_resize_drag (GdkSurface *window, + GdkSurfaceEdge edge, + gint button, + gint root_x, + gint root_y, + guint32 timestamp); +GDK_AVAILABLE_IN_ALL +void gdk_surface_begin_resize_drag_for_device (GdkSurface *window, + GdkSurfaceEdge edge, + GdkDevice *device, + gint button, + gint root_x, + gint root_y, + guint32 timestamp); +GDK_AVAILABLE_IN_ALL +void gdk_surface_begin_move_drag (GdkSurface *window, + gint button, + gint root_x, + gint root_y, + guint32 timestamp); +GDK_AVAILABLE_IN_ALL +void gdk_surface_begin_move_drag_for_device (GdkSurface *window, + GdkDevice *device, + gint button, + gint root_x, + gint root_y, + guint32 timestamp); + +/* Interface for dirty-region queueing */ +GDK_AVAILABLE_IN_ALL +void gdk_surface_invalidate_rect (GdkSurface *window, + const GdkRectangle *rect, + gboolean invalidate_children); +GDK_AVAILABLE_IN_ALL +void gdk_surface_invalidate_region (GdkSurface *window, + const cairo_region_t *region, + gboolean invalidate_children); + +/** + * GdkSurfaceChildFunc: + * @window: a #GdkSurface + * @user_data: user data + * + * A function of this type is passed to gdk_surface_invalidate_maybe_recurse(). + * It gets called for each child of the window to determine whether to + * recursively invalidate it or now. + * + * Returns: %TRUE to invalidate @window recursively + */ +typedef gboolean (*GdkSurfaceChildFunc) (GdkSurface *window, + gpointer user_data); + +GDK_AVAILABLE_IN_ALL +void gdk_surface_invalidate_maybe_recurse (GdkSurface *window, + const cairo_region_t *region, + GdkSurfaceChildFunc child_func, + gpointer user_data); +GDK_AVAILABLE_IN_ALL +cairo_region_t *gdk_surface_get_update_area (GdkSurface *window); + +GDK_AVAILABLE_IN_ALL +void gdk_surface_freeze_updates (GdkSurface *window); +GDK_AVAILABLE_IN_ALL +void gdk_surface_thaw_updates (GdkSurface *window); + +GDK_AVAILABLE_IN_ALL +void gdk_surface_constrain_size (GdkGeometry *geometry, + GdkSurfaceHints flags, + gint width, + gint height, + gint *new_width, + gint *new_height); + +/* Multidevice support */ +GDK_AVAILABLE_IN_ALL +void gdk_surface_set_support_multidevice (GdkSurface *window, + gboolean support_multidevice); +GDK_AVAILABLE_IN_ALL +gboolean gdk_surface_get_support_multidevice (GdkSurface *window); + +/* Frame clock */ +GDK_AVAILABLE_IN_ALL +GdkFrameClock* gdk_surface_get_frame_clock (GdkSurface *window); + +GDK_AVAILABLE_IN_ALL +void gdk_surface_set_opaque_region (GdkSurface *window, + cairo_region_t *region); + +GDK_AVAILABLE_IN_ALL +void gdk_surface_set_shadow_width (GdkSurface *window, + gint left, + gint right, + gint top, + gint bottom); +GDK_AVAILABLE_IN_ALL +gboolean gdk_surface_show_window_menu (GdkSurface *window, + GdkEvent *event); + +GDK_AVAILABLE_IN_ALL +GdkGLContext * gdk_surface_create_gl_context (GdkSurface *window, + GError **error); +GDK_AVAILABLE_IN_ALL +GdkVulkanContext * + gdk_surface_create_vulkan_context(GdkSurface *window, + GError **error); + +G_END_DECLS + +#endif /* __GDK_SURFACE_H__ */ diff --git a/gdk/gdksurfaceimpl.c b/gdk/gdksurfaceimpl.c new file mode 100644 index 0000000000..f5c329688a --- /dev/null +++ b/gdk/gdksurfaceimpl.c @@ -0,0 +1,347 @@ +/* GDK - The GIMP Drawing Kit + * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * 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, see . + */ + +/* + * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS + * file for a list of people on the GTK+ Team. See the ChangeLog + * files for a list of changes. These files are distributed with + * GTK+ at ftp://ftp.gtk.org/pub/gtk/. + */ + +#include "config.h" + +#include "gdksurfaceimpl.h" + +#include "gdkinternals.h" + + +G_DEFINE_TYPE (GdkSurfaceImpl, gdk_surface_impl, G_TYPE_OBJECT); + +static gboolean +gdk_surface_impl_beep (GdkSurface *window) +{ + /* FALSE means windows can't beep, so the display will be + * made to beep instead. */ + return FALSE; +} + +static GdkDisplay * +get_display_for_window (GdkSurface *primary, + GdkSurface *secondary) +{ + GdkDisplay *display = gdk_surface_get_display (primary); + + if (display) + return display; + + display = gdk_surface_get_display (secondary); + + if (display) + return display; + + g_warning ("no display for window, using default"); + return gdk_display_get_default (); +} + +static GdkMonitor * +get_monitor_for_rect (GdkDisplay *display, + const GdkRectangle *rect) +{ + gint biggest_area = G_MININT; + GdkMonitor *best_monitor = NULL; + GdkMonitor *monitor; + GdkRectangle workarea; + GdkRectangle intersection; + gint x; + gint y; + gint i; + + for (i = 0; i < gdk_display_get_n_monitors (display); i++) + { + monitor = gdk_display_get_monitor (display, i); + gdk_monitor_get_workarea (monitor, &workarea); + + if (gdk_rectangle_intersect (&workarea, rect, &intersection)) + { + if (intersection.width * intersection.height > biggest_area) + { + biggest_area = intersection.width * intersection.height; + best_monitor = monitor; + } + } + } + + if (best_monitor) + return best_monitor; + + x = rect->x + rect->width / 2; + y = rect->y + rect->height / 2; + + return gdk_display_get_monitor_at_point (display, x, y); +} + +static gint +get_anchor_x_sign (GdkGravity anchor) +{ + switch (anchor) + { + case GDK_GRAVITY_STATIC: + case GDK_GRAVITY_NORTH_WEST: + case GDK_GRAVITY_WEST: + case GDK_GRAVITY_SOUTH_WEST: + return -1; + + default: + case GDK_GRAVITY_NORTH: + case GDK_GRAVITY_CENTER: + case GDK_GRAVITY_SOUTH: + return 0; + + case GDK_GRAVITY_NORTH_EAST: + case GDK_GRAVITY_EAST: + case GDK_GRAVITY_SOUTH_EAST: + return 1; + } +} + +static gint +get_anchor_y_sign (GdkGravity anchor) +{ + switch (anchor) + { + case GDK_GRAVITY_STATIC: + case GDK_GRAVITY_NORTH_WEST: + case GDK_GRAVITY_NORTH: + case GDK_GRAVITY_NORTH_EAST: + return -1; + + default: + case GDK_GRAVITY_WEST: + case GDK_GRAVITY_CENTER: + case GDK_GRAVITY_EAST: + return 0; + + case GDK_GRAVITY_SOUTH_WEST: + case GDK_GRAVITY_SOUTH: + case GDK_GRAVITY_SOUTH_EAST: + return 1; + } +} + +static gint +maybe_flip_position (gint bounds_pos, + gint bounds_size, + gint rect_pos, + gint rect_size, + gint window_size, + gint rect_sign, + gint window_sign, + gint offset, + gboolean flip, + gboolean *flipped) +{ + gint primary; + gint secondary; + + *flipped = FALSE; + primary = rect_pos + (1 + rect_sign) * rect_size / 2 + offset - (1 + window_sign) * window_size / 2; + + if (!flip || (primary >= bounds_pos && primary + window_size <= bounds_pos + bounds_size)) + return primary; + + *flipped = TRUE; + secondary = rect_pos + (1 - rect_sign) * rect_size / 2 - offset - (1 - window_sign) * window_size / 2; + + if (secondary >= bounds_pos && secondary + window_size <= bounds_pos + bounds_size) + return secondary; + + *flipped = FALSE; + return primary; +} + +static GdkSurface * +traverse_to_toplevel (GdkSurface *window, + gint x, + gint y, + gint *toplevel_x, + gint *toplevel_y) +{ + GdkSurface *parent; + gdouble xf = x; + gdouble yf = y; + + while ((parent = window->parent) != NULL && + (gdk_surface_get_window_type (parent) != GDK_SURFACE_ROOT)) + { + gdk_surface_coords_to_parent (window, xf, yf, &xf, &yf); + window = parent; + } + + *toplevel_x = (gint) xf; + *toplevel_y = (gint) yf; + return window; +} + +static void +gdk_surface_impl_move_to_rect (GdkSurface *window, + const GdkRectangle *rect, + GdkGravity rect_anchor, + GdkGravity window_anchor, + GdkAnchorHints anchor_hints, + gint rect_anchor_dx, + gint rect_anchor_dy) +{ + GdkSurface *transient_for_toplevel; + GdkDisplay *display; + GdkMonitor *monitor; + GdkRectangle bounds; + GdkRectangle root_rect = *rect; + GdkRectangle flipped_rect; + GdkRectangle final_rect; + gboolean flipped_x; + gboolean flipped_y; + + /* + * First translate the anchor rect to toplevel coordinates. This is needed + * because not all backends will be able to get root coordinates for + * non-toplevel windows. + */ + transient_for_toplevel = traverse_to_toplevel (window->transient_for, + root_rect.x, + root_rect.y, + &root_rect.x, + &root_rect.y); + + gdk_surface_get_root_coords (transient_for_toplevel, + root_rect.x, + root_rect.y, + &root_rect.x, + &root_rect.y); + + display = get_display_for_window (window, window->transient_for); + monitor = get_monitor_for_rect (display, &root_rect); + gdk_monitor_get_workarea (monitor, &bounds); + + flipped_rect.width = window->width - window->shadow_left - window->shadow_right; + flipped_rect.height = window->height - window->shadow_top - window->shadow_bottom; + flipped_rect.x = maybe_flip_position (bounds.x, + bounds.width, + root_rect.x, + root_rect.width, + flipped_rect.width, + get_anchor_x_sign (rect_anchor), + get_anchor_x_sign (window_anchor), + rect_anchor_dx, + anchor_hints & GDK_ANCHOR_FLIP_X, + &flipped_x); + flipped_rect.y = maybe_flip_position (bounds.y, + bounds.height, + root_rect.y, + root_rect.height, + flipped_rect.height, + get_anchor_y_sign (rect_anchor), + get_anchor_y_sign (window_anchor), + rect_anchor_dy, + anchor_hints & GDK_ANCHOR_FLIP_Y, + &flipped_y); + + final_rect = flipped_rect; + + if (anchor_hints & GDK_ANCHOR_SLIDE_X) + { + if (final_rect.x + final_rect.width > bounds.x + bounds.width) + final_rect.x = bounds.x + bounds.width - final_rect.width; + + if (final_rect.x < bounds.x) + final_rect.x = bounds.x; + } + + if (anchor_hints & GDK_ANCHOR_SLIDE_Y) + { + if (final_rect.y + final_rect.height > bounds.y + bounds.height) + final_rect.y = bounds.y + bounds.height - final_rect.height; + + if (final_rect.y < bounds.y) + final_rect.y = bounds.y; + } + + if (anchor_hints & GDK_ANCHOR_RESIZE_X) + { + if (final_rect.x < bounds.x) + { + final_rect.width -= bounds.x - final_rect.x; + final_rect.x = bounds.x; + } + + if (final_rect.x + final_rect.width > bounds.x + bounds.width) + final_rect.width = bounds.x + bounds.width - final_rect.x; + } + + if (anchor_hints & GDK_ANCHOR_RESIZE_Y) + { + if (final_rect.y < bounds.y) + { + final_rect.height -= bounds.y - final_rect.y; + final_rect.y = bounds.y; + } + + if (final_rect.y + final_rect.height > bounds.y + bounds.height) + final_rect.height = bounds.y + bounds.height - final_rect.y; + } + + flipped_rect.x -= window->shadow_left; + flipped_rect.y -= window->shadow_top; + flipped_rect.width += window->shadow_left + window->shadow_right; + flipped_rect.height += window->shadow_top + window->shadow_bottom; + + final_rect.x -= window->shadow_left; + final_rect.y -= window->shadow_top; + final_rect.width += window->shadow_left + window->shadow_right; + final_rect.height += window->shadow_top + window->shadow_bottom; + + if (final_rect.width != window->width || final_rect.height != window->height) + gdk_surface_move_resize (window, final_rect.x, final_rect.y, final_rect.width, final_rect.height); + else + gdk_surface_move (window, final_rect.x, final_rect.y); + + g_signal_emit_by_name (window, + "moved-to-rect", + &flipped_rect, + &final_rect, + flipped_x, + flipped_y); +} + +static void +gdk_surface_impl_process_updates_recurse (GdkSurface *window, + cairo_region_t *region) +{ + _gdk_surface_process_updates_recurse (window, region); +} + +static void +gdk_surface_impl_class_init (GdkSurfaceImplClass *impl_class) +{ + impl_class->beep = gdk_surface_impl_beep; + impl_class->move_to_rect = gdk_surface_impl_move_to_rect; + impl_class->process_updates_recurse = gdk_surface_impl_process_updates_recurse; +} + +static void +gdk_surface_impl_init (GdkSurfaceImpl *impl) +{ +} diff --git a/gdk/gdksurfaceimpl.h b/gdk/gdksurfaceimpl.h new file mode 100644 index 0000000000..7a1dcb4bb0 --- /dev/null +++ b/gdk/gdksurfaceimpl.h @@ -0,0 +1,256 @@ +/* GDK - The GIMP Drawing Kit + * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * 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, see . + */ + +/* + * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS + * file for a list of people on the GTK+ Team. See the ChangeLog + * files for a list of changes. These files are distributed with + * GTK+ at ftp://ftp.gtk.org/pub/gtk/. + */ + +#ifndef __GDK_SURFACE_IMPL_H__ +#define __GDK_SURFACE_IMPL_H__ + +#include +#include + +G_BEGIN_DECLS + +#define GDK_TYPE_SURFACE_IMPL (gdk_surface_impl_get_type ()) +#define GDK_SURFACE_IMPL(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), GDK_TYPE_SURFACE_IMPL, GdkSurfaceImpl)) +#define GDK_SURFACE_IMPL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GDK_TYPE_SURFACE_IMPL, GdkSurfaceImplClass)) +#define GDK_IS_SURFACE_IMPL(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), GDK_TYPE_SURFACE_IMPL)) +#define GDK_IS_SURFACE_IMPL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GDK_TYPE_SURFACE_IMPL)) +#define GDK_SURFACE_IMPL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GDK_TYPE_SURFACE_IMPL, GdkSurfaceImplClass)) + +typedef struct _GdkSurfaceImpl GdkSurfaceImpl; +typedef struct _GdkSurfaceImplClass GdkSurfaceImplClass; + +struct _GdkSurfaceImpl +{ + GObject parent; +}; + +struct _GdkSurfaceImplClass +{ + GObjectClass parent_class; + + cairo_surface_t * + (* ref_cairo_surface) (GdkSurface *window); + cairo_surface_t * + (* create_similar_image_surface) (GdkSurface * window, + cairo_format_t format, + int width, + int height); + + void (* show) (GdkSurface *window, + gboolean already_mapped); + void (* hide) (GdkSurface *window); + void (* withdraw) (GdkSurface *window); + void (* raise) (GdkSurface *window); + void (* lower) (GdkSurface *window); + void (* restack_toplevel) (GdkSurface *window, + GdkSurface *sibling, + gboolean above); + + void (* move_resize) (GdkSurface *window, + gboolean with_move, + gint x, + gint y, + gint width, + gint height); + void (* move_to_rect) (GdkSurface *window, + const GdkRectangle *rect, + GdkGravity rect_anchor, + GdkGravity window_anchor, + GdkAnchorHints anchor_hints, + gint rect_anchor_dx, + gint rect_anchor_dy); + + GdkEventMask (* get_events) (GdkSurface *window); + void (* set_events) (GdkSurface *window, + GdkEventMask event_mask); + + void (* get_geometry) (GdkSurface *window, + gint *x, + gint *y, + gint *width, + gint *height); + void (* get_root_coords) (GdkSurface *window, + gint x, + gint y, + gint *root_x, + gint *root_y); + gboolean (* get_device_state) (GdkSurface *window, + GdkDevice *device, + gdouble *x, + gdouble *y, + GdkModifierType *mask); + gboolean (* begin_paint) (GdkSurface *window); + void (* end_paint) (GdkSurface *window); + + void (* shape_combine_region) (GdkSurface *window, + const cairo_region_t *shape_region, + gint offset_x, + gint offset_y); + void (* input_shape_combine_region) (GdkSurface *window, + const cairo_region_t *shape_region, + gint offset_x, + gint offset_y); + + /* Called before processing updates for a window. This gives the windowing + * layer a chance to save the region for later use in avoiding duplicate + * exposes. + */ + void (* queue_antiexpose) (GdkSurface *window, + cairo_region_t *update_area); + +/* Called to do the windowing system specific part of gdk_surface_destroy(), + * + * window: The window being destroyed + * recursing: If TRUE, then this is being called because a parent + * was destroyed. This generally means that the call to the windowing + * system to destroy the window can be omitted, since it will be + * destroyed as a result of the parent being destroyed. + * Unless @foreign_destroy + * foreign_destroy: If TRUE, the window or a parent was destroyed by some + * external agency. The window has already been destroyed and no + * windowing system calls should be made. (This may never happen + * for some windowing systems.) + */ + void (* destroy) (GdkSurface *window, + gboolean recursing, + gboolean foreign_destroy); + + + /* optional */ + gboolean (* beep) (GdkSurface *window); + + void (* focus) (GdkSurface *window, + guint32 timestamp); + void (* set_type_hint) (GdkSurface *window, + GdkSurfaceTypeHint hint); + GdkSurfaceTypeHint (* get_type_hint) (GdkSurface *window); + void (* set_modal_hint) (GdkSurface *window, + gboolean modal); + void (* set_skip_taskbar_hint) (GdkSurface *window, + gboolean skips_taskbar); + void (* set_skip_pager_hint) (GdkSurface *window, + gboolean skips_pager); + void (* set_urgency_hint) (GdkSurface *window, + gboolean urgent); + void (* set_geometry_hints) (GdkSurface *window, + const GdkGeometry *geometry, + GdkSurfaceHints geom_mask); + void (* set_title) (GdkSurface *window, + const gchar *title); + void (* set_role) (GdkSurface *window, + const gchar *role); + void (* set_startup_id) (GdkSurface *window, + const gchar *startup_id); + void (* set_transient_for) (GdkSurface *window, + GdkSurface *parent); + void (* get_frame_extents) (GdkSurface *window, + GdkRectangle *rect); + void (* set_accept_focus) (GdkSurface *window, + gboolean accept_focus); + void (* set_focus_on_map) (GdkSurface *window, + gboolean focus_on_map); + void (* set_icon_list) (GdkSurface *window, + GList *pixbufs); + void (* set_icon_name) (GdkSurface *window, + const gchar *name); + void (* iconify) (GdkSurface *window); + void (* deiconify) (GdkSurface *window); + void (* stick) (GdkSurface *window); + void (* unstick) (GdkSurface *window); + void (* maximize) (GdkSurface *window); + void (* unmaximize) (GdkSurface *window); + void (* fullscreen) (GdkSurface *window); + void (* fullscreen_on_monitor) (GdkSurface *window, + GdkMonitor *monitor); + void (* apply_fullscreen_mode) (GdkSurface *window); + void (* unfullscreen) (GdkSurface *window); + void (* set_keep_above) (GdkSurface *window, + gboolean setting); + void (* set_keep_below) (GdkSurface *window, + gboolean setting); + GdkSurface * (* get_group) (GdkSurface *window); + void (* set_group) (GdkSurface *window, + GdkSurface *leader); + void (* set_decorations) (GdkSurface *window, + GdkWMDecoration decorations); + gboolean (* get_decorations) (GdkSurface *window, + GdkWMDecoration *decorations); + void (* set_functions) (GdkSurface *window, + GdkWMFunction functions); + void (* begin_resize_drag) (GdkSurface *window, + GdkSurfaceEdge edge, + GdkDevice *device, + gint button, + gint root_x, + gint root_y, + guint32 timestamp); + void (* begin_move_drag) (GdkSurface *window, + GdkDevice *device, + gint button, + gint root_x, + gint root_y, + guint32 timestamp); + void (* enable_synchronized_configure) (GdkSurface *window); + void (* configure_finished) (GdkSurface *window); + void (* set_opacity) (GdkSurface *window, + gdouble opacity); + void (* destroy_notify) (GdkSurface *window); + void (* register_dnd) (GdkSurface *window); + GdkDragContext * (*drag_begin) (GdkSurface *window, + GdkDevice *device, + GdkContentProvider*content, + GdkDragAction actions, + gint dx, + gint dy); + + void (*process_updates_recurse) (GdkSurface *window, + cairo_region_t *region); + + gint (* get_scale_factor) (GdkSurface *window); + void (* get_unscaled_size) (GdkSurface *window, + int *unscaled_width, + int *unscaled_height); + + void (* set_opaque_region) (GdkSurface *window, + cairo_region_t *region); + void (* set_shadow_width) (GdkSurface *window, + gint left, + gint right, + gint top, + gint bottom); + gboolean (* show_window_menu) (GdkSurface *window, + GdkEvent *event); + GdkGLContext *(*create_gl_context) (GdkSurface *window, + gboolean attached, + GdkGLContext *share, + GError **error); + gboolean (* supports_edge_constraints)(GdkSurface *window); +}; + +/* Interface Functions */ +GType gdk_surface_impl_get_type (void) G_GNUC_CONST; + +G_END_DECLS + +#endif /* __GDK_SURFACE_IMPL_H__ */ diff --git a/gdk/gdkwindow.c b/gdk/gdkwindow.c deleted file mode 100644 index 3eea4914ba..0000000000 --- a/gdk/gdkwindow.c +++ /dev/null @@ -1,6957 +0,0 @@ -/* GDK - The GIMP Drawing Kit - * Copyright (C) 1995-2007 Peter Mattis, Spencer Kimball, - * Josh MacDonald, Ryan Lortie - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * 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, see . - */ - -/* - * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS - * file for a list of people on the GTK+ Team. See the ChangeLog - * files for a list of changes. These files are distributed with - * GTK+ at ftp://ftp.gtk.org/pub/gtk/. - */ - -#include "config.h" - -#include - -#include "gdkwindow.h" - -#include "gdkrectangle.h" -#include "gdkinternals.h" -#include "gdkintl.h" -#include "gdkdisplayprivate.h" -#include "gdkdeviceprivate.h" -#include "gdkmarshalers.h" -#include "gdkframeclockidle.h" -#include "gdkwindowimpl.h" -#include "gdkglcontextprivate.h" -#include "gdkdrawingcontextprivate.h" -#include "gdk-private.h" - -#include - -#include - -/* for the use of round() */ -#include "fallback-c89.c" - -#ifdef GDK_WINDOWING_WAYLAND -#include "wayland/gdkwayland.h" -#endif - -#undef DEBUG_WINDOW_PRINTING - - -/** - * SECTION:windows - * @Short_description: Onscreen display areas in the target window system - * @Title: Windows - * - * A #GdkSurface is a (usually) rectangular region on the screen. - * It’s a low-level object, used to implement high-level objects such as - * #GtkWidget and #GtkWindow on the GTK+ level. A #GtkWindow is a toplevel - * window, the thing a user might think of as a “window” with a titlebar - * and so on; a #GtkWindow may contain many sub-GdkSurfaces. - */ - -/** - * GdkSurface: - * - * The GdkSurface struct contains only private fields and - * should not be accessed directly. - */ - -/* Historically a GdkSurface always matches a platform native window, - * be it a toplevel window or a child window. In this setup the - * GdkSurface (and other GdkDrawables) were platform independent classes, - * and the actual platform specific implementation was in a delegate - * object available as “impl” in the window object. - * - * With the addition of client side windows this changes a bit. The - * application-visible GdkSurface object behaves as it did before, but - * such windows now don't a corresponding native window. Instead subwindows - * windows are “client side”, i.e. emulated by the gdk code such - * that clipping, drawing, moving, events etc work as expected. - * - * GdkSurfaces have a pointer to the “impl window” they are in, i.e. - * the topmost GdkSurface which have the same “impl” value. This is stored - * in impl_window, which is different from the window itself only for client - * side windows. - * All GdkSurfaces (native or not) track the position of the window in the parent - * (x, y), the size of the window (width, height), the position of the window - * with respect to the impl window (abs_x, abs_y). We also track the clip - * region of the window wrt parent windows, in window-relative coordinates (clip_region). - */ - -enum { - MOVED_TO_RECT, - LAST_SIGNAL -}; - -enum { - PROP_0, - PROP_CURSOR, - PROP_DISPLAY, - PROP_STATE, - LAST_PROP -}; - -/* Global info */ - -static void gdk_surface_finalize (GObject *object); - -static void gdk_surface_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec); -static void gdk_surface_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec); - -static void gdk_surface_clear_backing_region (GdkSurface *window); - -static void recompute_visible_regions (GdkSurface *private, - gboolean recalculate_children); -static void gdk_surface_invalidate_in_parent (GdkSurface *private); -static void update_cursor (GdkDisplay *display, - GdkDevice *device); -static void impl_window_add_update_area (GdkSurface *impl_window, - cairo_region_t *region); -static void gdk_surface_invalidate_region_full (GdkSurface *window, - const cairo_region_t *region, - gboolean invalidate_children); -static void gdk_surface_invalidate_rect_full (GdkSurface *window, - const GdkRectangle *rect, - gboolean invalidate_children); -static cairo_surface_t *gdk_surface_ref_impl_surface (GdkSurface *window); - -static void gdk_surface_set_frame_clock (GdkSurface *window, - GdkFrameClock *clock); - - -static guint signals[LAST_SIGNAL] = { 0 }; -static GParamSpec *properties[LAST_PROP] = { NULL, }; - -G_DEFINE_ABSTRACT_TYPE (GdkSurface, gdk_surface, 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 - { - int i; - 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 (i = 0; i < num; i++) - { - cairo_region_get_rectangle (region, i, &r); - 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 - -static GList * -list_insert_link_before (GList *list, - GList *sibling, - GList *link) -{ - if (list == NULL || sibling == list) - { - link->prev = NULL; - link->next = list; - if (list) - list->prev = link; - return link; - } - else if (sibling == NULL) - { - GList *last = g_list_last (list); - - last->next = link; - link->prev = last; - link->next = NULL; - - return list; - } - else - { - link->next = sibling; - link->prev = sibling->prev; - sibling->prev = link; - - if (link->prev) - link->prev->next = link; - - return list; - } -} - -static void -gdk_surface_init (GdkSurface *window) -{ - /* 0-initialization is good for all other fields. */ - - window->window_type = GDK_SURFACE_CHILD; - - window->state = GDK_SURFACE_STATE_WITHDRAWN; - window->fullscreen_mode = GDK_FULLSCREEN_ON_CURRENT_MONITOR; - window->width = 1; - window->height = 1; - window->toplevel_window_type = -1; - window->children_list_node.data = window; - - window->device_cursor = g_hash_table_new_full (NULL, NULL, - NULL, g_object_unref); -} - -static void -gdk_surface_class_init (GdkSurfaceClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - - object_class->finalize = gdk_surface_finalize; - object_class->set_property = gdk_surface_set_property; - object_class->get_property = gdk_surface_get_property; - - /* Properties */ - - /** - * GdkSurface:cursor: - * - * The mouse pointer for a #GdkSurface. See gdk_surface_set_cursor() and - * gdk_surface_get_cursor() for details. - */ - properties[PROP_CURSOR] = - g_param_spec_object ("cursor", - P_("Cursor"), - P_("Cursor"), - GDK_TYPE_CURSOR, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); - - /** - * GdkSurface:display: - * - * The #GdkDisplay connection of the window. See gdk_surface_get_display() - * for details. - */ - properties[PROP_DISPLAY] = - g_param_spec_object ("display", - P_("Display"), - P_("Display"), - GDK_TYPE_DISPLAY, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); - - properties[PROP_STATE] = - g_param_spec_flags ("state", - P_("State"), - P_("State"), - GDK_TYPE_SURFACE_STATE, GDK_SURFACE_STATE_WITHDRAWN, - G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); - - g_object_class_install_properties (object_class, LAST_PROP, properties); - - /** - * GdkSurface::moved-to-rect: - * @window: the #GdkSurface that moved - * @flipped_rect: (nullable): the position of @window after any possible - * flipping or %NULL if the backend can't obtain it - * @final_rect: (nullable): the final position of @window or %NULL if the - * backend can't obtain it - * @flipped_x: %TRUE if the anchors were flipped horizontally - * @flipped_y: %TRUE if the anchors were flipped vertically - * - * Emitted when the position of @window is finalized after being moved to a - * destination rectangle. - * - * @window might be flipped over the destination rectangle in order to keep - * it on-screen, in which case @flipped_x and @flipped_y will be set to %TRUE - * accordingly. - * - * @flipped_rect is the ideal position of @window after any possible - * flipping, but before any possible sliding. @final_rect is @flipped_rect, - * but possibly translated in the case that flipping is still ineffective in - * keeping @window on-screen. - * Stability: Private - */ - signals[MOVED_TO_RECT] = - g_signal_new (g_intern_static_string ("moved-to-rect"), - G_OBJECT_CLASS_TYPE (object_class), - G_SIGNAL_RUN_FIRST, - 0, - NULL, - NULL, - _gdk_marshal_VOID__POINTER_POINTER_BOOLEAN_BOOLEAN, - G_TYPE_NONE, - 4, - G_TYPE_POINTER, - G_TYPE_POINTER, - G_TYPE_BOOLEAN, - G_TYPE_BOOLEAN); -} - -static void -seat_removed_cb (GdkDisplay *display, - GdkSeat *seat, - GdkSurface *window) -{ - GdkDevice *device = gdk_seat_get_pointer (seat); - - window->devices_inside = g_list_remove (window->devices_inside, device); - g_hash_table_remove (window->device_cursor, device); - - if (window->device_events) - g_hash_table_remove (window->device_events, device); -} - -static void -gdk_surface_finalize (GObject *object) -{ - GdkSurface *window = GDK_SURFACE (object); - - g_signal_handlers_disconnect_by_func (gdk_surface_get_display (window), - seat_removed_cb, window); - - if (!GDK_SURFACE_DESTROYED (window)) - { - if (GDK_SURFACE_TYPE (window) != GDK_SURFACE_FOREIGN) - { - g_warning ("losing last reference to undestroyed window"); - _gdk_surface_destroy (window, FALSE); - } - else - /* We use TRUE here, to keep us from actually calling - * XDestroyWindow() on the window - */ - _gdk_surface_destroy (window, TRUE); - } - - if (window->impl) - { - g_object_unref (window->impl); - window->impl = NULL; - } - - if (window->impl_window != window) - { - g_object_unref (window->impl_window); - window->impl_window = NULL; - } - - if (window->shape) - cairo_region_destroy (window->shape); - - if (window->input_shape) - cairo_region_destroy (window->input_shape); - - if (window->cursor) - g_object_unref (window->cursor); - - if (window->device_cursor) - g_hash_table_destroy (window->device_cursor); - - if (window->device_events) - g_hash_table_destroy (window->device_events); - - if (window->devices_inside) - g_list_free (window->devices_inside); - - g_clear_object (&window->display); - - if (window->opaque_region) - cairo_region_destroy (window->opaque_region); - - G_OBJECT_CLASS (gdk_surface_parent_class)->finalize (object); -} - -static void -gdk_surface_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec) -{ - GdkSurface *window = GDK_SURFACE (object); - - switch (prop_id) - { - case PROP_CURSOR: - gdk_surface_set_cursor (window, g_value_get_object (value)); - break; - - case PROP_DISPLAY: - window->display = g_value_dup_object (value); - g_assert (window->display != NULL); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -gdk_surface_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) -{ - GdkSurface *window = GDK_SURFACE (object); - - switch (prop_id) - { - case PROP_CURSOR: - g_value_set_object (value, gdk_surface_get_cursor (window)); - break; - - case PROP_DISPLAY: - g_value_set_object (value, window->display); - break; - - case PROP_STATE: - g_value_set_flags (value, window->state); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static gboolean -gdk_surface_is_subsurface (GdkSurface *window) -{ - return window->window_type == GDK_SURFACE_SUBSURFACE; -} - -static GdkSurface * -gdk_surface_get_impl_window (GdkSurface *window) -{ - return window->impl_window; -} - -GdkSurface * -_gdk_surface_get_impl_window (GdkSurface *window) -{ - return gdk_surface_get_impl_window (window); -} - -static gboolean -gdk_surface_has_impl (GdkSurface *window) -{ - return window->impl_window == window; -} - -static gboolean -gdk_surface_is_toplevel (GdkSurface *window) -{ - return - window->parent == NULL || - window->parent->window_type == GDK_SURFACE_ROOT; -} - -gboolean -_gdk_surface_has_impl (GdkSurface *window) -{ - return gdk_surface_has_impl (window); -} - -static gboolean -gdk_surface_has_no_impl (GdkSurface *window) -{ - return window->impl_window != window; -} - -static void -remove_sibling_overlapped_area (GdkSurface *window, - cairo_region_t *region) -{ - GdkSurface *parent; - GdkSurface *sibling; - cairo_region_t *child_region; - GdkRectangle r; - GList *l; - - parent = window->parent; - - if (gdk_surface_is_toplevel (window)) - return; - - /* Convert from from window coords to parent coords */ - cairo_region_translate (region, window->x, window->y); - - for (l = parent->children; l; l = l->next) - { - sibling = l->data; - - if (sibling == window) - break; - - if (!GDK_SURFACE_IS_MAPPED (sibling) || sibling->input_only) - continue; - - r.x = sibling->x; - r.y = sibling->y; - r.width = sibling->width; - r.height = sibling->height; - - child_region = cairo_region_create_rectangle (&r); - - if (sibling->shape) - { - /* Adjust shape region to parent window coords */ - cairo_region_translate (sibling->shape, sibling->x, sibling->y); - cairo_region_intersect (child_region, sibling->shape); - cairo_region_translate (sibling->shape, -sibling->x, -sibling->y); - } - - cairo_region_subtract (region, child_region); - cairo_region_destroy (child_region); - } - - remove_sibling_overlapped_area (parent, region); - - /* Convert back to window coords */ - cairo_region_translate (region, -window->x, -window->y); -} - -static void -remove_child_area (GdkSurface *window, - gboolean for_input, - cairo_region_t *region) -{ - GdkSurface *child; - cairo_region_t *child_region; - GdkRectangle r; - GList *l; - - for (l = window->children; l; l = l->next) - { - child = l->data; - - /* If region is empty already, no need to do - anything potentially costly */ - if (cairo_region_is_empty (region)) - break; - - if (!GDK_SURFACE_IS_MAPPED (child) || child->input_only) - continue; - - r.x = child->x; - r.y = child->y; - r.width = child->width; - r.height = child->height; - - /* Bail early if child totally outside region */ - if (cairo_region_contains_rectangle (region, &r) == CAIRO_REGION_OVERLAP_OUT) - continue; - - child_region = cairo_region_create_rectangle (&r); - - if (child->shape) - { - /* Adjust shape region to parent window coords */ - cairo_region_translate (child->shape, child->x, child->y); - cairo_region_intersect (child_region, child->shape); - cairo_region_translate (child->shape, -child->x, -child->y); - } - - if (for_input) - { - if (child->input_shape) - cairo_region_intersect (child_region, child->input_shape); - } - - cairo_region_subtract (region, child_region); - cairo_region_destroy (child_region); - } -} - -static gboolean -should_apply_clip_as_shape (GdkSurface *window) -{ - return - gdk_surface_has_impl (window) && - /* Not for non-shaped toplevels */ - (window->shape != NULL || window->applied_shape) && - /* or for foreign windows */ - window->window_type != GDK_SURFACE_FOREIGN && - /* or for the root window */ - window->window_type != GDK_SURFACE_ROOT; -} - -static void -apply_shape (GdkSurface *window, - cairo_region_t *region) -{ - GdkSurfaceImplClass *impl_class; - - /* We trash whether we applied a shape so that - we can avoid unsetting it many times, which - could happen in e.g. apply_clip_as_shape as - windows get resized */ - impl_class = GDK_SURFACE_IMPL_GET_CLASS (window->impl); - if (region) - impl_class->shape_combine_region (window, - region, 0, 0); - else if (window->applied_shape) - impl_class->shape_combine_region (window, - NULL, 0, 0); - - window->applied_shape = region != NULL; -} - -static gboolean -region_rect_equal (const cairo_region_t *region, - const GdkRectangle *rect) -{ - GdkRectangle extents; - - if (cairo_region_num_rectangles (region) != 1) - return FALSE; - - cairo_region_get_extents (region, &extents); - - return extents.x == rect->x && - extents.y == rect->y && - extents.width == rect->width && - extents.height == rect->height; -} - -static void -apply_clip_as_shape (GdkSurface *window) -{ - GdkRectangle r; - cairo_region_t *region; - - r.x = r.y = 0; - r.width = window->width; - r.height = window->height; - - region = cairo_region_copy (window->clip_region); - remove_sibling_overlapped_area (window, region); - - /* We only apply the clip region if would differ - from the actual clip region implied by the size - of the window. This is to avoid unneccessarily - adding meaningless shapes to all native subwindows */ - if (!region_rect_equal (region, &r)) - apply_shape (window, region); - else - apply_shape (window, NULL); - - cairo_region_destroy (region); -} - -static void -recompute_visible_regions_internal (GdkSurface *private, - gboolean recalculate_clip, - gboolean recalculate_children) -{ - GdkRectangle r; - GList *l; - GdkSurface *child; - cairo_region_t *new_clip; - gboolean clip_region_changed; - gboolean abs_pos_changed; - int old_abs_x, old_abs_y; - - old_abs_x = private->abs_x; - old_abs_y = private->abs_y; - - /* Update absolute position */ - if ((gdk_surface_has_impl (private) && - private->window_type != GDK_SURFACE_SUBSURFACE) || - (gdk_surface_is_toplevel (private) && - private->window_type == GDK_SURFACE_SUBSURFACE)) - { - /* Native windows and toplevel subsurfaces start here */ - private->abs_x = 0; - private->abs_y = 0; - } - else - { - private->abs_x = private->parent->abs_x + private->x; - private->abs_y = private->parent->abs_y + private->y; - } - - abs_pos_changed = - private->abs_x != old_abs_x || - private->abs_y != old_abs_y; - - /* Update clip region based on: - * parent clip - * window size/position - */ - clip_region_changed = FALSE; - if (recalculate_clip) - { - if (private->viewable) - { - /* Calculate visible region (sans children) in parent window coords */ - r.x = private->x; - r.y = private->y; - r.width = private->width; - r.height = private->height; - new_clip = cairo_region_create_rectangle (&r); - - if (!gdk_surface_is_toplevel (private)) - cairo_region_intersect (new_clip, private->parent->clip_region); - - /* Convert from parent coords to window coords */ - cairo_region_translate (new_clip, -private->x, -private->y); - - if (should_apply_clip_as_shape (private) && private->shape) - cairo_region_intersect (new_clip, private->shape); - } - else - new_clip = cairo_region_create (); - - if (private->clip_region == NULL || - !cairo_region_equal (private->clip_region, new_clip)) - clip_region_changed = TRUE; - - if (private->clip_region) - cairo_region_destroy (private->clip_region); - private->clip_region = new_clip; - } - - /* Update all children, recursively (except for root, where children are not exact). */ - if ((abs_pos_changed || clip_region_changed || recalculate_children) && - private->window_type != GDK_SURFACE_ROOT) - { - for (l = private->children; l; l = l->next) - { - child = l->data; - /* Only recalculate clip if the the clip region changed, otherwise - * there is no way the child clip region could change (its has not e.g. moved) - * Except if recalculate_children is set to force child updates - */ - recompute_visible_regions_internal (child, - recalculate_clip && (clip_region_changed || recalculate_children), - FALSE); - } - } -} - -/* Call this when private has changed in one or more of these ways: - * size changed - * window moved - * new window added - * stacking order of window changed - * child deleted - * - * It will recalculate abs_x/y and the clip regions - * - * Unless the window didn’t change stacking order or size/pos, pass in TRUE - * for recalculate_siblings. (Mostly used internally for the recursion) - * - * If a child window was removed (and you can’t use that child for - * recompute_visible_regions), pass in TRUE for recalculate_children on the parent - */ -static void -recompute_visible_regions (GdkSurface *private, - gboolean recalculate_children) -{ - GdkSurface *toplevel; - - toplevel = gdk_surface_get_toplevel (private); - toplevel->geometry_dirty = TRUE; - - recompute_visible_regions_internal (private, - TRUE, - recalculate_children); -} - -static void -gdk_surface_clear_old_updated_area (GdkSurface *window) -{ - int i; - - for (i = 0; i < 2; i++) - { - if (window->old_updated_area[i]) - { - cairo_region_destroy (window->old_updated_area[i]); - window->old_updated_area[i] = NULL; - } - } -} - -static void -gdk_surface_append_old_updated_area (GdkSurface *window, - cairo_region_t *region) -{ - if (window->old_updated_area[1]) - cairo_region_destroy (window->old_updated_area[1]); - window->old_updated_area[1] = window->old_updated_area[0]; - window->old_updated_area[0] = cairo_region_reference (region); -} - -void -_gdk_surface_update_size (GdkSurface *window) -{ - gdk_surface_clear_old_updated_area (window); - recompute_visible_regions (window, FALSE); -} - -static GdkEventMask -get_native_device_event_mask (GdkSurface *private, - GdkDevice *device) -{ - GdkEventMask event_mask; - - if (device) - event_mask = GPOINTER_TO_INT (g_hash_table_lookup (private->device_events, device)); - else - event_mask = private->event_mask; - - if (private->window_type == GDK_SURFACE_ROOT || - private->window_type == GDK_SURFACE_FOREIGN) - return event_mask; - else - { - GdkEventMask mask; - - mask = private->event_mask; - - /* We need thse for all native windows so we can - emulate events on children: */ - mask |= - GDK_EXPOSURE_MASK | - GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK | - GDK_TOUCH_MASK | - GDK_POINTER_MOTION_MASK | - GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | - GDK_SCROLL_MASK; - - return mask; - } -} - -static GdkEventMask -get_native_event_mask (GdkSurface *private) -{ - return get_native_device_event_mask (private, NULL); -} - -GdkSurface* -gdk_surface_new (GdkDisplay *display, - GdkSurface *parent, - GdkSurfaceAttr *attributes) -{ - GdkSurface *window; - gboolean native; - GdkEventMask event_mask; - - g_return_val_if_fail (attributes != NULL, NULL); - - if (parent != NULL && GDK_SURFACE_DESTROYED (parent)) - { - g_warning ("gdk_surface_new(): parent is destroyed"); - return NULL; - } - - window = _gdk_display_create_window (display); - - window->parent = parent; - - window->accept_focus = TRUE; - window->focus_on_map = TRUE; - - window->x = attributes->x; - window->y = attributes->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) - { - /* Backwards compatiblity - we've always ignored - * attributes->window_type for input-only windows - * before - */ - if (parent == NULL) - window->window_type = GDK_SURFACE_TEMP; - else - window->window_type = GDK_SURFACE_CHILD; - } - else - window->window_type = attributes->window_type; - - /* Sanity checks */ - switch (window->window_type) - { - case GDK_SURFACE_TOPLEVEL: - case GDK_SURFACE_TEMP: - if (parent != NULL && GDK_SURFACE_TYPE (parent) != GDK_SURFACE_ROOT) - g_warning (G_STRLOC "Toplevel windows must be created as children of\n" - "a window of type GDK_SURFACE_ROOT"); - break; - case GDK_SURFACE_SUBSURFACE: -#ifdef GDK_WINDOWING_WAYLAND - if (!GDK_IS_WAYLAND_DISPLAY (display)) - { - g_warning (G_STRLOC "Subsurface windows can only be used on Wayland"); - return NULL; - } -#endif - break; - case GDK_SURFACE_CHILD: - if (GDK_SURFACE_TYPE (parent) == GDK_SURFACE_ROOT || - GDK_SURFACE_TYPE (parent) == GDK_SURFACE_FOREIGN) - { - g_warning (G_STRLOC "Child windows must not be created as children of\n" - "a window of type GDK_SURFACE_ROOT or GDK_SURFACE_FOREIGN"); - return NULL; - } - break; - default: - g_warning (G_STRLOC "cannot make windows of type %d", window->window_type); - return NULL; - } - - window->event_mask = GDK_ALL_EVENTS_MASK; - - if (attributes->wclass == GDK_INPUT_OUTPUT) - { - window->input_only = FALSE; - } - else - { - window->input_only = TRUE; - } - - native = FALSE; - - if (window->parent != NULL) - window->parent->children = g_list_concat (&window->children_list_node, window->parent->children); - else - { - GdkFrameClock *frame_clock = g_object_new (GDK_TYPE_FRAME_CLOCK_IDLE, NULL); - gdk_surface_set_frame_clock (window, frame_clock); - g_object_unref (frame_clock); - - native = TRUE; /* Always use native windows for toplevels */ - } - -#ifdef GDK_WINDOWING_WAYLAND - if (window->window_type == GDK_SURFACE_SUBSURFACE) - native = TRUE; /* Always use native windows for subsurfaces as well */ -#endif - - if (native) - { - event_mask = get_native_event_mask (window); - - /* Create the impl */ - _gdk_display_create_window_impl (display, window, parent, event_mask, attributes); - window->impl_window = window; - } - else - { - window->impl_window = g_object_ref (window->parent->impl_window); - window->impl = g_object_ref (window->impl_window->impl); - } - - recompute_visible_regions (window, FALSE); - - g_signal_connect (display, "seat-removed", G_CALLBACK (seat_removed_cb), window); - - return window; -} - -/** - * gdk_surface_new_toplevel: (constructor) - * @display: the display to create the window on - * @width: width of new window - * @height: height of new window - * - * Creates a new toplevel window. The window will be managed by the window - * manager. - * - * Returns: (transfer full): the new #GdkSurface - **/ -GdkSurface * -gdk_surface_new_toplevel (GdkDisplay *display, - gint width, - gint height) -{ - GdkSurfaceAttr attr; - - g_return_val_if_fail (GDK_IS_DISPLAY (display), NULL); - - attr.wclass = GDK_INPUT_OUTPUT; - attr.x = 0; - attr.y = 0; - attr.width = width; - attr.height = height; - attr.window_type = GDK_SURFACE_TOPLEVEL; - - return gdk_surface_new (display, NULL, &attr); -} - -/** - * gdk_surface_new_popup: (constructor) - * @display: the display to create the window on - * @position: position of the window on screen - * - * Creates a new toplevel popup window. The window will bypass window - * management. - * - * Returns: (transfer full): the new #GdkSurface - **/ -GdkSurface * -gdk_surface_new_popup (GdkDisplay *display, - const GdkRectangle *position) -{ - GdkSurfaceAttr attr; - - g_return_val_if_fail (GDK_IS_DISPLAY (display), NULL); - g_return_val_if_fail (position != NULL, NULL); - - attr.wclass = GDK_INPUT_OUTPUT; - attr.x = position->x; - attr.y = position->y; - attr.width = position->width; - attr.height = position->height; - attr.window_type = GDK_SURFACE_TEMP; - - return gdk_surface_new (display, NULL, &attr); -} - -/** - * gdk_surface_new_temp: (constructor) - * @display: the display to create the window on - * - * Creates a new toplevel temporary window. The window will be - * situated off-screen and not handle output. - * - * You most likely do not want to use this function. - * - * Returns: (transfer full): the new #GdkSurface - **/ -GdkSurface * -gdk_surface_new_temp (GdkDisplay *display) -{ - GdkSurfaceAttr attr; - - g_return_val_if_fail (GDK_IS_DISPLAY (display), NULL); - - attr.wclass = GDK_INPUT_ONLY; - attr.x = -100; - attr.y = -100; - attr.width = 10; - attr.height = 10; - attr.window_type = GDK_SURFACE_TEMP; - - return gdk_surface_new (display, NULL, &attr); -} - -/** - * gdk_surface_new_child: (constructor) - * @parent: the parent window - * @position: placement of the window inside @parent - * - * Creates a new client-side child window. - * - * Returns: (transfer full): the new #GdkSurface - **/ -GdkSurface * -gdk_surface_new_child (GdkSurface *parent, - const GdkRectangle *position) -{ - GdkSurfaceAttr attr; - - g_return_val_if_fail (GDK_IS_SURFACE (parent), NULL); - - attr.wclass = GDK_INPUT_OUTPUT; - attr.x = position->x; - attr.y = position->y; - attr.width = position->width; - attr.height = position->height; - attr.window_type = GDK_SURFACE_CHILD; - - return gdk_surface_new (gdk_surface_get_display (parent), parent, &attr); -} - -static void -update_pointer_info_foreach (GdkDisplay *display, - GdkDevice *device, - GdkPointerSurfaceInfo *pointer_info, - gpointer user_data) -{ - GdkSurface *window = user_data; - - if (pointer_info->window_under_pointer == window) - { - g_object_unref (pointer_info->window_under_pointer); - pointer_info->window_under_pointer = NULL; - } -} - -static void -window_remove_from_pointer_info (GdkSurface *window, - GdkDisplay *display) -{ - _gdk_display_pointer_info_foreach (display, - update_pointer_info_foreach, - window); -} - -static void -gdk_surface_free_current_paint (GdkSurface *window) -{ - cairo_surface_destroy (window->current_paint.surface); - window->current_paint.surface = NULL; - - cairo_region_destroy (window->current_paint.region); - window->current_paint.region = NULL; - - window->current_paint.surface_needs_composite = FALSE; -} - -/** - * _gdk_surface_destroy_hierarchy: - * @window: a #GdkSurface - * @recursing: If %TRUE, then this is being called because a parent - * was destroyed. - * @recursing_native: If %TRUE, then this is being called because a native parent - * was destroyed. This generally means that the call to the - * windowing system to destroy the window can be omitted, since - * it will be destroyed as a result of the parent being destroyed. - * Unless @foreign_destroy. - * @foreign_destroy: If %TRUE, the window or a parent was destroyed by some - * external agency. The window has already been destroyed and no - * windowing system calls should be made. (This may never happen - * for some windowing systems.) - * - * Internal function to destroy a window. Like gdk_surface_destroy(), - * but does not drop the reference count created by gdk_surface_new(). - **/ -static void -_gdk_surface_destroy_hierarchy (GdkSurface *window, - gboolean recursing, - gboolean recursing_native, - gboolean foreign_destroy) -{ - GdkSurfaceImplClass *impl_class; - GdkSurface *temp_window; - GdkDisplay *display; - GList *tmp; - - g_return_if_fail (GDK_IS_SURFACE (window)); - - if (GDK_SURFACE_DESTROYED (window)) - return; - - display = gdk_surface_get_display (window); - - switch (window->window_type) - { - default: - g_assert_not_reached (); - break; - - case GDK_SURFACE_ROOT: - if (!gdk_display_is_closed (display)) - { - g_error ("attempted to destroy root window"); - break; - } - /* else fall thru */ - case GDK_SURFACE_TOPLEVEL: - case GDK_SURFACE_CHILD: - case GDK_SURFACE_TEMP: - case GDK_SURFACE_FOREIGN: - case GDK_SURFACE_SUBSURFACE: - if (window->window_type == GDK_SURFACE_FOREIGN && !foreign_destroy) - { - } - else - { - if (window->parent) - { - if (window->parent->children) - window->parent->children = g_list_remove_link (window->parent->children, &window->children_list_node); - - if (!recursing && - GDK_SURFACE_IS_MAPPED (window)) - { - recompute_visible_regions (window, FALSE); - gdk_surface_invalidate_in_parent (window); - } - } - - if (window->gl_paint_context) - { - /* Make sure to destroy if current */ - g_object_run_dispose (G_OBJECT (window->gl_paint_context)); - g_object_unref (window->gl_paint_context); - window->gl_paint_context = NULL; - } - - if (window->frame_clock) - { - g_object_run_dispose (G_OBJECT (window->frame_clock)); - gdk_surface_set_frame_clock (window, NULL); - } - - gdk_surface_free_current_paint (window); - - if (window->window_type == GDK_SURFACE_FOREIGN) - g_assert (window->children == NULL); - else - { - tmp = window->children; - window->children = NULL; - /* No need to free children list, its all made up of in-struct nodes */ - - while (tmp) - { - temp_window = tmp->data; - tmp = tmp->next; - - if (temp_window) - _gdk_surface_destroy_hierarchy (temp_window, - TRUE, - recursing_native || gdk_surface_has_impl (window), - foreign_destroy); - } - } - - _gdk_surface_clear_update_area (window); - - impl_class = GDK_SURFACE_IMPL_GET_CLASS (window->impl); - - if (gdk_surface_has_impl (window)) - impl_class->destroy (window, recursing_native, foreign_destroy); - else - { - /* hide to make sure we repaint and break grabs */ - gdk_surface_hide (window); - } - - window->state |= GDK_SURFACE_STATE_WITHDRAWN; - window->parent = NULL; - window->destroyed = TRUE; - - window_remove_from_pointer_info (window, display); - - if (window->clip_region) - { - cairo_region_destroy (window->clip_region); - window->clip_region = NULL; - } - - g_object_notify_by_pspec (G_OBJECT (window), properties[PROP_STATE]); - } - break; - } -} - -/** - * _gdk_surface_destroy: - * @window: a #GdkSurface - * @foreign_destroy: If %TRUE, the window or a parent was destroyed by some - * external agency. The window has already been destroyed and no - * windowing system calls should be made. (This may never happen - * for some windowing systems.) - * - * Internal function to destroy a window. Like gdk_surface_destroy(), - * but does not drop the reference count created by gdk_surface_new(). - **/ -void -_gdk_surface_destroy (GdkSurface *window, - gboolean foreign_destroy) -{ - _gdk_surface_destroy_hierarchy (window, FALSE, FALSE, foreign_destroy); -} - -/** - * gdk_surface_destroy: - * @window: a #GdkSurface - * - * Destroys the window system resources associated with @window and decrements @window's - * reference count. The window system resources for all children of @window are also - * destroyed, but the children’s reference counts are not decremented. - * - * Note that a window will not be destroyed automatically when its reference count - * reaches zero. You must call this function yourself before that happens. - * - **/ -void -gdk_surface_destroy (GdkSurface *window) -{ - _gdk_surface_destroy_hierarchy (window, FALSE, FALSE, FALSE); - g_object_unref (window); -} - -/** - * gdk_surface_set_user_data: - * @window: a #GdkSurface - * @user_data: (allow-none) (type GObject.Object): user data - * - * For most purposes this function is deprecated in favor of - * g_object_set_data(). However, for historical reasons GTK+ stores - * the #GtkWidget that owns a #GdkSurface as user data on the - * #GdkSurface. So, custom widget implementations should use - * this function for that. If GTK+ receives an event for a #GdkSurface, - * and the user data for the window is non-%NULL, GTK+ will assume the - * user data is a #GtkWidget, and forward the event to that widget. - * - **/ -void -gdk_surface_set_user_data (GdkSurface *window, - gpointer user_data) -{ - g_return_if_fail (GDK_IS_SURFACE (window)); - - window->user_data = user_data; -} - -/** - * gdk_surface_get_user_data: - * @window: a #GdkSurface - * @data: (out): return location for user data - * - * Retrieves the user data for @window, which is normally the widget - * that @window belongs to. See gdk_surface_set_user_data(). - * - **/ -void -gdk_surface_get_user_data (GdkSurface *window, - gpointer *data) -{ - *data = window->user_data; -} - -/** - * gdk_surface_get_window_type: - * @window: a #GdkSurface - * - * Gets the type of the window. See #GdkSurfaceType. - * - * Returns: type of window - **/ -GdkSurfaceType -gdk_surface_get_window_type (GdkSurface *window) -{ - g_return_val_if_fail (GDK_IS_SURFACE (window), (GdkSurfaceType) -1); - - return GDK_SURFACE_TYPE (window); -} - -/** - * gdk_surface_get_display: - * @window: a #GdkSurface - * - * Gets the #GdkDisplay associated with a #GdkSurface. - * - * Returns: (transfer none): the #GdkDisplay associated with @window - **/ -GdkDisplay * -gdk_surface_get_display (GdkSurface *window) -{ - g_return_val_if_fail (GDK_IS_SURFACE (window), NULL); - - return window->display; -} -/** - * gdk_surface_is_destroyed: - * @window: a #GdkSurface - * - * Check to see if a window is destroyed.. - * - * Returns: %TRUE if the window is destroyed - **/ -gboolean -gdk_surface_is_destroyed (GdkSurface *window) -{ - return GDK_SURFACE_DESTROYED (window); -} - -/** - * gdk_surface_has_native: - * @window: a #GdkSurface - * - * Checks whether the window has a native window or not. - * - * Returns: %TRUE if the @window has a native window, %FALSE otherwise. - */ -gboolean -gdk_surface_has_native (GdkSurface *window) -{ - g_return_val_if_fail (GDK_IS_SURFACE (window), FALSE); - - return window->parent == NULL || window->parent->impl != window->impl; -} - -/** - * gdk_surface_get_position: - * @window: a #GdkSurface - * @x: (out) (allow-none): X coordinate of window - * @y: (out) (allow-none): Y coordinate of window - * - * Obtains the position of the window as reported in the - * most-recently-processed #GdkEventConfigure. Contrast with - * gdk_surface_get_geometry() which queries the X server for the - * current window position, regardless of which events have been - * received or processed. - * - * The position coordinates are relative to the window’s parent window. - * - **/ -void -gdk_surface_get_position (GdkSurface *window, - gint *x, - gint *y) -{ - g_return_if_fail (GDK_IS_SURFACE (window)); - - if (x) - *x = window->x; - if (y) - *y = window->y; -} - -/** - * gdk_surface_get_parent: - * @window: a #GdkSurface - * - * Obtains the parent of @window, as known to GDK. Does not query the - * X server; thus this returns the parent as passed to gdk_surface_new(), - * not the actual parent. This should never matter unless you’re using - * Xlib calls mixed with GDK calls on the X11 platform. It may also - * matter for toplevel windows, because the window manager may choose - * to reparent them. - * - * Returns: (transfer none): parent of @window - **/ -GdkSurface* -gdk_surface_get_parent (GdkSurface *window) -{ - g_return_val_if_fail (GDK_IS_SURFACE (window), NULL); - - if (gdk_surface_is_subsurface (window)) - return window->transient_for; - else - return window->parent; -} - -/** - * gdk_surface_get_toplevel: - * @window: a #GdkSurface - * - * Gets the toplevel window that’s an ancestor of @window. - * - * Any window type but %GDK_SURFACE_CHILD is considered a - * toplevel window, as is a %GDK_SURFACE_CHILD window that - * has a root window as parent. - * - * Returns: (transfer none): the toplevel window containing @window - **/ -GdkSurface * -gdk_surface_get_toplevel (GdkSurface *window) -{ - g_return_val_if_fail (GDK_IS_SURFACE (window), NULL); - - while (window->window_type == GDK_SURFACE_CHILD || - window->window_type == GDK_SURFACE_SUBSURFACE) - { - if (gdk_surface_is_toplevel (window)) - break; - window = window->parent; - } - - return window; -} - -/** - * gdk_surface_get_children: - * @window: a #GdkSurface - * - * Gets the list of children of @window known to GDK. - * This function only returns children created via GDK, - * so for example it’s useless when used with the root window; - * it only returns windows an application created itself. - * - * The returned list must be freed, but the elements in the - * list need not be. - * - * Returns: (transfer container) (element-type GdkSurface): - * list of child windows inside @window - **/ -GList* -gdk_surface_get_children (GdkSurface *window) -{ - g_return_val_if_fail (GDK_IS_SURFACE (window), NULL); - - if (GDK_SURFACE_DESTROYED (window)) - return NULL; - - return g_list_copy (window->children); -} - -/** - * gdk_surface_peek_children: - * @window: a #GdkSurface - * - * Like gdk_surface_get_children(), but does not copy the list of - * children, so the list does not need to be freed. - * - * Returns: (transfer none) (element-type GdkSurface): - * a reference to the list of child windows in @window - **/ -GList * -gdk_surface_peek_children (GdkSurface *window) -{ - g_return_val_if_fail (GDK_IS_SURFACE (window), NULL); - - if (GDK_SURFACE_DESTROYED (window)) - return NULL; - - return window->children; -} - - -/** - * gdk_surface_get_children_with_user_data: - * @window: a #GdkSurface - * @user_data: user data to look for - * - * Gets the list of children of @window known to GDK with a - * particular @user_data set on it. - * - * The returned list must be freed, but the elements in the - * list need not be. - * - * The list is returned in (relative) stacking order, i.e. the - * lowest window is first. - * - * Returns: (transfer container) (element-type GdkSurface): - * list of child windows inside @window - **/ -GList * -gdk_surface_get_children_with_user_data (GdkSurface *window, - gpointer user_data) -{ - GdkSurface *child; - GList *res, *l; - - g_return_val_if_fail (GDK_IS_SURFACE (window), NULL); - - if (GDK_SURFACE_DESTROYED (window)) - return NULL; - - res = NULL; - for (l = window->children; l != NULL; l = l->next) - { - child = l->data; - - if (child->user_data == user_data) - res = g_list_prepend (res, child); - } - - return res; -} - - -/** - * gdk_surface_is_visible: - * @window: a #GdkSurface - * - * Checks whether the window has been mapped (with gdk_surface_show() or - * gdk_surface_show_unraised()). - * - * Returns: %TRUE if the window is mapped - **/ -gboolean -gdk_surface_is_visible (GdkSurface *window) -{ - g_return_val_if_fail (GDK_IS_SURFACE (window), FALSE); - - return GDK_SURFACE_IS_MAPPED (window); -} - -/** - * gdk_surface_is_viewable: - * @window: a #GdkSurface - * - * Check if the window and all ancestors of the window are - * mapped. (This is not necessarily "viewable" in the X sense, since - * we only check as far as we have GDK window parents, not to the root - * window.) - * - * Returns: %TRUE if the window is viewable - **/ -gboolean -gdk_surface_is_viewable (GdkSurface *window) -{ - g_return_val_if_fail (GDK_IS_SURFACE (window), FALSE); - - if (window->destroyed) - return FALSE; - - return window->viewable; -} - -/** - * gdk_surface_get_state: - * @window: a #GdkSurface - * - * Gets the bitwise OR of the currently active window state flags, - * from the #GdkSurfaceState enumeration. - * - * Returns: window state bitfield - **/ -GdkSurfaceState -gdk_surface_get_state (GdkSurface *window) -{ - g_return_val_if_fail (GDK_IS_SURFACE (window), FALSE); - - return window->state; -} - -static cairo_content_t -gdk_surface_get_content (GdkSurface *window) -{ - cairo_surface_t *surface; - cairo_content_t content; - - g_return_val_if_fail (GDK_IS_SURFACE (window), 0); - - surface = gdk_surface_ref_impl_surface (window); - content = cairo_surface_get_content (surface); - cairo_surface_destroy (surface); - - return content; -} - -static cairo_surface_t * -gdk_surface_ref_impl_surface (GdkSurface *window) -{ - return GDK_SURFACE_IMPL_GET_CLASS (window->impl)->ref_cairo_surface (gdk_surface_get_impl_window (window)); -} - -GdkGLContext * -gdk_surface_get_paint_gl_context (GdkSurface *window, - GError **error) -{ - GError *internal_error = NULL; - - if (GDK_DISPLAY_DEBUG_CHECK (window->display, GL_DISABLE)) - { - g_set_error_literal (error, GDK_GL_ERROR, - GDK_GL_ERROR_NOT_AVAILABLE, - _("GL support disabled via GDK_DEBUG")); - return NULL; - } - - if (window->impl_window->gl_paint_context == NULL) - { - GdkSurfaceImplClass *impl_class = GDK_SURFACE_IMPL_GET_CLASS (window->impl); - - if (impl_class->create_gl_context == NULL) - { - g_set_error_literal (error, GDK_GL_ERROR, GDK_GL_ERROR_NOT_AVAILABLE, - _("The current backend does not support OpenGL")); - return NULL; - } - - window->impl_window->gl_paint_context = - impl_class->create_gl_context (window->impl_window, - TRUE, - NULL, - &internal_error); - } - - if (internal_error != NULL) - { - g_propagate_error (error, internal_error); - g_clear_object (&(window->impl_window->gl_paint_context)); - return NULL; - } - - gdk_gl_context_realize (window->impl_window->gl_paint_context, &internal_error); - if (internal_error != NULL) - { - g_propagate_error (error, internal_error); - g_clear_object (&(window->impl_window->gl_paint_context)); - return NULL; - } - - return window->impl_window->gl_paint_context; -} - -/** - * gdk_surface_create_gl_context: - * @window: a #GdkSurface - * @error: return location for an error - * - * Creates a new #GdkGLContext matching the - * framebuffer format to the visual of the #GdkSurface. The context - * is disconnected from any particular window or surface. - * - * If the creation of the #GdkGLContext failed, @error will be set. - * - * Before using the returned #GdkGLContext, you will need to - * call gdk_gl_context_make_current() or gdk_gl_context_realize(). - * - * Returns: (transfer full): the newly created #GdkGLContext, or - * %NULL on error - **/ -GdkGLContext * -gdk_surface_create_gl_context (GdkSurface *window, - GError **error) -{ - GdkGLContext *paint_context; - - g_return_val_if_fail (GDK_IS_SURFACE (window), NULL); - g_return_val_if_fail (error == NULL || *error == NULL, NULL); - - paint_context = gdk_surface_get_paint_gl_context (window, error); - if (paint_context == NULL) - return NULL; - - return GDK_SURFACE_IMPL_GET_CLASS (window->impl)->create_gl_context (window->impl_window, - FALSE, - paint_context, - error); -} - -/** - * gdk_surface_create_vulkan_context: - * @window: a #GdkSurface - * @error: return location for an error - * - * Creates a new #GdkVulkanContext for rendering on @window. - * - * If the creation of the #GdkVulkanContext failed, @error will be set. - * - * Returns: (transfer full): the newly created #GdkVulkanContext, or - * %NULL on error - **/ -GdkVulkanContext * -gdk_surface_create_vulkan_context (GdkSurface *window, - GError **error) -{ - GdkDisplay *display; - - g_return_val_if_fail (GDK_IS_SURFACE (window), NULL); - g_return_val_if_fail (error == NULL || *error == NULL, NULL); - - if (GDK_DISPLAY_DEBUG_CHECK (window->display, VULKAN_DISABLE)) - { - g_set_error_literal (error, GDK_VULKAN_ERROR, GDK_VULKAN_ERROR_NOT_AVAILABLE, - _("Vulkan support disabled via GDK_DEBUG")); - return NULL; - } - - display = gdk_surface_get_display (window); - - if (GDK_DISPLAY_GET_CLASS (display)->vk_extension_name == NULL) - { - g_set_error (error, GDK_VULKAN_ERROR, GDK_VULKAN_ERROR_UNSUPPORTED, - "The %s backend has no Vulkan support.", G_OBJECT_TYPE_NAME (display)); - return FALSE; - } - - return g_initable_new (GDK_DISPLAY_GET_CLASS (display)->vk_context_type, - NULL, - error, - "window", window, - NULL); -} - -static void -gdk_surface_begin_paint_internal (GdkSurface *window, - const cairo_region_t *region) -{ - GdkRectangle clip_box; - GdkSurfaceImplClass *impl_class; - double sx, sy; - gboolean needs_surface; - cairo_content_t surface_content; - - if (window->current_paint.surface != NULL) - { - g_warning ("A paint operation on the window is alredy in progress. " - "This is not allowed."); - return; - } - - impl_class = GDK_SURFACE_IMPL_GET_CLASS (window->impl); - - needs_surface = TRUE; - if (impl_class->begin_paint) - needs_surface = impl_class->begin_paint (window); - - window->current_paint.region = cairo_region_copy (region); - cairo_region_intersect (window->current_paint.region, window->clip_region); - cairo_region_get_extents (window->current_paint.region, &clip_box); - - surface_content = gdk_surface_get_content (window); - - if (needs_surface) - { - window->current_paint.surface = gdk_surface_create_similar_surface (window, - surface_content, - MAX (clip_box.width, 1), - MAX (clip_box.height, 1)); - sx = sy = 1; - cairo_surface_get_device_scale (window->current_paint.surface, &sx, &sy); - cairo_surface_set_device_offset (window->current_paint.surface, -clip_box.x*sx, -clip_box.y*sy); - gdk_cairo_surface_mark_as_direct (window->current_paint.surface, window); - - window->current_paint.surface_needs_composite = TRUE; - } - else - { - window->current_paint.surface = gdk_surface_ref_impl_surface (window); - window->current_paint.surface_needs_composite = FALSE; - } - - if (!cairo_region_is_empty (window->current_paint.region)) - gdk_surface_clear_backing_region (window); -} - -static void -gdk_surface_end_paint_internal (GdkSurface *window) -{ - GdkSurfaceImplClass *impl_class; - cairo_t *cr; - - if (window->current_paint.surface == NULL) - { - g_warning (G_STRLOC": no preceding call to gdk_surface_begin_draw_frame(), see documentation"); - return; - } - - impl_class = GDK_SURFACE_IMPL_GET_CLASS (window->impl); - - if (impl_class->end_paint) - impl_class->end_paint (window); - - if (window->current_paint.surface_needs_composite) - { - cairo_surface_t *surface; - - surface = gdk_surface_ref_impl_surface (window); - cr = cairo_create (surface); - - cairo_set_source_surface (cr, window->current_paint.surface, 0, 0); - gdk_cairo_region (cr, window->current_paint.region); - cairo_clip (cr); - - cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE); - cairo_paint (cr); - - cairo_destroy (cr); - - cairo_surface_flush (surface); - cairo_surface_destroy (surface); - } - - gdk_surface_free_current_paint (window); -} - -/** - * gdk_surface_begin_draw_frame: - * @window: a #GdkSurface - * @context: (allow-none): the context used to draw the frame - * @region: a Cairo region - * - * Indicates that you are beginning the process of redrawing @region - * on @window, and provides you with a #GdkDrawingContext. - * - * If @window is a top level #GdkSurface, backed by a native window - * implementation, a backing store (offscreen buffer) large enough to - * contain @region will be created. The backing store will be initialized - * with the background color or background surface for @window. Then, all - * drawing operations performed on @window will be diverted to the - * backing store. When you call gdk_surface_end_frame(), the contents of - * the backing store will be copied to @window, making it visible - * on screen. Only the part of @window contained in @region will be - * modified; that is, drawing operations are clipped to @region. - * - * The net result of all this is to remove flicker, because the user - * sees the finished product appear all at once when you call - * gdk_surface_end_draw_frame(). If you draw to @window directly without - * calling gdk_surface_begin_draw_frame(), the user may see flicker - * as individual drawing operations are performed in sequence. - * - * When using GTK+, the widget system automatically places calls to - * gdk_surface_begin_draw_frame() and gdk_surface_end_draw_frame() around - * emissions of the `GtkWidget::draw` signal. That is, if you’re - * drawing the contents of the widget yourself, you can assume that the - * widget has a cleared background, is already set as the clip region, - * and already has a backing store. Therefore in most cases, application - * code in GTK does not need to call gdk_surface_begin_draw_frame() - * explicitly. - * - * Returns: (transfer none): a #GdkDrawingContext context that should be - * used to draw the contents of the window; the returned context is owned - * by GDK. - */ -GdkDrawingContext * -gdk_surface_begin_draw_frame (GdkSurface *window, - GdkDrawContext *draw_context, - const cairo_region_t *region) -{ - GdkDrawingContext *context; - cairo_region_t *real_region; - - g_return_val_if_fail (GDK_IS_SURFACE (window), NULL); - g_return_val_if_fail (gdk_surface_has_native (window), NULL); - g_return_val_if_fail (gdk_surface_is_toplevel (window), NULL); - g_return_val_if_fail (region != NULL, NULL); - if (draw_context != NULL) - { - g_return_val_if_fail (GDK_IS_DRAW_CONTEXT (draw_context), NULL); - g_return_val_if_fail (gdk_draw_context_get_window (draw_context) == window, NULL); - } - - if (GDK_SURFACE_DESTROYED (window)) - return NULL; - - if (window->drawing_context != NULL) - { - g_critical ("The window %p already has a drawing context. You cannot " - "call gdk_surface_begin_draw_frame() without calling " - "gdk_surface_end_draw_frame() first.", window); - return NULL; - } - - real_region = cairo_region_copy (region); - - if (draw_context) - gdk_draw_context_begin_frame (draw_context, real_region); - else - gdk_surface_begin_paint_internal (window, real_region); - - context = g_object_new (GDK_TYPE_DRAWING_CONTEXT, - "window", window, - "paint-context", draw_context, - "clip", real_region, - NULL); - - /* Do not take a reference, to avoid creating cycles */ - window->drawing_context = context; - - cairo_region_destroy (real_region); - - return context; -} - -/** - * gdk_surface_end_draw_frame: - * @window: a #GdkSurface - * @context: the #GdkDrawingContext created by gdk_surface_begin_draw_frame() - * - * Indicates that the drawing of the contents of @window started with - * gdk_surface_begin_frame() has been completed. - * - * This function will take care of destroying the #GdkDrawingContext. - * - * It is an error to call this function without a matching - * gdk_surface_begin_frame() first. - */ -void -gdk_surface_end_draw_frame (GdkSurface *window, - GdkDrawingContext *context) -{ - GdkDrawContext *paint_context; - - g_return_if_fail (GDK_IS_SURFACE (window)); - g_return_if_fail (GDK_IS_DRAWING_CONTEXT (context)); - - if (GDK_SURFACE_DESTROYED (window)) - return; - - if (window->drawing_context == NULL) - { - g_critical ("The window %p has no drawing context. You must call " - "gdk_surface_begin_draw_frame() before calling " - "gdk_surface_end_draw_frame().", window); - return; - } - g_return_if_fail (window->drawing_context == context); - - paint_context = gdk_drawing_context_get_paint_context (context); - if (paint_context) - { - cairo_region_t *clip = gdk_drawing_context_get_clip (context); - - gdk_draw_context_end_frame (paint_context, - clip, - window->active_update_area); - - cairo_region_destroy (clip); - } - else - { - gdk_surface_end_paint_internal (window); - } - - window->drawing_context = NULL; - - g_object_unref (context); -} - -/*< private > - * gdk_surface_get_current_paint_region: - * @window: a #GdkSurface - * - * Retrieves a copy of the current paint region. - * - * Returns: (transfer full): a Cairo region - */ -cairo_region_t * -gdk_surface_get_current_paint_region (GdkSurface *window) -{ - cairo_region_t *region; - - if (window->impl_window->current_paint.region != NULL) - { - region = cairo_region_copy (window->impl_window->current_paint.region); - cairo_region_translate (region, -window->abs_x, -window->abs_y); - } - else - { - region = cairo_region_copy (window->clip_region); - } - - return region; -} - -/*< private > - * gdk_surface_get_drawing_context: - * @window: a #GdkSurface - * - * Retrieves the #GdkDrawingContext associated to @window by - * gdk_surface_begin_draw_frame(). - * - * Returns: (transfer none) (nullable): a #GdkDrawingContext, if any is set - */ -GdkDrawingContext * -gdk_surface_get_drawing_context (GdkSurface *window) -{ - g_return_val_if_fail (GDK_IS_SURFACE (window), NULL); - - if (GDK_SURFACE_DESTROYED (window)) - return NULL; - - return window->drawing_context; -} - -/** - * gdk_surface_get_clip_region: - * @window: a #GdkSurface - * - * Computes the region of a window that potentially can be written - * to by drawing primitives. This region may not take into account - * other factors such as if the window is obscured by other windows, - * but no area outside of this region will be affected by drawing - * primitives. - * - * Returns: a #cairo_region_t. This must be freed with cairo_region_destroy() - * when you are done. - **/ -cairo_region_t* -gdk_surface_get_clip_region (GdkSurface *window) -{ - cairo_region_t *result; - - g_return_val_if_fail (GDK_SURFACE (window), NULL); - - result = cairo_region_copy (window->clip_region); - - if (window->current_paint.region != NULL) - cairo_region_intersect (result, window->current_paint.region); - - return result; -} - -/** - * gdk_surface_get_visible_region: - * @window: a #GdkSurface - * - * Computes the region of the @window that is potentially visible. - * This does not necessarily take into account if the window is - * obscured by other windows, but no area outside of this region - * is visible. - * - * Returns: a #cairo_region_t. This must be freed with cairo_region_destroy() - * when you are done. - **/ -cairo_region_t * -gdk_surface_get_visible_region (GdkSurface *window) -{ - g_return_val_if_fail (GDK_IS_SURFACE (window), NULL); - - return cairo_region_copy (window->clip_region); -} - -static void -gdk_surface_clear_backing_region (GdkSurface *window) -{ - cairo_t *cr; - - if (GDK_SURFACE_DESTROYED (window)) - return; - - cr = cairo_create (window->current_paint.surface); - - cairo_set_operator (cr, CAIRO_OPERATOR_CLEAR); - gdk_cairo_region (cr, window->current_paint.region); - cairo_fill (cr); - - cairo_destroy (cr); -} - -/* This returns either the current working surface on the paint stack - * or the actual impl surface of the window. This should not be used - * from very many places: be careful! */ -static cairo_surface_t * -ref_window_surface (GdkSurface *window) -{ - if (window->impl_window->current_paint.surface) - return cairo_surface_reference (window->impl_window->current_paint.surface); - else - return gdk_surface_ref_impl_surface (window); -} - -/* This is used in places like gdk_cairo_set_source_window and - * other places to take "screenshots" of windows. Thus, we allow - * it to be used outside of a begin_paint / end_paint. */ -cairo_surface_t * -_gdk_surface_ref_cairo_surface (GdkSurface *window) -{ - cairo_surface_t *surface; - - g_return_val_if_fail (GDK_IS_SURFACE (window), NULL); - - surface = ref_window_surface (window); - - if (gdk_surface_has_impl (window)) - { - return surface; - } - else - { - cairo_surface_t *subsurface; - subsurface = cairo_surface_create_for_rectangle (surface, - window->abs_x, - window->abs_y, - window->width, - window->height); - cairo_surface_destroy (surface); - return subsurface; - } -} - -/* Code for dirty-region queueing - */ -static GSList *update_windows = NULL; - -static inline gboolean -gdk_surface_is_ancestor (GdkSurface *window, - GdkSurface *ancestor) -{ - while (window) - { - GdkSurface *parent = window->parent; - - if (parent == ancestor) - return TRUE; - - window = parent; - } - - return FALSE; -} - -static void -gdk_surface_add_update_window (GdkSurface *window) -{ - GSList *tmp; - GSList *prev = NULL; - gboolean has_ancestor_in_list = FALSE; - - /* Check whether "window" is already in "update_windows" list. - * It could be added during execution of gtk_widget_destroy() when - * setting focus widget to NULL and redrawing old focus widget. - * See bug 711552. - */ - tmp = g_slist_find (update_windows, window); - if (tmp != NULL) - return; - - for (tmp = update_windows; tmp; tmp = tmp->next) - { - GdkSurface *parent = window->parent; - - /* check if tmp is an ancestor of "window"; if it is, set a - * flag indicating that all following windows are either - * children of "window" or from a differen hierarchy - */ - if (!has_ancestor_in_list && gdk_surface_is_ancestor (window, tmp->data)) - has_ancestor_in_list = TRUE; - - /* insert in reverse stacking order when adding around siblings, - * so processing updates properly paints over lower stacked windows - */ - if (parent == GDK_SURFACE (tmp->data)->parent) - { - if (parent != NULL) - { - gint index = g_list_index (parent->children, window); - for (; tmp && parent == GDK_SURFACE (tmp->data)->parent; tmp = tmp->next) - { - gint sibling_index = g_list_index (parent->children, tmp->data); - if (index > sibling_index) - break; - prev = tmp; - } - } - /* here, tmp got advanced past all lower stacked siblings */ - tmp = g_slist_prepend (tmp, g_object_ref (window)); - if (prev) - prev->next = tmp; - else - update_windows = tmp; - return; - } - - /* if "window" has an ancestor in the list and tmp is one of - * "window's" children, insert "window" before tmp - */ - if (has_ancestor_in_list && gdk_surface_is_ancestor (tmp->data, window)) - { - tmp = g_slist_prepend (tmp, g_object_ref (window)); - - if (prev) - prev->next = tmp; - else - update_windows = tmp; - return; - } - - /* if we're at the end of the list and had an ancestor it it, - * append to the list - */ - if (! tmp->next && has_ancestor_in_list) - { - tmp = g_slist_append (tmp, g_object_ref (window)); - return; - } - - prev = tmp; - } - - /* if all above checks failed ("window" is from a different - * hierarchy than what is already in the list) or the list is - * empty, prepend - */ - update_windows = g_slist_prepend (update_windows, g_object_ref (window)); -} - -static void -gdk_surface_remove_update_window (GdkSurface *window) -{ - GSList *link; - - link = g_slist_find (update_windows, window); - if (link != NULL) - { - update_windows = g_slist_delete_link (update_windows, link); - g_object_unref (window); - } -} - -static gboolean -gdk_surface_is_toplevel_frozen (GdkSurface *window) -{ - GdkSurface *toplevel; - - toplevel = gdk_surface_get_toplevel (window); - - return toplevel->update_and_descendants_freeze_count > 0; -} - -static void -gdk_surface_schedule_update (GdkSurface *window) -{ - GdkFrameClock *frame_clock; - - if (window && - (window->update_freeze_count || - gdk_surface_is_toplevel_frozen (window))) - return; - - /* If there's no frame clock (a foreign window), then the invalid - * region will just stick around unless gdk_surface_process_updates() - * is called. */ - frame_clock = gdk_surface_get_frame_clock (window); - if (frame_clock) - gdk_frame_clock_request_phase (gdk_surface_get_frame_clock (window), - GDK_FRAME_CLOCK_PHASE_PAINT); -} - -void -_gdk_surface_process_updates_recurse (GdkSurface *window, - cairo_region_t *expose_region) -{ - cairo_region_t *clipped_expose_region; - GdkEvent *event; - - if (window->destroyed) - return; - - clipped_expose_region = cairo_region_copy (expose_region); - - cairo_region_intersect (clipped_expose_region, window->clip_region); - - if (cairo_region_is_empty (clipped_expose_region)) - goto out; - - /* Paint the window before the children, clipped to the window region */ - - event = gdk_event_new (GDK_EXPOSE); - event->any.window = g_object_ref (window); - event->any.send_event = FALSE; - event->expose.count = 0; - event->expose.region = cairo_region_reference (clipped_expose_region); - cairo_region_get_extents (clipped_expose_region, &event->expose.area); - - _gdk_event_emit (event); - gdk_event_free (event); - - out: - cairo_region_destroy (clipped_expose_region); -} - - -static void -gdk_surface_update_native_shapes (GdkSurface *window) -{ - if (should_apply_clip_as_shape (window)) - apply_clip_as_shape (window); -} - -/* Process and remove any invalid area on the native window by creating - * expose events for the window and all non-native descendants. - */ -static void -gdk_surface_process_updates_internal (GdkSurface *window) -{ - GdkSurfaceImplClass *impl_class; - GdkSurface *toplevel; - - toplevel = gdk_surface_get_toplevel (window); - if (toplevel->geometry_dirty) - { - gdk_surface_update_native_shapes (toplevel); - toplevel->geometry_dirty = FALSE; - } - - /* Ensure the window lives while updating it */ - g_object_ref (window); - - window->in_update = TRUE; - - /* If an update got queued during update processing, we can get a - * window in the update queue that has an empty update_area. - * just ignore it. - */ - if (window->update_area) - { - g_assert (window->active_update_area == NULL); /* No reentrancy */ - - window->active_update_area = window->update_area; - window->update_area = NULL; - - if (gdk_surface_is_viewable (window)) - { - cairo_region_t *expose_region; - - expose_region = cairo_region_copy (window->active_update_area); - - impl_class = GDK_SURFACE_IMPL_GET_CLASS (window->impl); - - /* Clip to part visible in impl window */ - cairo_region_intersect (expose_region, window->clip_region); - - if (impl_class->queue_antiexpose) - impl_class->queue_antiexpose (window, expose_region); - - impl_class->process_updates_recurse (window, expose_region); - - gdk_surface_append_old_updated_area (window, window->active_update_area); - - cairo_region_destroy (expose_region); - } - - cairo_region_destroy (window->active_update_area); - window->active_update_area = NULL; - } - - window->in_update = FALSE; - - g_object_unref (window); -} - -static void -gdk_surface_paint_on_clock (GdkFrameClock *clock, - void *data) -{ - GdkSurface *window; - - window = GDK_SURFACE (data); - - g_return_if_fail (GDK_IS_SURFACE (window)); - g_return_if_fail (window->impl_window == window); - - if (GDK_SURFACE_DESTROYED (window)) - return; - - g_object_ref (window); - - if (window->update_area && - !window->update_freeze_count && - !gdk_surface_is_toplevel_frozen (window) && - - /* Don't recurse into process_updates_internal, we'll - * do the update later when idle instead. */ - !window->in_update) - { - gdk_surface_process_updates_internal (window); - gdk_surface_remove_update_window (window); - } - - g_object_unref (window); -} - -static void -gdk_surface_invalidate_rect_full (GdkSurface *window, - const GdkRectangle *rect, - gboolean invalidate_children) -{ - GdkRectangle window_rect; - cairo_region_t *region; - - g_return_if_fail (GDK_IS_SURFACE (window)); - - if (GDK_SURFACE_DESTROYED (window)) - return; - - if (window->input_only || !window->viewable) - return; - - if (!rect) - { - window_rect.x = 0; - window_rect.y = 0; - window_rect.width = window->width; - window_rect.height = window->height; - rect = &window_rect; - } - - region = cairo_region_create_rectangle (rect); - gdk_surface_invalidate_region_full (window, region, invalidate_children); - cairo_region_destroy (region); -} - -/** - * gdk_surface_invalidate_rect: - * @window: a #GdkSurface - * @rect: (allow-none): rectangle to invalidate or %NULL to invalidate the whole - * window - * @invalidate_children: whether to also invalidate child windows - * - * A convenience wrapper around gdk_surface_invalidate_region() which - * invalidates a rectangular region. See - * gdk_surface_invalidate_region() for details. - **/ -void -gdk_surface_invalidate_rect (GdkSurface *window, - const GdkRectangle *rect, - gboolean invalidate_children) -{ - gdk_surface_invalidate_rect_full (window, rect, invalidate_children); -} - -static void -impl_window_add_update_area (GdkSurface *impl_window, - cairo_region_t *region) -{ - if (impl_window->update_area) - cairo_region_union (impl_window->update_area, region); - else - { - gdk_surface_add_update_window (impl_window); - impl_window->update_area = cairo_region_copy (region); - gdk_surface_schedule_update (impl_window); - } -} - -static void -gdk_surface_invalidate_maybe_recurse_full (GdkSurface *window, - const cairo_region_t *region, - GdkSurfaceChildFunc child_func, - gpointer user_data) -{ - cairo_region_t *visible_region; - cairo_rectangle_int_t r; - - g_return_if_fail (GDK_IS_SURFACE (window)); - - if (GDK_SURFACE_DESTROYED (window)) - return; - - if (window->input_only || - !window->viewable || - cairo_region_is_empty (region) || - window->window_type == GDK_SURFACE_ROOT) - return; - - r.x = 0; - r.y = 0; - - visible_region = cairo_region_copy (region); - - while (window != NULL && - !cairo_region_is_empty (visible_region)) - { - r.width = window->width; - r.height = window->height; - cairo_region_intersect_rectangle (visible_region, &r); - - if (gdk_surface_has_impl (window)) - { - impl_window_add_update_area (window, visible_region); - break; - } - else - { - cairo_region_translate (visible_region, - window->x, window->y); - window = window->parent; - } - } - - cairo_region_destroy (visible_region); -} - -/** - * gdk_surface_invalidate_maybe_recurse: - * @window: a #GdkSurface - * @region: a #cairo_region_t - * @child_func: (scope call) (allow-none): function to use to decide if to - * recurse to a child, %NULL means never recurse. - * @user_data: data passed to @child_func - * - * Adds @region to the update area for @window. The update area is the - * region that needs to be redrawn, or “dirty region.” - * - * GDK will process all updates whenever the frame clock schedules a redraw, - * so there’s no need to do forces redraws manually, you just need to - * invalidate regions that you know should be redrawn. - * - * The @child_func parameter controls whether the region of - * each child window that intersects @region will also be invalidated. - * Only children for which @child_func returns #TRUE will have the area - * invalidated. - **/ -void -gdk_surface_invalidate_maybe_recurse (GdkSurface *window, - const cairo_region_t *region, - GdkSurfaceChildFunc child_func, - gpointer user_data) -{ - gdk_surface_invalidate_maybe_recurse_full (window, region, - child_func, user_data); -} - -static gboolean -true_predicate (GdkSurface *window, - gpointer user_data) -{ - return TRUE; -} - -static void -gdk_surface_invalidate_region_full (GdkSurface *window, - const cairo_region_t *region, - gboolean invalidate_children) -{ - gdk_surface_invalidate_maybe_recurse_full (window, region, - invalidate_children ? - true_predicate : (gboolean (*) (GdkSurface *, gpointer))NULL, - NULL); -} - -/** - * gdk_surface_invalidate_region: - * @window: a #GdkSurface - * @region: a #cairo_region_t - * @invalidate_children: %TRUE to also invalidate child windows - * - * Adds @region to the update area for @window. The update area is the - * region that needs to be redrawn, or “dirty region.” - * - * GDK will process all updates whenever the frame clock schedules a redraw, - * so there’s no need to do forces redraws manually, you just need to - * invalidate regions that you know should be redrawn. - * - * The @invalidate_children parameter controls whether the region of - * each child window that intersects @region will also be invalidated. - * If %FALSE, then the update area for child windows will remain - * unaffected. See gdk_surface_invalidate_maybe_recurse if you need - * fine grained control over which children are invalidated. - **/ -void -gdk_surface_invalidate_region (GdkSurface *window, - const cairo_region_t *region, - gboolean invalidate_children) -{ - gdk_surface_invalidate_maybe_recurse (window, region, - invalidate_children ? - true_predicate : (gboolean (*) (GdkSurface *, gpointer))NULL, - NULL); -} - -/** - * _gdk_surface_invalidate_for_expose: - * @window: a #GdkSurface - * @region: a #cairo_region_t - * - * Adds @region to the update area for @window. - * - * GDK will process all updates whenever the frame clock schedules a redraw, - * so there’s no need to do forces redraws manually, you just need to - * invalidate regions that you know should be redrawn. - * - * This version of invalidation is used when you recieve expose events - * from the native window system. It exposes the native window, plus - * any non-native child windows. - **/ -void -_gdk_surface_invalidate_for_expose (GdkSurface *window, - cairo_region_t *region) -{ - gdk_surface_invalidate_maybe_recurse_full (window, region, - (gboolean (*) (GdkSurface *, gpointer))gdk_surface_has_no_impl, - NULL); -} - - -/** - * gdk_surface_get_update_area: - * @window: a #GdkSurface - * - * Transfers ownership of the update area from @window to the caller - * of the function. That is, after calling this function, @window will - * no longer have an invalid/dirty region; the update area is removed - * from @window and handed to you. If a window has no update area, - * gdk_surface_get_update_area() returns %NULL. You are responsible for - * calling cairo_region_destroy() on the returned region if it’s non-%NULL. - * - * Returns: the update area for @window - **/ -cairo_region_t * -gdk_surface_get_update_area (GdkSurface *window) -{ - GdkSurface *impl_window; - cairo_region_t *tmp_region, *to_remove; - - g_return_val_if_fail (GDK_IS_SURFACE (window), NULL); - - impl_window = gdk_surface_get_impl_window (window); - - if (impl_window->update_area) - { - tmp_region = cairo_region_copy (window->clip_region); - /* Convert to impl coords */ - cairo_region_translate (tmp_region, window->abs_x, window->abs_y); - cairo_region_intersect (tmp_region, impl_window->update_area); - - if (cairo_region_is_empty (tmp_region)) - { - cairo_region_destroy (tmp_region); - return NULL; - } - else - { - /* Convert from impl coords */ - cairo_region_translate (tmp_region, -window->abs_x, -window->abs_y); - - /* Don't remove any update area that is overlapped by sibling windows - or child windows as these really need to be repainted independently of this window. */ - to_remove = cairo_region_copy (tmp_region); - - remove_child_area (window, FALSE, to_remove); - remove_sibling_overlapped_area (window, to_remove); - - /* Remove from update_area */ - cairo_region_translate (to_remove, window->abs_x, window->abs_y); - cairo_region_subtract (impl_window->update_area, to_remove); - - cairo_region_destroy (to_remove); - - if (cairo_region_is_empty (impl_window->update_area)) - { - cairo_region_destroy (impl_window->update_area); - impl_window->update_area = NULL; - - gdk_surface_remove_update_window ((GdkSurface *)impl_window); - } - - return tmp_region; - } - } - else - return NULL; -} - -/** - * _gdk_surface_clear_update_area: - * @window: a #GdkSurface. - * - * Internal function to clear the update area for a window. This - * is called when the window is hidden or destroyed. - **/ -void -_gdk_surface_clear_update_area (GdkSurface *window) -{ - g_return_if_fail (GDK_IS_SURFACE (window)); - - if (window->update_area) - { - gdk_surface_remove_update_window (window); - - cairo_region_destroy (window->update_area); - window->update_area = NULL; - } -} - -/** - * gdk_surface_freeze_updates: - * @window: a #GdkSurface - * - * Temporarily freezes a window such that it won’t receive expose - * events. The window will begin receiving expose events again when - * gdk_surface_thaw_updates() is called. If gdk_surface_freeze_updates() - * has been called more than once, gdk_surface_thaw_updates() must be called - * an equal number of times to begin processing exposes. - **/ -void -gdk_surface_freeze_updates (GdkSurface *window) -{ - GdkSurface *impl_window; - - g_return_if_fail (GDK_IS_SURFACE (window)); - - impl_window = gdk_surface_get_impl_window (window); - impl_window->update_freeze_count++; -} - -/** - * gdk_surface_thaw_updates: - * @window: a #GdkSurface - * - * Thaws a window frozen with gdk_surface_freeze_updates(). - **/ -void -gdk_surface_thaw_updates (GdkSurface *window) -{ - GdkSurface *impl_window; - - g_return_if_fail (GDK_IS_SURFACE (window)); - - impl_window = gdk_surface_get_impl_window (window); - - g_return_if_fail (impl_window->update_freeze_count > 0); - - if (--impl_window->update_freeze_count == 0) - gdk_surface_schedule_update (GDK_SURFACE (impl_window)); -} - -void -gdk_surface_freeze_toplevel_updates (GdkSurface *window) -{ - g_return_if_fail (GDK_IS_SURFACE (window)); - g_return_if_fail (window->window_type != GDK_SURFACE_CHILD); - - window->update_and_descendants_freeze_count++; - _gdk_frame_clock_freeze (gdk_surface_get_frame_clock (window)); -} - -void -gdk_surface_thaw_toplevel_updates (GdkSurface *window) -{ - g_return_if_fail (GDK_IS_SURFACE (window)); - g_return_if_fail (window->window_type != GDK_SURFACE_CHILD); - g_return_if_fail (window->update_and_descendants_freeze_count > 0); - - window->update_and_descendants_freeze_count--; - _gdk_frame_clock_thaw (gdk_surface_get_frame_clock (window)); - - gdk_surface_schedule_update (window); -} - -/** - * gdk_surface_constrain_size: - * @geometry: a #GdkGeometry structure - * @flags: a mask indicating what portions of @geometry are set - * @width: desired width of window - * @height: desired height of the window - * @new_width: (out): location to store resulting width - * @new_height: (out): location to store resulting height - * - * Constrains a desired width and height according to a - * set of geometry hints (such as minimum and maximum size). - */ -void -gdk_surface_constrain_size (GdkGeometry *geometry, - GdkSurfaceHints flags, - gint width, - gint height, - gint *new_width, - gint *new_height) -{ - /* This routine is partially borrowed from fvwm. - * - * Copyright 1993, Robert Nation - * You may use this code for any purpose, as long as the original - * copyright remains in the source code and all documentation - * - * which in turn borrows parts of the algorithm from uwm - */ - gint min_width = 0; - gint min_height = 0; - gint base_width = 0; - gint base_height = 0; - gint xinc = 1; - gint yinc = 1; - gint max_width = G_MAXINT; - gint max_height = G_MAXINT; - -#define FLOOR(value, base) ( ((gint) ((value) / (base))) * (base) ) - - if ((flags & GDK_HINT_BASE_SIZE) && (flags & GDK_HINT_MIN_SIZE)) - { - base_width = geometry->base_width; - base_height = geometry->base_height; - min_width = geometry->min_width; - min_height = geometry->min_height; - } - else if (flags & GDK_HINT_BASE_SIZE) - { - base_width = geometry->base_width; - base_height = geometry->base_height; - min_width = geometry->base_width; - min_height = geometry->base_height; - } - else if (flags & GDK_HINT_MIN_SIZE) - { - base_width = geometry->min_width; - base_height = geometry->min_height; - min_width = geometry->min_width; - min_height = geometry->min_height; - } - - if (flags & GDK_HINT_MAX_SIZE) - { - max_width = geometry->max_width ; - max_height = geometry->max_height; - } - - if (flags & GDK_HINT_RESIZE_INC) - { - xinc = MAX (xinc, geometry->width_inc); - yinc = MAX (yinc, geometry->height_inc); - } - - /* clamp width and height to min and max values - */ - width = CLAMP (width, min_width, max_width); - height = CLAMP (height, min_height, max_height); - - /* shrink to base + N * inc - */ - width = base_width + FLOOR (width - base_width, xinc); - height = base_height + FLOOR (height - base_height, yinc); - - /* constrain aspect ratio, according to: - * - * width - * min_aspect <= -------- <= max_aspect - * height - */ - - if (flags & GDK_HINT_ASPECT && - geometry->min_aspect > 0 && - geometry->max_aspect > 0) - { - gint delta; - - if (geometry->min_aspect * height > width) - { - delta = FLOOR (height - width / geometry->min_aspect, yinc); - if (height - delta >= min_height) - height -= delta; - else - { - delta = FLOOR (height * geometry->min_aspect - width, xinc); - if (width + delta <= max_width) - width += delta; - } - } - - if (geometry->max_aspect * height < width) - { - delta = FLOOR (width - height * geometry->max_aspect, xinc); - if (width - delta >= min_width) - width -= delta; - else - { - delta = FLOOR (width / geometry->max_aspect - height, yinc); - if (height + delta <= max_height) - height += delta; - } - } - } - -#undef FLOOR - - *new_width = width; - *new_height = height; -} - -/** - * gdk_surface_get_device_position_double: - * @window: a #GdkSurface. - * @device: pointer #GdkDevice to query to. - * @x: (out) (allow-none): return location for the X coordinate of @device, or %NULL. - * @y: (out) (allow-none): return location for the Y coordinate of @device, or %NULL. - * @mask: (out) (allow-none): return location for the modifier mask, or %NULL. - * - * Obtains the current device position in doubles and modifier state. - * The position is given in coordinates relative to the upper left - * corner of @window. - * - * Returns: (nullable) (transfer none): The window underneath @device - * (as with gdk_device_get_window_at_position()), or %NULL if the - * window is not known to GDK. - **/ -GdkSurface * -gdk_surface_get_device_position_double (GdkSurface *window, - GdkDevice *device, - double *x, - double *y, - GdkModifierType *mask) -{ - gdouble tmp_x, tmp_y; - GdkModifierType tmp_mask; - gboolean normal_child; - - g_return_val_if_fail (GDK_IS_SURFACE (window), NULL); - g_return_val_if_fail (GDK_IS_DEVICE (device), NULL); - g_return_val_if_fail (gdk_device_get_source (device) != GDK_SOURCE_KEYBOARD, NULL); - - tmp_x = tmp_y = 0; - tmp_mask = 0; - normal_child = GDK_SURFACE_IMPL_GET_CLASS (window->impl)->get_device_state (window, - device, - &tmp_x, &tmp_y, - &tmp_mask); - /* We got the coords on the impl, convert to the window */ - tmp_x -= window->abs_x; - tmp_y -= window->abs_y; - - if (x) - *x = tmp_x; - if (y) - *y = tmp_y; - if (mask) - *mask = tmp_mask; - - if (normal_child) - return _gdk_surface_find_child_at (window, tmp_x, tmp_y); - return NULL; -} - -/** - * gdk_surface_get_device_position: - * @window: a #GdkSurface. - * @device: pointer #GdkDevice to query to. - * @x: (out) (allow-none): return location for the X coordinate of @device, or %NULL. - * @y: (out) (allow-none): return location for the Y coordinate of @device, or %NULL. - * @mask: (out) (allow-none): return location for the modifier mask, or %NULL. - * - * Obtains the current device position and modifier state. - * The position is given in coordinates relative to the upper left - * corner of @window. - * - * Use gdk_surface_get_device_position_double() if you need subpixel precision. - * - * Returns: (nullable) (transfer none): The window underneath @device - * (as with gdk_device_get_window_at_position()), or %NULL if the - * window is not known to GDK. - **/ -GdkSurface * -gdk_surface_get_device_position (GdkSurface *window, - GdkDevice *device, - gint *x, - gint *y, - GdkModifierType *mask) -{ - gdouble tmp_x, tmp_y; - - window = gdk_surface_get_device_position_double (window, device, - &tmp_x, &tmp_y, mask); - if (x) - *x = round (tmp_x); - if (y) - *y = round (tmp_y); - - return window; -} - -static gboolean -gdk_surface_raise_internal (GdkSurface *window) -{ - GdkSurface *parent = window->parent; - GdkSurfaceImplClass *impl_class; - gboolean did_raise = FALSE; - - if (parent && parent->children->data != window) - { - parent->children = g_list_remove_link (parent->children, &window->children_list_node); - parent->children = g_list_concat (&window->children_list_node, parent->children); - did_raise = TRUE; - } - - impl_class = GDK_SURFACE_IMPL_GET_CLASS (window->impl); - - /* Just do native raise for toplevels */ - if (gdk_surface_has_impl (window)) - impl_class->raise (window); - - return did_raise; -} - -/* Returns TRUE If the native window was mapped or unmapped */ -static gboolean -set_viewable (GdkSurface *w, - gboolean val) -{ - GdkSurface *child; - GList *l; - - if (w->viewable == val) - return FALSE; - - w->viewable = val; - - if (val) - recompute_visible_regions (w, FALSE); - - for (l = w->children; l != NULL; l = l->next) - { - child = l->data; - - if (GDK_SURFACE_IS_MAPPED (child)) - set_viewable (child, val); - } - - return FALSE; -} - -/* Returns TRUE If the native window was mapped or unmapped */ -gboolean -_gdk_surface_update_viewable (GdkSurface *window) -{ - gboolean viewable; - - if (window->window_type == GDK_SURFACE_FOREIGN || - window->window_type == GDK_SURFACE_ROOT) - viewable = TRUE; - else if (gdk_surface_is_toplevel (window) || - window->parent->viewable) - viewable = GDK_SURFACE_IS_MAPPED (window); - else - viewable = FALSE; - - return set_viewable (window, viewable); -} - -static void -gdk_surface_show_internal (GdkSurface *window, gboolean raise) -{ - GdkSurfaceImplClass *impl_class; - gboolean was_mapped, was_viewable; - gboolean did_show, did_raise = FALSE; - - g_return_if_fail (GDK_IS_SURFACE (window)); - - if (window->destroyed) - return; - - was_mapped = GDK_SURFACE_IS_MAPPED (window); - was_viewable = window->viewable; - - if (raise) - { - /* Keep children in (reverse) stacking order */ - did_raise = gdk_surface_raise_internal (window); - } - - if (gdk_surface_has_impl (window)) - { - if (!was_mapped) - gdk_synthesize_window_state (window, - GDK_SURFACE_STATE_WITHDRAWN, - 0); - } - else - { - window->state = 0; - g_object_notify_by_pspec (G_OBJECT (window), properties[PROP_STATE]); - } - - did_show = _gdk_surface_update_viewable (window); - - /* If it was already viewable the backend show op won't be called, call it - again to ensure things happen right if the mapped tracking was not right - for e.g. a foreign window. - Dunno if this is strictly needed but its what happened pre-csw. - Also show if not done by gdk_surface_update_viewable. */ - if (gdk_surface_has_impl (window) && (was_viewable || !did_show)) - { - impl_class = GDK_SURFACE_IMPL_GET_CLASS (window->impl); - impl_class->show (window, !did_show ? was_mapped : TRUE); - } - - if (!was_mapped && !gdk_surface_has_impl (window)) - { - if (window->event_mask & GDK_STRUCTURE_MASK) - _gdk_make_event (window, GDK_MAP, NULL, FALSE); - - if (window->parent && window->parent->event_mask & GDK_SUBSTRUCTURE_MASK) - _gdk_make_event (window, GDK_MAP, NULL, FALSE); - } - - if (!was_mapped || did_raise) - { - recompute_visible_regions (window, FALSE); - - if (gdk_surface_is_viewable (window)) - gdk_surface_invalidate_rect_full (window, NULL, TRUE); - } -} - -/** - * gdk_surface_show_unraised: - * @window: a #GdkSurface - * - * Shows a #GdkSurface onscreen, but does not modify its stacking - * order. In contrast, gdk_surface_show() will raise the window - * to the top of the window stack. - * - * On the X11 platform, in Xlib terms, this function calls - * XMapWindow() (it also updates some internal GDK state, which means - * that you can’t really use XMapWindow() directly on a GDK window). - */ -void -gdk_surface_show_unraised (GdkSurface *window) -{ - gdk_surface_show_internal (window, FALSE); -} - -/** - * gdk_surface_raise: - * @window: a #GdkSurface - * - * Raises @window to the top of the Z-order (stacking order), so that - * other windows with the same parent window appear below @window. - * This is true whether or not the windows are visible. - * - * If @window is a toplevel, the window manager may choose to deny the - * request to move the window in the Z-order, gdk_surface_raise() only - * requests the restack, does not guarantee it. - */ -void -gdk_surface_raise (GdkSurface *window) -{ - gboolean did_raise; - - g_return_if_fail (GDK_IS_SURFACE (window)); - - if (window->destroyed) - return; - - /* Keep children in (reverse) stacking order */ - did_raise = gdk_surface_raise_internal (window); - - if (did_raise && - !gdk_surface_is_toplevel (window) && - gdk_surface_is_viewable (window) && - !window->input_only) - gdk_surface_invalidate_region_full (window, window->clip_region, TRUE); -} - -static void -gdk_surface_lower_internal (GdkSurface *window) -{ - GdkSurface *parent = window->parent; - GdkSurfaceImplClass *impl_class; - - if (parent) - { - parent->children = g_list_remove_link (parent->children, &window->children_list_node); - parent->children = g_list_concat (parent->children, &window->children_list_node); - } - - impl_class = GDK_SURFACE_IMPL_GET_CLASS (window->impl); - - /* Just do native lower for toplevels */ - if (gdk_surface_has_impl (window)) - impl_class->lower (window); -} - -static void -gdk_surface_invalidate_in_parent (GdkSurface *private) -{ - GdkRectangle r, child; - - if (gdk_surface_is_toplevel (private)) - return; - - /* get the visible rectangle of the parent */ - r.x = r.y = 0; - r.width = private->parent->width; - r.height = private->parent->height; - - child.x = private->x; - child.y = private->y; - child.width = private->width; - child.height = private->height; - gdk_rectangle_intersect (&r, &child, &r); - - gdk_surface_invalidate_rect_full (private->parent, &r, TRUE); -} - - -/** - * gdk_surface_lower: - * @window: a #GdkSurface - * - * Lowers @window to the bottom of the Z-order (stacking order), so that - * other windows with the same parent window appear above @window. - * This is true whether or not the other windows are visible. - * - * If @window is a toplevel, the window manager may choose to deny the - * request to move the window in the Z-order, gdk_surface_lower() only - * requests the restack, does not guarantee it. - * - * Note that gdk_surface_show() raises the window again, so don’t call this - * function before gdk_surface_show(). (Try gdk_surface_show_unraised().) - */ -void -gdk_surface_lower (GdkSurface *window) -{ - g_return_if_fail (GDK_IS_SURFACE (window)); - - if (window->destroyed) - return; - - /* Keep children in (reverse) stacking order */ - gdk_surface_lower_internal (window); - - gdk_surface_invalidate_in_parent (window); -} - -/** - * gdk_surface_restack: - * @window: a #GdkSurface - * @sibling: (allow-none): a #GdkSurface that is a sibling of @window, or %NULL - * @above: a boolean - * - * Changes the position of @window in the Z-order (stacking order), so that - * it is above @sibling (if @above is %TRUE) or below @sibling (if @above is - * %FALSE). - * - * If @sibling is %NULL, then this either raises (if @above is %TRUE) or - * lowers the window. - * - * If @window is a toplevel, the window manager may choose to deny the - * request to move the window in the Z-order, gdk_surface_restack() only - * requests the restack, does not guarantee it. - */ -void -gdk_surface_restack (GdkSurface *window, - GdkSurface *sibling, - gboolean above) -{ - GdkSurfaceImplClass *impl_class; - GdkSurface *parent; - GList *sibling_link; - - g_return_if_fail (GDK_IS_SURFACE (window)); - g_return_if_fail (sibling == NULL || GDK_IS_SURFACE (sibling)); - - if (window->destroyed) - return; - - if (sibling == NULL) - { - if (above) - gdk_surface_raise (window); - else - gdk_surface_lower (window); - return; - } - - if (gdk_surface_is_toplevel (window)) - { - g_return_if_fail (gdk_surface_is_toplevel (sibling)); - impl_class = GDK_SURFACE_IMPL_GET_CLASS (window->impl); - impl_class->restack_toplevel (window, sibling, above); - return; - } - - parent = window->parent; - if (parent) - { - sibling_link = g_list_find (parent->children, sibling); - g_return_if_fail (sibling_link != NULL); - if (sibling_link == NULL) - return; - - parent->children = g_list_remove_link (parent->children, &window->children_list_node); - if (above) - parent->children = list_insert_link_before (parent->children, - sibling_link, - &window->children_list_node); - else - parent->children = list_insert_link_before (parent->children, - sibling_link->next, - &window->children_list_node); - } - - gdk_surface_invalidate_in_parent (window); -} - - -/** - * gdk_surface_show: - * @window: a #GdkSurface - * - * Like gdk_surface_show_unraised(), but also raises the window to the - * top of the window stack (moves the window to the front of the - * Z-order). - * - * This function maps a window so it’s visible onscreen. Its opposite - * is gdk_surface_hide(). - * - * When implementing a #GtkWidget, you should call this function on the widget's - * #GdkSurface as part of the “map” method. - */ -void -gdk_surface_show (GdkSurface *window) -{ - gdk_surface_show_internal (window, TRUE); -} - -/** - * gdk_surface_hide: - * @window: a #GdkSurface - * - * For toplevel windows, withdraws them, so they will no longer be - * known to the window manager; for all windows, unmaps them, so - * they won’t be displayed. Normally done automatically as - * part of gtk_widget_hide(). - */ -void -gdk_surface_hide (GdkSurface *window) -{ - GdkSurfaceImplClass *impl_class; - gboolean was_mapped, did_hide; - - g_return_if_fail (GDK_IS_SURFACE (window)); - - if (window->destroyed) - return; - - was_mapped = GDK_SURFACE_IS_MAPPED (window); - - if (gdk_surface_has_impl (window)) - { - - if (GDK_SURFACE_IS_MAPPED (window)) - gdk_synthesize_window_state (window, - 0, - GDK_SURFACE_STATE_WITHDRAWN); - } - else if (was_mapped) - { - window->state = GDK_SURFACE_STATE_WITHDRAWN; - g_object_notify_by_pspec (G_OBJECT (window), properties[PROP_STATE]); - } - - if (was_mapped) - { - GdkDisplay *display; - GdkSeat *seat; - GList *devices, *d; - - /* May need to break grabs on children */ - display = gdk_surface_get_display (window); - seat = gdk_display_get_default_seat (display); - - devices = gdk_seat_get_slaves (seat, GDK_SEAT_CAPABILITY_ALL); - devices = g_list_prepend (devices, gdk_seat_get_keyboard (seat)); - devices = g_list_prepend (devices, gdk_seat_get_pointer (seat)); - - for (d = devices; d; d = d->next) - { - GdkDevice *device = d->data; - - if (_gdk_display_end_device_grab (display, - device, - _gdk_display_get_next_serial (display), - window, - TRUE)) - { -G_GNUC_BEGIN_IGNORE_DEPRECATIONS - gdk_device_ungrab (device, GDK_CURRENT_TIME); -G_GNUC_END_IGNORE_DEPRECATIONS - } - } - - g_list_free (devices); - } - - did_hide = _gdk_surface_update_viewable (window); - - /* Hide foreign window as those are not handled by update_viewable. */ - if (gdk_surface_has_impl (window) && (!did_hide)) - { - impl_class = GDK_SURFACE_IMPL_GET_CLASS (window->impl); - impl_class->hide (window); - } - - gdk_surface_clear_old_updated_area (window); - recompute_visible_regions (window, FALSE); - - if (was_mapped && !gdk_surface_has_impl (window)) - { - if (window->event_mask & GDK_STRUCTURE_MASK) - _gdk_make_event (window, GDK_UNMAP, NULL, FALSE); - - if (window->parent && window->parent->event_mask & GDK_SUBSTRUCTURE_MASK) - _gdk_make_event (window, GDK_UNMAP, NULL, FALSE); - } - - /* Invalidate the rect */ - if (was_mapped) - gdk_surface_invalidate_in_parent (window); -} - -/** - * gdk_surface_withdraw: - * @window: a toplevel #GdkSurface - * - * Withdraws a window (unmaps it and asks the window manager to forget about it). - * This function is not really useful as gdk_surface_hide() automatically - * withdraws toplevel windows before hiding them. - **/ -void -gdk_surface_withdraw (GdkSurface *window) -{ - GdkSurfaceImplClass *impl_class; - gboolean was_mapped; - GdkGLContext *current_context; - - g_return_if_fail (GDK_IS_SURFACE (window)); - - if (window->destroyed) - return; - - was_mapped = GDK_SURFACE_IS_MAPPED (window); - - if (gdk_surface_has_impl (window)) - { - impl_class = GDK_SURFACE_IMPL_GET_CLASS (window->impl); - impl_class->withdraw (window); - - if (was_mapped) - { - if (window->event_mask & GDK_STRUCTURE_MASK) - _gdk_make_event (window, GDK_UNMAP, NULL, FALSE); - - if (window->parent && window->parent->event_mask & GDK_SUBSTRUCTURE_MASK) - _gdk_make_event (window, GDK_UNMAP, NULL, FALSE); - } - - current_context = gdk_gl_context_get_current (); - if (current_context != NULL && gdk_gl_context_get_window (current_context) == window) - gdk_gl_context_clear_current (); - - recompute_visible_regions (window, FALSE); - gdk_surface_clear_old_updated_area (window); - } -} - -/** - * gdk_surface_set_events: - * @window: a #GdkSurface - * @event_mask: event mask for @window - * - * The event mask for a window determines which events will be reported - * for that window from all master input devices. For example, an event mask - * including #GDK_BUTTON_PRESS_MASK means the window should report button - * press events. The event mask is the bitwise OR of values from the - * #GdkEventMask enumeration. - * - * See the [input handling overview][event-masks] for details. - **/ -void -gdk_surface_set_events (GdkSurface *window, - GdkEventMask event_mask) -{ - GdkSurfaceImplClass *impl_class; - - g_return_if_fail (GDK_IS_SURFACE (window)); - - if (window->destroyed) - return; - - window->event_mask = event_mask; - - if (gdk_surface_has_impl (window)) - { - impl_class = GDK_SURFACE_IMPL_GET_CLASS (window->impl); - impl_class->set_events (window, - get_native_event_mask (window)); - } - -} - -/** - * gdk_surface_get_events: - * @window: a #GdkSurface - * - * Gets the event mask for @window for all master input devices. See - * gdk_surface_set_events(). - * - * Returns: event mask for @window - **/ -GdkEventMask -gdk_surface_get_events (GdkSurface *window) -{ - g_return_val_if_fail (GDK_IS_SURFACE (window), 0); - - if (window->destroyed) - return 0; - - return window->event_mask; -} - -/** - * gdk_surface_set_device_events: - * @window: a #GdkSurface - * @device: #GdkDevice to enable events for. - * @event_mask: event mask for @window - * - * Sets the event mask for a given device (Normally a floating device, not - * attached to any visible pointer) to @window. For example, an event mask - * including #GDK_BUTTON_PRESS_MASK means the window should report button - * press events. The event mask is the bitwise OR of values from the - * #GdkEventMask enumeration. - * - * See the [input handling overview][event-masks] for details. - **/ -void -gdk_surface_set_device_events (GdkSurface *window, - GdkDevice *device, - GdkEventMask event_mask) -{ - GdkEventMask device_mask; - GdkSurface *native; - - g_return_if_fail (GDK_IS_SURFACE (window)); - g_return_if_fail (GDK_IS_DEVICE (device)); - - if (GDK_SURFACE_DESTROYED (window)) - return; - - if (G_UNLIKELY (!window->device_events)) - window->device_events = g_hash_table_new (NULL, NULL); - - if (event_mask == 0) - { - /* FIXME: unsetting events on a master device - * would restore window->event_mask - */ - g_hash_table_remove (window->device_events, device); - } - else - g_hash_table_insert (window->device_events, device, - GINT_TO_POINTER (event_mask)); - - native = gdk_surface_get_toplevel (window); - - device_mask = get_native_device_event_mask (window, device); - GDK_DEVICE_GET_CLASS (device)->select_window_events (device, native, device_mask); -} - -/** - * gdk_surface_get_device_events: - * @window: a #GdkSurface. - * @device: a #GdkDevice. - * - * Returns the event mask for @window corresponding to an specific device. - * - * Returns: device event mask for @window - **/ -GdkEventMask -gdk_surface_get_device_events (GdkSurface *window, - GdkDevice *device) -{ - GdkEventMask mask; - - g_return_val_if_fail (GDK_IS_SURFACE (window), 0); - g_return_val_if_fail (GDK_IS_DEVICE (device), 0); - - if (GDK_SURFACE_DESTROYED (window)) - return 0; - - if (!window->device_events) - return 0; - - mask = GPOINTER_TO_INT (g_hash_table_lookup (window->device_events, device)); - - /* FIXME: device could be controlled by window->event_mask */ - - return mask; -} - -static void -gdk_surface_move_resize_toplevel (GdkSurface *window, - gboolean with_move, - gint x, - gint y, - gint width, - gint height) -{ - cairo_region_t *old_region, *new_region; - GdkSurfaceImplClass *impl_class; - gboolean expose; - gboolean is_resize; - - expose = FALSE; - old_region = NULL; - - is_resize = (width != -1) || (height != -1); - - if (gdk_surface_is_viewable (window) && - !window->input_only) - { - expose = TRUE; - old_region = cairo_region_copy (window->clip_region); - } - - impl_class = GDK_SURFACE_IMPL_GET_CLASS (window->impl); - impl_class->move_resize (window, with_move, x, y, width, height); - - /* Avoid recomputing for pure toplevel moves, for performance reasons */ - if (is_resize) - recompute_visible_regions (window, FALSE); - - if (expose) - { - new_region = cairo_region_copy (window->clip_region); - - /* This is the newly exposed area (due to any resize), - * X will expose it, but lets do that without the roundtrip - */ - cairo_region_subtract (new_region, old_region); - gdk_surface_invalidate_region_full (window, new_region, TRUE); - - cairo_region_destroy (old_region); - cairo_region_destroy (new_region); - } -} - - -static void -gdk_surface_move_resize_internal (GdkSurface *window, - gboolean with_move, - gint x, - gint y, - gint width, - gint height) -{ - cairo_region_t *old_region, *new_region; - gboolean expose; - - g_return_if_fail (GDK_IS_SURFACE (window)); - - if (window->destroyed) - return; - - if (gdk_surface_is_toplevel (window)) - { - gdk_surface_move_resize_toplevel (window, with_move, x, y, width, height); - return; - } - - if (width == 0) - width = 1; - if (height == 0) - height = 1; - - /* Bail early if no change */ - if (window->width == width && - window->height == height && - (!with_move || - (window->x == x && - window->y == y))) - return; - - /* Handle child windows */ - - expose = FALSE; - old_region = NULL; - - if (gdk_surface_is_viewable (window) && - !window->input_only) - { - GdkRectangle r; - - expose = TRUE; - - r.x = window->x; - r.y = window->y; - r.width = window->width; - r.height = window->height; - - old_region = cairo_region_create_rectangle (&r); - } - - /* Set the new position and size */ - if (with_move) - { - window->x = x; - window->y = y; - } - if (!(width < 0 && height < 0)) - { - window->width = width; - window->height = height; - } - - recompute_visible_regions (window, FALSE); - - if (expose) - { - GdkRectangle r; - - r.x = window->x; - r.y = window->y; - r.width = window->width; - r.height = window->height; - - new_region = cairo_region_create_rectangle (&r); - - cairo_region_union (new_region, old_region); - - gdk_surface_invalidate_region_full (window->parent, new_region, TRUE); - - cairo_region_destroy (old_region); - cairo_region_destroy (new_region); - } -} - - - -/** - * gdk_surface_move: - * @window: a #GdkSurface - * @x: X coordinate relative to window’s parent - * @y: Y coordinate relative to window’s parent - * - * Repositions a window relative to its parent window. - * For toplevel windows, window managers may ignore or modify the move; - * you should probably use gtk_window_move() on a #GtkWindow widget - * anyway, instead of using GDK functions. For child windows, - * the move will reliably succeed. - * - * If you’re also planning to resize the window, use gdk_surface_move_resize() - * to both move and resize simultaneously, for a nicer visual effect. - **/ -void -gdk_surface_move (GdkSurface *window, - gint x, - gint y) -{ - gdk_surface_move_resize_internal (window, TRUE, x, y, -1, -1); -} - -/** - * gdk_surface_resize: - * @window: a #GdkSurface - * @width: new width of the window - * @height: new height of the window - * - * Resizes @window; for toplevel windows, asks the window manager to resize - * the window. The window manager may not allow the resize. When using GTK+, - * use gtk_window_resize() instead of this low-level GDK function. - * - * Windows may not be resized below 1x1. - * - * If you’re also planning to move the window, use gdk_surface_move_resize() - * to both move and resize simultaneously, for a nicer visual effect. - **/ -void -gdk_surface_resize (GdkSurface *window, - gint width, - gint height) -{ - gdk_surface_move_resize_internal (window, FALSE, 0, 0, width, height); -} - - -/** - * gdk_surface_move_resize: - * @window: a #GdkSurface - * @x: new X position relative to window’s parent - * @y: new Y position relative to window’s parent - * @width: new width - * @height: new height - * - * Equivalent to calling gdk_surface_move() and gdk_surface_resize(), - * except that both operations are performed at once, avoiding strange - * visual effects. (i.e. the user may be able to see the window first - * move, then resize, if you don’t use gdk_surface_move_resize().) - **/ -void -gdk_surface_move_resize (GdkSurface *window, - gint x, - gint y, - gint width, - gint height) -{ - gdk_surface_move_resize_internal (window, TRUE, x, y, width, height); -} - -/** - * gdk_surface_move_to_rect: - * @window: the #GdkSurface to move - * @rect: (not nullable): the destination #GdkRectangle to align @window with - * @rect_anchor: the point on @rect to align with @window's anchor point - * @window_anchor: the point on @window to align with @rect's anchor point - * @anchor_hints: positioning hints to use when limited on space - * @rect_anchor_dx: horizontal offset to shift @window, i.e. @rect's anchor - * point - * @rect_anchor_dy: vertical offset to shift @window, i.e. @rect's anchor point - * - * Moves @window to @rect, aligning their anchor points. - * - * @rect is relative to the top-left corner of the window that @window is - * transient for. @rect_anchor and @window_anchor determine anchor points on - * @rect and @window to pin together. @rect's anchor point can optionally be - * offset by @rect_anchor_dx and @rect_anchor_dy, which is equivalent to - * offsetting the position of @window. - * - * @anchor_hints determines how @window will be moved if the anchor points cause - * it to move off-screen. For example, %GDK_ANCHOR_FLIP_X will replace - * %GDK_GRAVITY_NORTH_WEST with %GDK_GRAVITY_NORTH_EAST and vice versa if - * @window extends beyond the left or right edges of the monitor. - * - * Connect to the #GdkSurface::moved-to-rect signal to find out how it was - * actually positioned. - * - * Stability: Private - */ -void -gdk_surface_move_to_rect (GdkSurface *window, - const GdkRectangle *rect, - GdkGravity rect_anchor, - GdkGravity window_anchor, - GdkAnchorHints anchor_hints, - gint rect_anchor_dx, - gint rect_anchor_dy) -{ - GdkSurfaceImplClass *impl_class; - - g_return_if_fail (GDK_IS_SURFACE (window)); - g_return_if_fail (window->transient_for); - g_return_if_fail (rect); - - impl_class = GDK_SURFACE_IMPL_GET_CLASS (window->impl); - impl_class->move_to_rect (window, - rect, - rect_anchor, - window_anchor, - anchor_hints, - rect_anchor_dx, - rect_anchor_dy); -} - -/** - * gdk_surface_scroll: - * @window: a #GdkSurface - * @dx: Amount to scroll in the X direction - * @dy: Amount to scroll in the Y direction - * - * Scroll the contents of @window, both pixels and children, by the - * given amount. @window itself does not move. Portions of the window - * that the scroll operation brings in from offscreen areas are - * invalidated. The invalidated region may be bigger than what would - * strictly be necessary. - * - * For X11, a minimum area will be invalidated if the window has no - * subwindows, or if the edges of the window’s parent do not extend - * beyond the edges of the window. In other cases, a multi-step process - * is used to scroll the window which may produce temporary visual - * artifacts and unnecessary invalidations. - **/ -void -gdk_surface_scroll (GdkSurface *window, - gint dx, - gint dy) -{ - GList *tmp_list; - - g_return_if_fail (GDK_IS_SURFACE (window)); - - if (dx == 0 && dy == 0) - return; - - if (window->destroyed) - return; - - /* First move all child windows, without causing invalidation */ - - tmp_list = window->children; - while (tmp_list) - { - GdkSurface *child = GDK_SURFACE (tmp_list->data); - - /* Just update the positions, the bits will move with the copy */ - child->x += dx; - child->y += dy; - - tmp_list = tmp_list->next; - } - - recompute_visible_regions (window, TRUE); - - gdk_surface_invalidate_rect_full (window, NULL, TRUE); -} - -/** - * gdk_surface_move_region: - * @window: a #GdkSurface - * @region: The #cairo_region_t to move - * @dx: Amount to move in the X direction - * @dy: Amount to move in the Y direction - * - * Move the part of @window indicated by @region by @dy pixels in the Y - * direction and @dx pixels in the X direction. The portions of @region - * that not covered by the new position of @region are invalidated. - * - * Child windows are not moved. - */ -void -gdk_surface_move_region (GdkSurface *window, - const cairo_region_t *region, - gint dx, - gint dy) -{ - cairo_region_t *expose_area; - - g_return_if_fail (GDK_IS_SURFACE (window)); - g_return_if_fail (region != NULL); - - if (dx == 0 && dy == 0) - return; - - if (window->destroyed) - return; - - expose_area = cairo_region_copy (region); - cairo_region_translate (expose_area, dx, dy); - cairo_region_union (expose_area, region); - - gdk_surface_invalidate_region_full (window, expose_area, FALSE); - cairo_region_destroy (expose_area); -} - -static void -gdk_surface_set_cursor_internal (GdkSurface *window, - GdkDevice *device, - GdkCursor *cursor) -{ - if (GDK_SURFACE_DESTROYED (window)) - return; - - g_assert (gdk_surface_get_display (window) == gdk_device_get_display (device)); - - if (window->window_type == GDK_SURFACE_ROOT || - window->window_type == GDK_SURFACE_FOREIGN) - GDK_DEVICE_GET_CLASS (device)->set_window_cursor (device, window, cursor); - else - { - GdkPointerSurfaceInfo *pointer_info; - GdkDisplay *display; - - display = gdk_surface_get_display (window); - pointer_info = _gdk_display_get_pointer_info (display, device); - - if (_gdk_surface_event_parent_of (window, pointer_info->window_under_pointer)) - update_cursor (display, device); - } -} - -/** - * gdk_surface_get_cursor: - * @window: a #GdkSurface - * - * Retrieves a #GdkCursor pointer for the cursor currently set on the - * specified #GdkSurface, or %NULL. If the return value is %NULL then - * there is no custom cursor set on the specified window, and it is - * using the cursor for its parent window. - * - * Returns: (nullable) (transfer none): a #GdkCursor, or %NULL. The - * returned object is owned by the #GdkSurface and should not be - * unreferenced directly. Use gdk_surface_set_cursor() to unset the - * cursor of the window - */ -GdkCursor * -gdk_surface_get_cursor (GdkSurface *window) -{ - g_return_val_if_fail (GDK_IS_SURFACE (window), NULL); - - return window->cursor; -} - -/** - * gdk_surface_set_cursor: - * @window: a #GdkSurface - * @cursor: (allow-none): a cursor - * - * Sets the default mouse pointer for a #GdkSurface. - * - * Note that @cursor must be for the same display as @window. - * - * Use gdk_cursor_new_for_display() or gdk_cursor_new_from_texture() to - * create the cursor. To make the cursor invisible, use %GDK_BLANK_CURSOR. - * Passing %NULL for the @cursor argument to gdk_surface_set_cursor() means - * that @window will use the cursor of its parent window. Most windows - * should use this default. - */ -void -gdk_surface_set_cursor (GdkSurface *window, - GdkCursor *cursor) -{ - GdkDisplay *display; - - g_return_if_fail (GDK_IS_SURFACE (window)); - - display = gdk_surface_get_display (window); - - if (window->cursor) - { - g_object_unref (window->cursor); - window->cursor = NULL; - } - - if (!GDK_SURFACE_DESTROYED (window)) - { - GdkDevice *device; - GList *seats, *s; - - if (cursor) - window->cursor = g_object_ref (cursor); - - seats = gdk_display_list_seats (display); - - for (s = seats; s; s = s->next) - { - GList *devices, *d; - - device = gdk_seat_get_pointer (s->data); - gdk_surface_set_cursor_internal (window, device, window->cursor); - - devices = gdk_seat_get_slaves (s->data, GDK_SEAT_CAPABILITY_TABLET_STYLUS); - for (d = devices; d; d = d->next) - { - device = gdk_device_get_associated_device (d->data); - gdk_surface_set_cursor_internal (window, device, window->cursor); - } - g_list_free (devices); - } - - g_list_free (seats); - g_object_notify_by_pspec (G_OBJECT (window), properties[PROP_CURSOR]); - } -} - -/** - * gdk_surface_get_device_cursor: - * @window: a #GdkSurface. - * @device: a master, pointer #GdkDevice. - * - * Retrieves a #GdkCursor pointer for the @device currently set on the - * specified #GdkSurface, or %NULL. If the return value is %NULL then - * there is no custom cursor set on the specified window, and it is - * using the cursor for its parent window. - * - * Returns: (nullable) (transfer none): a #GdkCursor, or %NULL. The - * returned object is owned by the #GdkSurface and should not be - * unreferenced directly. Use gdk_surface_set_cursor() to unset the - * cursor of the window - **/ -GdkCursor * -gdk_surface_get_device_cursor (GdkSurface *window, - GdkDevice *device) -{ - g_return_val_if_fail (GDK_IS_SURFACE (window), NULL); - g_return_val_if_fail (GDK_IS_DEVICE (device), NULL); - g_return_val_if_fail (gdk_device_get_source (device) != GDK_SOURCE_KEYBOARD, NULL); - g_return_val_if_fail (gdk_device_get_device_type (device) == GDK_DEVICE_TYPE_MASTER, NULL); - - return g_hash_table_lookup (window->device_cursor, device); -} - -/** - * gdk_surface_set_device_cursor: - * @window: a #GdkSurface - * @device: a master, pointer #GdkDevice - * @cursor: a #GdkCursor - * - * Sets a specific #GdkCursor for a given device when it gets inside @window. - * Use gdk_cursor_new_for_display() or gdk_cursor_new_from_texture() to create - * the cursor. To make the cursor invisible, use %GDK_BLANK_CURSOR. Passing - * %NULL for the @cursor argument to gdk_surface_set_cursor() means that - * @window will use the cursor of its parent window. Most windows should - * use this default. - **/ -void -gdk_surface_set_device_cursor (GdkSurface *window, - GdkDevice *device, - GdkCursor *cursor) -{ - g_return_if_fail (GDK_IS_SURFACE (window)); - g_return_if_fail (GDK_IS_DEVICE (device)); - g_return_if_fail (gdk_device_get_source (device) != GDK_SOURCE_KEYBOARD); - g_return_if_fail (gdk_device_get_device_type (device) == GDK_DEVICE_TYPE_MASTER); - - if (!cursor) - g_hash_table_remove (window->device_cursor, device); - else - g_hash_table_replace (window->device_cursor, device, g_object_ref (cursor)); - - gdk_surface_set_cursor_internal (window, device, cursor); -} - -/** - * gdk_surface_get_geometry: - * @window: a #GdkSurface - * @x: (out) (allow-none): return location for X coordinate of window (relative to its parent) - * @y: (out) (allow-none): return location for Y coordinate of window (relative to its parent) - * @width: (out) (allow-none): return location for width of window - * @height: (out) (allow-none): return location for height of window - * - * Any of the return location arguments to this function may be %NULL, - * if you aren’t interested in getting the value of that field. - * - * The X and Y coordinates returned are relative to the parent window - * of @window, which for toplevels usually means relative to the - * window decorations (titlebar, etc.) rather than relative to the - * root window (screen-size background window). - * - * On the X11 platform, the geometry is obtained from the X server, - * so reflects the latest position of @window; this may be out-of-sync - * with the position of @window delivered in the most-recently-processed - * #GdkEventConfigure. gdk_surface_get_position() in contrast gets the - * position from the most recent configure event. - * - * Note: If @window is not a toplevel, it is much better - * to call gdk_surface_get_position(), gdk_surface_get_width() and - * gdk_surface_get_height() instead, because it avoids the roundtrip to - * the X server and because these functions support the full 32-bit - * coordinate space, whereas gdk_surface_get_geometry() is restricted to - * the 16-bit coordinates of X11. - */ -void -gdk_surface_get_geometry (GdkSurface *window, - gint *x, - gint *y, - gint *width, - gint *height) -{ - GdkSurface *parent; - GdkSurfaceImplClass *impl_class; - - g_return_if_fail (GDK_IS_SURFACE (window)); - - if (!GDK_SURFACE_DESTROYED (window)) - { - if (gdk_surface_has_impl (window)) - { - impl_class = GDK_SURFACE_IMPL_GET_CLASS (window->impl); - impl_class->get_geometry (window, x, y, - width, height); - /* This reports the position wrt to the native parent, we need to convert - it to be relative to the client side parent */ - parent = window->parent; - if (parent && !gdk_surface_has_impl (parent)) - { - if (x) - *x -= parent->abs_x; - if (y) - *y -= parent->abs_y; - } - } - else - { - if (x) - *x = window->x; - if (y) - *y = window->y; - if (width) - *width = window->width; - if (height) - *height = window->height; - } - } -} - -/** - * gdk_surface_get_width: - * @window: a #GdkSurface - * - * Returns the width of the given @window. - * - * On the X11 platform the returned size is the size reported in the - * most-recently-processed configure event, rather than the current - * size on the X server. - * - * Returns: The width of @window - */ -int -gdk_surface_get_width (GdkSurface *window) -{ - g_return_val_if_fail (GDK_IS_SURFACE (window), 0); - - return window->width; -} - -/** - * gdk_surface_get_height: - * @window: a #GdkSurface - * - * Returns the height of the given @window. - * - * On the X11 platform the returned size is the size reported in the - * most-recently-processed configure event, rather than the current - * size on the X server. - * - * Returns: The height of @window - */ -int -gdk_surface_get_height (GdkSurface *window) -{ - g_return_val_if_fail (GDK_IS_SURFACE (window), 0); - - return window->height; -} - -/** - * gdk_surface_get_origin: - * @window: a #GdkSurface - * @x: (out) (allow-none): return location for X coordinate - * @y: (out) (allow-none): return location for Y coordinate - * - * Obtains the position of a window in root window coordinates. - * (Compare with gdk_surface_get_position() and - * gdk_surface_get_geometry() which return the position of a window - * relative to its parent window.) - * - * Returns: not meaningful, ignore - */ -gint -gdk_surface_get_origin (GdkSurface *window, - gint *x, - gint *y) -{ - gint dummy_x, dummy_y; - - g_return_val_if_fail (GDK_IS_SURFACE (window), 0); - - gdk_surface_get_root_coords (window, - 0, 0, - x ? x : &dummy_x, - y ? y : &dummy_y); - - return TRUE; -} - -/** - * gdk_surface_get_root_coords: - * @window: a #GdkSurface - * @x: X coordinate in window - * @y: Y coordinate in window - * @root_x: (out): return location for X coordinate - * @root_y: (out): return location for Y coordinate - * - * Obtains the position of a window position in root - * window coordinates. This is similar to - * gdk_surface_get_origin() but allows you to pass - * in any position in the window, not just the origin. - */ -void -gdk_surface_get_root_coords (GdkSurface *window, - gint x, - gint y, - gint *root_x, - gint *root_y) -{ - GdkSurfaceImplClass *impl_class; - - g_return_if_fail (GDK_IS_SURFACE (window)); - - if (GDK_SURFACE_DESTROYED (window)) - { - *root_x = 0; - *root_y = 0; - return; - } - - impl_class = GDK_SURFACE_IMPL_GET_CLASS (window->impl); - impl_class->get_root_coords (window->impl_window, - x + window->abs_x, - y + window->abs_y, - root_x, root_y); -} - -/** - * gdk_surface_coords_to_parent: - * @window: a child window - * @x: X coordinate in child’s coordinate system - * @y: Y coordinate in child’s coordinate system - * @parent_x: (out) (allow-none): return location for X coordinate - * in parent’s coordinate system, or %NULL - * @parent_y: (out) (allow-none): return location for Y coordinate - * in parent’s coordinate system, or %NULL - * - * Transforms window coordinates from a child window to its parent - * window. Calling this function is equivalent to adding the return - * values of gdk_surface_get_position() to the child coordinates. - * - * See also: gdk_surface_coords_from_parent() - **/ -void -gdk_surface_coords_to_parent (GdkSurface *window, - gdouble x, - gdouble y, - gdouble *parent_x, - gdouble *parent_y) -{ - g_return_if_fail (GDK_IS_SURFACE (window)); - - if (parent_x) - *parent_x = x + window->x; - - if (parent_y) - *parent_y = y + window->y; -} - -/** - * gdk_surface_coords_from_parent: - * @window: a child window - * @parent_x: X coordinate in parent’s coordinate system - * @parent_y: Y coordinate in parent’s coordinate system - * @x: (out) (allow-none): return location for X coordinate in child’s coordinate system - * @y: (out) (allow-none): return location for Y coordinate in child’s coordinate system - * - * Transforms window coordinates from a parent window to a child - * window. - * - * Calling this function is equivalent to subtracting the return - * values of gdk_surface_get_position() from the parent coordinates. - * - * See also: gdk_surface_coords_to_parent() - **/ -void -gdk_surface_coords_from_parent (GdkSurface *window, - gdouble parent_x, - gdouble parent_y, - gdouble *x, - gdouble *y) -{ - g_return_if_fail (GDK_IS_SURFACE (window)); - - if (x) - *x = parent_x - window->x; - - if (y) - *y = parent_y - window->y; -} - -/** - * gdk_surface_shape_combine_region: - * @window: a #GdkSurface - * @shape_region: (allow-none): region of window to be non-transparent - * @offset_x: X position of @shape_region in @window coordinates - * @offset_y: Y position of @shape_region in @window coordinates - * - * Makes pixels in @window outside @shape_region be transparent, - * so that the window may be nonrectangular. - * - * If @shape_region is %NULL, the shape will be unset, so the whole - * window will be opaque again. @offset_x and @offset_y are ignored - * if @shape_region is %NULL. - * - * On the X11 platform, this uses an X server extension which is - * widely available on most common platforms, but not available on - * very old X servers, and occasionally the implementation will be - * buggy. On servers without the shape extension, this function - * will do nothing. - * - * This function works on both toplevel and child windows. - */ -void -gdk_surface_shape_combine_region (GdkSurface *window, - const cairo_region_t *shape_region, - gint offset_x, - gint offset_y) -{ - cairo_region_t *old_region, *new_region, *diff; - - g_return_if_fail (GDK_IS_SURFACE (window)); - - if (GDK_SURFACE_DESTROYED (window)) - return; - - if (!window->shape && shape_region == NULL) - return; - - window->shaped = (shape_region != NULL); - - if (window->shape) - cairo_region_destroy (window->shape); - - old_region = NULL; - if (GDK_SURFACE_IS_MAPPED (window)) - old_region = cairo_region_copy (window->clip_region); - - if (shape_region) - { - window->shape = cairo_region_copy (shape_region); - cairo_region_translate (window->shape, offset_x, offset_y); - } - else - window->shape = NULL; - - recompute_visible_regions (window, FALSE); - - if (old_region) - { - new_region = cairo_region_copy (window->clip_region); - - /* New area in the window, needs invalidation */ - diff = cairo_region_copy (new_region); - cairo_region_subtract (diff, old_region); - - gdk_surface_invalidate_region_full (window, diff, TRUE); - - cairo_region_destroy (diff); - - if (!gdk_surface_is_toplevel (window)) - { - /* New area in the non-root parent window, needs invalidation */ - diff = cairo_region_copy (old_region); - cairo_region_subtract (diff, new_region); - - /* Adjust region to parent window coords */ - cairo_region_translate (diff, window->x, window->y); - - gdk_surface_invalidate_region_full (window->parent, diff, TRUE); - - cairo_region_destroy (diff); - } - - cairo_region_destroy (new_region); - cairo_region_destroy (old_region); - } -} - -static void -do_child_shapes (GdkSurface *window, - gboolean merge) -{ - GdkRectangle r; - cairo_region_t *region; - - r.x = 0; - r.y = 0; - r.width = window->width; - r.height = window->height; - - region = cairo_region_create_rectangle (&r); - remove_child_area (window, FALSE, region); - - if (merge && window->shape) - cairo_region_subtract (region, window->shape); - - cairo_region_xor_rectangle (region, &r); - - gdk_surface_shape_combine_region (window, region, 0, 0); - - cairo_region_destroy (region); -} - -/** - * gdk_surface_set_child_shapes: - * @window: a #GdkSurface - * - * Sets the shape mask of @window to the union of shape masks - * for all children of @window, ignoring the shape mask of @window - * itself. Contrast with gdk_surface_merge_child_shapes() which includes - * the shape mask of @window in the masks to be merged. - **/ -void -gdk_surface_set_child_shapes (GdkSurface *window) -{ - g_return_if_fail (GDK_IS_SURFACE (window)); - - do_child_shapes (window, FALSE); -} - -/** - * gdk_surface_merge_child_shapes: - * @window: a #GdkSurface - * - * Merges the shape masks for any child windows into the - * shape mask for @window. i.e. the union of all masks - * for @window and its children will become the new mask - * for @window. See gdk_surface_shape_combine_region(). - * - * This function is distinct from gdk_surface_set_child_shapes() - * because it includes @window’s shape mask in the set of shapes to - * be merged. - */ -void -gdk_surface_merge_child_shapes (GdkSurface *window) -{ - g_return_if_fail (GDK_IS_SURFACE (window)); - - do_child_shapes (window, TRUE); -} - -/** - * gdk_surface_input_shape_combine_region: - * @window: a #GdkSurface - * @shape_region: region of window to be non-transparent - * @offset_x: X position of @shape_region in @window coordinates - * @offset_y: Y position of @shape_region in @window coordinates - * - * Like gdk_surface_shape_combine_region(), but the shape applies - * only to event handling. Mouse events which happen while - * the pointer position corresponds to an unset bit in the - * mask will be passed on the window below @window. - * - * An input shape is typically used with RGBA windows. - * The alpha channel of the window defines which pixels are - * invisible and allows for nicely antialiased borders, - * and the input shape controls where the window is - * “clickable”. - * - * On the X11 platform, this requires version 1.1 of the - * shape extension. - * - * On the Win32 platform, this functionality is not present and the - * function does nothing. - */ -void -gdk_surface_input_shape_combine_region (GdkSurface *window, - const cairo_region_t *shape_region, - gint offset_x, - gint offset_y) -{ - GdkSurfaceImplClass *impl_class; - - g_return_if_fail (GDK_IS_SURFACE (window)); - - if (GDK_SURFACE_DESTROYED (window)) - return; - - if (window->input_shape) - cairo_region_destroy (window->input_shape); - - if (shape_region) - { - window->input_shape = cairo_region_copy (shape_region); - cairo_region_translate (window->input_shape, offset_x, offset_y); - } - else - window->input_shape = NULL; - - if (gdk_surface_has_impl (window)) - { - impl_class = GDK_SURFACE_IMPL_GET_CLASS (window->impl); - impl_class->input_shape_combine_region (window, window->input_shape, 0, 0); - } -} - -static void -do_child_input_shapes (GdkSurface *window, - gboolean merge) -{ - GdkRectangle r; - cairo_region_t *region; - - r.x = 0; - r.y = 0; - r.width = window->width; - r.height = window->height; - - region = cairo_region_create_rectangle (&r); - remove_child_area (window, TRUE, region); - - if (merge && window->shape) - cairo_region_subtract (region, window->shape); - if (merge && window->input_shape) - cairo_region_subtract (region, window->input_shape); - - cairo_region_xor_rectangle (region, &r); - - gdk_surface_input_shape_combine_region (window, region, 0, 0); -} - - -/** - * gdk_surface_set_child_input_shapes: - * @window: a #GdkSurface - * - * Sets the input shape mask of @window to the union of input shape masks - * for all children of @window, ignoring the input shape mask of @window - * itself. Contrast with gdk_surface_merge_child_input_shapes() which includes - * the input shape mask of @window in the masks to be merged. - **/ -void -gdk_surface_set_child_input_shapes (GdkSurface *window) -{ - g_return_if_fail (GDK_IS_SURFACE (window)); - - do_child_input_shapes (window, FALSE); -} - -/** - * gdk_surface_set_pass_through: - * @window: a #GdkSurface - * @pass_through: a boolean - * - * Sets whether input to the window is passed through to the window - * below. - * - * The default value of this is %FALSE, which means that pointer - * events that happen inside the window are send first to the window, - * but if the event is not selected by the event mask then the event - * is sent to the parent window, and so on up the hierarchy. - * - * If @pass_through is %TRUE then such pointer events happen as if the - * window wasn't there at all, and thus will be sent first to any - * windows below @window. This is useful if the window is used in a - * transparent fashion. In the terminology of the web this would be called - * "pointer-events: none". - * - * Note that a window with @pass_through %TRUE can still have a subwindow - * without pass through, so you can get events on a subset of a window. And in - * that cases you would get the in-between related events such as the pointer - * enter/leave events on its way to the destination window. - **/ -void -gdk_surface_set_pass_through (GdkSurface *window, - gboolean pass_through) -{ - g_return_if_fail (GDK_IS_SURFACE (window)); - - window->pass_through = !!pass_through; -} - -/** - * gdk_surface_get_pass_through: - * @window: a #GdkSurface - * - * Returns whether input to the window is passed through to the window - * below. - * - * See gdk_surface_set_pass_through() for details - **/ -gboolean -gdk_surface_get_pass_through (GdkSurface *window) -{ - g_return_val_if_fail (GDK_IS_SURFACE (window), FALSE); - - return window->pass_through; -} - -/** - * gdk_surface_merge_child_input_shapes: - * @window: a #GdkSurface - * - * Merges the input shape masks for any child windows into the - * input shape mask for @window. i.e. the union of all input masks - * for @window and its children will become the new input mask - * for @window. See gdk_surface_input_shape_combine_region(). - * - * This function is distinct from gdk_surface_set_child_input_shapes() - * because it includes @window’s input shape mask in the set of - * shapes to be merged. - **/ -void -gdk_surface_merge_child_input_shapes (GdkSurface *window) -{ - g_return_if_fail (GDK_IS_SURFACE (window)); - - do_child_input_shapes (window, TRUE); -} - - -/** - * gdk_surface_get_modal_hint: - * @window: A toplevel #GdkSurface. - * - * Determines whether or not the window manager is hinted that @window - * has modal behaviour. - * - * Returns: whether or not the window has the modal hint set. - */ -gboolean -gdk_surface_get_modal_hint (GdkSurface *window) -{ - g_return_val_if_fail (GDK_IS_SURFACE (window), FALSE); - - return window->modal_hint; -} - -/** - * gdk_surface_get_accept_focus: - * @window: a toplevel #GdkSurface. - * - * Determines whether or not the desktop environment shuld be hinted that - * the window does not want to receive input focus. - * - * Returns: whether or not the window should receive input focus. - */ -gboolean -gdk_surface_get_accept_focus (GdkSurface *window) -{ - g_return_val_if_fail (GDK_IS_SURFACE (window), FALSE); - - return window->accept_focus; -} - -/** - * gdk_surface_get_focus_on_map: - * @window: a toplevel #GdkSurface. - * - * Determines whether or not the desktop environment should be hinted that the - * window does not want to receive input focus when it is mapped. - * - * Returns: whether or not the window wants to receive input focus when - * it is mapped. - */ -gboolean -gdk_surface_get_focus_on_map (GdkSurface *window) -{ - g_return_val_if_fail (GDK_IS_SURFACE (window), FALSE); - - return window->focus_on_map; -} - -/** - * gdk_surface_is_input_only: - * @window: a toplevel #GdkSurface - * - * Determines whether or not the window is an input only window. - * - * Returns: %TRUE if @window is input only - */ -gboolean -gdk_surface_is_input_only (GdkSurface *window) -{ - g_return_val_if_fail (GDK_IS_SURFACE (window), FALSE); - - return window->input_only; -} - -/** - * gdk_surface_is_shaped: - * @window: a toplevel #GdkSurface - * - * Determines whether or not the window is shaped. - * - * Returns: %TRUE if @window is shaped - */ -gboolean -gdk_surface_is_shaped (GdkSurface *window) -{ - g_return_val_if_fail (GDK_IS_SURFACE (window), FALSE); - - return window->shaped; -} - -/* Gets the toplevel for a window as used for events, - i.e. including offscreen parents going up to the native - toplevel */ -static GdkSurface * -get_event_toplevel (GdkSurface *window) -{ - GdkSurface *parent; - - while ((parent = window->parent) != NULL && - (parent->window_type != GDK_SURFACE_ROOT)) - window = parent; - - return window; -} - -gboolean -_gdk_surface_event_parent_of (GdkSurface *parent, - GdkSurface *child) -{ - GdkSurface *w; - - w = child; - while (w != NULL) - { - if (w == parent) - return TRUE; - - w = w->parent; - } - - return FALSE; -} - -static void -update_cursor (GdkDisplay *display, - GdkDevice *device) -{ - GdkSurface *cursor_window, *parent, *toplevel; - GdkSurface *pointer_window; - GdkPointerSurfaceInfo *pointer_info; - GdkDeviceGrabInfo *grab; - GdkCursor *cursor; - - pointer_info = _gdk_display_get_pointer_info (display, device); - pointer_window = pointer_info->window_under_pointer; - - /* We ignore the serials here and just pick the last grab - we've sent, as that would shortly be used anyway. */ - grab = _gdk_display_get_last_device_grab (display, device); - if (/* have grab */ - grab != NULL && - /* the pointer is not in a descendant of the grab window */ - !_gdk_surface_event_parent_of (grab->window, pointer_window)) - { - /* use the cursor from the grab window */ - cursor_window = grab->window; - } - else - { - /* otherwise use the cursor from the pointer window */ - cursor_window = pointer_window; - } - - /* Find the first window with the cursor actually set, as - the cursor is inherited from the parent */ - while (cursor_window->cursor == NULL && - !g_hash_table_contains (cursor_window->device_cursor, device) && - (parent = cursor_window->parent) != NULL && - parent->window_type != GDK_SURFACE_ROOT) - cursor_window = parent; - - cursor = g_hash_table_lookup (cursor_window->device_cursor, device); - - if (!cursor) - cursor = cursor_window->cursor; - - /* Set all cursors on toplevel, otherwise its tricky to keep track of - * which native window has what cursor set. */ - toplevel = get_event_toplevel (pointer_window); - GDK_DEVICE_GET_CLASS (device)->set_window_cursor (device, toplevel, cursor); -} - -static gboolean -point_in_window (GdkSurface *window, - gdouble x, - gdouble y) -{ - return - x >= 0 && x < window->width && - y >= 0 && y < window->height && - (window->shape == NULL || - cairo_region_contains_point (window->shape, - x, y)) && - (window->input_shape == NULL || - cairo_region_contains_point (window->input_shape, - x, y)); -} - -/* Same as point_in_window, except it also takes pass_through and its - interaction with child windows into account */ -static gboolean -point_in_input_window (GdkSurface *window, - gdouble x, - gdouble y, - GdkSurface **input_window, - gdouble *input_window_x, - gdouble *input_window_y) -{ - GdkSurface *sub; - double child_x, child_y; - GList *l; - - if (!point_in_window (window, x, y)) - return FALSE; - - if (!window->pass_through) - { - if (input_window) - { - *input_window = window; - *input_window_x = x; - *input_window_y = y; - } - return TRUE; - } - - /* For pass-through, must be over a child input window */ - - /* Children is ordered in reverse stack order, i.e. first is topmost */ - for (l = window->children; l != NULL; l = l->next) - { - sub = l->data; - - if (!GDK_SURFACE_IS_MAPPED (sub)) - continue; - - gdk_surface_coords_from_parent ((GdkSurface *)sub, - x, y, - &child_x, &child_y); - if (point_in_input_window (sub, child_x, child_y, - input_window, input_window_x, input_window_y)) - { - if (input_window) - gdk_surface_coords_to_parent (sub, - *input_window_x, - *input_window_y, - input_window_x, - input_window_y); - return TRUE; - } - } - - return FALSE; -} - -GdkSurface * -_gdk_surface_find_child_at (GdkSurface *window, - double x, - double y) -{ - GdkSurface *sub; - double child_x, child_y; - GList *l; - - if (point_in_window (window, x, y)) - { - /* Children is ordered in reverse stack order, i.e. first is topmost */ - for (l = window->children; l != NULL; l = l->next) - { - sub = l->data; - - if (!GDK_SURFACE_IS_MAPPED (sub)) - continue; - - gdk_surface_coords_from_parent ((GdkSurface *)sub, - x, y, - &child_x, &child_y); - if (point_in_input_window (sub, child_x, child_y, - NULL, NULL, NULL)) - return (GdkSurface *)sub; - } - } - - return NULL; -} - -GdkSurface * -_gdk_surface_find_descendant_at (GdkSurface *window, - gdouble x, - gdouble y, - gdouble *found_x, - gdouble *found_y) -{ - GdkSurface *sub, *input_window; - gdouble child_x, child_y; - GList *l; - gboolean found; - - if (point_in_window (window, x, y)) - { - do - { - found = FALSE; - /* Children is ordered in reverse stack order, i.e. first is topmost */ - for (l = window->children; l != NULL; l = l->next) - { - sub = l->data; - - if (!GDK_SURFACE_IS_MAPPED (sub)) - continue; - - gdk_surface_coords_from_parent ((GdkSurface *)sub, - x, y, - &child_x, &child_y); - if (point_in_input_window (sub, child_x, child_y, - &input_window, &child_x, &child_y)) - { - x = child_x; - y = child_y; - window = input_window; - found = TRUE; - break; - } - } - } - while (found); - } - else - { - /* Not in window at all */ - window = NULL; - } - - if (found_x) - *found_x = x; - if (found_y) - *found_y = y; - - return window; -} - -/** - * gdk_surface_beep: - * @window: a toplevel #GdkSurface - * - * Emits a short beep associated to @window in the appropriate - * display, if supported. Otherwise, emits a short beep on - * the display just as gdk_display_beep(). - **/ -void -gdk_surface_beep (GdkSurface *window) -{ - GdkDisplay *display; - GdkSurface *toplevel; - - g_return_if_fail (GDK_IS_SURFACE (window)); - - if (GDK_SURFACE_DESTROYED (window)) - return; - - toplevel = get_event_toplevel (window); - display = gdk_surface_get_display (window); - - if (toplevel) - { - if (GDK_SURFACE_IMPL_GET_CLASS (toplevel->impl)->beep (toplevel)) - return; - } - - /* If windows fail to beep, we beep the display. */ - gdk_display_beep (display); -} - -/** - * gdk_surface_set_support_multidevice: - * @window: a #GdkSurface. - * @support_multidevice: %TRUE to enable multidevice support in @window. - * - * This function will enable multidevice features in @window. - * - * Multidevice aware windows will need to handle properly multiple, - * per device enter/leave events, device grabs and grab ownerships. - **/ -void -gdk_surface_set_support_multidevice (GdkSurface *window, - gboolean support_multidevice) -{ - g_return_if_fail (GDK_IS_SURFACE (window)); - - if (GDK_SURFACE_DESTROYED (window)) - return; - - if (window->support_multidevice == support_multidevice) - return; - - window->support_multidevice = support_multidevice; - - /* FIXME: What to do if called when some pointers are inside the window ? */ -} - -/** - * gdk_surface_get_support_multidevice: - * @window: a #GdkSurface. - * - * Returns %TRUE if the window is aware of the existence of multiple - * devices. - * - * Returns: %TRUE if the window handles multidevice features. - **/ -gboolean -gdk_surface_get_support_multidevice (GdkSurface *window) -{ - g_return_val_if_fail (GDK_IS_SURFACE (window), FALSE); - - if (GDK_SURFACE_DESTROYED (window)) - return FALSE; - - return window->support_multidevice; -} - -/* send motion events if the right buttons are down */ - -GdkEvent * -_gdk_make_event (GdkSurface *window, - GdkEventType type, - GdkEvent *event_in_queue, - gboolean before_event) -{ - GdkEvent *event = gdk_event_new (type); - guint32 the_time; - GdkModifierType the_state; - - the_time = gdk_event_get_time (event_in_queue); - gdk_event_get_state (event_in_queue, &the_state); - - event->any.window = g_object_ref (window); - event->any.send_event = FALSE; - if (event_in_queue && event_in_queue->any.send_event) - event->any.send_event = TRUE; - - switch ((guint) type) - { - case GDK_MOTION_NOTIFY: - event->motion.time = the_time; - event->motion.axes = NULL; - event->motion.state = the_state; - break; - - case GDK_BUTTON_PRESS: - case GDK_BUTTON_RELEASE: - event->button.time = the_time; - event->button.axes = NULL; - 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; - break; - - case GDK_KEY_PRESS: - case GDK_KEY_RELEASE: - event->key.time = the_time; - event->key.state = the_state; - break; - - case GDK_ENTER_NOTIFY: - case GDK_LEAVE_NOTIFY: - event->crossing.time = the_time; - event->crossing.state = the_state; - break; - - case GDK_PROXIMITY_IN: - case GDK_PROXIMITY_OUT: - event->proximity.time = the_time; - break; - - case GDK_DRAG_ENTER: - case GDK_DRAG_LEAVE: - case GDK_DRAG_MOTION: - case GDK_DROP_START: - event->dnd.time = the_time; - break; - - case GDK_TOUCHPAD_SWIPE: - event->touchpad_swipe.time = the_time; - event->touchpad_swipe.state = the_state; - break; - - case GDK_TOUCHPAD_PINCH: - event->touchpad_pinch.time = the_time; - event->touchpad_pinch.state = the_state; - break; - - case GDK_FOCUS_CHANGE: - case GDK_CONFIGURE: - case GDK_MAP: - case GDK_UNMAP: - case GDK_DELETE: - case GDK_DESTROY: - case GDK_EXPOSE: - default: - break; - } - - if (event_in_queue) - { - if (before_event) - _gdk_event_queue_insert_before (gdk_surface_get_display (window), event_in_queue, event); - else - _gdk_event_queue_insert_after (gdk_surface_get_display (window), event_in_queue, event); - } - else - _gdk_event_queue_append (gdk_surface_get_display (window), event); - - return event; -} - -void -_gdk_display_set_window_under_pointer (GdkDisplay *display, - GdkDevice *device, - GdkSurface *window) -{ - GdkPointerSurfaceInfo *device_info; - - device_info = _gdk_display_get_pointer_info (display, device); - - if (device_info->window_under_pointer) - g_object_unref (device_info->window_under_pointer); - device_info->window_under_pointer = window; - - if (window) - { - g_object_ref (window); - update_cursor (display, device); - } -} - -#define GDK_ANY_BUTTON_MASK (GDK_BUTTON1_MASK | \ - GDK_BUTTON2_MASK | \ - GDK_BUTTON3_MASK | \ - GDK_BUTTON4_MASK | \ - GDK_BUTTON5_MASK) - -#ifdef DEBUG_WINDOW_PRINTING - -#ifdef GDK_WINDOWING_X11 -#include "x11/gdkx.h" -#endif - -static void -gdk_surface_print (GdkSurface *window, - int indent) -{ - char *s; - const char *window_types[] = { - "root", - "toplevel", - "child", - "dialog", - "temp", - "foreign", - "subsurface" - }; - - g_print ("%*s%p: [%s] %d,%d %dx%d", indent, "", window, - window->user_data ? g_type_name_from_instance (window->user_data) : "no widget", - window->x, window->y, - window->width, window->height - ); - - if (gdk_surface_has_impl (window)) - { -#ifdef GDK_WINDOWING_X11 - g_print (" impl(0x%lx)", gdk_x11_surface_get_xid (window)); -#endif - } - - if (window->window_type != GDK_SURFACE_CHILD) - g_print (" %s", window_types[window->window_type]); - - if (window->input_only) - g_print (" input-only"); - - if (window->shaped) - g_print (" shaped"); - - if (!gdk_surface_is_visible ((GdkSurface *)window)) - g_print (" hidden"); - - g_print (" abs[%d,%d]", - window->abs_x, window->abs_y); - - if (window->alpha != 255) - g_print (" alpha[%d]", - window->alpha); - - s = print_region (window->clip_region); - g_print (" clipbox[%s]", s); - - g_print ("\n"); -} - - -static void -gdk_surface_print_tree (GdkSurface *window, - int indent, - gboolean include_input_only) -{ - GList *l; - - if (window->input_only && !include_input_only) - return; - - gdk_surface_print (window, indent); - - for (l = window->children; l != NULL; l = l->next) - gdk_surface_print_tree (l->data, indent + 4, include_input_only); -} - -#endif /* DEBUG_WINDOW_PRINTING */ - -void -_gdk_windowing_got_event (GdkDisplay *display, - GList *event_link, - GdkEvent *event, - gulong serial) -{ - GdkSurface *event_window; - gboolean unlink_event = FALSE; - GdkDeviceGrabInfo *button_release_grab; - GdkPointerSurfaceInfo *pointer_info = NULL; - GdkDevice *device, *source_device; - - _gdk_display_update_last_event (display, event); - - device = gdk_event_get_device (event); - source_device = gdk_event_get_source_device (event); - - if (device) - { - if (gdk_device_get_source (device) != GDK_SOURCE_KEYBOARD && - gdk_device_get_source (device) != GDK_SOURCE_TABLET_PAD) - { - 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; - } - - _gdk_display_device_grab_update (display, device, source_device, serial); - - if (gdk_device_get_input_mode (device) == GDK_MODE_DISABLED || - !_gdk_display_check_grab_ownership (display, device, serial)) - { - /* Device events are blocked by another - * device grab, or the device is disabled - */ - unlink_event = TRUE; - goto out; - } - } - - event_window = event->any.window; - if (!event_window) - goto out; - -#ifdef DEBUG_WINDOW_PRINTING - if (event->any.type == GDK_KEY_PRESS && - (event->key.keyval == 0xa7 || - event->key.keyval == 0xbd)) - { - gdk_surface_print_tree (event_window, 0, event->key.keyval == 0xbd); - } -#endif - - if (event_window->window_type == GDK_SURFACE_ROOT) - goto out; - - if (event->any.type == GDK_ENTER_NOTIFY) - _gdk_display_set_window_under_pointer (display, device, event_window); - else if (event->any.type == GDK_LEAVE_NOTIFY) - _gdk_display_set_window_under_pointer (display, device, NULL); - - if ((event->any.type == GDK_BUTTON_RELEASE || - event->any.type == GDK_TOUCH_CANCEL || - event->any.type == GDK_TOUCH_END) && - !event->any.send_event) - { - if (event->any.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: - if (unlink_event) - { - _gdk_event_queue_remove_link (display, event_link); - g_list_free_1 (event_link); - gdk_event_free (event); - } - - /* This does two things - first it sees if there are motions at the - * end of the queue that can be compressed. Second, if there is just - * a single motion that won't be dispatched because it is a compression - * candidate it queues up flushing the event queue. - */ - _gdk_event_queue_handle_motion_compression (display); -} - -/** - * gdk_surface_create_similar_surface: - * @window: window to make new surface similar to - * @content: the content for the new surface - * @width: width of the new surface - * @height: height of the new surface - * - * Create a new surface that is as compatible as possible with the - * given @window. For example the new surface will have the same - * fallback resolution and font options as @window. Generally, the new - * surface will also use the same backend as @window, unless that is - * not possible for some reason. The type of the returned surface may - * be examined with cairo_surface_get_type(). - * - * Initially the surface contents are all 0 (transparent if contents - * have transparency, black otherwise.) - * - * Returns: a pointer to the newly allocated surface. The caller - * owns the surface and should call cairo_surface_destroy() when done - * with it. - * - * This function always returns a valid pointer, but it will return a - * pointer to a “nil” surface if @other is already in an error state - * or any other error occurs. - **/ -cairo_surface_t * -gdk_surface_create_similar_surface (GdkSurface * window, - cairo_content_t content, - int width, - int height) -{ - cairo_surface_t *window_surface, *surface; - double sx, sy; - - g_return_val_if_fail (GDK_IS_SURFACE (window), NULL); - - window_surface = gdk_surface_ref_impl_surface (window); - sx = sy = 1; - cairo_surface_get_device_scale (window_surface, &sx, &sy); - - if (GDK_DISPLAY_DEBUG_CHECK (window->display, CAIRO_IMAGE)) - { - surface = cairo_image_surface_create (content == CAIRO_CONTENT_COLOR ? CAIRO_FORMAT_RGB24 : - content == CAIRO_CONTENT_ALPHA ? CAIRO_FORMAT_A8 : CAIRO_FORMAT_ARGB32, - width * sx, height * sy); - cairo_surface_set_device_scale (surface, sx, sy); - } - else - { - surface = cairo_surface_create_similar (window_surface, - content, - width, height); - } - - cairo_surface_destroy (window_surface); - - return surface; -} - - -/** - * gdk_surface_create_similar_image_surface: - * @window: (nullable): window to make new surface similar to, or - * %NULL if none - * @format: (type int): the format for the new surface - * @width: width of the new surface - * @height: height of the new surface - * @scale: the scale of the new surface, or 0 to use same as @window - * - * Create a new image surface that is efficient to draw on the - * given @window. - * - * Initially the surface contents are all 0 (transparent if contents - * have transparency, black otherwise.) - * - * The @width and @height of the new surface are not affected by - * the scaling factor of the @window, or by the @scale argument; they - * are the size of the surface in device pixels. If you wish to create - * an image surface capable of holding the contents of @window you can - * use: - * - * |[ - * int scale = gdk_surface_get_scale_factor (window); - * int width = gdk_surface_get_width (window) * scale; - * int height = gdk_surface_get_height (window) * scale; - * - * // format is set elsewhere - * cairo_surface_t *surface = - * gdk_surface_create_similar_image_surface (window, - * format, - * width, height, - * scale); - * ]| - * - * Note that unlike cairo_surface_create_similar_image(), the new - * surface's device scale is set to @scale, or to the scale factor of - * @window if @scale is 0. - * - * Returns: a pointer to the newly allocated surface. The caller - * owns the surface and should call cairo_surface_destroy() when done - * with it. - * - * This function always returns a valid pointer, but it will return a - * pointer to a “nil” surface if @other is already in an error state - * or any other error occurs. - **/ -cairo_surface_t * -gdk_surface_create_similar_image_surface (GdkSurface * window, - cairo_format_t format, - int width, - int height, - int scale) -{ - cairo_surface_t *surface; - - g_return_val_if_fail (window == NULL || GDK_IS_SURFACE (window), NULL); - - if (window == NULL) - { - surface = cairo_image_surface_create (format, width, height); - } - else if (GDK_SURFACE_IMPL_GET_CLASS (window->impl)->create_similar_image_surface) - { - surface = - GDK_SURFACE_IMPL_GET_CLASS (window->impl)->create_similar_image_surface (window, format, width, height); - } - else - { - cairo_surface_t *window_surface; - - window_surface = gdk_surface_ref_impl_surface (window); - surface = - cairo_surface_create_similar_image (window_surface, - format, - width, - height); - cairo_surface_destroy (window_surface); - } - - if (scale == 0) - scale = gdk_surface_get_scale_factor (window); - - cairo_surface_set_device_scale (surface, scale, scale); - - return surface; -} - - -/** - * gdk_surface_focus: - * @window: a #GdkSurface - * @timestamp: timestamp of the event triggering the window focus - * - * Sets keyboard focus to @window. In most cases, gtk_window_present() - * should be used on a #GtkWindow, rather than calling this function. - * - **/ -void -gdk_surface_focus (GdkSurface *window, - guint32 timestamp) -{ - GDK_SURFACE_IMPL_GET_CLASS (window->impl)->focus (window, timestamp); -} - -/** - * gdk_surface_set_type_hint: - * @window: A toplevel #GdkSurface - * @hint: A hint of the function this window will have - * - * The application can use this call to provide a hint to the window - * manager about the functionality of a window. The window manager - * can use this information when determining the decoration and behaviour - * of the window. - * - * The hint must be set before the window is mapped. - **/ -void -gdk_surface_set_type_hint (GdkSurface *window, - GdkSurfaceTypeHint hint) -{ - GDK_SURFACE_IMPL_GET_CLASS (window->impl)->set_type_hint (window, hint); -} - -/** - * gdk_surface_get_type_hint: - * @window: A toplevel #GdkSurface - * - * This function returns the type hint set for a window. - * - * Returns: The type hint set for @window - **/ -GdkSurfaceTypeHint -gdk_surface_get_type_hint (GdkSurface *window) -{ - return GDK_SURFACE_IMPL_GET_CLASS (window->impl)->get_type_hint (window); -} - -/** - * gdk_surface_set_modal_hint: - * @window: A toplevel #GdkSurface - * @modal: %TRUE if the window is modal, %FALSE otherwise. - * - * The application can use this hint to tell the window manager - * that a certain window has modal behaviour. The window manager - * can use this information to handle modal windows in a special - * way. - * - * You should only use this on windows for which you have - * previously called gdk_surface_set_transient_for() - **/ -void -gdk_surface_set_modal_hint (GdkSurface *window, - gboolean modal) -{ - GDK_SURFACE_IMPL_GET_CLASS (window->impl)->set_modal_hint (window, modal); -} - -/** - * gdk_surface_set_skip_taskbar_hint: - * @window: a toplevel #GdkSurface - * @skips_taskbar: %TRUE to skip the taskbar - * - * Toggles whether a window should appear in a task list or window - * list. If a window’s semantic type as specified with - * gdk_surface_set_type_hint() already fully describes the window, this - * function should not be called in addition, - * instead you should allow the window to be treated according to - * standard policy for its semantic type. - **/ -void -gdk_surface_set_skip_taskbar_hint (GdkSurface *window, - gboolean skips_taskbar) -{ - GDK_SURFACE_IMPL_GET_CLASS (window->impl)->set_skip_taskbar_hint (window, skips_taskbar); -} - -/** - * gdk_surface_set_skip_pager_hint: - * @window: a toplevel #GdkSurface - * @skips_pager: %TRUE to skip the pager - * - * Toggles whether a window should appear in a pager (workspace - * switcher, or other desktop utility program that displays a small - * thumbnail representation of the windows on the desktop). If a - * window’s semantic type as specified with gdk_surface_set_type_hint() - * already fully describes the window, this function should - * not be called in addition, instead you should - * allow the window to be treated according to standard policy for - * its semantic type. - **/ -void -gdk_surface_set_skip_pager_hint (GdkSurface *window, - gboolean skips_pager) -{ - GDK_SURFACE_IMPL_GET_CLASS (window->impl)->set_skip_pager_hint (window, skips_pager); -} - -/** - * gdk_surface_set_urgency_hint: - * @window: a toplevel #GdkSurface - * @urgent: %TRUE if the window is urgent - * - * Toggles whether a window needs the user's - * urgent attention. - **/ -void -gdk_surface_set_urgency_hint (GdkSurface *window, - gboolean urgent) -{ - GDK_SURFACE_IMPL_GET_CLASS (window->impl)->set_urgency_hint (window, urgent); -} - -/** - * gdk_surface_set_geometry_hints: - * @window: a toplevel #GdkSurface - * @geometry: geometry hints - * @geom_mask: bitmask indicating fields of @geometry to pay attention to - * - * Sets the geometry hints for @window. Hints flagged in @geom_mask - * are set, hints not flagged in @geom_mask are unset. - * To unset all hints, use a @geom_mask of 0 and a @geometry of %NULL. - * - * This function provides hints to the windowing system about - * acceptable sizes for a toplevel window. The purpose of - * this is to constrain user resizing, but the windowing system - * will typically (but is not required to) also constrain the - * current size of the window to the provided values and - * constrain programatic resizing via gdk_surface_resize() or - * gdk_surface_move_resize(). - * - * Note that on X11, this effect has no effect on windows - * of type %GDK_SURFACE_TEMP since these windows are not resizable - * by the user. - * - * Since you can’t count on the windowing system doing the - * constraints for programmatic resizes, you should generally - * call gdk_surface_constrain_size() yourself to determine - * appropriate sizes. - * - **/ -void -gdk_surface_set_geometry_hints (GdkSurface *window, - const GdkGeometry *geometry, - GdkSurfaceHints geom_mask) -{ - g_return_if_fail (geometry != NULL || geom_mask == 0); - - GDK_SURFACE_IMPL_GET_CLASS (window->impl)->set_geometry_hints (window, geometry, geom_mask); -} - -/** - * gdk_surface_set_title: - * @window: a toplevel #GdkSurface - * @title: title of @window - * - * Sets the title of a toplevel window, to be displayed in the titlebar. - * If you haven’t explicitly set the icon name for the window - * (using gdk_surface_set_icon_name()), the icon name will be set to - * @title as well. @title must be in UTF-8 encoding (as with all - * user-readable strings in GDK/GTK+). @title may not be %NULL. - **/ -void -gdk_surface_set_title (GdkSurface *window, - const gchar *title) -{ - GDK_SURFACE_IMPL_GET_CLASS (window->impl)->set_title (window, title); -} - -/** - * gdk_surface_set_role: - * @window: a toplevel #GdkSurface - * @role: a string indicating its role - * - * When using GTK+, typically you should use gtk_window_set_role() instead - * of this low-level function. - * - * The window manager and session manager use a window’s role to - * distinguish it from other kinds of window in the same application. - * When an application is restarted after being saved in a previous - * session, all windows with the same title and role are treated as - * interchangeable. So if you have two windows with the same title - * that should be distinguished for session management purposes, you - * should set the role on those windows. It doesn’t matter what string - * you use for the role, as long as you have a different role for each - * non-interchangeable kind of window. - * - **/ -void -gdk_surface_set_role (GdkSurface *window, - const gchar *role) -{ - GDK_SURFACE_IMPL_GET_CLASS (window->impl)->set_role (window, role); -} - -/** - * gdk_surface_set_startup_id: - * @window: a toplevel #GdkSurface - * @startup_id: a string with startup-notification identifier - * - * When using GTK+, typically you should use gtk_window_set_startup_id() - * instead of this low-level function. - **/ -void -gdk_surface_set_startup_id (GdkSurface *window, - const gchar *startup_id) -{ - GDK_SURFACE_IMPL_GET_CLASS (window->impl)->set_startup_id (window, startup_id); -} - -/** - * gdk_surface_set_transient_for: - * @window: a toplevel #GdkSurface - * @parent: another toplevel #GdkSurface - * - * Indicates to the window manager that @window is a transient dialog - * associated with the application window @parent. This allows the - * window manager to do things like center @window on @parent and - * keep @window above @parent. - * - * See gtk_window_set_transient_for() if you’re using #GtkWindow or - * #GtkDialog. - **/ -void -gdk_surface_set_transient_for (GdkSurface *window, - GdkSurface *parent) -{ - window->transient_for = parent; - - GDK_SURFACE_IMPL_GET_CLASS (window->impl)->set_transient_for (window, parent); -} - -/** - * gdk_surface_get_root_origin: - * @window: a toplevel #GdkSurface - * @x: (out): return location for X position of window frame - * @y: (out): return location for Y position of window frame - * - * Obtains the top-left corner of the window manager frame in root - * window coordinates. - * - **/ -void -gdk_surface_get_root_origin (GdkSurface *window, - gint *x, - gint *y) -{ - GdkRectangle rect; - - gdk_surface_get_frame_extents (window, &rect); - - if (x) - *x = rect.x; - - if (y) - *y = rect.y; -} - -/** - * gdk_surface_get_frame_extents: - * @window: a toplevel #GdkSurface - * @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 - * coordinates. To get the position of the window itself (rather than - * the frame) in root window coordinates, use gdk_surface_get_origin(). - * - **/ -void -gdk_surface_get_frame_extents (GdkSurface *window, - GdkRectangle *rect) -{ - GDK_SURFACE_IMPL_GET_CLASS (window->impl)->get_frame_extents (window, rect); -} - -/** - * gdk_surface_set_accept_focus: - * @window: a toplevel #GdkSurface - * @accept_focus: %TRUE if the window should receive input focus - * - * Setting @accept_focus to %FALSE hints the desktop environment that the - * window doesn’t want to receive input focus. - * - * On X, it is the responsibility of the window manager to interpret this - * hint. ICCCM-compliant window manager usually respect it. - **/ -void -gdk_surface_set_accept_focus (GdkSurface *window, - gboolean accept_focus) -{ - GDK_SURFACE_IMPL_GET_CLASS (window->impl)->set_accept_focus (window, accept_focus); -} - -/** - * gdk_surface_set_focus_on_map: - * @window: a toplevel #GdkSurface - * @focus_on_map: %TRUE if the window should receive input focus when mapped - * - * Setting @focus_on_map to %FALSE hints the desktop environment that the - * window doesn’t want to receive input focus when it is mapped. - * focus_on_map should be turned off for windows that aren’t triggered - * interactively (such as popups from network activity). - * - * On X, it is the responsibility of the window manager to interpret - * this hint. Window managers following the freedesktop.org window - * manager extension specification should respect it. - **/ -void -gdk_surface_set_focus_on_map (GdkSurface *window, - gboolean focus_on_map) -{ - GDK_SURFACE_IMPL_GET_CLASS (window->impl)->set_focus_on_map (window, focus_on_map); -} - -/** - * gdk_surface_set_icon_list: - * @window: The #GdkSurface toplevel window to set the icon of. - * @surfaces: (transfer none) (element-type GdkTexture): - * A list of image surfaces, of different sizes. - * - * Sets a list of icons for the window. One of these will be used - * to represent the window when it has been iconified. The icon is - * usually shown in an icon box or some sort of task bar. Which icon - * size is shown depends on the window manager. The window manager - * can scale the icon but setting several size icons can give better - * image quality since the window manager may only need to scale the - * icon by a small amount or not at all. - * - * Note that some platforms don't support window icons. - */ -void -gdk_surface_set_icon_list (GdkSurface *window, - GList *textures) -{ - GDK_SURFACE_IMPL_GET_CLASS (window->impl)->set_icon_list (window, textures); -} - -/** - * gdk_surface_set_icon_name: - * @window: a toplevel #GdkSurface - * @name: (allow-none): name of window while iconified (minimized) - * - * Windows may have a name used while minimized, distinct from the - * name they display in their titlebar. Most of the time this is a bad - * idea from a user interface standpoint. But you can set such a name - * with this function, if you like. - * - * After calling this with a non-%NULL @name, calls to gdk_surface_set_title() - * will not update the icon title. - * - * Using %NULL for @name unsets the icon title; further calls to - * gdk_surface_set_title() will again update the icon title as well. - * - * Note that some platforms don't support window icons. - **/ -void -gdk_surface_set_icon_name (GdkSurface *window, - const gchar *name) -{ - GDK_SURFACE_IMPL_GET_CLASS (window->impl)->set_icon_name (window, name); -} - -/** - * gdk_surface_iconify: - * @window: a toplevel #GdkSurface - * - * Asks to iconify (minimize) @window. The window manager may choose - * to ignore the request, but normally will honor it. Using - * gtk_window_iconify() is preferred, if you have a #GtkWindow widget. - * - * This function only makes sense when @window is a toplevel window. - * - **/ -void -gdk_surface_iconify (GdkSurface *window) -{ - GDK_SURFACE_IMPL_GET_CLASS (window->impl)->iconify (window); -} - -/** - * gdk_surface_deiconify: - * @window: a toplevel #GdkSurface - * - * Attempt to deiconify (unminimize) @window. On X11 the window manager may - * choose to ignore the request to deiconify. When using GTK+, - * use gtk_window_deiconify() instead of the #GdkSurface variant. Or better yet, - * you probably want to use gtk_window_present(), which raises the window, focuses it, - * unminimizes it, and puts it on the current desktop. - * - **/ -void -gdk_surface_deiconify (GdkSurface *window) -{ - GDK_SURFACE_IMPL_GET_CLASS (window->impl)->deiconify (window); -} - -/** - * gdk_surface_stick: - * @window: a toplevel #GdkSurface - * - * “Pins” a window such that it’s on all workspaces and does not scroll - * with viewports, for window managers that have scrollable viewports. - * (When using #GtkWindow, gtk_window_stick() may be more useful.) - * - * On the X11 platform, this function depends on window manager - * support, so may have no effect with many window managers. However, - * GDK will do the best it can to convince the window manager to stick - * the window. For window managers that don’t support this operation, - * there’s nothing you can do to force it to happen. - * - **/ -void -gdk_surface_stick (GdkSurface *window) -{ - GDK_SURFACE_IMPL_GET_CLASS (window->impl)->stick (window); -} - -/** - * gdk_surface_unstick: - * @window: a toplevel #GdkSurface - * - * Reverse operation for gdk_surface_stick(); see gdk_surface_stick(), - * and gtk_window_unstick(). - * - **/ -void -gdk_surface_unstick (GdkSurface *window) -{ - GDK_SURFACE_IMPL_GET_CLASS (window->impl)->unstick (window); -} - -/** - * gdk_surface_maximize: - * @window: a toplevel #GdkSurface - * - * Maximizes the window. If the window was already maximized, then - * this function does nothing. - * - * On X11, asks the window manager to maximize @window, if the window - * manager supports this operation. Not all window managers support - * this, and some deliberately ignore it or don’t have a concept of - * “maximized”; so you can’t rely on the maximization actually - * happening. But it will happen with most standard window managers, - * and GDK makes a best effort to get it to happen. - * - * On Windows, reliably maximizes the window. - * - **/ -void -gdk_surface_maximize (GdkSurface *window) -{ - GDK_SURFACE_IMPL_GET_CLASS (window->impl)->maximize (window); -} - -/** - * gdk_surface_unmaximize: - * @window: a toplevel #GdkSurface - * - * Unmaximizes the window. If the window wasn’t maximized, then this - * function does nothing. - * - * On X11, asks the window manager to unmaximize @window, if the - * window manager supports this operation. Not all window managers - * support this, and some deliberately ignore it or don’t have a - * concept of “maximized”; so you can’t rely on the unmaximization - * actually happening. But it will happen with most standard window - * managers, and GDK makes a best effort to get it to happen. - * - * On Windows, reliably unmaximizes the window. - * - **/ -void -gdk_surface_unmaximize (GdkSurface *window) -{ - GDK_SURFACE_IMPL_GET_CLASS (window->impl)->unmaximize (window); -} - -/** - * gdk_surface_fullscreen: - * @window: a toplevel #GdkSurface - * - * Moves the window into fullscreen mode. This means the - * window covers the entire screen and is above any panels - * or task bars. - * - * If the window was already fullscreen, then this function does nothing. - * - * On X11, asks the window manager to put @window in a fullscreen - * state, if the window manager supports this operation. Not all - * window managers support this, and some deliberately ignore it or - * don’t have a concept of “fullscreen”; so you can’t rely on the - * fullscreenification actually happening. But it will happen with - * most standard window managers, and GDK makes a best effort to get - * it to happen. - **/ -void -gdk_surface_fullscreen (GdkSurface *window) -{ - GDK_SURFACE_IMPL_GET_CLASS (window->impl)->fullscreen (window); -} - -/** - * gdk_surface_fullscreen_on_monitor: - * @window: a toplevel #GdkSurface - * @monitor: Which monitor to display fullscreen on. - * - * Moves the window into fullscreen mode on the given monitor. This means - * the window covers the entire screen and is above any panels or task bars. - * - * If the window was already fullscreen, then this function does nothing. - **/ -void -gdk_surface_fullscreen_on_monitor (GdkSurface *window, - GdkMonitor *monitor) -{ - g_return_if_fail (GDK_IS_SURFACE (window)); - g_return_if_fail (GDK_IS_MONITOR (monitor)); - g_return_if_fail (gdk_monitor_get_display (monitor) == gdk_surface_get_display (window)); - g_return_if_fail (gdk_monitor_is_valid (monitor)); - - if (GDK_SURFACE_IMPL_GET_CLASS (window->impl)->fullscreen_on_monitor != NULL) - GDK_SURFACE_IMPL_GET_CLASS (window->impl)->fullscreen_on_monitor (window, monitor); - else - GDK_SURFACE_IMPL_GET_CLASS (window->impl)->fullscreen (window); -} - -/** - * gdk_surface_set_fullscreen_mode: - * @window: a toplevel #GdkSurface - * @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 of the display. - * - * On X11, searches through the list of monitors display the ones - * which delimit the 4 edges of the entire display 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. - **/ -void -gdk_surface_set_fullscreen_mode (GdkSurface *window, - GdkFullscreenMode mode) -{ - GdkSurfaceImplClass *impl_class; - - g_return_if_fail (GDK_IS_SURFACE (window)); - - if (window->fullscreen_mode != mode) - { - window->fullscreen_mode = mode; - - impl_class = GDK_SURFACE_IMPL_GET_CLASS (window->impl); - if (impl_class->apply_fullscreen_mode != NULL) - impl_class->apply_fullscreen_mode (window); - } -} - -/** - * gdk_surface_get_fullscreen_mode: - * @window: a toplevel #GdkSurface - * - * Obtains the #GdkFullscreenMode of the @window. - * - * Returns: The #GdkFullscreenMode applied to the window when fullscreen. - **/ -GdkFullscreenMode -gdk_surface_get_fullscreen_mode (GdkSurface *window) -{ - g_return_val_if_fail (GDK_IS_SURFACE (window), GDK_FULLSCREEN_ON_CURRENT_MONITOR); - - return window->fullscreen_mode; -} - -/** - * gdk_surface_unfullscreen: - * @window: a toplevel #GdkSurface - * - * Moves the window out of fullscreen mode. If the window was not - * fullscreen, does nothing. - * - * On X11, asks the window manager to move @window out of the fullscreen - * state, if the window manager supports this operation. Not all - * window managers support this, and some deliberately ignore it or - * don’t have a concept of “fullscreen”; so you can’t rely on the - * unfullscreenification actually happening. But it will happen with - * most standard window managers, and GDK makes a best effort to get - * it to happen. - **/ -void -gdk_surface_unfullscreen (GdkSurface *window) -{ - GDK_SURFACE_IMPL_GET_CLASS (window->impl)->unfullscreen (window); -} - -/** - * gdk_surface_set_keep_above: - * @window: a toplevel #GdkSurface - * @setting: whether to keep @window above other windows - * - * Set if @window must be kept above other windows. If the - * window was already above, then this function does nothing. - * - * On X11, asks the window manager to keep @window above, if the window - * manager supports this operation. Not all window managers support - * this, and some deliberately ignore it or don’t have a concept of - * “keep above”; so you can’t rely on the window being kept above. - * But it will happen with most standard window managers, - * and GDK makes a best effort to get it to happen. - **/ -void -gdk_surface_set_keep_above (GdkSurface *window, - gboolean setting) -{ - GDK_SURFACE_IMPL_GET_CLASS (window->impl)->set_keep_above (window, setting); -} - -/** - * gdk_surface_set_keep_below: - * @window: a toplevel #GdkSurface - * @setting: whether to keep @window below other windows - * - * Set if @window must be kept below other windows. If the - * window was already below, then this function does nothing. - * - * On X11, asks the window manager to keep @window below, if the window - * manager supports this operation. Not all window managers support - * this, and some deliberately ignore it or don’t have a concept of - * “keep below”; so you can’t rely on the window being kept below. - * But it will happen with most standard window managers, - * and GDK makes a best effort to get it to happen. - **/ -void -gdk_surface_set_keep_below (GdkSurface *window, gboolean setting) -{ - GDK_SURFACE_IMPL_GET_CLASS (window->impl)->set_keep_below (window, setting); -} - -/** - * gdk_surface_get_group: - * @window: a toplevel #GdkSurface - * - * Returns the group leader window for @window. See gdk_surface_set_group(). - * - * Returns: (transfer none): the group leader window for @window - **/ -GdkSurface * -gdk_surface_get_group (GdkSurface *window) -{ - return GDK_SURFACE_IMPL_GET_CLASS (window->impl)->get_group (window); -} - -/** - * gdk_surface_set_group: - * @window: a toplevel #GdkSurface - * @leader: (allow-none): group leader window, or %NULL to restore the default group leader window - * - * Sets the group leader window for @window. By default, - * GDK sets the group leader for all toplevel windows - * to a global window implicitly created by GDK. With this function - * you can override this default. - * - * The group leader window allows the window manager to distinguish - * all windows that belong to a single application. It may for example - * allow users to minimize/unminimize all windows belonging to an - * application at once. You should only set a non-default group window - * if your application pretends to be multiple applications. - **/ -void -gdk_surface_set_group (GdkSurface *window, - GdkSurface *leader) -{ - GDK_SURFACE_IMPL_GET_CLASS (window->impl)->set_group (window, leader); -} - -/** - * gdk_surface_set_decorations: - * @window: a toplevel #GdkSurface - * @decorations: decoration hint mask - * - * “Decorations” are the features the window manager adds to a toplevel #GdkSurface. - * This function sets the traditional Motif window manager hints that tell the - * window manager which decorations you would like your window to have. - * Usually you should use gtk_window_set_decorated() on a #GtkWindow instead of - * using the GDK function directly. - * - * The @decorations argument is the logical OR of the fields in - * the #GdkWMDecoration enumeration. If #GDK_DECOR_ALL is included in the - * mask, the other bits indicate which decorations should be turned off. - * If #GDK_DECOR_ALL is not included, then the other bits indicate - * which decorations should be turned on. - * - * Most window managers honor a decorations hint of 0 to disable all decorations, - * but very few honor all possible combinations of bits. - * - **/ -void -gdk_surface_set_decorations (GdkSurface *window, - GdkWMDecoration decorations) -{ - GDK_SURFACE_IMPL_GET_CLASS (window->impl)->set_decorations (window, decorations); -} - -/** - * gdk_surface_get_decorations: - * @window: The toplevel #GdkSurface to get the decorations from - * @decorations: (out): The window decorations will be written here - * - * Returns the decorations set on the GdkSurface with - * gdk_surface_set_decorations(). - * - * Returns: %TRUE if the window has decorations set, %FALSE otherwise. - **/ -gboolean -gdk_surface_get_decorations(GdkSurface *window, - GdkWMDecoration *decorations) -{ - return GDK_SURFACE_IMPL_GET_CLASS (window->impl)->get_decorations (window, decorations); -} - -/** - * gdk_surface_set_functions: - * @window: a toplevel #GdkSurface - * @functions: bitmask of operations to allow on @window - * - * Sets hints about the window management functions to make available - * via buttons on the window frame. - * - * On the X backend, this function sets the traditional Motif window - * manager hint for this purpose. However, few window managers do - * anything reliable or interesting with this hint. Many ignore it - * entirely. - * - * The @functions argument is the logical OR of values from the - * #GdkWMFunction enumeration. If the bitmask includes #GDK_FUNC_ALL, - * then the other bits indicate which functions to disable; if - * it doesn’t include #GDK_FUNC_ALL, it indicates which functions to - * enable. - * - **/ -void -gdk_surface_set_functions (GdkSurface *window, - GdkWMFunction functions) -{ - GDK_SURFACE_IMPL_GET_CLASS (window->impl)->set_functions (window, functions); -} - -/** - * gdk_surface_begin_resize_drag_for_device: - * @window: a toplevel #GdkSurface - * @edge: the edge or corner from which the drag is started - * @device: the device used for the operation - * @button: the button being used to drag, or 0 for a keyboard-initiated drag - * @root_x: root window X coordinate of mouse click that began the drag - * @root_y: root window Y coordinate of mouse click that began the drag - * @timestamp: timestamp of mouse click that began the drag (use gdk_event_get_time()) - * - * Begins a window resize operation (for a toplevel window). - * You might use this function to implement a “window resize grip,” for - * example; in fact #GtkStatusbar uses it. The function works best - * with window managers that support the - * [Extended Window Manager Hints](http://www.freedesktop.org/Standards/wm-spec) - * but has a fallback implementation for other window managers. - */ -void -gdk_surface_begin_resize_drag_for_device (GdkSurface *window, - GdkSurfaceEdge edge, - GdkDevice *device, - gint button, - gint root_x, - gint root_y, - guint32 timestamp) -{ - GDK_SURFACE_IMPL_GET_CLASS (window->impl)->begin_resize_drag (window, edge, device, button, root_x, root_y, timestamp); -} - -/** - * gdk_surface_begin_resize_drag: - * @window: a toplevel #GdkSurface - * @edge: the edge or corner from which the drag is started - * @button: the button being used to drag, or 0 for a keyboard-initiated drag - * @root_x: root window X coordinate of mouse click that began the drag - * @root_y: root window Y coordinate of mouse click that began the drag - * @timestamp: timestamp of mouse click that began the drag (use gdk_event_get_time()) - * - * Begins a window resize operation (for a toplevel window). - * - * This function assumes that the drag is controlled by the - * client pointer device, use gdk_surface_begin_resize_drag_for_device() - * to begin a drag with a different device. - */ -void -gdk_surface_begin_resize_drag (GdkSurface *window, - GdkSurfaceEdge edge, - gint button, - gint root_x, - gint root_y, - guint32 timestamp) -{ - GdkDisplay *display; - GdkDevice *device; - - display = gdk_surface_get_display (window); - device = gdk_seat_get_pointer (gdk_display_get_default_seat (display)); - gdk_surface_begin_resize_drag_for_device (window, edge, - device, button, root_x, root_y, timestamp); -} - -/** - * gdk_surface_begin_move_drag_for_device: - * @window: a toplevel #GdkSurface - * @device: the device used for the operation - * @button: the button being used to drag, or 0 for a keyboard-initiated drag - * @root_x: root window X coordinate of mouse click that began the drag - * @root_y: root window Y coordinate of mouse click that began the drag - * @timestamp: timestamp of mouse click that began the drag - * - * Begins a window move operation (for a toplevel window). - * You might use this function to implement a “window move grip,” for - * example. The function works best with window managers that support the - * [Extended Window Manager Hints](http://www.freedesktop.org/Standards/wm-spec) - * but has a fallback implementation for other window managers. - */ -void -gdk_surface_begin_move_drag_for_device (GdkSurface *window, - GdkDevice *device, - gint button, - gint root_x, - gint root_y, - guint32 timestamp) -{ - GDK_SURFACE_IMPL_GET_CLASS (window->impl)->begin_move_drag (window, - device, button, root_x, root_y, timestamp); -} - -/** - * gdk_surface_begin_move_drag: - * @window: a toplevel #GdkSurface - * @button: the button being used to drag, or 0 for a keyboard-initiated drag - * @root_x: root window X coordinate of mouse click that began the drag - * @root_y: root window Y coordinate of mouse click that began the drag - * @timestamp: timestamp of mouse click that began the drag - * - * Begins a window move operation (for a toplevel window). - * - * This function assumes that the drag is controlled by the - * client pointer device, use gdk_surface_begin_move_drag_for_device() - * to begin a drag with a different device. - */ -void -gdk_surface_begin_move_drag (GdkSurface *window, - gint button, - gint root_x, - gint root_y, - guint32 timestamp) -{ - GdkDisplay *display; - GdkDevice *device; - - display = gdk_surface_get_display (window); - device = gdk_seat_get_pointer (gdk_display_get_default_seat (display)); - gdk_surface_begin_move_drag_for_device (window, device, button, root_x, root_y, timestamp); -} - -/** - * gdk_surface_set_opacity: - * @window: a top-level or non-native #GdkSurface - * @opacity: opacity - * - * 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.) - * - * 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. On Wayland, there is no - * per-window opacity value that the compositor would apply. Instead, use - * `gdk_surface_set_opaque_region (window, NULL)` to tell the compositor - * that the entire window is (potentially) non-opaque, and draw your content - * with alpha, or use gtk_widget_set_opacity() to set an overall opacity - * for your widgets. - * - * Support for non-toplevel windows was added in 3.8. - */ -void -gdk_surface_set_opacity (GdkSurface *window, - gdouble opacity) -{ - if (opacity < 0) - opacity = 0; - else if (opacity > 1) - opacity = 1; - - window->alpha = round (opacity * 255); - - if (window->destroyed) - return; - - if (gdk_surface_has_impl (window)) - GDK_SURFACE_IMPL_GET_CLASS (window->impl)->set_opacity (window, opacity); - else - { - recompute_visible_regions (window, FALSE); - gdk_surface_invalidate_rect_full (window, NULL, TRUE); - } -} - -/* This function is called when the XWindow is really gone. - */ -void -gdk_surface_destroy_notify (GdkSurface *window) -{ - GDK_SURFACE_IMPL_GET_CLASS (window->impl)->destroy_notify (window); -} - -/** - * gdk_surface_register_dnd: - * @window: a #GdkSurface. - * - * Registers a window as a potential drop destination. - */ -void -gdk_surface_register_dnd (GdkSurface *window) -{ - GDK_SURFACE_IMPL_GET_CLASS (window->impl)->register_dnd (window); -} - -/** - * gdk_drag_begin: - * @window: the source window for this drag - * @device: the device that controls this drag - * @content: (transfer none): the offered content - * @actions: the actions supported by this drag - * @dx: the x offset to @device's position where the drag nominally started - * @dy: the y offset to @device's position where the drag nominally started - * - * Starts a drag and creates a new drag context for it. - * - * This function is called by the drag source. - * - * Returns: (transfer full) (nullable): a newly created #GdkDragContext or - * %NULL on error. - */ -GdkDragContext * -gdk_drag_begin (GdkSurface *window, - GdkDevice *device, - GdkContentProvider *content, - GdkDragAction actions, - gint dx, - gint dy) -{ - g_return_val_if_fail (GDK_IS_SURFACE (window), NULL); - g_return_val_if_fail (GDK_IS_DEVICE (device), NULL); - g_return_val_if_fail (gdk_surface_get_display (window) == gdk_device_get_display (device), NULL); - g_return_val_if_fail (GDK_IS_CONTENT_PROVIDER (content), NULL); - - return GDK_SURFACE_IMPL_GET_CLASS (window->impl)->drag_begin (window, device, content, actions, dx, dy); -} - -static void -gdk_surface_flush_events (GdkFrameClock *clock, - void *data) -{ - GdkSurface *window; - GdkDisplay *display; - - window = GDK_SURFACE (data); - - display = gdk_surface_get_display (window); - _gdk_event_queue_flush (display); - _gdk_display_pause_events (display); - - gdk_frame_clock_request_phase (clock, GDK_FRAME_CLOCK_PHASE_RESUME_EVENTS); - - window->frame_clock_events_paused = TRUE; -} - -static void -gdk_surface_resume_events (GdkFrameClock *clock, - void *data) -{ - GdkSurface *window; - GdkDisplay *display; - - window = GDK_SURFACE (data); - - display = gdk_surface_get_display (window); - _gdk_display_unpause_events (display); - - window->frame_clock_events_paused = FALSE; -} - -static void -gdk_surface_set_frame_clock (GdkSurface *window, - GdkFrameClock *clock) -{ - g_return_if_fail (GDK_IS_SURFACE (window)); - g_return_if_fail (clock == NULL || GDK_IS_FRAME_CLOCK (clock)); - g_return_if_fail (clock == NULL || gdk_surface_is_toplevel (window)); - - if (clock == window->frame_clock) - return; - - if (clock) - { - g_object_ref (clock); - g_signal_connect (G_OBJECT (clock), - "flush-events", - G_CALLBACK (gdk_surface_flush_events), - window); - g_signal_connect (G_OBJECT (clock), - "paint", - G_CALLBACK (gdk_surface_paint_on_clock), - window); - g_signal_connect (G_OBJECT (clock), - "resume-events", - G_CALLBACK (gdk_surface_resume_events), - window); - } - - if (window->frame_clock) - { - if (window->frame_clock_events_paused) - gdk_surface_resume_events (window->frame_clock, G_OBJECT (window)); - - g_signal_handlers_disconnect_by_func (G_OBJECT (window->frame_clock), - G_CALLBACK (gdk_surface_flush_events), - window); - g_signal_handlers_disconnect_by_func (G_OBJECT (window->frame_clock), - G_CALLBACK (gdk_surface_paint_on_clock), - window); - g_signal_handlers_disconnect_by_func (G_OBJECT (window->frame_clock), - G_CALLBACK (gdk_surface_resume_events), - window); - g_object_unref (window->frame_clock); - } - - window->frame_clock = clock; -} - -/** - * gdk_surface_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 unless the window is reparented to a new toplevel - * window. - * - * Returns: (transfer none): the frame clock - */ -GdkFrameClock* -gdk_surface_get_frame_clock (GdkSurface *window) -{ - GdkSurface *toplevel; - - g_return_val_if_fail (GDK_IS_SURFACE (window), NULL); - - toplevel = gdk_surface_get_toplevel (window); - - return toplevel->frame_clock; -} - -/** - * gdk_surface_get_scale_factor: - * @window: window to get scale factor for - * - * Returns the internal scale factor that maps from window coordiantes - * to the actual device pixels. On traditional systems this is 1, but - * on very high density outputs this can be a higher value (often 2). - * - * A higher value means that drawing is automatically scaled up to - * a higher resolution, so any code doing drawing will automatically look - * nicer. However, if you are supplying pixel-based data the scale - * value can be used to determine whether to use a pixel resource - * with higher resolution data. - * - * The scale of a window may change during runtime, if this happens - * a configure event will be sent to the toplevel window. - * - * Returns: the scale factor - */ -gint -gdk_surface_get_scale_factor (GdkSurface *window) -{ - GdkSurfaceImplClass *impl_class; - - g_return_val_if_fail (GDK_IS_SURFACE (window), 1); - - if (GDK_SURFACE_DESTROYED (window)) - return 1; - - impl_class = GDK_SURFACE_IMPL_GET_CLASS (window->impl); - - if (impl_class->get_scale_factor) - return impl_class->get_scale_factor (window); - - return 1; -} - -/* Returns the *real* unscaled size, which may be a fractional size - in window scale coordinates. We need this to properly handle GL - coordinates which are y-flipped in the real coordinates. */ -void -gdk_surface_get_unscaled_size (GdkSurface *window, - int *unscaled_width, - int *unscaled_height) -{ - GdkSurfaceImplClass *impl_class; - gint scale; - - g_return_if_fail (GDK_IS_SURFACE (window)); - - if (window->impl_window == window) - { - impl_class = GDK_SURFACE_IMPL_GET_CLASS (window->impl); - - if (impl_class->get_unscaled_size) - { - impl_class->get_unscaled_size (window, unscaled_width, unscaled_height); - return; - } - } - - scale = gdk_surface_get_scale_factor (window); - - if (unscaled_width) - *unscaled_width = window->width * scale; - - if (unscaled_height) - *unscaled_height = window->height * scale; -} - - -/** - * gdk_surface_set_opaque_region: - * @window: a top-level or non-native #GdkSurface - * @region: (allow-none): a region, or %NULL - * - * For optimisation purposes, compositing window managers may - * like to not draw obscured regions of windows, or turn off blending - * during for these regions. With RGB windows with no transparency, - * this is just the shape of the window, but with ARGB32 windows, the - * compositor does not know what regions of the window are transparent - * or not. - * - * This function only works for toplevel windows. - * - * GTK+ will update this property automatically if - * the @window background is opaque, as we know where the opaque regions - * are. If your window background is not opaque, please update this - * property in your #GtkWidget::style-updated handler. - */ -void -gdk_surface_set_opaque_region (GdkSurface *window, - cairo_region_t *region) -{ - GdkSurfaceImplClass *impl_class; - - g_return_if_fail (GDK_IS_SURFACE (window)); - g_return_if_fail (!GDK_SURFACE_DESTROYED (window)); - - if (cairo_region_equal (window->opaque_region, region)) - return; - - g_clear_pointer (&window->opaque_region, cairo_region_destroy); - - if (region != NULL) - window->opaque_region = cairo_region_reference (region); - - impl_class = GDK_SURFACE_IMPL_GET_CLASS (window->impl); - - if (impl_class->set_opaque_region) - impl_class->set_opaque_region (window, region); -} - -/** - * gdk_surface_set_shadow_width: - * @window: a #GdkSurface - * @left: The left extent - * @right: The right extent - * @top: The top extent - * @bottom: The bottom extent - * - * Newer GTK+ windows using client-side decorations use extra geometry - * around their frames for effects like shadows and invisible borders. - * Window managers that want to maximize windows or snap to edges need - * to know where the extents of the actual frame lie, so that users - * don’t feel like windows are snapping against random invisible edges. - * - * Note that this property is automatically updated by GTK+, so this - * function should only be used by applications which do not use GTK+ - * to create toplevel windows. - */ -void -gdk_surface_set_shadow_width (GdkSurface *window, - gint left, - gint right, - gint top, - gint bottom) -{ - GdkSurfaceImplClass *impl_class; - - g_return_if_fail (GDK_IS_SURFACE (window)); - g_return_if_fail (!GDK_SURFACE_DESTROYED (window)); - g_return_if_fail (left >= 0 && right >= 0 && top >= 0 && bottom >= 0); - - window->shadow_top = top; - window->shadow_left = left; - window->shadow_right = right; - window->shadow_bottom = bottom; - - impl_class = GDK_SURFACE_IMPL_GET_CLASS (window->impl); - - if (impl_class->set_shadow_width) - impl_class->set_shadow_width (window, left, right, top, bottom); -} - -/** - * gdk_surface_show_window_menu: - * @window: a #GdkSurface - * @event: a #GdkEvent to show the menu for - * - * Asks the windowing system to show the window menu. The window menu - * is the menu shown when right-clicking the titlebar on traditional - * windows managed by the window manager. This is useful for windows - * using client-side decorations, activating it with a right-click - * on the window decorations. - * - * Returns: %TRUE if the window menu was shown and %FALSE otherwise. - */ -gboolean -gdk_surface_show_window_menu (GdkSurface *window, - GdkEvent *event) -{ - GdkSurfaceImplClass *impl_class; - - g_return_val_if_fail (GDK_IS_SURFACE (window), FALSE); - g_return_val_if_fail (!GDK_SURFACE_DESTROYED (window), FALSE); - - impl_class = GDK_SURFACE_IMPL_GET_CLASS (window->impl); - - if (impl_class->show_window_menu) - return impl_class->show_window_menu (window, event); - else - return FALSE; -} - -gboolean -gdk_surface_supports_edge_constraints (GdkSurface *window) -{ - GdkSurfaceImplClass *impl_class; - - g_return_val_if_fail (GDK_IS_SURFACE (window), FALSE); - g_return_val_if_fail (!GDK_SURFACE_DESTROYED (window), FALSE); - - impl_class = GDK_SURFACE_IMPL_GET_CLASS (window->impl); - - if (impl_class->supports_edge_constraints) - return impl_class->supports_edge_constraints (window); - else - return FALSE; -} - -void -gdk_surface_set_state (GdkSurface *window, - GdkSurfaceState new_state) -{ - g_return_if_fail (GDK_IS_SURFACE (window)); - - if (new_state == window->state) - return; /* No actual work to do, nothing changed. */ - - /* Actually update the field in GdkSurface, this is sort of an odd - * place to do it, but seems like the safest since it ensures we expose no - * inconsistent state to the user. - */ - - window->state = new_state; - - _gdk_surface_update_viewable (window); - - /* We only really send the event to toplevels, since - * all the window states don't apply to non-toplevels. - * Non-toplevels do use the GDK_SURFACE_STATE_WITHDRAWN flag - * internally so we needed to update window->state. - */ - switch (window->window_type) - { - case GDK_SURFACE_TOPLEVEL: - case GDK_SURFACE_TEMP: /* ? */ - g_object_notify (G_OBJECT (window), "state"); - break; - case GDK_SURFACE_FOREIGN: - case GDK_SURFACE_ROOT: - case GDK_SURFACE_CHILD: - default: - break; - } -} - -void -gdk_synthesize_window_state (GdkSurface *window, - GdkSurfaceState unset_flags, - GdkSurfaceState set_flags) -{ - gdk_surface_set_state (window, (window->state | set_flags) & ~unset_flags); -} diff --git a/gdk/gdkwindow.h b/gdk/gdkwindow.h deleted file mode 100644 index ea5ca92858..0000000000 --- a/gdk/gdkwindow.h +++ /dev/null @@ -1,933 +0,0 @@ -/* GDK - The GIMP Drawing Kit - * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * 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, see . - */ - -/* - * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS - * file for a list of people on the GTK+ Team. See the ChangeLog - * files for a list of changes. These files are distributed with - * GTK+ at ftp://ftp.gtk.org/pub/gtk/. - */ - -#ifndef __GDK_SURFACE_H__ -#define __GDK_SURFACE_H__ - -#if !defined (__GDK_H_INSIDE__) && !defined (GDK_COMPILATION) -#error "Only can be included directly." -#endif - -#include -#include -#include -#include -#include -#include - -G_BEGIN_DECLS - -typedef struct _GdkGeometry GdkGeometry; - -/** - * GdkSurfaceType: - * @GDK_SURFACE_ROOT: root window; this window has no parent, covers the entire - * screen, and is created by the window system - * @GDK_SURFACE_TOPLEVEL: toplevel window (used to implement #GtkWindow) - * @GDK_SURFACE_CHILD: child window (used to implement e.g. #GtkEntry) - * @GDK_SURFACE_TEMP: override redirect temporary window (used to implement - * #GtkMenu) - * @GDK_SURFACE_FOREIGN: foreign window (see gdk_surface_foreign_new()) - * @GDK_SURFACE_SUBSURFACE: subsurface-based window; This window is visually - * tied to a toplevel, and is moved/stacked with it. Currently this window - * type is only implemented in Wayland. Since 3.14 - * - * Describes the kind of window. - */ -typedef enum -{ - GDK_SURFACE_ROOT, - GDK_SURFACE_TOPLEVEL, - GDK_SURFACE_CHILD, - GDK_SURFACE_TEMP, - GDK_SURFACE_FOREIGN, - GDK_SURFACE_SUBSURFACE -} GdkSurfaceType; - -/* Size restriction enumeration. - */ -/** - * GdkSurfaceHints: - * @GDK_HINT_POS: indicates that the program has positioned the window - * @GDK_HINT_MIN_SIZE: min size fields are set - * @GDK_HINT_MAX_SIZE: max size fields are set - * @GDK_HINT_BASE_SIZE: base size fields are set - * @GDK_HINT_ASPECT: aspect ratio fields are set - * @GDK_HINT_RESIZE_INC: resize increment fields are set - * @GDK_HINT_WIN_GRAVITY: window gravity field is set - * @GDK_HINT_USER_POS: indicates that the window’s position was explicitly set - * by the user - * @GDK_HINT_USER_SIZE: indicates that the window’s size was explicitly set by - * the user - * - * Used to indicate which fields of a #GdkGeometry struct should be paid - * attention to. Also, the presence/absence of @GDK_HINT_POS, - * @GDK_HINT_USER_POS, and @GDK_HINT_USER_SIZE is significant, though they don't - * directly refer to #GdkGeometry fields. @GDK_HINT_USER_POS will be set - * automatically by #GtkWindow if you call gtk_window_move(). - * @GDK_HINT_USER_POS and @GDK_HINT_USER_SIZE should be set if the user - * specified a size/position using a --geometry command-line argument; - * gtk_window_parse_geometry() automatically sets these flags. - */ -typedef enum -{ - GDK_HINT_POS = 1 << 0, - GDK_HINT_MIN_SIZE = 1 << 1, - GDK_HINT_MAX_SIZE = 1 << 2, - GDK_HINT_BASE_SIZE = 1 << 3, - GDK_HINT_ASPECT = 1 << 4, - GDK_HINT_RESIZE_INC = 1 << 5, - GDK_HINT_WIN_GRAVITY = 1 << 6, - GDK_HINT_USER_POS = 1 << 7, - GDK_HINT_USER_SIZE = 1 << 8 -} GdkSurfaceHints; - -/* The next two enumeration values current match the - * Motif constants. If this is changed, the implementation - * of gdk_surface_set_decorations/gdk_surface_set_functions - * will need to change as well. - */ -/** - * GdkWMDecoration: - * @GDK_DECOR_ALL: all decorations should be applied. - * @GDK_DECOR_BORDER: a frame should be drawn around the window. - * @GDK_DECOR_RESIZEH: the frame should have resize handles. - * @GDK_DECOR_TITLE: a titlebar should be placed above the window. - * @GDK_DECOR_MENU: a button for opening a menu should be included. - * @GDK_DECOR_MINIMIZE: a minimize button should be included. - * @GDK_DECOR_MAXIMIZE: a maximize button should be included. - * - * These are hints originally defined by the Motif toolkit. - * The window manager can use them when determining how to decorate - * the window. The hint must be set before mapping the window. - */ -typedef enum -{ - GDK_DECOR_ALL = 1 << 0, - GDK_DECOR_BORDER = 1 << 1, - GDK_DECOR_RESIZEH = 1 << 2, - GDK_DECOR_TITLE = 1 << 3, - GDK_DECOR_MENU = 1 << 4, - GDK_DECOR_MINIMIZE = 1 << 5, - GDK_DECOR_MAXIMIZE = 1 << 6 -} GdkWMDecoration; - -/** - * GdkWMFunction: - * @GDK_FUNC_ALL: all functions should be offered. - * @GDK_FUNC_RESIZE: the window should be resizable. - * @GDK_FUNC_MOVE: the window should be movable. - * @GDK_FUNC_MINIMIZE: the window should be minimizable. - * @GDK_FUNC_MAXIMIZE: the window should be maximizable. - * @GDK_FUNC_CLOSE: the window should be closable. - * - * These are hints originally defined by the Motif toolkit. The window manager - * can use them when determining the functions to offer for the window. The - * hint must be set before mapping the window. - */ -typedef enum -{ - GDK_FUNC_ALL = 1 << 0, - GDK_FUNC_RESIZE = 1 << 1, - GDK_FUNC_MOVE = 1 << 2, - GDK_FUNC_MINIMIZE = 1 << 3, - GDK_FUNC_MAXIMIZE = 1 << 4, - GDK_FUNC_CLOSE = 1 << 5 -} GdkWMFunction; - -/* Currently, these are the same values numerically as in the - * X protocol. If you change that, gdkwindow-x11.c/gdk_surface_set_geometry_hints() - * will need fixing. - */ -/** - * GdkGravity: - * @GDK_GRAVITY_NORTH_WEST: the reference point is at the top left corner. - * @GDK_GRAVITY_NORTH: the reference point is in the middle of the top edge. - * @GDK_GRAVITY_NORTH_EAST: the reference point is at the top right corner. - * @GDK_GRAVITY_WEST: the reference point is at the middle of the left edge. - * @GDK_GRAVITY_CENTER: the reference point is at the center of the window. - * @GDK_GRAVITY_EAST: the reference point is at the middle of the right edge. - * @GDK_GRAVITY_SOUTH_WEST: the reference point is at the lower left corner. - * @GDK_GRAVITY_SOUTH: the reference point is at the middle of the lower edge. - * @GDK_GRAVITY_SOUTH_EAST: the reference point is at the lower right corner. - * @GDK_GRAVITY_STATIC: the reference point is at the top left corner of the - * window itself, ignoring window manager decorations. - * - * Defines the reference point of a window and the meaning of coordinates - * passed to gtk_window_move(). See gtk_window_move() and the "implementation - * notes" section of the - * [Extended Window Manager Hints](http://www.freedesktop.org/Standards/wm-spec) - * specification for more details. - */ -typedef enum -{ - GDK_GRAVITY_NORTH_WEST = 1, - GDK_GRAVITY_NORTH, - GDK_GRAVITY_NORTH_EAST, - GDK_GRAVITY_WEST, - GDK_GRAVITY_CENTER, - GDK_GRAVITY_EAST, - GDK_GRAVITY_SOUTH_WEST, - GDK_GRAVITY_SOUTH, - GDK_GRAVITY_SOUTH_EAST, - GDK_GRAVITY_STATIC -} GdkGravity; - -/** - * GdkAnchorHints: - * @GDK_ANCHOR_FLIP_X: allow flipping anchors horizontally - * @GDK_ANCHOR_FLIP_Y: allow flipping anchors vertically - * @GDK_ANCHOR_SLIDE_X: allow sliding window horizontally - * @GDK_ANCHOR_SLIDE_Y: allow sliding window vertically - * @GDK_ANCHOR_RESIZE_X: allow resizing window horizontally - * @GDK_ANCHOR_RESIZE_Y: allow resizing window vertically - * @GDK_ANCHOR_FLIP: allow flipping anchors on both axes - * @GDK_ANCHOR_SLIDE: allow sliding window on both axes - * @GDK_ANCHOR_RESIZE: allow resizing window on both axes - * - * Positioning hints for aligning a window relative to a rectangle. - * - * These hints determine how the window should be positioned in the case that - * the window would fall off-screen if placed in its ideal position. - * - * For example, %GDK_ANCHOR_FLIP_X will replace %GDK_GRAVITY_NORTH_WEST with - * %GDK_GRAVITY_NORTH_EAST and vice versa if the window extends beyond the left - * or right edges of the monitor. - * - * If %GDK_ANCHOR_SLIDE_X is set, the window can be shifted horizontally to fit - * on-screen. If %GDK_ANCHOR_RESIZE_X is set, the window can be shrunken - * horizontally to fit. - * - * In general, when multiple flags are set, flipping should take precedence over - * sliding, which should take precedence over resizing. - * - * Since: 3.22 - * Stability: Unstable - */ -typedef enum -{ - GDK_ANCHOR_FLIP_X = 1 << 0, - GDK_ANCHOR_FLIP_Y = 1 << 1, - GDK_ANCHOR_SLIDE_X = 1 << 2, - GDK_ANCHOR_SLIDE_Y = 1 << 3, - GDK_ANCHOR_RESIZE_X = 1 << 4, - GDK_ANCHOR_RESIZE_Y = 1 << 5, - GDK_ANCHOR_FLIP = GDK_ANCHOR_FLIP_X | GDK_ANCHOR_FLIP_Y, - GDK_ANCHOR_SLIDE = GDK_ANCHOR_SLIDE_X | GDK_ANCHOR_SLIDE_Y, - GDK_ANCHOR_RESIZE = GDK_ANCHOR_RESIZE_X | GDK_ANCHOR_RESIZE_Y -} GdkAnchorHints; - -/** - * GdkSurfaceEdge: - * @GDK_SURFACE_EDGE_NORTH_WEST: the top left corner. - * @GDK_SURFACE_EDGE_NORTH: the top edge. - * @GDK_SURFACE_EDGE_NORTH_EAST: the top right corner. - * @GDK_SURFACE_EDGE_WEST: the left edge. - * @GDK_SURFACE_EDGE_EAST: the right edge. - * @GDK_SURFACE_EDGE_SOUTH_WEST: the lower left corner. - * @GDK_SURFACE_EDGE_SOUTH: the lower edge. - * @GDK_SURFACE_EDGE_SOUTH_EAST: the lower right corner. - * - * Determines a window edge or corner. - */ -typedef enum -{ - GDK_SURFACE_EDGE_NORTH_WEST, - GDK_SURFACE_EDGE_NORTH, - GDK_SURFACE_EDGE_NORTH_EAST, - GDK_SURFACE_EDGE_WEST, - GDK_SURFACE_EDGE_EAST, - GDK_SURFACE_EDGE_SOUTH_WEST, - GDK_SURFACE_EDGE_SOUTH, - GDK_SURFACE_EDGE_SOUTH_EAST -} GdkSurfaceEdge; - -/** - * GdkFullscreenMode: - * @GDK_FULLSCREEN_ON_CURRENT_MONITOR: Fullscreen on current monitor only. - * @GDK_FULLSCREEN_ON_ALL_MONITORS: Span across all monitors when fullscreen. - * - * Indicates which monitor (in a multi-head setup) a window should span over - * when in fullscreen mode. - * - * Since: 3.8 - **/ -typedef enum -{ - GDK_FULLSCREEN_ON_CURRENT_MONITOR, - GDK_FULLSCREEN_ON_ALL_MONITORS -} GdkFullscreenMode; - -/** - * GdkGeometry: - * @min_width: minimum width of window (or -1 to use requisition, with - * #GtkWindow only) - * @min_height: minimum height of window (or -1 to use requisition, with - * #GtkWindow only) - * @max_width: maximum width of window (or -1 to use requisition, with - * #GtkWindow only) - * @max_height: maximum height of window (or -1 to use requisition, with - * #GtkWindow only) - * @base_width: allowed window widths are @base_width + @width_inc * N where N - * is any integer (-1 allowed with #GtkWindow) - * @base_height: allowed window widths are @base_height + @height_inc * N where - * N is any integer (-1 allowed with #GtkWindow) - * @width_inc: width resize increment - * @height_inc: height resize increment - * @min_aspect: minimum width/height ratio - * @max_aspect: maximum width/height ratio - * @win_gravity: window gravity, see gtk_window_set_gravity() - * - * The #GdkGeometry struct gives the window manager information about - * a window’s geometry constraints. Normally you would set these on - * the GTK+ level using gtk_window_set_geometry_hints(). #GtkWindow - * then sets the hints on the #GdkSurface it creates. - * - * gdk_surface_set_geometry_hints() expects the hints to be fully valid already - * and simply passes them to the window manager; in contrast, - * gtk_window_set_geometry_hints() performs some interpretation. For example, - * #GtkWindow will apply the hints to the geometry widget instead of the - * toplevel window, if you set a geometry widget. Also, the - * @min_width/@min_height/@max_width/@max_height fields may be set to -1, and - * #GtkWindow will substitute the size request of the window or geometry widget. - * If the minimum size hint is not provided, #GtkWindow will use its requisition - * as the minimum size. If the minimum size is provided and a geometry widget is - * set, #GtkWindow will take the minimum size as the minimum size of the - * geometry widget rather than the entire window. The base size is treated - * similarly. - * - * The canonical use-case for gtk_window_set_geometry_hints() is to get a - * terminal widget to resize properly. Here, the terminal text area should be - * the geometry widget; #GtkWindow will then automatically set the base size to - * the size of other widgets in the terminal window, such as the menubar and - * scrollbar. Then, the @width_inc and @height_inc fields should be set to the - * size of one character in the terminal. Finally, the base size should be set - * to the size of one character. The net effect is that the minimum size of the - * terminal will have a 1x1 character terminal area, and only terminal sizes on - * the “character grid” will be allowed. - * - * Here’s an example of how the terminal example would be implemented, assuming - * a terminal area widget called “terminal” and a toplevel window “toplevel”: - * - * |[ - * GdkGeometry hints; - * - * hints.base_width = terminal->char_width; - * hints.base_height = terminal->char_height; - * hints.min_width = terminal->char_width; - * hints.min_height = terminal->char_height; - * hints.width_inc = terminal->char_width; - * hints.height_inc = terminal->char_height; - * - * gtk_window_set_geometry_hints (GTK_WINDOW (toplevel), - * GTK_WIDGET (terminal), - * &hints, - * GDK_HINT_RESIZE_INC | - * GDK_HINT_MIN_SIZE | - * GDK_HINT_BASE_SIZE); - * ]| - * - * The other useful fields are the @min_aspect and @max_aspect fields; these - * contain a width/height ratio as a floating point number. If a geometry widget - * is set, the aspect applies to the geometry widget rather than the entire - * window. The most common use of these hints is probably to set @min_aspect and - * @max_aspect to the same value, thus forcing the window to keep a constant - * aspect ratio. - */ -struct _GdkGeometry -{ - gint min_width; - gint min_height; - gint max_width; - gint max_height; - gint base_width; - gint base_height; - gint width_inc; - gint height_inc; - gdouble min_aspect; - gdouble max_aspect; - GdkGravity win_gravity; -}; - -/** - * GdkSurfaceState: - * @GDK_SURFACE_STATE_WITHDRAWN: the window is not shown. - * @GDK_SURFACE_STATE_ICONIFIED: the window is minimized. - * @GDK_SURFACE_STATE_MAXIMIZED: the window is maximized. - * @GDK_SURFACE_STATE_STICKY: the window is sticky. - * @GDK_SURFACE_STATE_FULLSCREEN: the window is maximized without - * decorations. - * @GDK_SURFACE_STATE_ABOVE: the window is kept above other windows. - * @GDK_SURFACE_STATE_BELOW: the window is kept below other windows. - * @GDK_SURFACE_STATE_FOCUSED: the window is presented as focused (with active decorations). - * @GDK_SURFACE_STATE_TILED: the window is in a tiled state, Since 3.10. Since 3.91.2, this - * is deprecated in favor of per-edge information. - * @GDK_SURFACE_STATE_TOP_TILED: whether the top edge is tiled, Since 3.91.2 - * @GDK_SURFACE_STATE_TOP_RESIZABLE: whether the top edge is resizable, Since 3.91.2 - * @GDK_SURFACE_STATE_RIGHT_TILED: whether the right edge is tiled, Since 3.91.2 - * @GDK_SURFACE_STATE_RIGHT_RESIZABLE: whether the right edge is resizable, Since 3.91.2 - * @GDK_SURFACE_STATE_BOTTOM_TILED: whether the bottom edge is tiled, Since 3.91.2 - * @GDK_SURFACE_STATE_BOTTOM_RESIZABLE: whether the bottom edge is resizable, Since 3.91.2 - * @GDK_SURFACE_STATE_LEFT_TILED: whether the left edge is tiled, Since 3.91.2 - * @GDK_SURFACE_STATE_LEFT_RESIZABLE: whether the left edge is resizable, Since 3.91.2 - * - * Specifies the state of a toplevel window. - */ -typedef enum -{ - GDK_SURFACE_STATE_WITHDRAWN = 1 << 0, - GDK_SURFACE_STATE_ICONIFIED = 1 << 1, - GDK_SURFACE_STATE_MAXIMIZED = 1 << 2, - GDK_SURFACE_STATE_STICKY = 1 << 3, - GDK_SURFACE_STATE_FULLSCREEN = 1 << 4, - GDK_SURFACE_STATE_ABOVE = 1 << 5, - GDK_SURFACE_STATE_BELOW = 1 << 6, - GDK_SURFACE_STATE_FOCUSED = 1 << 7, - GDK_SURFACE_STATE_TILED = 1 << 8, - GDK_SURFACE_STATE_TOP_TILED = 1 << 9, - GDK_SURFACE_STATE_TOP_RESIZABLE = 1 << 10, - GDK_SURFACE_STATE_RIGHT_TILED = 1 << 11, - GDK_SURFACE_STATE_RIGHT_RESIZABLE = 1 << 12, - GDK_SURFACE_STATE_BOTTOM_TILED = 1 << 13, - GDK_SURFACE_STATE_BOTTOM_RESIZABLE = 1 << 14, - GDK_SURFACE_STATE_LEFT_TILED = 1 << 15, - GDK_SURFACE_STATE_LEFT_RESIZABLE = 1 << 16 -} GdkSurfaceState; - - -typedef struct _GdkSurfaceClass GdkSurfaceClass; - -#define GDK_TYPE_SURFACE (gdk_surface_get_type ()) -#define GDK_SURFACE(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), GDK_TYPE_SURFACE, GdkSurface)) -#define GDK_SURFACE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GDK_TYPE_SURFACE, GdkSurfaceClass)) -#define GDK_IS_SURFACE(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), GDK_TYPE_SURFACE)) -#define GDK_IS_SURFACE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GDK_TYPE_SURFACE)) -#define GDK_SURFACE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GDK_TYPE_SURFACE, GdkSurfaceClass)) - - -struct _GdkSurfaceClass -{ - GObjectClass parent_class; - - /* Padding for future expansion */ - void (*_gdk_reserved1) (void); - void (*_gdk_reserved2) (void); - void (*_gdk_reserved3) (void); - void (*_gdk_reserved4) (void); - void (*_gdk_reserved5) (void); - void (*_gdk_reserved6) (void); - void (*_gdk_reserved7) (void); - void (*_gdk_reserved8) (void); -}; - -/* Windows - */ -GDK_AVAILABLE_IN_ALL -GType gdk_surface_get_type (void) G_GNUC_CONST; -GDK_AVAILABLE_IN_ALL -GdkSurface * gdk_surface_new_toplevel (GdkDisplay *display, - int width, - int height); -GDK_AVAILABLE_IN_ALL -GdkSurface * gdk_surface_new_popup (GdkDisplay *display, - const GdkRectangle *position); -GDK_AVAILABLE_IN_ALL -GdkSurface * gdk_surface_new_temp (GdkDisplay *display); -GDK_AVAILABLE_IN_ALL -GdkSurface * gdk_surface_new_child (GdkSurface *parent, - const GdkRectangle *position); - -GDK_AVAILABLE_IN_ALL -void gdk_surface_destroy (GdkSurface *window); -GDK_AVAILABLE_IN_ALL -GdkSurfaceType gdk_surface_get_window_type (GdkSurface *window); -GDK_AVAILABLE_IN_ALL -gboolean gdk_surface_is_destroyed (GdkSurface *window); - -GDK_AVAILABLE_IN_ALL -GdkDisplay * gdk_surface_get_display (GdkSurface *window); -GDK_AVAILABLE_IN_ALL -void gdk_surface_show (GdkSurface *window); -GDK_AVAILABLE_IN_ALL -void gdk_surface_hide (GdkSurface *window); -GDK_AVAILABLE_IN_ALL -void gdk_surface_withdraw (GdkSurface *window); -GDK_AVAILABLE_IN_ALL -void gdk_surface_show_unraised (GdkSurface *window); -GDK_AVAILABLE_IN_ALL -void gdk_surface_move (GdkSurface *window, - gint x, - gint y); -GDK_AVAILABLE_IN_ALL -void gdk_surface_resize (GdkSurface *window, - gint width, - gint height); -GDK_AVAILABLE_IN_ALL -void gdk_surface_move_resize (GdkSurface *window, - gint x, - gint y, - gint width, - gint height); -GDK_AVAILABLE_IN_ALL -void gdk_surface_raise (GdkSurface *window); -GDK_AVAILABLE_IN_ALL -void gdk_surface_lower (GdkSurface *window); -GDK_AVAILABLE_IN_ALL -void gdk_surface_restack (GdkSurface *window, - GdkSurface *sibling, - gboolean above); -GDK_AVAILABLE_IN_ALL -void gdk_surface_focus (GdkSurface *window, - guint32 timestamp); -GDK_AVAILABLE_IN_ALL -void gdk_surface_set_user_data (GdkSurface *window, - gpointer user_data); -GDK_AVAILABLE_IN_ALL -gboolean gdk_surface_get_accept_focus (GdkSurface *window); -GDK_AVAILABLE_IN_ALL -void gdk_surface_set_accept_focus (GdkSurface *window, - gboolean accept_focus); -GDK_AVAILABLE_IN_ALL -gboolean gdk_surface_get_focus_on_map (GdkSurface *window); -GDK_AVAILABLE_IN_ALL -void gdk_surface_set_focus_on_map (GdkSurface *window, - gboolean focus_on_map); -GDK_AVAILABLE_IN_ALL -void gdk_surface_scroll (GdkSurface *window, - gint dx, - gint dy); -GDK_AVAILABLE_IN_ALL -void gdk_surface_move_region (GdkSurface *window, - const cairo_region_t *region, - gint dx, - gint dy); - -/* - * This allows for making shaped (partially transparent) windows - * - cool feature, needed for Drag and Drag for example. - */ -GDK_AVAILABLE_IN_ALL -void gdk_surface_shape_combine_region (GdkSurface *window, - const cairo_region_t *shape_region, - gint offset_x, - gint offset_y); - -/* - * This routine allows you to quickly take the shapes of all the child windows - * of a window and use their shapes as the shape mask for this window - useful - * for container windows that dont want to look like a big box - * - * - Raster - */ -GDK_AVAILABLE_IN_ALL -void gdk_surface_set_child_shapes (GdkSurface *window); - -/* - * This routine allows you to merge (ie ADD) child shapes to your - * own window’s shape keeping its current shape and ADDING the child - * shapes to it. - * - * - Raster - */ -GDK_AVAILABLE_IN_ALL -void gdk_surface_merge_child_shapes (GdkSurface *window); - -GDK_AVAILABLE_IN_ALL -void gdk_surface_input_shape_combine_region (GdkSurface *window, - const cairo_region_t *shape_region, - gint offset_x, - gint offset_y); -GDK_AVAILABLE_IN_ALL -void gdk_surface_set_child_input_shapes (GdkSurface *window); -GDK_AVAILABLE_IN_ALL -void gdk_surface_merge_child_input_shapes (GdkSurface *window); - - -GDK_AVAILABLE_IN_ALL -void gdk_surface_set_pass_through (GdkSurface *window, - gboolean pass_through); -GDK_AVAILABLE_IN_ALL -gboolean gdk_surface_get_pass_through (GdkSurface *window); - -/* - * Check if a window has been shown, and whether all its - * parents up to a toplevel have been shown, respectively. - * Note that a window that is_viewable below is not necessarily - * viewable in the X sense. - */ -GDK_AVAILABLE_IN_ALL -gboolean gdk_surface_is_visible (GdkSurface *window); -GDK_AVAILABLE_IN_ALL -gboolean gdk_surface_is_viewable (GdkSurface *window); -GDK_AVAILABLE_IN_ALL -gboolean gdk_surface_is_input_only (GdkSurface *window); -GDK_AVAILABLE_IN_ALL -gboolean gdk_surface_is_shaped (GdkSurface *window); - -GDK_AVAILABLE_IN_ALL -GdkSurfaceState gdk_surface_get_state (GdkSurface *window); - - -/* GdkSurface */ - -GDK_AVAILABLE_IN_ALL -gboolean gdk_surface_has_native (GdkSurface *window); -GDK_AVAILABLE_IN_ALL -void gdk_surface_set_type_hint (GdkSurface *window, - GdkSurfaceTypeHint hint); -GDK_AVAILABLE_IN_ALL -GdkSurfaceTypeHint gdk_surface_get_type_hint (GdkSurface *window); - -GDK_AVAILABLE_IN_ALL -gboolean gdk_surface_get_modal_hint (GdkSurface *window); -GDK_AVAILABLE_IN_ALL -void gdk_surface_set_modal_hint (GdkSurface *window, - gboolean modal); - -GDK_AVAILABLE_IN_ALL -void gdk_surface_set_skip_taskbar_hint (GdkSurface *window, - gboolean skips_taskbar); -GDK_AVAILABLE_IN_ALL -void gdk_surface_set_skip_pager_hint (GdkSurface *window, - gboolean skips_pager); -GDK_AVAILABLE_IN_ALL -void gdk_surface_set_urgency_hint (GdkSurface *window, - gboolean urgent); - -GDK_AVAILABLE_IN_ALL -void gdk_surface_set_geometry_hints (GdkSurface *window, - const GdkGeometry *geometry, - GdkSurfaceHints geom_mask); - -GDK_AVAILABLE_IN_ALL -cairo_region_t *gdk_surface_get_clip_region (GdkSurface *window); -GDK_AVAILABLE_IN_ALL -cairo_region_t *gdk_surface_get_visible_region(GdkSurface *window); - -GDK_AVAILABLE_IN_ALL -GdkDrawingContext *gdk_surface_begin_draw_frame (GdkSurface *window, - GdkDrawContext *context, - const cairo_region_t *region); -GDK_AVAILABLE_IN_ALL -void gdk_surface_end_draw_frame (GdkSurface *window, - GdkDrawingContext *context); - -GDK_AVAILABLE_IN_ALL -void gdk_surface_set_title (GdkSurface *window, - const gchar *title); -GDK_AVAILABLE_IN_ALL -void gdk_surface_set_role (GdkSurface *window, - const gchar *role); -GDK_AVAILABLE_IN_ALL -void gdk_surface_set_startup_id (GdkSurface *window, - const gchar *startup_id); -GDK_AVAILABLE_IN_ALL -void gdk_surface_set_transient_for (GdkSurface *window, - GdkSurface *parent); - -GDK_AVAILABLE_IN_ALL -void gdk_surface_set_cursor (GdkSurface *window, - GdkCursor *cursor); -GDK_AVAILABLE_IN_ALL -GdkCursor *gdk_surface_get_cursor (GdkSurface *window); -GDK_AVAILABLE_IN_ALL -void gdk_surface_set_device_cursor (GdkSurface *window, - GdkDevice *device, - GdkCursor *cursor); -GDK_AVAILABLE_IN_ALL -GdkCursor *gdk_surface_get_device_cursor (GdkSurface *window, - GdkDevice *device); -GDK_AVAILABLE_IN_ALL -void gdk_surface_get_user_data (GdkSurface *window, - gpointer *data); -GDK_AVAILABLE_IN_ALL -void gdk_surface_get_geometry (GdkSurface *window, - gint *x, - gint *y, - gint *width, - gint *height); -GDK_AVAILABLE_IN_ALL -int gdk_surface_get_width (GdkSurface *window); -GDK_AVAILABLE_IN_ALL -int gdk_surface_get_height (GdkSurface *window); -GDK_AVAILABLE_IN_ALL -void gdk_surface_get_position (GdkSurface *window, - gint *x, - gint *y); -GDK_AVAILABLE_IN_ALL -gint gdk_surface_get_origin (GdkSurface *window, - gint *x, - gint *y); -GDK_AVAILABLE_IN_ALL -void gdk_surface_get_root_coords (GdkSurface *window, - gint x, - gint y, - gint *root_x, - gint *root_y); -GDK_AVAILABLE_IN_ALL -void gdk_surface_coords_to_parent (GdkSurface *window, - gdouble x, - gdouble y, - gdouble *parent_x, - gdouble *parent_y); -GDK_AVAILABLE_IN_ALL -void gdk_surface_coords_from_parent (GdkSurface *window, - gdouble parent_x, - gdouble parent_y, - gdouble *x, - gdouble *y); - -GDK_AVAILABLE_IN_ALL -void gdk_surface_get_root_origin (GdkSurface *window, - gint *x, - gint *y); -GDK_AVAILABLE_IN_ALL -void gdk_surface_get_frame_extents (GdkSurface *window, - GdkRectangle *rect); - -GDK_AVAILABLE_IN_ALL -gint gdk_surface_get_scale_factor (GdkSurface *window); - -GDK_AVAILABLE_IN_ALL -GdkSurface * gdk_surface_get_device_position (GdkSurface *window, - GdkDevice *device, - gint *x, - gint *y, - GdkModifierType *mask); -GDK_AVAILABLE_IN_ALL -GdkSurface * gdk_surface_get_device_position_double (GdkSurface *window, - GdkDevice *device, - gdouble *x, - gdouble *y, - GdkModifierType *mask); -GDK_AVAILABLE_IN_ALL -GdkSurface * gdk_surface_get_parent (GdkSurface *window); -GDK_AVAILABLE_IN_ALL -GdkSurface * gdk_surface_get_toplevel (GdkSurface *window); - -GDK_AVAILABLE_IN_ALL -GList * gdk_surface_get_children (GdkSurface *window); -GDK_AVAILABLE_IN_ALL -GList * gdk_surface_peek_children (GdkSurface *window); -GDK_AVAILABLE_IN_ALL -GList * gdk_surface_get_children_with_user_data (GdkSurface *window, - gpointer user_data); - -GDK_AVAILABLE_IN_ALL -GdkEventMask gdk_surface_get_events (GdkSurface *window); -GDK_AVAILABLE_IN_ALL -void gdk_surface_set_events (GdkSurface *window, - GdkEventMask event_mask); -GDK_AVAILABLE_IN_ALL -void gdk_surface_set_device_events (GdkSurface *window, - GdkDevice *device, - GdkEventMask event_mask); -GDK_AVAILABLE_IN_ALL -GdkEventMask gdk_surface_get_device_events (GdkSurface *window, - GdkDevice *device); - -GDK_AVAILABLE_IN_ALL -void gdk_surface_set_icon_list (GdkSurface *window, - GList *surfaces); -GDK_AVAILABLE_IN_ALL -void gdk_surface_set_icon_name (GdkSurface *window, - const gchar *name); -GDK_AVAILABLE_IN_ALL -void gdk_surface_set_group (GdkSurface *window, - GdkSurface *leader); -GDK_AVAILABLE_IN_ALL -GdkSurface* gdk_surface_get_group (GdkSurface *window); -GDK_AVAILABLE_IN_ALL -void gdk_surface_set_decorations (GdkSurface *window, - GdkWMDecoration decorations); -GDK_AVAILABLE_IN_ALL -gboolean gdk_surface_get_decorations (GdkSurface *window, - GdkWMDecoration *decorations); -GDK_AVAILABLE_IN_ALL -void gdk_surface_set_functions (GdkSurface *window, - GdkWMFunction functions); - -GDK_AVAILABLE_IN_ALL -cairo_surface_t * - gdk_surface_create_similar_surface (GdkSurface *window, - cairo_content_t content, - int width, - int height); -GDK_AVAILABLE_IN_ALL -cairo_surface_t * - gdk_surface_create_similar_image_surface (GdkSurface *window, - cairo_format_t format, - int width, - int height, - int scale); - -GDK_AVAILABLE_IN_ALL -void gdk_surface_beep (GdkSurface *window); -GDK_AVAILABLE_IN_ALL -void gdk_surface_iconify (GdkSurface *window); -GDK_AVAILABLE_IN_ALL -void gdk_surface_deiconify (GdkSurface *window); -GDK_AVAILABLE_IN_ALL -void gdk_surface_stick (GdkSurface *window); -GDK_AVAILABLE_IN_ALL -void gdk_surface_unstick (GdkSurface *window); -GDK_AVAILABLE_IN_ALL -void gdk_surface_maximize (GdkSurface *window); -GDK_AVAILABLE_IN_ALL -void gdk_surface_unmaximize (GdkSurface *window); -GDK_AVAILABLE_IN_ALL -void gdk_surface_fullscreen (GdkSurface *window); -GDK_AVAILABLE_IN_ALL -void gdk_surface_fullscreen_on_monitor (GdkSurface *window, - GdkMonitor *monitor); -GDK_AVAILABLE_IN_ALL -void gdk_surface_set_fullscreen_mode (GdkSurface *window, - GdkFullscreenMode mode); -GDK_AVAILABLE_IN_ALL -GdkFullscreenMode - gdk_surface_get_fullscreen_mode (GdkSurface *window); -GDK_AVAILABLE_IN_ALL -void gdk_surface_unfullscreen (GdkSurface *window); -GDK_AVAILABLE_IN_ALL -void gdk_surface_set_keep_above (GdkSurface *window, - gboolean setting); -GDK_AVAILABLE_IN_ALL -void gdk_surface_set_keep_below (GdkSurface *window, - gboolean setting); -GDK_AVAILABLE_IN_ALL -void gdk_surface_set_opacity (GdkSurface *window, - gdouble opacity); -GDK_AVAILABLE_IN_ALL -void gdk_surface_register_dnd (GdkSurface *window); - -GDK_AVAILABLE_IN_ALL -void gdk_surface_begin_resize_drag (GdkSurface *window, - GdkSurfaceEdge edge, - gint button, - gint root_x, - gint root_y, - guint32 timestamp); -GDK_AVAILABLE_IN_ALL -void gdk_surface_begin_resize_drag_for_device (GdkSurface *window, - GdkSurfaceEdge edge, - GdkDevice *device, - gint button, - gint root_x, - gint root_y, - guint32 timestamp); -GDK_AVAILABLE_IN_ALL -void gdk_surface_begin_move_drag (GdkSurface *window, - gint button, - gint root_x, - gint root_y, - guint32 timestamp); -GDK_AVAILABLE_IN_ALL -void gdk_surface_begin_move_drag_for_device (GdkSurface *window, - GdkDevice *device, - gint button, - gint root_x, - gint root_y, - guint32 timestamp); - -/* Interface for dirty-region queueing */ -GDK_AVAILABLE_IN_ALL -void gdk_surface_invalidate_rect (GdkSurface *window, - const GdkRectangle *rect, - gboolean invalidate_children); -GDK_AVAILABLE_IN_ALL -void gdk_surface_invalidate_region (GdkSurface *window, - const cairo_region_t *region, - gboolean invalidate_children); - -/** - * GdkSurfaceChildFunc: - * @window: a #GdkSurface - * @user_data: user data - * - * A function of this type is passed to gdk_surface_invalidate_maybe_recurse(). - * It gets called for each child of the window to determine whether to - * recursively invalidate it or now. - * - * Returns: %TRUE to invalidate @window recursively - */ -typedef gboolean (*GdkSurfaceChildFunc) (GdkSurface *window, - gpointer user_data); - -GDK_AVAILABLE_IN_ALL -void gdk_surface_invalidate_maybe_recurse (GdkSurface *window, - const cairo_region_t *region, - GdkSurfaceChildFunc child_func, - gpointer user_data); -GDK_AVAILABLE_IN_ALL -cairo_region_t *gdk_surface_get_update_area (GdkSurface *window); - -GDK_AVAILABLE_IN_ALL -void gdk_surface_freeze_updates (GdkSurface *window); -GDK_AVAILABLE_IN_ALL -void gdk_surface_thaw_updates (GdkSurface *window); - -GDK_AVAILABLE_IN_ALL -void gdk_surface_constrain_size (GdkGeometry *geometry, - GdkSurfaceHints flags, - gint width, - gint height, - gint *new_width, - gint *new_height); - -/* Multidevice support */ -GDK_AVAILABLE_IN_ALL -void gdk_surface_set_support_multidevice (GdkSurface *window, - gboolean support_multidevice); -GDK_AVAILABLE_IN_ALL -gboolean gdk_surface_get_support_multidevice (GdkSurface *window); - -/* Frame clock */ -GDK_AVAILABLE_IN_ALL -GdkFrameClock* gdk_surface_get_frame_clock (GdkSurface *window); - -GDK_AVAILABLE_IN_ALL -void gdk_surface_set_opaque_region (GdkSurface *window, - cairo_region_t *region); - -GDK_AVAILABLE_IN_ALL -void gdk_surface_set_shadow_width (GdkSurface *window, - gint left, - gint right, - gint top, - gint bottom); -GDK_AVAILABLE_IN_ALL -gboolean gdk_surface_show_window_menu (GdkSurface *window, - GdkEvent *event); - -GDK_AVAILABLE_IN_ALL -GdkGLContext * gdk_surface_create_gl_context (GdkSurface *window, - GError **error); -GDK_AVAILABLE_IN_ALL -GdkVulkanContext * - gdk_surface_create_vulkan_context(GdkSurface *window, - GError **error); - -G_END_DECLS - -#endif /* __GDK_SURFACE_H__ */ diff --git a/gdk/gdkwindowimpl.c b/gdk/gdkwindowimpl.c deleted file mode 100644 index d182a40cfc..0000000000 --- a/gdk/gdkwindowimpl.c +++ /dev/null @@ -1,347 +0,0 @@ -/* GDK - The GIMP Drawing Kit - * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * 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, see . - */ - -/* - * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS - * file for a list of people on the GTK+ Team. See the ChangeLog - * files for a list of changes. These files are distributed with - * GTK+ at ftp://ftp.gtk.org/pub/gtk/. - */ - -#include "config.h" - -#include "gdkwindowimpl.h" - -#include "gdkinternals.h" - - -G_DEFINE_TYPE (GdkSurfaceImpl, gdk_surface_impl, G_TYPE_OBJECT); - -static gboolean -gdk_surface_impl_beep (GdkSurface *window) -{ - /* FALSE means windows can't beep, so the display will be - * made to beep instead. */ - return FALSE; -} - -static GdkDisplay * -get_display_for_window (GdkSurface *primary, - GdkSurface *secondary) -{ - GdkDisplay *display = gdk_surface_get_display (primary); - - if (display) - return display; - - display = gdk_surface_get_display (secondary); - - if (display) - return display; - - g_warning ("no display for window, using default"); - return gdk_display_get_default (); -} - -static GdkMonitor * -get_monitor_for_rect (GdkDisplay *display, - const GdkRectangle *rect) -{ - gint biggest_area = G_MININT; - GdkMonitor *best_monitor = NULL; - GdkMonitor *monitor; - GdkRectangle workarea; - GdkRectangle intersection; - gint x; - gint y; - gint i; - - for (i = 0; i < gdk_display_get_n_monitors (display); i++) - { - monitor = gdk_display_get_monitor (display, i); - gdk_monitor_get_workarea (monitor, &workarea); - - if (gdk_rectangle_intersect (&workarea, rect, &intersection)) - { - if (intersection.width * intersection.height > biggest_area) - { - biggest_area = intersection.width * intersection.height; - best_monitor = monitor; - } - } - } - - if (best_monitor) - return best_monitor; - - x = rect->x + rect->width / 2; - y = rect->y + rect->height / 2; - - return gdk_display_get_monitor_at_point (display, x, y); -} - -static gint -get_anchor_x_sign (GdkGravity anchor) -{ - switch (anchor) - { - case GDK_GRAVITY_STATIC: - case GDK_GRAVITY_NORTH_WEST: - case GDK_GRAVITY_WEST: - case GDK_GRAVITY_SOUTH_WEST: - return -1; - - default: - case GDK_GRAVITY_NORTH: - case GDK_GRAVITY_CENTER: - case GDK_GRAVITY_SOUTH: - return 0; - - case GDK_GRAVITY_NORTH_EAST: - case GDK_GRAVITY_EAST: - case GDK_GRAVITY_SOUTH_EAST: - return 1; - } -} - -static gint -get_anchor_y_sign (GdkGravity anchor) -{ - switch (anchor) - { - case GDK_GRAVITY_STATIC: - case GDK_GRAVITY_NORTH_WEST: - case GDK_GRAVITY_NORTH: - case GDK_GRAVITY_NORTH_EAST: - return -1; - - default: - case GDK_GRAVITY_WEST: - case GDK_GRAVITY_CENTER: - case GDK_GRAVITY_EAST: - return 0; - - case GDK_GRAVITY_SOUTH_WEST: - case GDK_GRAVITY_SOUTH: - case GDK_GRAVITY_SOUTH_EAST: - return 1; - } -} - -static gint -maybe_flip_position (gint bounds_pos, - gint bounds_size, - gint rect_pos, - gint rect_size, - gint window_size, - gint rect_sign, - gint window_sign, - gint offset, - gboolean flip, - gboolean *flipped) -{ - gint primary; - gint secondary; - - *flipped = FALSE; - primary = rect_pos + (1 + rect_sign) * rect_size / 2 + offset - (1 + window_sign) * window_size / 2; - - if (!flip || (primary >= bounds_pos && primary + window_size <= bounds_pos + bounds_size)) - return primary; - - *flipped = TRUE; - secondary = rect_pos + (1 - rect_sign) * rect_size / 2 - offset - (1 - window_sign) * window_size / 2; - - if (secondary >= bounds_pos && secondary + window_size <= bounds_pos + bounds_size) - return secondary; - - *flipped = FALSE; - return primary; -} - -static GdkSurface * -traverse_to_toplevel (GdkSurface *window, - gint x, - gint y, - gint *toplevel_x, - gint *toplevel_y) -{ - GdkSurface *parent; - gdouble xf = x; - gdouble yf = y; - - while ((parent = window->parent) != NULL && - (gdk_surface_get_window_type (parent) != GDK_SURFACE_ROOT)) - { - gdk_surface_coords_to_parent (window, xf, yf, &xf, &yf); - window = parent; - } - - *toplevel_x = (gint) xf; - *toplevel_y = (gint) yf; - return window; -} - -static void -gdk_surface_impl_move_to_rect (GdkSurface *window, - const GdkRectangle *rect, - GdkGravity rect_anchor, - GdkGravity window_anchor, - GdkAnchorHints anchor_hints, - gint rect_anchor_dx, - gint rect_anchor_dy) -{ - GdkSurface *transient_for_toplevel; - GdkDisplay *display; - GdkMonitor *monitor; - GdkRectangle bounds; - GdkRectangle root_rect = *rect; - GdkRectangle flipped_rect; - GdkRectangle final_rect; - gboolean flipped_x; - gboolean flipped_y; - - /* - * First translate the anchor rect to toplevel coordinates. This is needed - * because not all backends will be able to get root coordinates for - * non-toplevel windows. - */ - transient_for_toplevel = traverse_to_toplevel (window->transient_for, - root_rect.x, - root_rect.y, - &root_rect.x, - &root_rect.y); - - gdk_surface_get_root_coords (transient_for_toplevel, - root_rect.x, - root_rect.y, - &root_rect.x, - &root_rect.y); - - display = get_display_for_window (window, window->transient_for); - monitor = get_monitor_for_rect (display, &root_rect); - gdk_monitor_get_workarea (monitor, &bounds); - - flipped_rect.width = window->width - window->shadow_left - window->shadow_right; - flipped_rect.height = window->height - window->shadow_top - window->shadow_bottom; - flipped_rect.x = maybe_flip_position (bounds.x, - bounds.width, - root_rect.x, - root_rect.width, - flipped_rect.width, - get_anchor_x_sign (rect_anchor), - get_anchor_x_sign (window_anchor), - rect_anchor_dx, - anchor_hints & GDK_ANCHOR_FLIP_X, - &flipped_x); - flipped_rect.y = maybe_flip_position (bounds.y, - bounds.height, - root_rect.y, - root_rect.height, - flipped_rect.height, - get_anchor_y_sign (rect_anchor), - get_anchor_y_sign (window_anchor), - rect_anchor_dy, - anchor_hints & GDK_ANCHOR_FLIP_Y, - &flipped_y); - - final_rect = flipped_rect; - - if (anchor_hints & GDK_ANCHOR_SLIDE_X) - { - if (final_rect.x + final_rect.width > bounds.x + bounds.width) - final_rect.x = bounds.x + bounds.width - final_rect.width; - - if (final_rect.x < bounds.x) - final_rect.x = bounds.x; - } - - if (anchor_hints & GDK_ANCHOR_SLIDE_Y) - { - if (final_rect.y + final_rect.height > bounds.y + bounds.height) - final_rect.y = bounds.y + bounds.height - final_rect.height; - - if (final_rect.y < bounds.y) - final_rect.y = bounds.y; - } - - if (anchor_hints & GDK_ANCHOR_RESIZE_X) - { - if (final_rect.x < bounds.x) - { - final_rect.width -= bounds.x - final_rect.x; - final_rect.x = bounds.x; - } - - if (final_rect.x + final_rect.width > bounds.x + bounds.width) - final_rect.width = bounds.x + bounds.width - final_rect.x; - } - - if (anchor_hints & GDK_ANCHOR_RESIZE_Y) - { - if (final_rect.y < bounds.y) - { - final_rect.height -= bounds.y - final_rect.y; - final_rect.y = bounds.y; - } - - if (final_rect.y + final_rect.height > bounds.y + bounds.height) - final_rect.height = bounds.y + bounds.height - final_rect.y; - } - - flipped_rect.x -= window->shadow_left; - flipped_rect.y -= window->shadow_top; - flipped_rect.width += window->shadow_left + window->shadow_right; - flipped_rect.height += window->shadow_top + window->shadow_bottom; - - final_rect.x -= window->shadow_left; - final_rect.y -= window->shadow_top; - final_rect.width += window->shadow_left + window->shadow_right; - final_rect.height += window->shadow_top + window->shadow_bottom; - - if (final_rect.width != window->width || final_rect.height != window->height) - gdk_surface_move_resize (window, final_rect.x, final_rect.y, final_rect.width, final_rect.height); - else - gdk_surface_move (window, final_rect.x, final_rect.y); - - g_signal_emit_by_name (window, - "moved-to-rect", - &flipped_rect, - &final_rect, - flipped_x, - flipped_y); -} - -static void -gdk_surface_impl_process_updates_recurse (GdkSurface *window, - cairo_region_t *region) -{ - _gdk_surface_process_updates_recurse (window, region); -} - -static void -gdk_surface_impl_class_init (GdkSurfaceImplClass *impl_class) -{ - impl_class->beep = gdk_surface_impl_beep; - impl_class->move_to_rect = gdk_surface_impl_move_to_rect; - impl_class->process_updates_recurse = gdk_surface_impl_process_updates_recurse; -} - -static void -gdk_surface_impl_init (GdkSurfaceImpl *impl) -{ -} diff --git a/gdk/gdkwindowimpl.h b/gdk/gdkwindowimpl.h deleted file mode 100644 index 076feff090..0000000000 --- a/gdk/gdkwindowimpl.h +++ /dev/null @@ -1,256 +0,0 @@ -/* GDK - The GIMP Drawing Kit - * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * 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, see . - */ - -/* - * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS - * file for a list of people on the GTK+ Team. See the ChangeLog - * files for a list of changes. These files are distributed with - * GTK+ at ftp://ftp.gtk.org/pub/gtk/. - */ - -#ifndef __GDK_SURFACE_IMPL_H__ -#define __GDK_SURFACE_IMPL_H__ - -#include -#include - -G_BEGIN_DECLS - -#define GDK_TYPE_SURFACE_IMPL (gdk_surface_impl_get_type ()) -#define GDK_SURFACE_IMPL(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), GDK_TYPE_SURFACE_IMPL, GdkSurfaceImpl)) -#define GDK_SURFACE_IMPL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GDK_TYPE_SURFACE_IMPL, GdkSurfaceImplClass)) -#define GDK_IS_SURFACE_IMPL(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), GDK_TYPE_SURFACE_IMPL)) -#define GDK_IS_SURFACE_IMPL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GDK_TYPE_SURFACE_IMPL)) -#define GDK_SURFACE_IMPL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GDK_TYPE_SURFACE_IMPL, GdkSurfaceImplClass)) - -typedef struct _GdkSurfaceImpl GdkSurfaceImpl; -typedef struct _GdkSurfaceImplClass GdkSurfaceImplClass; - -struct _GdkSurfaceImpl -{ - GObject parent; -}; - -struct _GdkSurfaceImplClass -{ - GObjectClass parent_class; - - cairo_surface_t * - (* ref_cairo_surface) (GdkSurface *window); - cairo_surface_t * - (* create_similar_image_surface) (GdkSurface * window, - cairo_format_t format, - int width, - int height); - - void (* show) (GdkSurface *window, - gboolean already_mapped); - void (* hide) (GdkSurface *window); - void (* withdraw) (GdkSurface *window); - void (* raise) (GdkSurface *window); - void (* lower) (GdkSurface *window); - void (* restack_toplevel) (GdkSurface *window, - GdkSurface *sibling, - gboolean above); - - void (* move_resize) (GdkSurface *window, - gboolean with_move, - gint x, - gint y, - gint width, - gint height); - void (* move_to_rect) (GdkSurface *window, - const GdkRectangle *rect, - GdkGravity rect_anchor, - GdkGravity window_anchor, - GdkAnchorHints anchor_hints, - gint rect_anchor_dx, - gint rect_anchor_dy); - - GdkEventMask (* get_events) (GdkSurface *window); - void (* set_events) (GdkSurface *window, - GdkEventMask event_mask); - - void (* get_geometry) (GdkSurface *window, - gint *x, - gint *y, - gint *width, - gint *height); - void (* get_root_coords) (GdkSurface *window, - gint x, - gint y, - gint *root_x, - gint *root_y); - gboolean (* get_device_state) (GdkSurface *window, - GdkDevice *device, - gdouble *x, - gdouble *y, - GdkModifierType *mask); - gboolean (* begin_paint) (GdkSurface *window); - void (* end_paint) (GdkSurface *window); - - void (* shape_combine_region) (GdkSurface *window, - const cairo_region_t *shape_region, - gint offset_x, - gint offset_y); - void (* input_shape_combine_region) (GdkSurface *window, - const cairo_region_t *shape_region, - gint offset_x, - gint offset_y); - - /* Called before processing updates for a window. This gives the windowing - * layer a chance to save the region for later use in avoiding duplicate - * exposes. - */ - void (* queue_antiexpose) (GdkSurface *window, - cairo_region_t *update_area); - -/* Called to do the windowing system specific part of gdk_surface_destroy(), - * - * window: The window being destroyed - * recursing: If TRUE, then this is being called because a parent - * was destroyed. This generally means that the call to the windowing - * system to destroy the window can be omitted, since it will be - * destroyed as a result of the parent being destroyed. - * Unless @foreign_destroy - * foreign_destroy: If TRUE, the window or a parent was destroyed by some - * external agency. The window has already been destroyed and no - * windowing system calls should be made. (This may never happen - * for some windowing systems.) - */ - void (* destroy) (GdkSurface *window, - gboolean recursing, - gboolean foreign_destroy); - - - /* optional */ - gboolean (* beep) (GdkSurface *window); - - void (* focus) (GdkSurface *window, - guint32 timestamp); - void (* set_type_hint) (GdkSurface *window, - GdkSurfaceTypeHint hint); - GdkSurfaceTypeHint (* get_type_hint) (GdkSurface *window); - void (* set_modal_hint) (GdkSurface *window, - gboolean modal); - void (* set_skip_taskbar_hint) (GdkSurface *window, - gboolean skips_taskbar); - void (* set_skip_pager_hint) (GdkSurface *window, - gboolean skips_pager); - void (* set_urgency_hint) (GdkSurface *window, - gboolean urgent); - void (* set_geometry_hints) (GdkSurface *window, - const GdkGeometry *geometry, - GdkSurfaceHints geom_mask); - void (* set_title) (GdkSurface *window, - const gchar *title); - void (* set_role) (GdkSurface *window, - const gchar *role); - void (* set_startup_id) (GdkSurface *window, - const gchar *startup_id); - void (* set_transient_for) (GdkSurface *window, - GdkSurface *parent); - void (* get_frame_extents) (GdkSurface *window, - GdkRectangle *rect); - void (* set_accept_focus) (GdkSurface *window, - gboolean accept_focus); - void (* set_focus_on_map) (GdkSurface *window, - gboolean focus_on_map); - void (* set_icon_list) (GdkSurface *window, - GList *pixbufs); - void (* set_icon_name) (GdkSurface *window, - const gchar *name); - void (* iconify) (GdkSurface *window); - void (* deiconify) (GdkSurface *window); - void (* stick) (GdkSurface *window); - void (* unstick) (GdkSurface *window); - void (* maximize) (GdkSurface *window); - void (* unmaximize) (GdkSurface *window); - void (* fullscreen) (GdkSurface *window); - void (* fullscreen_on_monitor) (GdkSurface *window, - GdkMonitor *monitor); - void (* apply_fullscreen_mode) (GdkSurface *window); - void (* unfullscreen) (GdkSurface *window); - void (* set_keep_above) (GdkSurface *window, - gboolean setting); - void (* set_keep_below) (GdkSurface *window, - gboolean setting); - GdkSurface * (* get_group) (GdkSurface *window); - void (* set_group) (GdkSurface *window, - GdkSurface *leader); - void (* set_decorations) (GdkSurface *window, - GdkWMDecoration decorations); - gboolean (* get_decorations) (GdkSurface *window, - GdkWMDecoration *decorations); - void (* set_functions) (GdkSurface *window, - GdkWMFunction functions); - void (* begin_resize_drag) (GdkSurface *window, - GdkSurfaceEdge edge, - GdkDevice *device, - gint button, - gint root_x, - gint root_y, - guint32 timestamp); - void (* begin_move_drag) (GdkSurface *window, - GdkDevice *device, - gint button, - gint root_x, - gint root_y, - guint32 timestamp); - void (* enable_synchronized_configure) (GdkSurface *window); - void (* configure_finished) (GdkSurface *window); - void (* set_opacity) (GdkSurface *window, - gdouble opacity); - void (* destroy_notify) (GdkSurface *window); - void (* register_dnd) (GdkSurface *window); - GdkDragContext * (*drag_begin) (GdkSurface *window, - GdkDevice *device, - GdkContentProvider*content, - GdkDragAction actions, - gint dx, - gint dy); - - void (*process_updates_recurse) (GdkSurface *window, - cairo_region_t *region); - - gint (* get_scale_factor) (GdkSurface *window); - void (* get_unscaled_size) (GdkSurface *window, - int *unscaled_width, - int *unscaled_height); - - void (* set_opaque_region) (GdkSurface *window, - cairo_region_t *region); - void (* set_shadow_width) (GdkSurface *window, - gint left, - gint right, - gint top, - gint bottom); - gboolean (* show_window_menu) (GdkSurface *window, - GdkEvent *event); - GdkGLContext *(*create_gl_context) (GdkSurface *window, - gboolean attached, - GdkGLContext *share, - GError **error); - gboolean (* supports_edge_constraints)(GdkSurface *window); -}; - -/* Interface Functions */ -GType gdk_surface_impl_get_type (void) G_GNUC_CONST; - -G_END_DECLS - -#endif /* __GDK_SURFACE_IMPL_H__ */ diff --git a/gdk/meson.build b/gdk/meson.build index 8f69e1ca24..d562d6c84f 100644 --- a/gdk/meson.build +++ b/gdk/meson.build @@ -42,8 +42,8 @@ gdk_public_sources = files([ 'gdksnapshot.c', 'gdktexture.c', 'gdkvulkancontext.c', - 'gdkwindow.c', - 'gdkwindowimpl.c', + 'gdksurface.c', + 'gdksurfaceimpl.c', ]) gdk_public_headers = files([ @@ -86,7 +86,7 @@ gdk_public_headers = files([ 'gdktexture.h', 'gdktypes.h', 'gdkvulkancontext.h', - 'gdkwindow.h', + 'gdksurface.h', ]) install_headers(gdk_public_headers, subdir: 'gtk-4.0/gdk/') diff --git a/gdk/quartz/GdkQuartzNSWindow.c b/gdk/quartz/GdkQuartzNSWindow.c index bdb86011ab..60067f5c7a 100644 --- a/gdk/quartz/GdkQuartzNSWindow.c +++ b/gdk/quartz/GdkQuartzNSWindow.c @@ -18,7 +18,7 @@ #include "config.h" #import "GdkQuartzNSWindow.h" -#include "gdkquartzwindow.h" +#include "gdkquartzsurface.h" #include "gdkdnd-quartz.h" #include "gdkprivate-quartz.h" diff --git a/gdk/quartz/GdkQuartzView.c b/gdk/quartz/GdkQuartzView.c index fc2f13458e..39d3821406 100644 --- a/gdk/quartz/GdkQuartzView.c +++ b/gdk/quartz/GdkQuartzView.c @@ -19,7 +19,7 @@ #include "config.h" #import "GdkQuartzView.h" -#include "gdkquartzwindow.h" +#include "gdkquartzsurface.h" #include "gdkprivate-quartz.h" #include "gdkquartz.h" diff --git a/gdk/quartz/gdkdevice-core-quartz.c b/gdk/quartz/gdkdevice-core-quartz.c index 9a2e270e08..c42680e3ac 100644 --- a/gdk/quartz/gdkdevice-core-quartz.c +++ b/gdk/quartz/gdkdevice-core-quartz.c @@ -22,7 +22,7 @@ #include #import "GdkQuartzView.h" -#include "gdkquartzwindow.h" +#include "gdkquartzsurface.h" #include "gdkquartzcursor.h" #include "gdkprivate-quartz.h" #include "gdkquartzdevice-core.h" diff --git a/gdk/quartz/gdkdisplay-quartz.c b/gdk/quartz/gdkdisplay-quartz.c index 781437d1c0..1bfb4459dd 100644 --- a/gdk/quartz/gdkdisplay-quartz.c +++ b/gdk/quartz/gdkdisplay-quartz.c @@ -23,7 +23,7 @@ #include "gdkprivate-quartz.h" #include "gdkquartzscreen.h" -#include "gdkquartzwindow.h" +#include "gdkquartzsurface.h" #include "gdkquartzdisplay.h" #include "gdkquartzdevicemanager-core.h" #include "gdkmonitorprivate.h" diff --git a/gdk/quartz/gdkdisplay-quartz.h b/gdk/quartz/gdkdisplay-quartz.h index 10d249e5a3..4e25106879 100644 --- a/gdk/quartz/gdkdisplay-quartz.h +++ b/gdk/quartz/gdkdisplay-quartz.h @@ -22,7 +22,7 @@ #include "gdkdisplayprivate.h" #include "gdkkeys.h" -#include "gdkwindow.h" +#include "gdksurface.h" #include "gdkinternals.h" G_BEGIN_DECLS diff --git a/gdk/quartz/gdkevents-quartz.c b/gdk/quartz/gdkevents-quartz.c index d1f520b9cc..c85f071c18 100644 --- a/gdk/quartz/gdkevents-quartz.c +++ b/gdk/quartz/gdkevents-quartz.c @@ -624,7 +624,7 @@ find_toplevel_under_pointer (GdkDisplay *display, * under the pointer and we thus return NULL. This can occur when * toplevel under pointer has not yet been updated due to a very recent * window resize. Alternatively, we should no longer be relying on - * the toplevel_under_pointer value which is maintained in gdkwindow.c. + * the toplevel_under_pointer value which is maintained in gdksurface.c. */ if (*x < 0 || *y < 0 || *x >= toplevel->width || *y >= toplevel->height) return NULL; diff --git a/gdk/quartz/gdkglcontext-quartz.h b/gdk/quartz/gdkglcontext-quartz.h index 86946a1df8..080316e5bc 100644 --- a/gdk/quartz/gdkglcontext-quartz.h +++ b/gdk/quartz/gdkglcontext-quartz.h @@ -24,7 +24,7 @@ #define __GDK_QUARTZ_GL_CONTEXT__ #include "gdkglcontextprivate.h" -#include "gdkwindow.h" +#include "gdksurface.h" #include "gdkinternals.h" G_BEGIN_DECLS diff --git a/gdk/quartz/gdkprivate-quartz.h b/gdk/quartz/gdkprivate-quartz.h index 48f5950d4c..e65a5f227f 100644 --- a/gdk/quartz/gdkprivate-quartz.h +++ b/gdk/quartz/gdkprivate-quartz.h @@ -1,4 +1,4 @@ -/* gdkwindow-quartz.c +/* gdksurface-quartz.c * * Copyright (C) 2005-2007 Imendio AB * @@ -26,7 +26,7 @@ #include #include #include -#include +#include #include diff --git a/gdk/quartz/gdkquartz.h b/gdk/quartz/gdkquartz.h index 0479c72303..132245d09f 100644 --- a/gdk/quartz/gdkquartz.h +++ b/gdk/quartz/gdkquartz.h @@ -80,7 +80,7 @@ G_END_DECLS #include #include #include -#include +#include #undef __GDKQUARTZ_H_INSIDE__ diff --git a/gdk/quartz/gdkquartzsurface.h b/gdk/quartz/gdkquartzsurface.h new file mode 100644 index 0000000000..a37cb37c1d --- /dev/null +++ b/gdk/quartz/gdkquartzsurface.h @@ -0,0 +1,55 @@ +/* gdkquartzsurface.h + * + * Copyright (C) 2005 Imendio AB + * Copyright (C) 2010 Kristian Rietveld + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * 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, see . + */ + +#ifndef __GDK_QUARTZ_SURFACE_H__ +#define __GDK_QUARTZ_SURFACE_H__ + +#if !defined (__GDKQUARTZ_H_INSIDE__) && !defined (GDK_COMPILATION) +#error "Only can be included directly." +#endif + +#include + +G_BEGIN_DECLS + +#define GDK_TYPE_QUARTZ_SURFACE (gdk_quartz_surface_get_type ()) +#define GDK_QUARTZ_SURFACE(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), GDK_TYPE_QUARTZ_SURFACE, GdkQuartzSurface)) +#define GDK_QUARTZ_SURFACE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GDK_TYPE_QUARTZ_SURFACE, GdkQuartzSurfaceClass)) +#define GDK_IS_QUARTZ_SURFACE(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), GDK_TYPE_QUARTZ_SURFACE)) +#define GDK_IS_QUARTZ_SURFACE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GDK_TYPE_QUARTZ_SURFACE)) +#define GDK_QUARTZ_SURFACE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GDK_TYPE_QUARTZ_SURFACE, GdkQuartzSurfaceClass)) + +#ifdef GDK_COMPILATION +typedef struct _GdkQuartzSurface GdkQuartzSurface; +#else +typedef GdkSurface GdkQuartzSurface; +#endif +typedef struct _GdkQuartzSurfaceClass GdkQuartzSurfaceClass; + +GDK_AVAILABLE_IN_ALL +GType gdk_quartz_surface_get_type (void); + +GDK_AVAILABLE_IN_ALL +NSWindow *gdk_quartz_surface_get_nswindow (GdkSurface *window); +GDK_AVAILABLE_IN_ALL +NSView *gdk_quartz_surface_get_nsview (GdkSurface *window); + +G_END_DECLS + +#endif /* __GDK_QUARTZ_SURFACE_H__ */ diff --git a/gdk/quartz/gdkquartzwindow.h b/gdk/quartz/gdkquartzwindow.h deleted file mode 100644 index 56ff424b76..0000000000 --- a/gdk/quartz/gdkquartzwindow.h +++ /dev/null @@ -1,55 +0,0 @@ -/* gdkquartzwindow.h - * - * Copyright (C) 2005 Imendio AB - * Copyright (C) 2010 Kristian Rietveld - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * 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, see . - */ - -#ifndef __GDK_QUARTZ_SURFACE_H__ -#define __GDK_QUARTZ_SURFACE_H__ - -#if !defined (__GDKQUARTZ_H_INSIDE__) && !defined (GDK_COMPILATION) -#error "Only can be included directly." -#endif - -#include - -G_BEGIN_DECLS - -#define GDK_TYPE_QUARTZ_SURFACE (gdk_quartz_surface_get_type ()) -#define GDK_QUARTZ_SURFACE(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), GDK_TYPE_QUARTZ_SURFACE, GdkQuartzSurface)) -#define GDK_QUARTZ_SURFACE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GDK_TYPE_QUARTZ_SURFACE, GdkQuartzSurfaceClass)) -#define GDK_IS_QUARTZ_SURFACE(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), GDK_TYPE_QUARTZ_SURFACE)) -#define GDK_IS_QUARTZ_SURFACE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GDK_TYPE_QUARTZ_SURFACE)) -#define GDK_QUARTZ_SURFACE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GDK_TYPE_QUARTZ_SURFACE, GdkQuartzSurfaceClass)) - -#ifdef GDK_COMPILATION -typedef struct _GdkQuartzSurface GdkQuartzSurface; -#else -typedef GdkSurface GdkQuartzSurface; -#endif -typedef struct _GdkQuartzSurfaceClass GdkQuartzSurfaceClass; - -GDK_AVAILABLE_IN_ALL -GType gdk_quartz_surface_get_type (void); - -GDK_AVAILABLE_IN_ALL -NSWindow *gdk_quartz_surface_get_nswindow (GdkSurface *window); -GDK_AVAILABLE_IN_ALL -NSView *gdk_quartz_surface_get_nsview (GdkSurface *window); - -G_END_DECLS - -#endif /* __GDK_QUARTZ_SURFACE_H__ */ diff --git a/gdk/quartz/gdkscreen-quartz.c b/gdk/quartz/gdkscreen-quartz.c index e9d2d6b094..f8094354a0 100644 --- a/gdk/quartz/gdkscreen-quartz.c +++ b/gdk/quartz/gdkscreen-quartz.c @@ -55,7 +55,7 @@ * coordinate space. Such coordinates are mapped to their respective * position in the GdkScreen root window (_gdk_quartz_surface_xy_to_gdk_xy) * and vice versa (_gdk_quartz_surface_gdk_xy_to_xy). Both functions can - * be found in gdkwindow-quartz.c. Note that Cocoa coordinates can have + * be found in gdksurface-quartz.c. Note that Cocoa coordinates can have * negative values (in case a monitor is located left or below of screen 0), * but GDK coordinates can *not*! */ diff --git a/gdk/quartz/gdksurface-quartz.c b/gdk/quartz/gdksurface-quartz.c new file mode 100644 index 0000000000..f3676cee47 --- /dev/null +++ b/gdk/quartz/gdksurface-quartz.c @@ -0,0 +1,2958 @@ +/* gdksurface-quartz.c + * + * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald + * Copyright (C) 2005-2007 Imendio AB + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * 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, see . + */ + +#include "config.h" + +#include +#include +#include + +#include "gdksurfaceimpl.h" +#include "gdkprivate-quartz.h" +#include "gdkglcontext-quartz.h" +#include "gdkquartzscreen.h" +#include "gdkquartzcursor.h" + +#include +#include + +#include +#include + +static gpointer parent_class; +static gpointer root_window_parent_class; + +static GSList *update_nswindows; +static gboolean in_process_all_updates = FALSE; + +static GSList *main_window_stack; + +void _gdk_quartz_surface_flush (GdkSurfaceImplQuartz *window_impl); + +typedef struct +{ + gint x, y; + gint width, height; + GdkWMDecoration decor; +} FullscreenSavedGeometry; + + +#ifndef AVAILABLE_MAC_OS_X_VERSION_10_7_AND_LATER +static FullscreenSavedGeometry *get_fullscreen_geometry (GdkSurface *window); +#endif + +#define FULLSCREEN_DATA "fullscreen-data" + +static void update_toplevel_order (void); +static void clear_toplevel_order (void); + +#define WINDOW_IS_TOPLEVEL(window) (GDK_SURFACE_TYPE (window) != GDK_SURFACE_CHILD) + +/* + * GdkQuartzSurface + */ + +struct _GdkQuartzSurface +{ + GdkSurface parent; +}; + +struct _GdkQuartzSurfaceClass +{ + GdkSurfaceClass parent_class; +}; + +G_DEFINE_TYPE (GdkQuartzSurface, gdk_quartz_surface, GDK_TYPE_SURFACE); + +static void +gdk_quartz_surface_class_init (GdkQuartzSurfaceClass *quartz_surface_class) +{ +} + +static void +gdk_quartz_surface_init (GdkQuartzSurface *quartz_surface) +{ +} + + +/* + * GdkQuartzSurfaceImpl + */ + +NSView * +gdk_quartz_surface_get_nsview (GdkSurface *window) +{ + if (GDK_SURFACE_DESTROYED (window)) + return NULL; + + return ((GdkSurfaceImplQuartz *)window->impl)->view; +} + +NSWindow * +gdk_quartz_surface_get_nswindow (GdkSurface *window) +{ + if (GDK_SURFACE_DESTROYED (window)) + return NULL; + + return ((GdkSurfaceImplQuartz *)window->impl)->toplevel; +} + +static CGContextRef +gdk_surface_impl_quartz_get_context (GdkSurfaceImplQuartz *window_impl, + gboolean antialias) +{ + CGContextRef cg_context; + CGSize scale; + + if (GDK_SURFACE_DESTROYED (window_impl->wrapper)) + return NULL; + + /* Lock focus when not called as part of a drawRect call. This + * is needed when called from outside "real" expose events, for + * example for synthesized expose events when realizing windows + * and for widgets that send fake expose events like the arrow + * buttons in spinbuttons or the position marker in rulers. + */ + if (window_impl->in_paint_rect_count == 0) + { + if (![window_impl->view lockFocusIfCanDraw]) + return NULL; + } + + cg_context = [[NSGraphicsContext currentContext] graphicsPort]; + CGContextSaveGState (cg_context); + CGContextSetAllowsAntialiasing (cg_context, antialias); + + /* Undo the default scaling transform, since we apply our own + * in gdk_quartz_ref_cairo_surface () */ + scale = CGContextConvertSizeToDeviceSpace (cg_context, + CGSizeMake (1.0, 1.0)); + CGContextScaleCTM (cg_context, 1.0 / scale.width, 1.0 / scale.height); + + return cg_context; +} + +static void +gdk_surface_impl_quartz_release_context (GdkSurfaceImplQuartz *window_impl, + CGContextRef cg_context) +{ + CGContextRestoreGState (cg_context); + CGContextSetAllowsAntialiasing (cg_context, TRUE); + + /* See comment in gdk_quartz_surface_get_context(). */ + if (window_impl->in_paint_rect_count == 0) + { + _gdk_quartz_surface_flush (window_impl); + [window_impl->view unlockFocus]; + } +} + +static void +check_grab_destroy (GdkSurface *window) +{ + GList *devices = NULL, *l; + GdkDisplay *display = gdk_surface_get_display (window); + GdkSeat *seat; + + seat = gdk_display_get_default_seat (display); + + devices = g_list_prepend (devices, gdk_seat_get_keyboard (seat)); + devices = g_list_prepend (devices, gdk_seat_get_pointer (seat)); + + for (l = devices; l; l = l->next) + { + GdkDeviceGrabInfo *grab; + + grab = _gdk_display_get_last_device_grab (display, l->data); + if (grab && grab->native_window == window) + { + /* Serials are always 0 in quartz, but for clarity: */ + grab->serial_end = grab->serial_start; + grab->implicit_ungrab = TRUE; + } + } + + g_list_free (devices); +} + +static void +gdk_surface_impl_quartz_finalize (GObject *object) +{ + GdkSurfaceImplQuartz *impl = GDK_SURFACE_IMPL_QUARTZ (object); + + check_grab_destroy (GDK_SURFACE_IMPL_QUARTZ (object)->wrapper); + + if (impl->transient_for) + g_object_unref (impl->transient_for); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +/* Help preventing "beam sync penalty" where CG makes all graphics code + * block until the next vsync if we try to flush (including call display on + * a view) too often. We do this by limiting the manual flushing done + * outside of expose calls to less than some frequency when measured over + * the last 4 flushes. This is a bit arbitray, but seems to make it possible + * for some quick manual flushes (such as gtkruler or gimp’s marching ants) + * without hitting the max flush frequency. + * + * If drawable NULL, no flushing is done, only registering that a flush was + * done externally. + */ +void +_gdk_quartz_surface_flush (GdkSurfaceImplQuartz *window_impl) +{ + static struct timeval prev_tv; + static gint intervals[4]; + static gint index; + struct timeval tv; + gint ms; + + gettimeofday (&tv, NULL); + ms = (tv.tv_sec - prev_tv.tv_sec) * 1000 + (tv.tv_usec - prev_tv.tv_usec) / 1000; + intervals[index++ % 4] = ms; + + if (window_impl) + { + ms = intervals[0] + intervals[1] + intervals[2] + intervals[3]; + + /* ~25Hz on average. */ + if (ms > 4*40) + { + if (window_impl) + [window_impl->toplevel flushWindow]; + + prev_tv = tv; + } + } + else + prev_tv = tv; +} + +static cairo_user_data_key_t gdk_quartz_cairo_key; + +typedef struct { + GdkSurfaceImplQuartz *window_impl; + CGContextRef cg_context; +} GdkQuartzCairoSurfaceData; + +static void +gdk_quartz_cairo_surface_destroy (void *data) +{ + GdkQuartzCairoSurfaceData *surface_data = data; + + surface_data->window_impl->cairo_surface = NULL; + + gdk_quartz_surface_release_context (surface_data->window_impl, + surface_data->cg_context); + + g_free (surface_data); +} + +static cairo_surface_t * +gdk_quartz_create_cairo_surface (GdkSurfaceImplQuartz *impl, + int width, + int height) +{ + CGContextRef cg_context; + GdkQuartzCairoSurfaceData *surface_data; + cairo_surface_t *surface; + + cg_context = gdk_quartz_surface_get_context (impl, TRUE); + + if (!cg_context) + return NULL; + + surface_data = g_new (GdkQuartzCairoSurfaceData, 1); + surface_data->window_impl = impl; + surface_data->cg_context = cg_context; + + surface = cairo_quartz_surface_create_for_cg_context (cg_context, + width, height); + + cairo_surface_set_user_data (surface, &gdk_quartz_cairo_key, + surface_data, + gdk_quartz_cairo_surface_destroy); + + return surface; +} + +static cairo_surface_t * +gdk_quartz_ref_cairo_surface (GdkSurface *window) +{ + GdkSurfaceImplQuartz *impl = GDK_SURFACE_IMPL_QUARTZ (window->impl); + + if (GDK_SURFACE_DESTROYED (window)) + return NULL; + + if (!impl->cairo_surface) + { + gint scale = gdk_surface_get_scale_factor (impl->wrapper); + + impl->cairo_surface = + gdk_quartz_create_cairo_surface (impl, + gdk_surface_get_width (impl->wrapper) * scale, + gdk_surface_get_height (impl->wrapper) * scale); + + cairo_surface_set_device_scale (impl->cairo_surface, scale, scale); + } + else + cairo_surface_reference (impl->cairo_surface); + + return impl->cairo_surface; +} + +static void +gdk_surface_impl_quartz_init (GdkSurfaceImplQuartz *impl) +{ + impl->type_hint = GDK_SURFACE_TYPE_HINT_NORMAL; +} + +static gboolean +gdk_surface_impl_quartz_begin_paint (GdkSurface *window) +{ + return FALSE; +} + +static void +gdk_quartz_surface_set_needs_display_in_region (GdkSurface *window, + cairo_region_t *region) +{ + GdkSurfaceImplQuartz *impl; + int i, n_rects; + + impl = GDK_SURFACE_IMPL_QUARTZ (window->impl); + + if (!impl->needs_display_region) + impl->needs_display_region = cairo_region_create (); + + cairo_region_union (impl->needs_display_region, region); + + n_rects = cairo_region_num_rectangles (region); + for (i = 0; i < n_rects; i++) + { + cairo_rectangle_int_t rect; + cairo_region_get_rectangle (region, i, &rect); + [impl->view setNeedsDisplayInRect:NSMakeRect (rect.x, rect.y, + rect.width, rect.height)]; + } +} + +void +_gdk_quartz_surface_process_updates_recurse (GdkSurface *window, + cairo_region_t *region) +{ + /* Make sure to only flush each toplevel at most once if we're called + * from process_all_updates. + */ + if (in_process_all_updates) + { + GdkSurface *toplevel; + + toplevel = gdk_surface_get_toplevel (window); + if (toplevel && WINDOW_IS_TOPLEVEL (toplevel)) + { + GdkSurfaceImplQuartz *toplevel_impl; + NSWindow *nswindow; + + toplevel_impl = (GdkSurfaceImplQuartz *)toplevel->impl; + nswindow = toplevel_impl->toplevel; + + /* In theory, we could skip the flush disabling, since we only + * have one NSView. + */ + if (nswindow && ![nswindow isFlushWindowDisabled]) + { + [nswindow retain]; + [nswindow disableFlushWindow]; + update_nswindows = g_slist_prepend (update_nswindows, nswindow); + } + } + } + + if (WINDOW_IS_TOPLEVEL (window)) + gdk_quartz_surface_set_needs_display_in_region (window, region); + else + _gdk_surface_process_updates_recurse (window, region); + + /* NOTE: I'm not sure if we should displayIfNeeded here. It slows down a + * lot (since it triggers the beam syncing) and things seem to work + * without it. + */ +} + +static const gchar * +get_default_title (void) +{ + const char *title; + + title = g_get_application_name (); + if (!title) + title = g_get_prgname (); + + return title; +} + +static void +get_ancestor_coordinates_from_child (GdkSurface *child_window, + gint child_x, + gint child_y, + GdkSurface *ancestor_window, + gint *ancestor_x, + gint *ancestor_y) +{ + while (child_window != ancestor_window) + { + child_x += child_window->x; + child_y += child_window->y; + + child_window = child_window->parent; + } + + *ancestor_x = child_x; + *ancestor_y = child_y; +} + +void +_gdk_quartz_surface_debug_highlight (GdkSurface *window, gint number) +{ + gint x, y; + gint gx, gy; + GdkSurface *toplevel; + gint tx, ty; + static NSWindow *debug_window[10]; + static NSRect old_rect[10]; + NSRect rect; + NSColor *color; + + g_return_if_fail (number >= 0 && number <= 9); + + if (window == _gdk_root) + return; + + if (window == NULL) + { + if (debug_window[number]) + [debug_window[number] close]; + debug_window[number] = NULL; + + return; + } + + toplevel = gdk_surface_get_toplevel (window); + get_ancestor_coordinates_from_child (window, 0, 0, toplevel, &x, &y); + + gdk_surface_get_origin (toplevel, &tx, &ty); + x += tx; + y += ty; + + _gdk_quartz_surface_gdk_xy_to_xy (x, y + window->height, + &gx, &gy); + + rect = NSMakeRect (gx, gy, window->width, window->height); + + if (debug_window[number] && NSEqualRects (rect, old_rect[number])) + return; + + old_rect[number] = rect; + + if (debug_window[number]) + [debug_window[number] close]; + + debug_window[number] = [[NSWindow alloc] initWithContentRect:rect + styleMask:NSBorderlessWindowMask + backing:NSBackingStoreBuffered + defer:NO]; + + switch (number) + { + case 0: + color = [NSColor redColor]; + break; + case 1: + color = [NSColor blueColor]; + break; + case 2: + color = [NSColor greenColor]; + break; + case 3: + color = [NSColor yellowColor]; + break; + case 4: + color = [NSColor brownColor]; + break; + case 5: + color = [NSColor purpleColor]; + break; + default: + color = [NSColor blackColor]; + break; + } + + [debug_window[number] setBackgroundColor:color]; + [debug_window[number] setAlphaValue:0.4]; + [debug_window[number] setOpaque:NO]; + [debug_window[number] setReleasedWhenClosed:YES]; + [debug_window[number] setIgnoresMouseEvents:YES]; + [debug_window[number] setLevel:NSFloatingWindowLevel]; + + [debug_window[number] orderFront:nil]; +} + +gboolean +_gdk_quartz_surface_is_ancestor (GdkSurface *ancestor, + GdkSurface *window) +{ + if (ancestor == NULL || window == NULL) + return FALSE; + + return (gdk_surface_get_parent (window) == ancestor || + _gdk_quartz_surface_is_ancestor (ancestor, + gdk_surface_get_parent (window))); +} + + +/* See notes on top of gdkscreen-quartz.c */ +void +_gdk_quartz_surface_gdk_xy_to_xy (gint gdk_x, + gint gdk_y, + gint *ns_x, + gint *ns_y) +{ + GdkQuartzScreen *screen_quartz = GDK_QUARTZ_SCREEN (_gdk_screen); + + if (ns_y) + *ns_y = screen_quartz->height - gdk_y + screen_quartz->min_y; + + if (ns_x) + *ns_x = gdk_x + screen_quartz->min_x; +} + +void +_gdk_quartz_surface_xy_to_gdk_xy (gint ns_x, + gint ns_y, + gint *gdk_x, + gint *gdk_y) +{ + GdkQuartzScreen *screen_quartz = GDK_QUARTZ_SCREEN (_gdk_screen); + + if (gdk_y) + *gdk_y = screen_quartz->height - ns_y + screen_quartz->min_y; + + if (gdk_x) + *gdk_x = ns_x - screen_quartz->min_x; +} + +void +_gdk_quartz_surface_nspoint_to_gdk_xy (NSPoint point, + gint *x, + gint *y) +{ + _gdk_quartz_surface_xy_to_gdk_xy (point.x, point.y, + x, y); +} + +static GdkSurface * +find_child_window_helper (GdkSurface *window, + gint x, + gint y, + gint x_offset, + gint y_offset, + gboolean get_toplevel) +{ + GdkSurfaceImplQuartz *impl; + GList *l; + + impl = GDK_SURFACE_IMPL_QUARTZ (window->impl); + + if (window == _gdk_root) + update_toplevel_order (); + + for (l = impl->sorted_children; l; l = l->next) + { + GdkSurface *child = l->data; + GdkSurfaceImplQuartz *child_impl = GDK_SURFACE_IMPL_QUARTZ (child->impl); + int temp_x, temp_y; + + if (!GDK_SURFACE_IS_MAPPED (child)) + continue; + + temp_x = x_offset + child->x; + temp_y = y_offset + child->y; + + /* Special-case the root window. We have to include the title + * bar in the checks, otherwise the window below the title bar + * will be found i.e. events punch through. (If we can find a + * better way to deal with the events in gdkevents-quartz, this + * might not be needed.) + */ + if (window == _gdk_root) + { + NSRect frame = NSMakeRect (0, 0, 100, 100); + NSRect content; + NSUInteger mask; + int titlebar_height; + + mask = [child_impl->toplevel styleMask]; + + /* Get the title bar height. */ + content = [NSWindow contentRectForFrameRect:frame + styleMask:mask]; + titlebar_height = frame.size.height - content.size.height; + + if (titlebar_height > 0 && + x >= temp_x && y >= temp_y - titlebar_height && + x < temp_x + child->width && y < temp_y) + { + /* The root means "unknown" i.e. a window not managed by + * GDK. + */ + return (GdkSurface *)_gdk_root; + } + } + + if ((!get_toplevel || (get_toplevel && window == _gdk_root)) && + x >= temp_x && y >= temp_y && + x < temp_x + child->width && y < temp_y + child->height) + { + /* Look for child windows. */ + return find_child_window_helper (l->data, + x, y, + temp_x, temp_y, + get_toplevel); + } + } + + return window; +} + +/* Given a GdkSurface and coordinates relative to it, returns the + * innermost subwindow that contains the point. If the coordinates are + * outside the passed in window, NULL is returned. + */ +GdkSurface * +_gdk_quartz_surface_find_child (GdkSurface *window, + gint x, + gint y, + gboolean get_toplevel) +{ + if (x >= 0 && y >= 0 && x < window->width && y < window->height) + return find_child_window_helper (window, x, y, 0, 0, get_toplevel); + + return NULL; +} + + +void +_gdk_quartz_surface_did_become_main (GdkSurface *window) +{ + main_window_stack = g_slist_remove (main_window_stack, window); + + if (window->window_type != GDK_SURFACE_TEMP) + main_window_stack = g_slist_prepend (main_window_stack, window); + + clear_toplevel_order (); +} + +void +_gdk_quartz_surface_did_resign_main (GdkSurface *window) +{ + GdkSurface *new_window = NULL; + + if (main_window_stack) + new_window = main_window_stack->data; + else + { + GList *toplevels; + + toplevels = get_toplevels (); + if (toplevels) + new_window = toplevels->data; + } + + if (new_window && + new_window != window && + GDK_SURFACE_IS_MAPPED (new_window) && + WINDOW_IS_TOPLEVEL (new_window)) + { + GdkSurfaceImplQuartz *impl = GDK_SURFACE_IMPL_QUARTZ (new_window->impl); + + [impl->toplevel makeKeyAndOrderFront:impl->toplevel]; + } + + clear_toplevel_order (); +} + +static NSScreen * +get_nsscreen_for_point (gint x, gint y) +{ + int i; + NSArray *screens; + NSScreen *screen = NULL; + + GDK_QUARTZ_ALLOC_POOL; + + screens = [NSScreen screens]; + + for (i = 0; i < [screens count]; i++) + { + NSRect rect = [[screens objectAtIndex:i] frame]; + + if (x >= rect.origin.x && x <= rect.origin.x + rect.size.width && + y >= rect.origin.y && y <= rect.origin.y + rect.size.height) + { + screen = [screens objectAtIndex:i]; + break; + } + } + + GDK_QUARTZ_RELEASE_POOL; + + return screen; +} + +void +_gdk_quartz_display_create_window_impl (GdkDisplay *display, + GdkSurface *window, + GdkSurface *real_parent, + GdkEventMask event_mask, + GdkSurfaceAttr *attributes) +{ + GdkSurfaceImplQuartz *impl; + GdkSurfaceImplQuartz *parent_impl; + + GDK_QUARTZ_ALLOC_POOL; + + impl = g_object_new (GDK_TYPE_SURFACE_IMPL_QUARTZ, NULL); + window->impl = GDK_SURFACE_IMPL (impl); + impl->wrapper = window; + + parent_impl = GDK_SURFACE_IMPL_QUARTZ (window->parent->impl); + + switch (window->window_type) + { + case GDK_SURFACE_TOPLEVEL: + case GDK_SURFACE_TEMP: + if (GDK_SURFACE_TYPE (window->parent) != GDK_SURFACE_ROOT) + { + /* The common code warns for this case */ + parent_impl = GDK_SURFACE_IMPL_QUARTZ (_gdk_root->impl); + } + } + + /* Maintain the z-ordered list of children. */ + if (window->parent != _gdk_root) + parent_impl->sorted_children = g_list_prepend (parent_impl->sorted_children, window); + else + clear_toplevel_order (); + + impl->view = NULL; + + switch (window->window_type) + { + case GDK_SURFACE_TOPLEVEL: + case GDK_SURFACE_TEMP: + { + NSScreen *screen; + NSRect screen_rect; + NSRect content_rect; + NSUInteger style_mask; + int nx, ny; + + /* initWithContentRect will place on the mainScreen by default. + * We want to select the screen to place on ourselves. We need + * to find the screen the window will be on and correct the + * content_rect coordinates to be relative to that screen. + */ + _gdk_quartz_surface_gdk_xy_to_xy (window->x, window->y, &nx, &ny); + + screen = get_nsscreen_for_point (nx, ny); + screen_rect = [screen frame]; + nx -= screen_rect.origin.x; + ny -= screen_rect.origin.y; + + content_rect = NSMakeRect (nx, ny - window->height, + window->width, + window->height); + + if (window->window_type == GDK_SURFACE_TEMP) + { + style_mask = NSBorderlessWindowMask; + } + else + { + style_mask = (NSTitledWindowMask | + NSClosableWindowMask | + NSMiniaturizableWindowMask | + NSResizableWindowMask); + } + + impl->toplevel = [[GdkQuartzNSWindow alloc] initWithContentRect:content_rect + styleMask:style_mask + backing:NSBackingStoreBuffered + defer:NO + screen:screen]; + + gdk_surface_set_title (window, get_default_title ()); + + [impl->toplevel setOpaque:NO]; + [impl->toplevel setBackgroundColor:[NSColor clearColor]]; + + content_rect.origin.x = 0; + content_rect.origin.y = 0; + + impl->view = [[GdkQuartzView alloc] initWithFrame:content_rect]; + [impl->view setGdkSurface:window]; + [impl->toplevel setContentView:impl->view]; + [impl->view release]; + } + break; + + case GDK_SURFACE_CHILD: + { + GdkSurfaceImplQuartz *parent_impl = GDK_SURFACE_IMPL_QUARTZ (window->parent->impl); + + if (!window->input_only) + { + NSRect frame_rect = NSMakeRect (window->x + window->parent->abs_x, + window->y + window->parent->abs_y, + window->width, + window->height); + + impl->view = [[GdkQuartzView alloc] initWithFrame:frame_rect]; + + [impl->view setGdkSurface:window]; + + /* GdkSurfaces should be hidden by default */ + [impl->view setHidden:YES]; + [parent_impl->view addSubview:impl->view]; + [impl->view release]; + } + } + break; + + default: + g_assert_not_reached (); + } + + GDK_QUARTZ_RELEASE_POOL; +} + +void +_gdk_quartz_surface_update_position (GdkSurface *window) +{ + NSRect frame_rect; + NSRect content_rect; + GdkSurfaceImplQuartz *impl = GDK_SURFACE_IMPL_QUARTZ (window->impl); + + GDK_QUARTZ_ALLOC_POOL; + + frame_rect = [impl->toplevel frame]; + content_rect = [impl->toplevel contentRectForFrameRect:frame_rect]; + + _gdk_quartz_surface_xy_to_gdk_xy (content_rect.origin.x, + content_rect.origin.y + content_rect.size.height, + &window->x, &window->y); + + + GDK_QUARTZ_RELEASE_POOL; +} + +void +_gdk_quartz_surface_init_windowing (GdkDisplay *display) +{ + GdkSurfaceImplQuartz *impl; + + g_assert (_gdk_root == NULL); + + _gdk_root = _gdk_display_create_window (display); + + _gdk_root->impl = g_object_new (_gdk_root_window_impl_quartz_get_type (), NULL); + _gdk_root->impl_window = _gdk_root; + + impl = GDK_SURFACE_IMPL_QUARTZ (_gdk_root->impl); + + _gdk_quartz_screen_update_window_sizes (screen); + + _gdk_root->state = 0; /* We don't want GDK_SURFACE_STATE_WITHDRAWN here */ + _gdk_root->window_type = GDK_SURFACE_ROOT; + _gdk_root->viewable = TRUE; + + impl->wrapper = _gdk_root; +} + +static void +gdk_quartz_surface_destroy (GdkSurface *window, + gboolean recursing, + gboolean foreign_destroy) +{ + GdkSurfaceImplQuartz *impl; + GdkSurface *parent; + + impl = GDK_SURFACE_IMPL_QUARTZ (window->impl); + + main_window_stack = g_slist_remove (main_window_stack, window); + + g_list_free (impl->sorted_children); + impl->sorted_children = NULL; + + parent = window->parent; + if (parent) + { + GdkSurfaceImplQuartz *parent_impl = GDK_SURFACE_IMPL_QUARTZ (parent->impl); + + parent_impl->sorted_children = g_list_remove (parent_impl->sorted_children, window); + } + + if (impl->cairo_surface) + { + cairo_surface_finish (impl->cairo_surface); + cairo_surface_set_user_data (impl->cairo_surface, &gdk_quartz_cairo_key, + NULL, NULL); + impl->cairo_surface = NULL; + } + + if (!recursing && !foreign_destroy) + { + GDK_QUARTZ_ALLOC_POOL; + + if (impl->toplevel) + [impl->toplevel close]; + else if (impl->view) + [impl->view removeFromSuperview]; + + GDK_QUARTZ_RELEASE_POOL; + } +} + +/* FIXME: This might be possible to simplify with client-side windows. Also + * note that already_mapped is not used yet, see the x11 backend. +*/ +static void +gdk_surface_quartz_show (GdkSurface *window, gboolean already_mapped) +{ + GdkSurfaceImplQuartz *impl = GDK_SURFACE_IMPL_QUARTZ (window->impl); + gboolean focus_on_map; + + GDK_QUARTZ_ALLOC_POOL; + + if (!GDK_SURFACE_IS_MAPPED (window)) + focus_on_map = window->focus_on_map; + else + focus_on_map = TRUE; + + if (WINDOW_IS_TOPLEVEL (window) && impl->toplevel) + { + gboolean make_key; + + make_key = (window->accept_focus && focus_on_map && + window->window_type != GDK_SURFACE_TEMP); + + [(GdkQuartzNSWindow*)impl->toplevel showAndMakeKey:make_key]; + clear_toplevel_order (); + + _gdk_quartz_events_send_map_event (window); + } + else + { + [impl->view setHidden:NO]; + } + + [impl->view setNeedsDisplay:YES]; + + gdk_synthesize_window_state (window, GDK_SURFACE_STATE_WITHDRAWN, 0); + + if (window->state & GDK_SURFACE_STATE_MAXIMIZED) + gdk_surface_maximize (window); + + if (window->state & GDK_SURFACE_STATE_ICONIFIED) + gdk_surface_iconify (window); + + if (impl->transient_for && !GDK_SURFACE_DESTROYED (impl->transient_for)) + _gdk_quartz_surface_attach_to_parent (window); + + GDK_QUARTZ_RELEASE_POOL; +} + +/* Temporarily unsets the parent window, if the window is a + * transient. + */ +void +_gdk_quartz_surface_detach_from_parent (GdkSurface *window) +{ + GdkSurfaceImplQuartz *impl; + + g_return_if_fail (GDK_IS_SURFACE (window)); + + impl = GDK_SURFACE_IMPL_QUARTZ (window->impl); + + g_return_if_fail (impl->toplevel != NULL); + + if (impl->transient_for && !GDK_SURFACE_DESTROYED (impl->transient_for)) + { + GdkSurfaceImplQuartz *parent_impl; + + parent_impl = GDK_SURFACE_IMPL_QUARTZ (impl->transient_for->impl); + [parent_impl->toplevel removeChildWindow:impl->toplevel]; + clear_toplevel_order (); + } +} + +/* Re-sets the parent window, if the window is a transient. */ +void +_gdk_quartz_surface_attach_to_parent (GdkSurface *window) +{ + GdkSurfaceImplQuartz *impl; + + g_return_if_fail (GDK_IS_SURFACE (window)); + + impl = GDK_SURFACE_IMPL_QUARTZ (window->impl); + + g_return_if_fail (impl->toplevel != NULL); + + if (impl->transient_for && !GDK_SURFACE_DESTROYED (impl->transient_for)) + { + GdkSurfaceImplQuartz *parent_impl; + + parent_impl = GDK_SURFACE_IMPL_QUARTZ (impl->transient_for->impl); + [parent_impl->toplevel addChildWindow:impl->toplevel ordered:NSWindowAbove]; + clear_toplevel_order (); + } +} + +void +gdk_surface_quartz_hide (GdkSurface *window) +{ + GdkSurfaceImplQuartz *impl; + + /* Make sure we're not stuck in fullscreen mode. */ +#ifndef AVAILABLE_MAC_OS_X_VERSION_10_7_AND_LATER + if (get_fullscreen_geometry (window)) + SetSystemUIMode (kUIModeNormal, 0); +#endif + + _gdk_surface_clear_update_area (window); + + impl = GDK_SURFACE_IMPL_QUARTZ (window->impl); + + if (WINDOW_IS_TOPLEVEL (window)) + { + /* Update main window. */ + main_window_stack = g_slist_remove (main_window_stack, window); + if ([NSApp mainWindow] == impl->toplevel) + _gdk_quartz_surface_did_resign_main (window); + + if (impl->transient_for) + _gdk_quartz_surface_detach_from_parent (window); + + [(GdkQuartzNSWindow*)impl->toplevel hide]; + } + else if (impl->view) + { + [impl->view setHidden:YES]; + } +} + +void +gdk_surface_quartz_withdraw (GdkSurface *window) +{ + gdk_surface_hide (window); +} + +static void +move_resize_window_internal (GdkSurface *window, + gint x, + gint y, + gint width, + gint height) +{ + GdkSurfaceImplQuartz *impl; + GdkRectangle old_visible; + GdkRectangle new_visible; + GdkRectangle scroll_rect; + cairo_region_t *old_region; + cairo_region_t *expose_region; + NSSize delta; + + if (GDK_SURFACE_DESTROYED (window)) + return; + + impl = GDK_SURFACE_IMPL_QUARTZ (window->impl); + + if ((x == -1 || (x == window->x)) && + (y == -1 || (y == window->y)) && + (width == -1 || (width == window->width)) && + (height == -1 || (height == window->height))) + { + return; + } + + if (!impl->toplevel) + { + /* The previously visible area of this window in a coordinate + * system rooted at the origin of this window. + */ + old_visible.x = -window->x; + old_visible.y = -window->y; + + old_visible.width = window->width; + old_visible.height = window->height; + } + + if (x != -1) + { + delta.width = x - window->x; + window->x = x; + } + else + { + delta.width = 0; + } + + if (y != -1) + { + delta.height = y - window->y; + window->y = y; + } + else + { + delta.height = 0; + } + + if (width != -1) + window->width = width; + + if (height != -1) + window->height = height; + + GDK_QUARTZ_ALLOC_POOL; + + if (impl->toplevel) + { + NSRect content_rect; + NSRect frame_rect; + gint gx, gy; + + _gdk_quartz_surface_gdk_xy_to_xy (window->x, window->y + window->height, + &gx, &gy); + + content_rect = NSMakeRect (gx, gy, window->width, window->height); + + frame_rect = [impl->toplevel frameRectForContentRect:content_rect]; + [impl->toplevel setFrame:frame_rect display:YES]; + } + else + { + if (!window->input_only) + { + NSRect nsrect; + + nsrect = NSMakeRect (window->x, window->y, window->width, window->height); + + /* The newly visible area of this window in a coordinate + * system rooted at the origin of this window. + */ + new_visible.x = -window->x; + new_visible.y = -window->y; + new_visible.width = old_visible.width; /* parent has not changed size */ + new_visible.height = old_visible.height; /* parent has not changed size */ + + expose_region = cairo_region_create_rectangle (&new_visible); + old_region = cairo_region_create_rectangle (&old_visible); + cairo_region_subtract (expose_region, old_region); + + /* Determine what (if any) part of the previously visible + * part of the window can be copied without a redraw + */ + scroll_rect = old_visible; + scroll_rect.x -= delta.width; + scroll_rect.y -= delta.height; + gdk_rectangle_intersect (&scroll_rect, &old_visible, &scroll_rect); + + if (!cairo_region_is_empty (expose_region)) + { + if (scroll_rect.width != 0 && scroll_rect.height != 0) + { + [impl->view scrollRect:NSMakeRect (scroll_rect.x, + scroll_rect.y, + scroll_rect.width, + scroll_rect.height) + by:delta]; + } + + [impl->view setFrame:nsrect]; + + gdk_quartz_surface_set_needs_display_in_region (window, expose_region); + } + else + { + [impl->view setFrame:nsrect]; + [impl->view setNeedsDisplay:YES]; + } + + cairo_region_destroy (expose_region); + cairo_region_destroy (old_region); + } + } + + GDK_QUARTZ_RELEASE_POOL; +} + +static inline void +window_quartz_move (GdkSurface *window, + gint x, + gint y) +{ + g_return_if_fail (GDK_IS_SURFACE (window)); + + if (window->state & GDK_SURFACE_STATE_FULLSCREEN) + return; + + move_resize_window_internal (window, x, y, -1, -1); +} + +static inline void +window_quartz_resize (GdkSurface *window, + gint width, + gint height) +{ + g_return_if_fail (GDK_IS_SURFACE (window)); + + if (window->state & GDK_SURFACE_STATE_FULLSCREEN) + return; + + if (width < 1) + width = 1; + if (height < 1) + height = 1; + + move_resize_window_internal (window, -1, -1, width, height); +} + +static inline void +window_quartz_move_resize (GdkSurface *window, + gint x, + gint y, + gint width, + gint height) +{ + if (width < 1) + width = 1; + if (height < 1) + height = 1; + + move_resize_window_internal (window, x, y, width, height); +} + +static void +gdk_surface_quartz_move_resize (GdkSurface *window, + gboolean with_move, + gint x, + gint y, + gint width, + gint height) +{ + if (with_move && (width < 0 && height < 0)) + window_quartz_move (window, x, y); + else + { + if (with_move) + window_quartz_move_resize (window, x, y, width, height); + else + window_quartz_resize (window, width, height); + } +} + +/* Get the toplevel ordering from NSApp and update our own list. We do + * this on demand since the NSApp’s list is not up to date directly + * after we get windowDidBecomeMain. + */ +static void +update_toplevel_order (void) +{ + GdkSurfaceImplQuartz *root_impl; + NSEnumerator *enumerator; + id nswindow; + GList *toplevels = NULL; + + root_impl = GDK_SURFACE_IMPL_QUARTZ (_gdk_root->impl); + + if (root_impl->sorted_children) + return; + + GDK_QUARTZ_ALLOC_POOL; + + enumerator = [[NSApp orderedWindows] objectEnumerator]; + while ((nswindow = [enumerator nextObject])) + { + GdkSurface *window; + + if (![[nswindow contentView] isKindOfClass:[GdkQuartzView class]]) + continue; + + window = [(GdkQuartzView *)[nswindow contentView] gdkWindow]; + toplevels = g_list_prepend (toplevels, window); + } + + GDK_QUARTZ_RELEASE_POOL; + + root_impl->sorted_children = g_list_reverse (toplevels); +} + +static void +clear_toplevel_order (void) +{ + GdkSurfaceImplQuartz *root_impl; + + root_impl = GDK_SURFACE_IMPL_QUARTZ (_gdk_root->impl); + + g_list_free (root_impl->sorted_children); + root_impl->sorted_children = NULL; +} + +static void +gdk_surface_quartz_raise (GdkSurface *window) +{ + if (GDK_SURFACE_DESTROYED (window)) + return; + + if (WINDOW_IS_TOPLEVEL (window)) + { + GdkSurfaceImplQuartz *impl; + + impl = GDK_SURFACE_IMPL_QUARTZ (window->impl); + [impl->toplevel orderFront:impl->toplevel]; + + clear_toplevel_order (); + } + else + { + GdkSurface *parent = window->parent; + + if (parent) + { + GdkSurfaceImplQuartz *impl; + + impl = (GdkSurfaceImplQuartz *)parent->impl; + + impl->sorted_children = g_list_remove (impl->sorted_children, window); + impl->sorted_children = g_list_prepend (impl->sorted_children, window); + } + } +} + +static void +gdk_surface_quartz_lower (GdkSurface *window) +{ + if (GDK_SURFACE_DESTROYED (window)) + return; + + if (WINDOW_IS_TOPLEVEL (window)) + { + GdkSurfaceImplQuartz *impl; + + impl = GDK_SURFACE_IMPL_QUARTZ (window->impl); + [impl->toplevel orderBack:impl->toplevel]; + + clear_toplevel_order (); + } + else + { + GdkSurface *parent = window->parent; + + if (parent) + { + GdkSurfaceImplQuartz *impl; + + impl = (GdkSurfaceImplQuartz *)parent->impl; + + impl->sorted_children = g_list_remove (impl->sorted_children, window); + impl->sorted_children = g_list_append (impl->sorted_children, window); + } + } +} + +static void +gdk_surface_quartz_restack_toplevel (GdkSurface *window, + GdkSurface *sibling, + gboolean above) +{ + GdkSurfaceImplQuartz *impl; + gint sibling_num; + + impl = GDK_SURFACE_IMPL_QUARTZ (sibling->impl); + sibling_num = [impl->toplevel windowNumber]; + + impl = GDK_SURFACE_IMPL_QUARTZ (window->impl); + + if (above) + [impl->toplevel orderWindow:NSWindowAbove relativeTo:sibling_num]; + else + [impl->toplevel orderWindow:NSWindowBelow relativeTo:sibling_num]; +} + +static void +gdk_surface_quartz_get_geometry (GdkSurface *window, + gint *x, + gint *y, + gint *width, + gint *height) +{ + GdkSurfaceImplQuartz *impl; + NSRect ns_rect; + + if (GDK_SURFACE_DESTROYED (window)) + return; + + impl = GDK_SURFACE_IMPL_QUARTZ (window->impl); + if (window == _gdk_root) + { + if (x) + *x = 0; + if (y) + *y = 0; + + if (width) + *width = window->width; + if (height) + *height = window->height; + } + else if (WINDOW_IS_TOPLEVEL (window)) + { + ns_rect = [impl->toplevel contentRectForFrameRect:[impl->toplevel frame]]; + + /* This doesn't work exactly as in X. There doesn't seem to be a + * way to get the coords relative to the parent window (usually + * the window frame), but that seems useless except for + * borderless windows where it's relative to the root window. So + * we return (0, 0) (should be something like (0, 22)) for + * windows with borders and the root relative coordinates + * otherwise. + */ + if ([impl->toplevel styleMask] == NSBorderlessWindowMask) + { + _gdk_quartz_surface_xy_to_gdk_xy (ns_rect.origin.x, + ns_rect.origin.y + ns_rect.size.height, + x, y); + } + else + { + if (x) + *x = 0; + if (y) + *y = 0; + } + + if (width) + *width = ns_rect.size.width; + if (height) + *height = ns_rect.size.height; + } + else + { + ns_rect = [impl->view frame]; + + if (x) + *x = ns_rect.origin.x; + if (y) + *y = ns_rect.origin.y; + if (width) + *width = ns_rect.size.width; + if (height) + *height = ns_rect.size.height; + } +} + +static void +gdk_surface_quartz_get_root_coords (GdkSurface *window, + gint x, + gint y, + gint *root_x, + gint *root_y) +{ + int tmp_x = 0, tmp_y = 0; + GdkSurface *toplevel; + NSRect content_rect; + GdkSurfaceImplQuartz *impl; + + if (GDK_SURFACE_DESTROYED (window)) + { + if (root_x) + *root_x = 0; + if (root_y) + *root_y = 0; + + return; + } + + if (window == _gdk_root) + { + if (root_x) + *root_x = x; + if (root_y) + *root_y = y; + + return; + } + + toplevel = gdk_surface_get_toplevel (window); + impl = GDK_SURFACE_IMPL_QUARTZ (toplevel->impl); + + content_rect = [impl->toplevel contentRectForFrameRect:[impl->toplevel frame]]; + + _gdk_quartz_surface_xy_to_gdk_xy (content_rect.origin.x, + content_rect.origin.y + content_rect.size.height, + &tmp_x, &tmp_y); + + tmp_x += x; + tmp_y += y; + + while (window != toplevel) + { + if (_gdk_surface_has_impl ((GdkSurface *)window)) + { + tmp_x += window->x; + tmp_y += window->y; + } + + window = window->parent; + } + + if (root_x) + *root_x = tmp_x; + if (root_y) + *root_y = tmp_y; +} + +/* Returns coordinates relative to the passed in window. */ +static GdkSurface * +gdk_surface_quartz_get_device_state_helper (GdkSurface *window, + GdkDevice *device, + gdouble *x, + gdouble *y, + GdkModifierType *mask) +{ + NSPoint point; + gint x_tmp, y_tmp; + GdkSurface *toplevel; + GdkSurface *found_window; + + g_return_val_if_fail (window == NULL || GDK_IS_SURFACE (window), NULL); + + if (GDK_SURFACE_DESTROYED (window)) + { + *x = 0; + *y = 0; + *mask = 0; + return NULL; + } + + toplevel = gdk_surface_get_toplevel (window); + + *mask = _gdk_quartz_events_get_current_keyboard_modifiers () | + _gdk_quartz_events_get_current_mouse_modifiers (); + + /* Get the y coordinate, needs to be flipped. */ + if (window == _gdk_root) + { + point = [NSEvent mouseLocation]; + _gdk_quartz_surface_nspoint_to_gdk_xy (point, &x_tmp, &y_tmp); + } + else + { + GdkSurfaceImplQuartz *impl; + NSWindow *nswindow; + + impl = GDK_SURFACE_IMPL_QUARTZ (toplevel->impl); + nswindow = impl->toplevel; + + point = [nswindow mouseLocationOutsideOfEventStream]; + + x_tmp = point.x; + y_tmp = toplevel->height - point.y; + + window = (GdkSurface *)toplevel; + } + + found_window = _gdk_quartz_surface_find_child (window, x_tmp, y_tmp, + FALSE); + + /* We never return the root window. */ + if (found_window == _gdk_root) + found_window = NULL; + + *x = x_tmp; + *y = y_tmp; + + return found_window; +} + +static gboolean +gdk_surface_quartz_get_device_state (GdkSurface *window, + GdkDevice *device, + gdouble *x, + gdouble *y, + GdkModifierType *mask) +{ + return gdk_surface_quartz_get_device_state_helper (window, + device, + x, y, mask) != NULL; +} + +static GdkEventMask +gdk_surface_quartz_get_events (GdkSurface *window) +{ + if (GDK_SURFACE_DESTROYED (window)) + return 0; + else + return window->event_mask; +} + +static void +gdk_surface_quartz_set_events (GdkSurface *window, + GdkEventMask event_mask) +{ + /* The mask is set in the common code. */ +} + +static void +gdk_quartz_surface_set_urgency_hint (GdkSurface *window, + gboolean urgent) +{ + if (GDK_SURFACE_DESTROYED (window) || + !WINDOW_IS_TOPLEVEL (window)) + return; + + /* FIXME: Implement */ +} + +static void +gdk_quartz_surface_set_geometry_hints (GdkSurface *window, + const GdkGeometry *geometry, + GdkSurfaceHints geom_mask) +{ + GdkSurfaceImplQuartz *impl; + + g_return_if_fail (geometry != NULL); + + if (GDK_SURFACE_DESTROYED (window) || + !WINDOW_IS_TOPLEVEL (window)) + return; + + impl = GDK_SURFACE_IMPL_QUARTZ (window->impl); + if (!impl->toplevel) + return; + + if (geom_mask & GDK_HINT_POS) + { + /* FIXME: Implement */ + } + + if (geom_mask & GDK_HINT_USER_POS) + { + /* FIXME: Implement */ + } + + if (geom_mask & GDK_HINT_USER_SIZE) + { + /* FIXME: Implement */ + } + + if (geom_mask & GDK_HINT_MIN_SIZE) + { + NSSize size; + + size.width = geometry->min_width; + size.height = geometry->min_height; + + [impl->toplevel setContentMinSize:size]; + } + + if (geom_mask & GDK_HINT_MAX_SIZE) + { + NSSize size; + + size.width = geometry->max_width; + size.height = geometry->max_height; + + [impl->toplevel setContentMaxSize:size]; + } + + if (geom_mask & GDK_HINT_BASE_SIZE) + { + /* FIXME: Implement */ + } + + if (geom_mask & GDK_HINT_RESIZE_INC) + { + NSSize size; + + size.width = geometry->width_inc; + size.height = geometry->height_inc; + + [impl->toplevel setContentResizeIncrements:size]; + } + + if (geom_mask & GDK_HINT_ASPECT) + { + NSSize size; + + if (geometry->min_aspect != geometry->max_aspect) + { + g_warning ("Only equal minimum and maximum aspect ratios are supported on Mac OS. Using minimum aspect ratio..."); + } + + size.width = geometry->min_aspect; + size.height = 1.0; + + [impl->toplevel setContentAspectRatio:size]; + } + + if (geom_mask & GDK_HINT_WIN_GRAVITY) + { + /* FIXME: Implement */ + } +} + +static void +gdk_quartz_surface_set_title (GdkSurface *window, + const gchar *title) +{ + GdkSurfaceImplQuartz *impl; + + g_return_if_fail (title != NULL); + + if (GDK_SURFACE_DESTROYED (window) || + !WINDOW_IS_TOPLEVEL (window)) + return; + + impl = GDK_SURFACE_IMPL_QUARTZ (window->impl); + + if (impl->toplevel) + { + GDK_QUARTZ_ALLOC_POOL; + [impl->toplevel setTitle:[NSString stringWithUTF8String:title]]; + GDK_QUARTZ_RELEASE_POOL; + } +} + +static void +gdk_quartz_surface_set_role (GdkSurface *window, + const gchar *role) +{ + if (GDK_SURFACE_DESTROYED (window) || + WINDOW_IS_TOPLEVEL (window)) + return; + + /* FIXME: Implement */ +} + +static void +gdk_quartz_surface_set_startup_id (GdkSurface *window, + const gchar *startup_id) +{ + /* FIXME: Implement? */ +} + +static void +gdk_quartz_surface_set_transient_for (GdkSurface *window, + GdkSurface *parent) +{ + GdkSurfaceImplQuartz *window_impl; + GdkSurfaceImplQuartz *parent_impl; + + if (GDK_SURFACE_DESTROYED (window) || GDK_SURFACE_DESTROYED (parent) || + !WINDOW_IS_TOPLEVEL (window)) + return; + + window_impl = GDK_SURFACE_IMPL_QUARTZ (window->impl); + if (!window_impl->toplevel) + return; + + GDK_QUARTZ_ALLOC_POOL; + + if (window_impl->transient_for) + { + _gdk_quartz_surface_detach_from_parent (window); + + g_object_unref (window_impl->transient_for); + window_impl->transient_for = NULL; + } + + parent_impl = GDK_SURFACE_IMPL_QUARTZ (parent->impl); + if (parent_impl->toplevel) + { + /* We save the parent because it needs to be unset/reset when + * hiding and showing the window. + */ + + /* We don't set transients for tooltips, they are already + * handled by the window level being the top one. If we do, then + * the parent window will be brought to the top just because the + * tooltip is, which is not what we want. + */ + if (gdk_surface_get_type_hint (window) != GDK_SURFACE_TYPE_HINT_TOOLTIP) + { + window_impl->transient_for = g_object_ref (parent); + + /* We only add the window if it is shown, otherwise it will + * be shown unconditionally here. If it is not shown, the + * window will be added in show() instead. + */ + if (!(window->state & GDK_SURFACE_STATE_WITHDRAWN)) + _gdk_quartz_surface_attach_to_parent (window); + } + } + + GDK_QUARTZ_RELEASE_POOL; +} + +static void +gdk_surface_quartz_shape_combine_region (GdkSurface *window, + const cairo_region_t *shape, + gint x, + gint y) +{ + /* FIXME: Implement */ +} + +static void +gdk_surface_quartz_input_shape_combine_region (GdkSurface *window, + const cairo_region_t *shape_region, + gint offset_x, + gint offset_y) +{ + /* FIXME: Implement */ +} + +static void +gdk_quartz_surface_set_accept_focus (GdkSurface *window, + gboolean accept_focus) +{ + window->accept_focus = accept_focus != FALSE; +} + +static void +gdk_quartz_surface_set_focus_on_map (GdkSurface *window, + gboolean focus_on_map) +{ + window->focus_on_map = focus_on_map != FALSE; +} + +static void +gdk_quartz_surface_set_icon_name (GdkSurface *window, + const gchar *name) +{ + /* FIXME: Implement */ +} + +static void +gdk_quartz_surface_focus (GdkSurface *window, + guint32 timestamp) +{ + GdkSurfaceImplQuartz *impl; + + impl = GDK_SURFACE_IMPL_QUARTZ (window->impl); + + if (GDK_SURFACE_DESTROYED (window) || + !WINDOW_IS_TOPLEVEL (window)) + return; + + if (window->accept_focus && window->window_type != GDK_SURFACE_TEMP) + { + GDK_QUARTZ_ALLOC_POOL; + [impl->toplevel makeKeyAndOrderFront:impl->toplevel]; + clear_toplevel_order (); + GDK_QUARTZ_RELEASE_POOL; + } +} + +static gint +window_type_hint_to_level (GdkSurfaceTypeHint hint) +{ + /* the order in this switch statement corresponds to the actual + * stacking order: the first group is top, the last group is bottom + */ + switch (hint) + { + case GDK_SURFACE_TYPE_HINT_POPUP_MENU: + case GDK_SURFACE_TYPE_HINT_COMBO: + case GDK_SURFACE_TYPE_HINT_DND: + case GDK_SURFACE_TYPE_HINT_TOOLTIP: + return NSPopUpMenuWindowLevel; + + case GDK_SURFACE_TYPE_HINT_NOTIFICATION: + case GDK_SURFACE_TYPE_HINT_SPLASHSCREEN: + return NSStatusWindowLevel; + + case GDK_SURFACE_TYPE_HINT_MENU: /* Torn-off menu */ + case GDK_SURFACE_TYPE_HINT_DROPDOWN_MENU: /* Menu from menubar */ + return NSTornOffMenuWindowLevel; + + case GDK_SURFACE_TYPE_HINT_DOCK: + return NSFloatingWindowLevel; /* NSDockWindowLevel is deprecated, and not replaced */ + + case GDK_SURFACE_TYPE_HINT_UTILITY: + case GDK_SURFACE_TYPE_HINT_DIALOG: /* Dialog window */ + case GDK_SURFACE_TYPE_HINT_NORMAL: /* Normal toplevel window */ + case GDK_SURFACE_TYPE_HINT_TOOLBAR: /* Window used to implement toolbars */ + return NSNormalWindowLevel; + + case GDK_SURFACE_TYPE_HINT_DESKTOP: + return kCGDesktopWindowLevelKey; /* doesn't map to any real Cocoa model */ + + default: + break; + } + + return NSNormalWindowLevel; +} + +static gboolean +window_type_hint_to_shadow (GdkSurfaceTypeHint hint) +{ + switch (hint) + { + case GDK_SURFACE_TYPE_HINT_NORMAL: /* Normal toplevel window */ + case GDK_SURFACE_TYPE_HINT_DIALOG: /* Dialog window */ + case GDK_SURFACE_TYPE_HINT_DOCK: + case GDK_SURFACE_TYPE_HINT_UTILITY: + case GDK_SURFACE_TYPE_HINT_MENU: /* Torn-off menu */ + case GDK_SURFACE_TYPE_HINT_DROPDOWN_MENU: /* Menu from menubar */ + case GDK_SURFACE_TYPE_HINT_SPLASHSCREEN: + case GDK_SURFACE_TYPE_HINT_POPUP_MENU: + case GDK_SURFACE_TYPE_HINT_COMBO: + case GDK_SURFACE_TYPE_HINT_NOTIFICATION: + case GDK_SURFACE_TYPE_HINT_TOOLTIP: + return TRUE; + + case GDK_SURFACE_TYPE_HINT_TOOLBAR: /* Window used to implement toolbars */ + case GDK_SURFACE_TYPE_HINT_DESKTOP: /* N/A */ + case GDK_SURFACE_TYPE_HINT_DND: + break; + + default: + break; + } + + return FALSE; +} + +static gboolean +window_type_hint_to_hides_on_deactivate (GdkSurfaceTypeHint hint) +{ + switch (hint) + { + case GDK_SURFACE_TYPE_HINT_UTILITY: + case GDK_SURFACE_TYPE_HINT_MENU: /* Torn-off menu */ + case GDK_SURFACE_TYPE_HINT_SPLASHSCREEN: + case GDK_SURFACE_TYPE_HINT_NOTIFICATION: + case GDK_SURFACE_TYPE_HINT_TOOLTIP: + return TRUE; + + default: + break; + } + + return FALSE; +} + +static void +_gdk_quartz_surface_update_has_shadow (GdkSurfaceImplQuartz *impl) +{ + gboolean has_shadow; + + /* In case there is any shadow set we have to turn off the + * NSWindow setHasShadow as the system drawn ones wont match our + * window boundary anymore */ + has_shadow = (window_type_hint_to_shadow (impl->type_hint) && !impl->shadow_max); + + [impl->toplevel setHasShadow: has_shadow]; +} + +static void +gdk_quartz_surface_set_type_hint (GdkSurface *window, + GdkSurfaceTypeHint hint) +{ + GdkSurfaceImplQuartz *impl; + + if (GDK_SURFACE_DESTROYED (window) || + !WINDOW_IS_TOPLEVEL (window)) + return; + + impl = GDK_SURFACE_IMPL_QUARTZ (window->impl); + + impl->type_hint = hint; + + /* Match the documentation, only do something if we're not mapped yet. */ + if (GDK_SURFACE_IS_MAPPED (window)) + return; + + _gdk_quartz_surface_update_has_shadow (impl); + [impl->toplevel setLevel: window_type_hint_to_level (hint)]; + [impl->toplevel setHidesOnDeactivate: window_type_hint_to_hides_on_deactivate (hint)]; +} + +static GdkSurfaceTypeHint +gdk_quartz_surface_get_type_hint (GdkSurface *window) +{ + if (GDK_SURFACE_DESTROYED (window) || + !WINDOW_IS_TOPLEVEL (window)) + return GDK_SURFACE_TYPE_HINT_NORMAL; + + return GDK_SURFACE_IMPL_QUARTZ (window->impl)->type_hint; +} + +static void +gdk_quartz_surface_set_modal_hint (GdkSurface *window, + gboolean modal) +{ + if (GDK_SURFACE_DESTROYED (window) || + !WINDOW_IS_TOPLEVEL (window)) + return; + + /* FIXME: Implement */ +} + +static void +gdk_quartz_surface_set_skip_taskbar_hint (GdkSurface *window, + gboolean skips_taskbar) +{ + if (GDK_SURFACE_DESTROYED (window) || + !WINDOW_IS_TOPLEVEL (window)) + return; + + /* FIXME: Implement */ +} + +static void +gdk_quartz_surface_set_skip_pager_hint (GdkSurface *window, + gboolean skips_pager) +{ + if (GDK_SURFACE_DESTROYED (window) || + !WINDOW_IS_TOPLEVEL (window)) + return; + + /* FIXME: Implement */ +} + +static void +gdk_quartz_surface_begin_resize_drag (GdkSurface *window, + GdkSurfaceEdge edge, + GdkDevice *device, + gint button, + gint root_x, + gint root_y, + guint32 timestamp) +{ + GdkSurfaceImplQuartz *impl; + + g_return_if_fail (GDK_IS_SURFACE (window)); + + if (GDK_SURFACE_DESTROYED (window)) + return; + + impl = GDK_SURFACE_IMPL_QUARTZ (window->impl); + + if (!impl->toplevel) + { + g_warning ("Can't call gdk_surface_begin_resize_drag on non-toplevel window"); + return; + } + + [(GdkQuartzNSWindow *)impl->toplevel beginManualResize:edge]; +} + +static void +gdk_quartz_surface_begin_move_drag (GdkSurface *window, + GdkDevice *device, + gint button, + gint root_x, + gint root_y, + guint32 timestamp) +{ + GdkSurfaceImplQuartz *impl; + + if (GDK_SURFACE_DESTROYED (window) || + !WINDOW_IS_TOPLEVEL (window)) + return; + + impl = GDK_SURFACE_IMPL_QUARTZ (window->impl); + + if (!impl->toplevel) + { + g_warning ("Can't call gdk_surface_begin_move_drag on non-toplevel window"); + return; + } + + [(GdkQuartzNSWindow *)impl->toplevel beginManualMove]; +} + +static void +gdk_quartz_surface_set_icon_list (GdkSurface *window, + GList *surfaces) +{ + /* FIXME: Implement */ +} + +static void +gdk_quartz_surface_get_frame_extents (GdkSurface *window, + GdkRectangle *rect) +{ + GdkSurface *toplevel; + GdkSurfaceImplQuartz *impl; + NSRect ns_rect; + + g_return_if_fail (rect != NULL); + + + rect->x = 0; + rect->y = 0; + rect->width = 1; + rect->height = 1; + + toplevel = gdk_surface_get_toplevel (window); + impl = GDK_SURFACE_IMPL_QUARTZ (toplevel->impl); + + ns_rect = [impl->toplevel frame]; + + _gdk_quartz_surface_xy_to_gdk_xy (ns_rect.origin.x, + ns_rect.origin.y + ns_rect.size.height, + &rect->x, &rect->y); + + rect->width = ns_rect.size.width; + rect->height = ns_rect.size.height; +} + +/* Fake protocol to make gcc think that it's OK to call setStyleMask + even if it isn't. We check to make sure before actually calling + it. */ + +@protocol CanSetStyleMask +- (void)setStyleMask:(int)mask; +@end + +static void +gdk_quartz_surface_set_decorations (GdkSurface *window, + GdkWMDecoration decorations) +{ + GdkSurfaceImplQuartz *impl; + NSUInteger old_mask, new_mask; + NSView *old_view; + + if (GDK_SURFACE_DESTROYED (window) || + !WINDOW_IS_TOPLEVEL (window)) + return; + + impl = GDK_SURFACE_IMPL_QUARTZ (window->impl); + + if (decorations == 0 || GDK_SURFACE_TYPE (window) == GDK_SURFACE_TEMP || + impl->type_hint == GDK_SURFACE_TYPE_HINT_SPLASHSCREEN ) + { + new_mask = NSBorderlessWindowMask; + } + else + { + /* FIXME: Honor other GDK_DECOR_* flags. */ + new_mask = (NSTitledWindowMask | NSClosableWindowMask | + NSMiniaturizableWindowMask | NSResizableWindowMask); + } + + GDK_QUARTZ_ALLOC_POOL; + + old_mask = [impl->toplevel styleMask]; + + if (old_mask != new_mask) + { + NSRect rect; + + old_view = [[impl->toplevel contentView] retain]; + + rect = [impl->toplevel frame]; + + /* Properly update the size of the window when the titlebar is + * added or removed. + */ + if (old_mask == NSBorderlessWindowMask && + new_mask != NSBorderlessWindowMask) + { + rect = [NSWindow frameRectForContentRect:rect styleMask:new_mask]; + + } + else if (old_mask != NSBorderlessWindowMask && + new_mask == NSBorderlessWindowMask) + { + rect = [NSWindow contentRectForFrameRect:rect styleMask:old_mask]; + } + + /* Note, before OS 10.6 there doesn't seem to be a way to change this + * without recreating the toplevel. From 10.6 onward, a simple call to + * setStyleMask takes care of most of this, except for ensuring that the + * title is set. + */ + if ([impl->toplevel respondsToSelector:@selector(setStyleMask:)]) + { + NSString *title = [impl->toplevel title]; + + [(id)impl->toplevel setStyleMask:new_mask]; + + /* It appears that unsetting and then resetting NSTitledWindowMask + * does not reset the title in the title bar as might be expected. + * + * In theory we only need to set this if new_mask includes + * NSTitledWindowMask. This behaved extremely oddly when + * conditionalized upon that and since it has no side effects (i.e. + * if NSTitledWindowMask is not requested, the title will not be + * displayed) just do it unconditionally. We also must null check + * 'title' before setting it to avoid crashing. + */ + if (title) + [impl->toplevel setTitle:title]; + } + else + { + NSString *title = [impl->toplevel title]; + NSColor *bg = [impl->toplevel backgroundColor]; + NSScreen *screen = [impl->toplevel screen]; + + /* Make sure the old window is closed, recall that releasedWhenClosed + * is set on GdkQuartzSurfaces. + */ + [impl->toplevel close]; + + impl->toplevel = [[GdkQuartzNSWindow alloc] initWithContentRect:rect + styleMask:new_mask + backing:NSBackingStoreBuffered + defer:NO + screen:screen]; + _gdk_quartz_surface_update_has_shadow (impl); + + [impl->toplevel setLevel: window_type_hint_to_level (impl->type_hint)]; + if (title) + [impl->toplevel setTitle:title]; + [impl->toplevel setBackgroundColor:bg]; + [impl->toplevel setHidesOnDeactivate: window_type_hint_to_hides_on_deactivate (impl->type_hint)]; + [impl->toplevel setContentView:old_view]; + } + + if (new_mask == NSBorderlessWindowMask) + { + [impl->toplevel setContentSize:rect.size]; + [impl->toplevel setCollectionBehavior:NSWindowCollectionBehaviorFullScreenPrimary]; + } + else + [impl->toplevel setFrame:rect display:YES]; + + /* Invalidate the window shadow for non-opaque views that have shadow + * enabled, to get the shadow shape updated. + */ + if (![old_view isOpaque] && [impl->toplevel hasShadow]) + [(GdkQuartzView*)old_view setNeedsInvalidateShadow:YES]; + + [old_view release]; + } + + GDK_QUARTZ_RELEASE_POOL; +} + +static gboolean +gdk_quartz_surface_get_decorations (GdkSurface *window, + GdkWMDecoration *decorations) +{ + GdkSurfaceImplQuartz *impl; + + if (GDK_SURFACE_DESTROYED (window) || + !WINDOW_IS_TOPLEVEL (window)) + return FALSE; + + impl = GDK_SURFACE_IMPL_QUARTZ (window->impl); + + if (decorations) + { + /* Borderless is 0, so we can't check it as a bit being set. */ + if ([impl->toplevel styleMask] == NSBorderlessWindowMask) + { + *decorations = 0; + } + else + { + /* FIXME: Honor the other GDK_DECOR_* flags. */ + *decorations = GDK_DECOR_ALL; + } + } + + return TRUE; +} + +static void +gdk_quartz_surface_set_functions (GdkSurface *window, + GdkWMFunction functions) +{ + GdkSurfaceImplQuartz *impl; + gboolean min, max, close; + + g_return_if_fail (GDK_IS_SURFACE (window)); + + impl = GDK_SURFACE_IMPL_QUARTZ (window->impl); + + if (functions & GDK_FUNC_ALL) + { + min = !(functions & GDK_FUNC_MINIMIZE); + max = !(functions & GDK_FUNC_MAXIMIZE); + close = !(functions & GDK_FUNC_CLOSE); + } + else + { + min = (functions & GDK_FUNC_MINIMIZE); + max = (functions & GDK_FUNC_MAXIMIZE); + close = (functions & GDK_FUNC_CLOSE); + } + + if (impl->toplevel) + { + NSUInteger mask = [impl->toplevel styleMask]; + + if (min) + mask = mask | NSMiniaturizableWindowMask; + else + mask = mask & ~NSMiniaturizableWindowMask; + + if (max) + mask = mask | NSResizableWindowMask; + else + mask = mask & ~NSResizableWindowMask; + + if (close) + mask = mask | NSClosableWindowMask; + else + mask = mask & ~NSClosableWindowMask; + + [impl->toplevel setStyleMask:mask]; + } +} + +static void +gdk_quartz_surface_stick (GdkSurface *window) +{ + if (GDK_SURFACE_DESTROYED (window) || + !WINDOW_IS_TOPLEVEL (window)) + return; +} + +static void +gdk_quartz_surface_unstick (GdkSurface *window) +{ + if (GDK_SURFACE_DESTROYED (window) || + !WINDOW_IS_TOPLEVEL (window)) + return; +} + +static void +gdk_quartz_surface_maximize (GdkSurface *window) +{ + GdkSurfaceImplQuartz *impl; + gboolean maximized; + + if (GDK_SURFACE_DESTROYED (window) || + !WINDOW_IS_TOPLEVEL (window)) + return; + + impl = GDK_SURFACE_IMPL_QUARTZ (window->impl); + maximized = gdk_surface_get_state (window) & GDK_SURFACE_STATE_MAXIMIZED; + + if (GDK_SURFACE_IS_MAPPED (window)) + { + GDK_QUARTZ_ALLOC_POOL; + + if (impl->toplevel && !maximized) + [impl->toplevel zoom:nil]; + + GDK_QUARTZ_RELEASE_POOL; + } +} + +static void +gdk_quartz_surface_unmaximize (GdkSurface *window) +{ + GdkSurfaceImplQuartz *impl; + gboolean maximized; + + if (GDK_SURFACE_DESTROYED (window) || + !WINDOW_IS_TOPLEVEL (window)) + return; + + impl = GDK_SURFACE_IMPL_QUARTZ (window->impl); + maximized = gdk_surface_get_state (window) & GDK_SURFACE_STATE_MAXIMIZED; + + if (GDK_SURFACE_IS_MAPPED (window)) + { + GDK_QUARTZ_ALLOC_POOL; + + if (impl->toplevel && maximized) + [impl->toplevel zoom:nil]; + + GDK_QUARTZ_RELEASE_POOL; + } +} + +static void +gdk_quartz_surface_iconify (GdkSurface *window) +{ + GdkSurfaceImplQuartz *impl; + + if (GDK_SURFACE_DESTROYED (window) || + !WINDOW_IS_TOPLEVEL (window)) + return; + + impl = GDK_SURFACE_IMPL_QUARTZ (window->impl); + + if (GDK_SURFACE_IS_MAPPED (window)) + { + GDK_QUARTZ_ALLOC_POOL; + + if (impl->toplevel) + [impl->toplevel miniaturize:nil]; + + GDK_QUARTZ_RELEASE_POOL; + } + else + { + gdk_synthesize_window_state (window, + 0, + GDK_SURFACE_STATE_ICONIFIED); + } +} + +static void +gdk_quartz_surface_deiconify (GdkSurface *window) +{ + GdkSurfaceImplQuartz *impl; + + if (GDK_SURFACE_DESTROYED (window) || + !WINDOW_IS_TOPLEVEL (window)) + return; + + impl = GDK_SURFACE_IMPL_QUARTZ (window->impl); + + if (GDK_SURFACE_IS_MAPPED (window)) + { + GDK_QUARTZ_ALLOC_POOL; + + if (impl->toplevel) + [impl->toplevel deminiaturize:nil]; + + GDK_QUARTZ_RELEASE_POOL; + } + else + { + gdk_synthesize_window_state (window, + GDK_SURFACE_STATE_ICONIFIED, + 0); + } +} + +#ifdef AVAILABLE_MAC_OS_X_VERSION_10_7_AND_LATER + +static gboolean +window_is_fullscreen (GdkSurface *window) +{ + GdkSurfaceImplQuartz *impl = GDK_SURFACE_IMPL_QUARTZ (window->impl); + + return ([impl->toplevel styleMask] & NSFullScreenWindowMask) != 0; +} + +static void +gdk_quartz_surface_fullscreen (GdkSurface *window) +{ + GdkSurfaceImplQuartz *impl; + + if (GDK_SURFACE_DESTROYED (window) || + !WINDOW_IS_TOPLEVEL (window)) + return; + + impl = GDK_SURFACE_IMPL_QUARTZ (window->impl); + + if (!window_is_fullscreen (window)) + [impl->toplevel toggleFullScreen:nil]; +} + +static void +gdk_quartz_surface_unfullscreen (GdkSurface *window) +{ + GdkSurfaceImplQuartz *impl; + + if (GDK_SURFACE_DESTROYED (window) || + !WINDOW_IS_TOPLEVEL (window)) + return; + + impl = GDK_SURFACE_IMPL_QUARTZ (window->impl); + + if (window_is_fullscreen (window)) + [impl->toplevel toggleFullScreen:nil]; +} + +void +_gdk_quartz_surface_update_fullscreen_state (GdkSurface *window) +{ + gboolean is_fullscreen; + gboolean was_fullscreen; + + is_fullscreen = window_is_fullscreen (window); + was_fullscreen = (gdk_surface_get_state (window) & GDK_SURFACE_STATE_FULLSCREEN) != 0; + + if (is_fullscreen != was_fullscreen) + { + if (is_fullscreen) + gdk_synthesize_window_state (window, 0, GDK_SURFACE_STATE_FULLSCREEN); + else + gdk_synthesize_window_state (window, GDK_SURFACE_STATE_FULLSCREEN, 0); + } +} + +#else + +static FullscreenSavedGeometry * +get_fullscreen_geometry (GdkSurface *window) +{ + return g_object_get_data (G_OBJECT (window), FULLSCREEN_DATA); +} + +static void +gdk_quartz_surface_fullscreen (GdkSurface *window) +{ + FullscreenSavedGeometry *geometry; + GdkSurfaceImplQuartz *impl = GDK_SURFACE_IMPL_QUARTZ (window->impl); + NSRect frame; + + if (GDK_SURFACE_DESTROYED (window) || + !WINDOW_IS_TOPLEVEL (window)) + return; + + geometry = get_fullscreen_geometry (window); + if (!geometry) + { + geometry = g_new (FullscreenSavedGeometry, 1); + + geometry->x = window->x; + geometry->y = window->y; + geometry->width = window->width; + geometry->height = window->height; + + if (!gdk_surface_get_decorations (window, &geometry->decor)) + geometry->decor = GDK_DECOR_ALL; + + g_object_set_data_full (G_OBJECT (window), + FULLSCREEN_DATA, geometry, + g_free); + + gdk_surface_set_decorations (window, 0); + + frame = [[impl->toplevel screen] frame]; + move_resize_window_internal (window, + 0, 0, + frame.size.width, frame.size.height); + [impl->toplevel setContentSize:frame.size]; + [impl->toplevel makeKeyAndOrderFront:impl->toplevel]; + + clear_toplevel_order (); + } + + SetSystemUIMode (kUIModeAllHidden, kUIOptionAutoShowMenuBar); + + gdk_synthesize_window_state (window, 0, GDK_SURFACE_STATE_FULLSCREEN); +} + +static void +gdk_quartz_surface_unfullscreen (GdkSurface *window) +{ + GdkSurfaceImplQuartz *impl = GDK_SURFACE_IMPL_QUARTZ (window->impl); + FullscreenSavedGeometry *geometry; + + if (GDK_SURFACE_DESTROYED (window) || + !WINDOW_IS_TOPLEVEL (window)) + return; + + geometry = get_fullscreen_geometry (window); + if (geometry) + { + SetSystemUIMode (kUIModeNormal, 0); + + move_resize_window_internal (window, + geometry->x, + geometry->y, + geometry->width, + geometry->height); + + gdk_surface_set_decorations (window, geometry->decor); + + g_object_set_data (G_OBJECT (window), FULLSCREEN_DATA, NULL); + + [impl->toplevel makeKeyAndOrderFront:impl->toplevel]; + clear_toplevel_order (); + + gdk_synthesize_window_state (window, GDK_SURFACE_STATE_FULLSCREEN, 0); + } +} + +#endif + +static void +gdk_quartz_surface_set_keep_above (GdkSurface *window, + gboolean setting) +{ + GdkSurfaceImplQuartz *impl = GDK_SURFACE_IMPL_QUARTZ (window->impl); + gint level; + + g_return_if_fail (GDK_IS_SURFACE (window)); + + if (GDK_SURFACE_DESTROYED (window) || + !WINDOW_IS_TOPLEVEL (window)) + return; + + level = window_type_hint_to_level (gdk_surface_get_type_hint (window)); + + /* Adjust normal window level by one if necessary. */ + [impl->toplevel setLevel: level + (setting ? 1 : 0)]; +} + +static void +gdk_quartz_surface_set_keep_below (GdkSurface *window, + gboolean setting) +{ + GdkSurfaceImplQuartz *impl = GDK_SURFACE_IMPL_QUARTZ (window->impl); + gint level; + + g_return_if_fail (GDK_IS_SURFACE (window)); + + if (GDK_SURFACE_DESTROYED (window) || + !WINDOW_IS_TOPLEVEL (window)) + return; + + level = window_type_hint_to_level (gdk_surface_get_type_hint (window)); + + /* Adjust normal window level by one if necessary. */ + [impl->toplevel setLevel: level - (setting ? 1 : 0)]; +} + +static GdkSurface * +gdk_quartz_surface_get_group (GdkSurface *window) +{ + g_return_val_if_fail (GDK_SURFACE_TYPE (window) != GDK_SURFACE_CHILD, NULL); + + if (GDK_SURFACE_DESTROYED (window) || + !WINDOW_IS_TOPLEVEL (window)) + return NULL; + + /* FIXME: Implement */ + + return NULL; +} + +static void +gdk_quartz_surface_set_group (GdkSurface *window, + GdkSurface *leader) +{ + /* FIXME: Implement */ +} + +static void +gdk_quartz_surface_destroy_notify (GdkSurface *window) +{ + check_grab_destroy (window); +} + +static void +gdk_quartz_surface_set_opacity (GdkSurface *window, + gdouble opacity) +{ + GdkSurfaceImplQuartz *impl = GDK_SURFACE_IMPL_QUARTZ (window->impl); + + g_return_if_fail (GDK_IS_SURFACE (window)); + g_return_if_fail (WINDOW_IS_TOPLEVEL (window)); + + if (GDK_SURFACE_DESTROYED (window) || + !WINDOW_IS_TOPLEVEL (window)) + return; + + if (opacity < 0) + opacity = 0; + else if (opacity > 1) + opacity = 1; + + [impl->toplevel setAlphaValue: opacity]; +} + +static void +gdk_quartz_surface_set_shadow_width (GdkSurface *window, + gint left, + gint right, + gint top, + gint bottom) +{ + GdkSurfaceImplQuartz *impl = GDK_SURFACE_IMPL_QUARTZ (window->impl); + + g_return_if_fail (GDK_IS_SURFACE (window)); + g_return_if_fail (WINDOW_IS_TOPLEVEL (window)); + + if (GDK_SURFACE_DESTROYED (window) || + !WINDOW_IS_TOPLEVEL (window)) + return; + + impl->shadow_top = top; + impl->shadow_max = MAX (MAX (left, right), MAX (top, bottom)); + _gdk_quartz_surface_update_has_shadow (impl); +} + +/* Protocol to build cleanly for OSX < 10.7 */ +@protocol ScaleFactor +- (CGFloat) backingScaleFactor; +@end + +static gint +gdk_quartz_surface_get_scale_factor (GdkSurface *window) +{ + GdkSurfaceImplQuartz *impl; + + if (GDK_SURFACE_DESTROYED (window)) + return 1; + + impl = GDK_SURFACE_IMPL_QUARTZ (window->impl); + + if (impl->toplevel != NULL && gdk_quartz_osx_version() >= GDK_OSX_LION) + return [(id ) impl->toplevel backingScaleFactor]; + + return 1; +} + +static void +gdk_surface_impl_quartz_class_init (GdkSurfaceImplQuartzClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GdkSurfaceImplClass *impl_class = GDK_SURFACE_IMPL_CLASS (klass); + GdkSurfaceImplQuartzClass *impl_quartz_class = GDK_SURFACE_IMPL_QUARTZ_CLASS (klass); + + parent_class = g_type_class_peek_parent (klass); + + object_class->finalize = gdk_surface_impl_quartz_finalize; + + impl_class->ref_cairo_surface = gdk_quartz_ref_cairo_surface; + impl_class->show = gdk_surface_quartz_show; + impl_class->hide = gdk_surface_quartz_hide; + impl_class->withdraw = gdk_surface_quartz_withdraw; + impl_class->set_events = gdk_surface_quartz_set_events; + impl_class->get_events = gdk_surface_quartz_get_events; + impl_class->raise = gdk_surface_quartz_raise; + impl_class->lower = gdk_surface_quartz_lower; + impl_class->restack_toplevel = gdk_surface_quartz_restack_toplevel; + impl_class->move_resize = gdk_surface_quartz_move_resize; + impl_class->get_geometry = gdk_surface_quartz_get_geometry; + impl_class->get_root_coords = gdk_surface_quartz_get_root_coords; + impl_class->get_device_state = gdk_surface_quartz_get_device_state; + impl_class->shape_combine_region = gdk_surface_quartz_shape_combine_region; + impl_class->input_shape_combine_region = gdk_surface_quartz_input_shape_combine_region; + impl_class->destroy = gdk_quartz_surface_destroy; + impl_class->begin_paint = gdk_surface_impl_quartz_begin_paint; + impl_class->get_scale_factor = gdk_quartz_surface_get_scale_factor; + + impl_class->focus = gdk_quartz_surface_focus; + impl_class->set_type_hint = gdk_quartz_surface_set_type_hint; + impl_class->get_type_hint = gdk_quartz_surface_get_type_hint; + impl_class->set_modal_hint = gdk_quartz_surface_set_modal_hint; + impl_class->set_skip_taskbar_hint = gdk_quartz_surface_set_skip_taskbar_hint; + impl_class->set_skip_pager_hint = gdk_quartz_surface_set_skip_pager_hint; + impl_class->set_urgency_hint = gdk_quartz_surface_set_urgency_hint; + impl_class->set_geometry_hints = gdk_quartz_surface_set_geometry_hints; + impl_class->set_title = gdk_quartz_surface_set_title; + impl_class->set_role = gdk_quartz_surface_set_role; + impl_class->set_startup_id = gdk_quartz_surface_set_startup_id; + impl_class->set_transient_for = gdk_quartz_surface_set_transient_for; + impl_class->get_frame_extents = gdk_quartz_surface_get_frame_extents; + impl_class->set_accept_focus = gdk_quartz_surface_set_accept_focus; + impl_class->set_focus_on_map = gdk_quartz_surface_set_focus_on_map; + impl_class->set_icon_list = gdk_quartz_surface_set_icon_list; + impl_class->set_icon_name = gdk_quartz_surface_set_icon_name; + impl_class->iconify = gdk_quartz_surface_iconify; + impl_class->deiconify = gdk_quartz_surface_deiconify; + impl_class->stick = gdk_quartz_surface_stick; + impl_class->unstick = gdk_quartz_surface_unstick; + impl_class->maximize = gdk_quartz_surface_maximize; + impl_class->unmaximize = gdk_quartz_surface_unmaximize; + impl_class->fullscreen = gdk_quartz_surface_fullscreen; + impl_class->unfullscreen = gdk_quartz_surface_unfullscreen; + impl_class->set_keep_above = gdk_quartz_surface_set_keep_above; + impl_class->set_keep_below = gdk_quartz_surface_set_keep_below; + impl_class->get_group = gdk_quartz_surface_get_group; + impl_class->set_group = gdk_quartz_surface_set_group; + impl_class->set_decorations = gdk_quartz_surface_set_decorations; + impl_class->get_decorations = gdk_quartz_surface_get_decorations; + impl_class->set_functions = gdk_quartz_surface_set_functions; + impl_class->set_functions = gdk_quartz_surface_set_functions; + impl_class->begin_resize_drag = gdk_quartz_surface_begin_resize_drag; + impl_class->begin_move_drag = gdk_quartz_surface_begin_move_drag; + impl_class->set_opacity = gdk_quartz_surface_set_opacity; + impl_class->set_shadow_width = gdk_quartz_surface_set_shadow_width; + impl_class->destroy_notify = gdk_quartz_surface_destroy_notify; + impl_class->register_dnd = _gdk_quartz_surface_register_dnd; + impl_class->drag_begin = _gdk_quartz_surface_drag_begin; + impl_class->process_updates_recurse = _gdk_quartz_surface_process_updates_recurse; + + impl_class->create_gl_context = gdk_quartz_surface_create_gl_context; + + impl_quartz_class->get_context = gdk_surface_impl_quartz_get_context; + impl_quartz_class->release_context = gdk_surface_impl_quartz_release_context; +} + +GType +_gdk_surface_impl_quartz_get_type (void) +{ + static GType object_type = 0; + + if (!object_type) + { + const GTypeInfo object_info = + { + sizeof (GdkSurfaceImplQuartzClass), + (GBaseInitFunc) NULL, + (GBaseFinalizeFunc) NULL, + (GClassInitFunc) gdk_surface_impl_quartz_class_init, + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (GdkSurfaceImplQuartz), + 0, /* n_preallocs */ + (GInstanceInitFunc) gdk_surface_impl_quartz_init, + }; + + object_type = g_type_register_static (GDK_TYPE_SURFACE_IMPL, + "GdkSurfaceImplQuartz", + &object_info, 0); + } + + return object_type; +} + +CGContextRef +gdk_quartz_surface_get_context (GdkSurfaceImplQuartz *window, + gboolean antialias) +{ + if (!GDK_SURFACE_IMPL_QUARTZ_GET_CLASS (window)->get_context) + { + g_warning ("%s doesn't implement GdkSurfaceImplQuartzClass::get_context()", + G_OBJECT_TYPE_NAME (window)); + return NULL; + } + + return GDK_SURFACE_IMPL_QUARTZ_GET_CLASS (window)->get_context (window, antialias); +} + +void +gdk_quartz_surface_release_context (GdkSurfaceImplQuartz *window, + CGContextRef cg_context) +{ + if (!GDK_SURFACE_IMPL_QUARTZ_GET_CLASS (window)->release_context) + { + g_warning ("%s doesn't implement GdkSurfaceImplQuartzClass::release_context()", + G_OBJECT_TYPE_NAME (window)); + return; + } + + GDK_SURFACE_IMPL_QUARTZ_GET_CLASS (window)->release_context (window, cg_context); +} + + + +static CGContextRef +gdk_root_window_impl_quartz_get_context (GdkSurfaceImplQuartz *window, + gboolean antialias) +{ + CGColorSpaceRef colorspace; + CGContextRef cg_context; + GdkSurfaceImplQuartz *window_impl = GDK_SURFACE_IMPL_QUARTZ (window); + + if (GDK_SURFACE_DESTROYED (window_impl->wrapper)) + return NULL; + + /* We do not have the notion of a root window on OS X. We fake this + * by creating a 1x1 bitmap and return a context to that. + */ + colorspace = CGColorSpaceCreateWithName (kCGColorSpaceGenericRGB); + cg_context = CGBitmapContextCreate (NULL, + 1, 1, 8, 4, colorspace, + kCGImageAlphaPremultipliedLast); + CGColorSpaceRelease (colorspace); + + return cg_context; +} + +static void +gdk_root_window_impl_quartz_release_context (GdkSurfaceImplQuartz *window, + CGContextRef cg_context) +{ + CGContextRelease (cg_context); +} + +static void +gdk_root_window_impl_quartz_class_init (GdkRootWindowImplQuartzClass *klass) +{ + GdkSurfaceImplQuartzClass *window_quartz_class = GDK_SURFACE_IMPL_QUARTZ_CLASS (klass); + + root_window_parent_class = g_type_class_peek_parent (klass); + + window_quartz_class->get_context = gdk_root_window_impl_quartz_get_context; + window_quartz_class->release_context = gdk_root_window_impl_quartz_release_context; +} + +static void +gdk_root_window_impl_quartz_init (GdkRootWindowImplQuartz *impl) +{ +} + +GType +_gdk_root_window_impl_quartz_get_type (void) +{ + static GType object_type = 0; + + if (!object_type) + { + const GTypeInfo object_info = + { + sizeof (GdkRootWindowImplQuartzClass), + (GBaseInitFunc) NULL, + (GBaseFinalizeFunc) NULL, + (GClassInitFunc) gdk_root_window_impl_quartz_class_init, + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (GdkRootWindowImplQuartz), + 0, /* n_preallocs */ + (GInstanceInitFunc) gdk_root_window_impl_quartz_init, + }; + + object_type = g_type_register_static (GDK_TYPE_SURFACE_IMPL_QUARTZ, + "GdkRootWindowQuartz", + &object_info, 0); + } + + return object_type; +} + +GList * +get_toplevels (void) +{ + update_toplevel_order (); + return GDK_SURFACE_IMPL_QUARTZ (_gdk_root->impl)->sorted_children; +} diff --git a/gdk/quartz/gdksurface-quartz.h b/gdk/quartz/gdksurface-quartz.h new file mode 100644 index 0000000000..ca4dd0d249 --- /dev/null +++ b/gdk/quartz/gdksurface-quartz.h @@ -0,0 +1,115 @@ +/* gdkdrawable-quartz.h + * + * Copyright (C) 2005 Imendio AB + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * 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, see . + */ + +#ifndef __GDK_SURFACE_QUARTZ_H__ +#define __GDK_SURFACE_QUARTZ_H__ + +#import +#import +#include "gdk/gdksurfaceimpl.h" + +G_BEGIN_DECLS + +/* Window implementation for Quartz + */ + +typedef struct _GdkSurfaceImplQuartz GdkSurfaceImplQuartz; +typedef struct _GdkSurfaceImplQuartzClass GdkSurfaceImplQuartzClass; + +#define GDK_TYPE_SURFACE_IMPL_QUARTZ (_gdk_surface_impl_quartz_get_type ()) +#define GDK_SURFACE_IMPL_QUARTZ(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), GDK_TYPE_SURFACE_IMPL_QUARTZ, GdkSurfaceImplQuartz)) +#define GDK_SURFACE_IMPL_QUARTZ_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GDK_TYPE_SURFACE_IMPL_QUARTZ, GdkSurfaceImplQuartzClass)) +#define GDK_IS_SURFACE_IMPL_QUARTZ(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), GDK_TYPE_SURFACE_IMPL_QUARTZ)) +#define GDK_IS_SURFACE_IMPL_QUARTZ_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GDK_TYPE_SURFACE_IMPL_QUARTZ)) +#define GDK_SURFACE_IMPL_QUARTZ_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GDK_TYPE_SURFACE_IMPL_QUARTZ, GdkSurfaceImplQuartzClass)) + +struct _GdkSurfaceImplQuartz +{ + GdkSurfaceImpl parent_instance; + + GdkSurface *wrapper; + + NSWindow *toplevel; + NSTrackingRectTag tracking_rect; + GdkQuartzView *view; + + GdkSurfaceTypeHint type_hint; + + gint in_paint_rect_count; + + GdkSurface *transient_for; + + /* Sorted by z-order */ + GList *sorted_children; + + cairo_region_t *needs_display_region; + + cairo_surface_t *cairo_surface; + + gint shadow_top; + + gint shadow_max; +}; + +struct _GdkSurfaceImplQuartzClass +{ + GdkSurfaceImplClass parent_class; + + CGContextRef (* get_context) (GdkSurfaceImplQuartz *window, + gboolean antialias); + void (* release_context) (GdkSurfaceImplQuartz *window, + CGContextRef cg_context); +}; + +GType _gdk_surface_impl_quartz_get_type (void); + +CGContextRef gdk_quartz_surface_get_context (GdkSurfaceImplQuartz *window, + gboolean antialias); +void gdk_quartz_surface_release_context (GdkSurfaceImplQuartz *window, + CGContextRef context); + +/* Root window implementation for Quartz + */ + +typedef struct _GdkRootWindowImplQuartz GdkRootWindowImplQuartz; +typedef struct _GdkRootWindowImplQuartzClass GdkRootWindowImplQuartzClass; + +#define GDK_TYPE_ROOT_WINDOW_IMPL_QUARTZ (_gdk_root_window_impl_quartz_get_type ()) +#define GDK_ROOT_WINDOW_IMPL_QUARTZ(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), GDK_TYPE_ROOT_WINDOW_IMPL_QUARTZ, GdkRootWindowImplQuartz)) +#define GDK_ROOT_WINDOW_IMPL_QUARTZ_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GDK_TYPE_ROOT_WINDOW_IMPL_QUARTZ, GdkRootWindowImplQuartzClass)) +#define GDK_IS_ROOT_WINDOW_IMPL_QUARTZ(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), GDK_TYPE_ROOT_WINDOW_IMPL_QUARTZ)) +#define GDK_IS_ROOT_WINDOW_IMPL_QUARTZ_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GDK_TYPE_ROOT_WINDOW_IMPL_QUARTZ)) +#define GDK_ROOT_WINDOW_IMPL_QUARTZ_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GDK_TYPE_ROOT_WINDOW_IMPL_QUARTZ, GdkRootWindowImplQuartzClass)) + +struct _GdkRootWindowImplQuartz +{ + GdkSurfaceImplQuartz parent_instance; +}; + +struct _GdkRootWindowImplQuartzClass +{ + GdkSurfaceImplQuartzClass parent_class; +}; + +GType _gdk_root_window_impl_quartz_get_type (void); + +GList *get_toplevels (void); + +G_END_DECLS + +#endif /* __GDK_SURFACE_QUARTZ_H__ */ diff --git a/gdk/quartz/gdkwindow-quartz.c b/gdk/quartz/gdkwindow-quartz.c deleted file mode 100644 index 170950a7da..0000000000 --- a/gdk/quartz/gdkwindow-quartz.c +++ /dev/null @@ -1,2958 +0,0 @@ -/* gdkwindow-quartz.c - * - * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald - * Copyright (C) 2005-2007 Imendio AB - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * 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, see . - */ - -#include "config.h" - -#include -#include -#include - -#include "gdkwindowimpl.h" -#include "gdkprivate-quartz.h" -#include "gdkglcontext-quartz.h" -#include "gdkquartzscreen.h" -#include "gdkquartzcursor.h" - -#include -#include - -#include -#include - -static gpointer parent_class; -static gpointer root_window_parent_class; - -static GSList *update_nswindows; -static gboolean in_process_all_updates = FALSE; - -static GSList *main_window_stack; - -void _gdk_quartz_surface_flush (GdkSurfaceImplQuartz *window_impl); - -typedef struct -{ - gint x, y; - gint width, height; - GdkWMDecoration decor; -} FullscreenSavedGeometry; - - -#ifndef AVAILABLE_MAC_OS_X_VERSION_10_7_AND_LATER -static FullscreenSavedGeometry *get_fullscreen_geometry (GdkSurface *window); -#endif - -#define FULLSCREEN_DATA "fullscreen-data" - -static void update_toplevel_order (void); -static void clear_toplevel_order (void); - -#define WINDOW_IS_TOPLEVEL(window) (GDK_SURFACE_TYPE (window) != GDK_SURFACE_CHILD) - -/* - * GdkQuartzSurface - */ - -struct _GdkQuartzSurface -{ - GdkSurface parent; -}; - -struct _GdkQuartzSurfaceClass -{ - GdkSurfaceClass parent_class; -}; - -G_DEFINE_TYPE (GdkQuartzSurface, gdk_quartz_surface, GDK_TYPE_SURFACE); - -static void -gdk_quartz_surface_class_init (GdkQuartzSurfaceClass *quartz_surface_class) -{ -} - -static void -gdk_quartz_surface_init (GdkQuartzSurface *quartz_surface) -{ -} - - -/* - * GdkQuartzSurfaceImpl - */ - -NSView * -gdk_quartz_surface_get_nsview (GdkSurface *window) -{ - if (GDK_SURFACE_DESTROYED (window)) - return NULL; - - return ((GdkSurfaceImplQuartz *)window->impl)->view; -} - -NSWindow * -gdk_quartz_surface_get_nswindow (GdkSurface *window) -{ - if (GDK_SURFACE_DESTROYED (window)) - return NULL; - - return ((GdkSurfaceImplQuartz *)window->impl)->toplevel; -} - -static CGContextRef -gdk_surface_impl_quartz_get_context (GdkSurfaceImplQuartz *window_impl, - gboolean antialias) -{ - CGContextRef cg_context; - CGSize scale; - - if (GDK_SURFACE_DESTROYED (window_impl->wrapper)) - return NULL; - - /* Lock focus when not called as part of a drawRect call. This - * is needed when called from outside "real" expose events, for - * example for synthesized expose events when realizing windows - * and for widgets that send fake expose events like the arrow - * buttons in spinbuttons or the position marker in rulers. - */ - if (window_impl->in_paint_rect_count == 0) - { - if (![window_impl->view lockFocusIfCanDraw]) - return NULL; - } - - cg_context = [[NSGraphicsContext currentContext] graphicsPort]; - CGContextSaveGState (cg_context); - CGContextSetAllowsAntialiasing (cg_context, antialias); - - /* Undo the default scaling transform, since we apply our own - * in gdk_quartz_ref_cairo_surface () */ - scale = CGContextConvertSizeToDeviceSpace (cg_context, - CGSizeMake (1.0, 1.0)); - CGContextScaleCTM (cg_context, 1.0 / scale.width, 1.0 / scale.height); - - return cg_context; -} - -static void -gdk_surface_impl_quartz_release_context (GdkSurfaceImplQuartz *window_impl, - CGContextRef cg_context) -{ - CGContextRestoreGState (cg_context); - CGContextSetAllowsAntialiasing (cg_context, TRUE); - - /* See comment in gdk_quartz_surface_get_context(). */ - if (window_impl->in_paint_rect_count == 0) - { - _gdk_quartz_surface_flush (window_impl); - [window_impl->view unlockFocus]; - } -} - -static void -check_grab_destroy (GdkSurface *window) -{ - GList *devices = NULL, *l; - GdkDisplay *display = gdk_surface_get_display (window); - GdkSeat *seat; - - seat = gdk_display_get_default_seat (display); - - devices = g_list_prepend (devices, gdk_seat_get_keyboard (seat)); - devices = g_list_prepend (devices, gdk_seat_get_pointer (seat)); - - for (l = devices; l; l = l->next) - { - GdkDeviceGrabInfo *grab; - - grab = _gdk_display_get_last_device_grab (display, l->data); - if (grab && grab->native_window == window) - { - /* Serials are always 0 in quartz, but for clarity: */ - grab->serial_end = grab->serial_start; - grab->implicit_ungrab = TRUE; - } - } - - g_list_free (devices); -} - -static void -gdk_surface_impl_quartz_finalize (GObject *object) -{ - GdkSurfaceImplQuartz *impl = GDK_SURFACE_IMPL_QUARTZ (object); - - check_grab_destroy (GDK_SURFACE_IMPL_QUARTZ (object)->wrapper); - - if (impl->transient_for) - g_object_unref (impl->transient_for); - - G_OBJECT_CLASS (parent_class)->finalize (object); -} - -/* Help preventing "beam sync penalty" where CG makes all graphics code - * block until the next vsync if we try to flush (including call display on - * a view) too often. We do this by limiting the manual flushing done - * outside of expose calls to less than some frequency when measured over - * the last 4 flushes. This is a bit arbitray, but seems to make it possible - * for some quick manual flushes (such as gtkruler or gimp’s marching ants) - * without hitting the max flush frequency. - * - * If drawable NULL, no flushing is done, only registering that a flush was - * done externally. - */ -void -_gdk_quartz_surface_flush (GdkSurfaceImplQuartz *window_impl) -{ - static struct timeval prev_tv; - static gint intervals[4]; - static gint index; - struct timeval tv; - gint ms; - - gettimeofday (&tv, NULL); - ms = (tv.tv_sec - prev_tv.tv_sec) * 1000 + (tv.tv_usec - prev_tv.tv_usec) / 1000; - intervals[index++ % 4] = ms; - - if (window_impl) - { - ms = intervals[0] + intervals[1] + intervals[2] + intervals[3]; - - /* ~25Hz on average. */ - if (ms > 4*40) - { - if (window_impl) - [window_impl->toplevel flushWindow]; - - prev_tv = tv; - } - } - else - prev_tv = tv; -} - -static cairo_user_data_key_t gdk_quartz_cairo_key; - -typedef struct { - GdkSurfaceImplQuartz *window_impl; - CGContextRef cg_context; -} GdkQuartzCairoSurfaceData; - -static void -gdk_quartz_cairo_surface_destroy (void *data) -{ - GdkQuartzCairoSurfaceData *surface_data = data; - - surface_data->window_impl->cairo_surface = NULL; - - gdk_quartz_surface_release_context (surface_data->window_impl, - surface_data->cg_context); - - g_free (surface_data); -} - -static cairo_surface_t * -gdk_quartz_create_cairo_surface (GdkSurfaceImplQuartz *impl, - int width, - int height) -{ - CGContextRef cg_context; - GdkQuartzCairoSurfaceData *surface_data; - cairo_surface_t *surface; - - cg_context = gdk_quartz_surface_get_context (impl, TRUE); - - if (!cg_context) - return NULL; - - surface_data = g_new (GdkQuartzCairoSurfaceData, 1); - surface_data->window_impl = impl; - surface_data->cg_context = cg_context; - - surface = cairo_quartz_surface_create_for_cg_context (cg_context, - width, height); - - cairo_surface_set_user_data (surface, &gdk_quartz_cairo_key, - surface_data, - gdk_quartz_cairo_surface_destroy); - - return surface; -} - -static cairo_surface_t * -gdk_quartz_ref_cairo_surface (GdkSurface *window) -{ - GdkSurfaceImplQuartz *impl = GDK_SURFACE_IMPL_QUARTZ (window->impl); - - if (GDK_SURFACE_DESTROYED (window)) - return NULL; - - if (!impl->cairo_surface) - { - gint scale = gdk_surface_get_scale_factor (impl->wrapper); - - impl->cairo_surface = - gdk_quartz_create_cairo_surface (impl, - gdk_surface_get_width (impl->wrapper) * scale, - gdk_surface_get_height (impl->wrapper) * scale); - - cairo_surface_set_device_scale (impl->cairo_surface, scale, scale); - } - else - cairo_surface_reference (impl->cairo_surface); - - return impl->cairo_surface; -} - -static void -gdk_surface_impl_quartz_init (GdkSurfaceImplQuartz *impl) -{ - impl->type_hint = GDK_SURFACE_TYPE_HINT_NORMAL; -} - -static gboolean -gdk_surface_impl_quartz_begin_paint (GdkSurface *window) -{ - return FALSE; -} - -static void -gdk_quartz_surface_set_needs_display_in_region (GdkSurface *window, - cairo_region_t *region) -{ - GdkSurfaceImplQuartz *impl; - int i, n_rects; - - impl = GDK_SURFACE_IMPL_QUARTZ (window->impl); - - if (!impl->needs_display_region) - impl->needs_display_region = cairo_region_create (); - - cairo_region_union (impl->needs_display_region, region); - - n_rects = cairo_region_num_rectangles (region); - for (i = 0; i < n_rects; i++) - { - cairo_rectangle_int_t rect; - cairo_region_get_rectangle (region, i, &rect); - [impl->view setNeedsDisplayInRect:NSMakeRect (rect.x, rect.y, - rect.width, rect.height)]; - } -} - -void -_gdk_quartz_surface_process_updates_recurse (GdkSurface *window, - cairo_region_t *region) -{ - /* Make sure to only flush each toplevel at most once if we're called - * from process_all_updates. - */ - if (in_process_all_updates) - { - GdkSurface *toplevel; - - toplevel = gdk_surface_get_toplevel (window); - if (toplevel && WINDOW_IS_TOPLEVEL (toplevel)) - { - GdkSurfaceImplQuartz *toplevel_impl; - NSWindow *nswindow; - - toplevel_impl = (GdkSurfaceImplQuartz *)toplevel->impl; - nswindow = toplevel_impl->toplevel; - - /* In theory, we could skip the flush disabling, since we only - * have one NSView. - */ - if (nswindow && ![nswindow isFlushWindowDisabled]) - { - [nswindow retain]; - [nswindow disableFlushWindow]; - update_nswindows = g_slist_prepend (update_nswindows, nswindow); - } - } - } - - if (WINDOW_IS_TOPLEVEL (window)) - gdk_quartz_surface_set_needs_display_in_region (window, region); - else - _gdk_surface_process_updates_recurse (window, region); - - /* NOTE: I'm not sure if we should displayIfNeeded here. It slows down a - * lot (since it triggers the beam syncing) and things seem to work - * without it. - */ -} - -static const gchar * -get_default_title (void) -{ - const char *title; - - title = g_get_application_name (); - if (!title) - title = g_get_prgname (); - - return title; -} - -static void -get_ancestor_coordinates_from_child (GdkSurface *child_window, - gint child_x, - gint child_y, - GdkSurface *ancestor_window, - gint *ancestor_x, - gint *ancestor_y) -{ - while (child_window != ancestor_window) - { - child_x += child_window->x; - child_y += child_window->y; - - child_window = child_window->parent; - } - - *ancestor_x = child_x; - *ancestor_y = child_y; -} - -void -_gdk_quartz_surface_debug_highlight (GdkSurface *window, gint number) -{ - gint x, y; - gint gx, gy; - GdkSurface *toplevel; - gint tx, ty; - static NSWindow *debug_window[10]; - static NSRect old_rect[10]; - NSRect rect; - NSColor *color; - - g_return_if_fail (number >= 0 && number <= 9); - - if (window == _gdk_root) - return; - - if (window == NULL) - { - if (debug_window[number]) - [debug_window[number] close]; - debug_window[number] = NULL; - - return; - } - - toplevel = gdk_surface_get_toplevel (window); - get_ancestor_coordinates_from_child (window, 0, 0, toplevel, &x, &y); - - gdk_surface_get_origin (toplevel, &tx, &ty); - x += tx; - y += ty; - - _gdk_quartz_surface_gdk_xy_to_xy (x, y + window->height, - &gx, &gy); - - rect = NSMakeRect (gx, gy, window->width, window->height); - - if (debug_window[number] && NSEqualRects (rect, old_rect[number])) - return; - - old_rect[number] = rect; - - if (debug_window[number]) - [debug_window[number] close]; - - debug_window[number] = [[NSWindow alloc] initWithContentRect:rect - styleMask:NSBorderlessWindowMask - backing:NSBackingStoreBuffered - defer:NO]; - - switch (number) - { - case 0: - color = [NSColor redColor]; - break; - case 1: - color = [NSColor blueColor]; - break; - case 2: - color = [NSColor greenColor]; - break; - case 3: - color = [NSColor yellowColor]; - break; - case 4: - color = [NSColor brownColor]; - break; - case 5: - color = [NSColor purpleColor]; - break; - default: - color = [NSColor blackColor]; - break; - } - - [debug_window[number] setBackgroundColor:color]; - [debug_window[number] setAlphaValue:0.4]; - [debug_window[number] setOpaque:NO]; - [debug_window[number] setReleasedWhenClosed:YES]; - [debug_window[number] setIgnoresMouseEvents:YES]; - [debug_window[number] setLevel:NSFloatingWindowLevel]; - - [debug_window[number] orderFront:nil]; -} - -gboolean -_gdk_quartz_surface_is_ancestor (GdkSurface *ancestor, - GdkSurface *window) -{ - if (ancestor == NULL || window == NULL) - return FALSE; - - return (gdk_surface_get_parent (window) == ancestor || - _gdk_quartz_surface_is_ancestor (ancestor, - gdk_surface_get_parent (window))); -} - - -/* See notes on top of gdkscreen-quartz.c */ -void -_gdk_quartz_surface_gdk_xy_to_xy (gint gdk_x, - gint gdk_y, - gint *ns_x, - gint *ns_y) -{ - GdkQuartzScreen *screen_quartz = GDK_QUARTZ_SCREEN (_gdk_screen); - - if (ns_y) - *ns_y = screen_quartz->height - gdk_y + screen_quartz->min_y; - - if (ns_x) - *ns_x = gdk_x + screen_quartz->min_x; -} - -void -_gdk_quartz_surface_xy_to_gdk_xy (gint ns_x, - gint ns_y, - gint *gdk_x, - gint *gdk_y) -{ - GdkQuartzScreen *screen_quartz = GDK_QUARTZ_SCREEN (_gdk_screen); - - if (gdk_y) - *gdk_y = screen_quartz->height - ns_y + screen_quartz->min_y; - - if (gdk_x) - *gdk_x = ns_x - screen_quartz->min_x; -} - -void -_gdk_quartz_surface_nspoint_to_gdk_xy (NSPoint point, - gint *x, - gint *y) -{ - _gdk_quartz_surface_xy_to_gdk_xy (point.x, point.y, - x, y); -} - -static GdkSurface * -find_child_window_helper (GdkSurface *window, - gint x, - gint y, - gint x_offset, - gint y_offset, - gboolean get_toplevel) -{ - GdkSurfaceImplQuartz *impl; - GList *l; - - impl = GDK_SURFACE_IMPL_QUARTZ (window->impl); - - if (window == _gdk_root) - update_toplevel_order (); - - for (l = impl->sorted_children; l; l = l->next) - { - GdkSurface *child = l->data; - GdkSurfaceImplQuartz *child_impl = GDK_SURFACE_IMPL_QUARTZ (child->impl); - int temp_x, temp_y; - - if (!GDK_SURFACE_IS_MAPPED (child)) - continue; - - temp_x = x_offset + child->x; - temp_y = y_offset + child->y; - - /* Special-case the root window. We have to include the title - * bar in the checks, otherwise the window below the title bar - * will be found i.e. events punch through. (If we can find a - * better way to deal with the events in gdkevents-quartz, this - * might not be needed.) - */ - if (window == _gdk_root) - { - NSRect frame = NSMakeRect (0, 0, 100, 100); - NSRect content; - NSUInteger mask; - int titlebar_height; - - mask = [child_impl->toplevel styleMask]; - - /* Get the title bar height. */ - content = [NSWindow contentRectForFrameRect:frame - styleMask:mask]; - titlebar_height = frame.size.height - content.size.height; - - if (titlebar_height > 0 && - x >= temp_x && y >= temp_y - titlebar_height && - x < temp_x + child->width && y < temp_y) - { - /* The root means "unknown" i.e. a window not managed by - * GDK. - */ - return (GdkSurface *)_gdk_root; - } - } - - if ((!get_toplevel || (get_toplevel && window == _gdk_root)) && - x >= temp_x && y >= temp_y && - x < temp_x + child->width && y < temp_y + child->height) - { - /* Look for child windows. */ - return find_child_window_helper (l->data, - x, y, - temp_x, temp_y, - get_toplevel); - } - } - - return window; -} - -/* Given a GdkSurface and coordinates relative to it, returns the - * innermost subwindow that contains the point. If the coordinates are - * outside the passed in window, NULL is returned. - */ -GdkSurface * -_gdk_quartz_surface_find_child (GdkSurface *window, - gint x, - gint y, - gboolean get_toplevel) -{ - if (x >= 0 && y >= 0 && x < window->width && y < window->height) - return find_child_window_helper (window, x, y, 0, 0, get_toplevel); - - return NULL; -} - - -void -_gdk_quartz_surface_did_become_main (GdkSurface *window) -{ - main_window_stack = g_slist_remove (main_window_stack, window); - - if (window->window_type != GDK_SURFACE_TEMP) - main_window_stack = g_slist_prepend (main_window_stack, window); - - clear_toplevel_order (); -} - -void -_gdk_quartz_surface_did_resign_main (GdkSurface *window) -{ - GdkSurface *new_window = NULL; - - if (main_window_stack) - new_window = main_window_stack->data; - else - { - GList *toplevels; - - toplevels = get_toplevels (); - if (toplevels) - new_window = toplevels->data; - } - - if (new_window && - new_window != window && - GDK_SURFACE_IS_MAPPED (new_window) && - WINDOW_IS_TOPLEVEL (new_window)) - { - GdkSurfaceImplQuartz *impl = GDK_SURFACE_IMPL_QUARTZ (new_window->impl); - - [impl->toplevel makeKeyAndOrderFront:impl->toplevel]; - } - - clear_toplevel_order (); -} - -static NSScreen * -get_nsscreen_for_point (gint x, gint y) -{ - int i; - NSArray *screens; - NSScreen *screen = NULL; - - GDK_QUARTZ_ALLOC_POOL; - - screens = [NSScreen screens]; - - for (i = 0; i < [screens count]; i++) - { - NSRect rect = [[screens objectAtIndex:i] frame]; - - if (x >= rect.origin.x && x <= rect.origin.x + rect.size.width && - y >= rect.origin.y && y <= rect.origin.y + rect.size.height) - { - screen = [screens objectAtIndex:i]; - break; - } - } - - GDK_QUARTZ_RELEASE_POOL; - - return screen; -} - -void -_gdk_quartz_display_create_window_impl (GdkDisplay *display, - GdkSurface *window, - GdkSurface *real_parent, - GdkEventMask event_mask, - GdkSurfaceAttr *attributes) -{ - GdkSurfaceImplQuartz *impl; - GdkSurfaceImplQuartz *parent_impl; - - GDK_QUARTZ_ALLOC_POOL; - - impl = g_object_new (GDK_TYPE_SURFACE_IMPL_QUARTZ, NULL); - window->impl = GDK_SURFACE_IMPL (impl); - impl->wrapper = window; - - parent_impl = GDK_SURFACE_IMPL_QUARTZ (window->parent->impl); - - switch (window->window_type) - { - case GDK_SURFACE_TOPLEVEL: - case GDK_SURFACE_TEMP: - if (GDK_SURFACE_TYPE (window->parent) != GDK_SURFACE_ROOT) - { - /* The common code warns for this case */ - parent_impl = GDK_SURFACE_IMPL_QUARTZ (_gdk_root->impl); - } - } - - /* Maintain the z-ordered list of children. */ - if (window->parent != _gdk_root) - parent_impl->sorted_children = g_list_prepend (parent_impl->sorted_children, window); - else - clear_toplevel_order (); - - impl->view = NULL; - - switch (window->window_type) - { - case GDK_SURFACE_TOPLEVEL: - case GDK_SURFACE_TEMP: - { - NSScreen *screen; - NSRect screen_rect; - NSRect content_rect; - NSUInteger style_mask; - int nx, ny; - - /* initWithContentRect will place on the mainScreen by default. - * We want to select the screen to place on ourselves. We need - * to find the screen the window will be on and correct the - * content_rect coordinates to be relative to that screen. - */ - _gdk_quartz_surface_gdk_xy_to_xy (window->x, window->y, &nx, &ny); - - screen = get_nsscreen_for_point (nx, ny); - screen_rect = [screen frame]; - nx -= screen_rect.origin.x; - ny -= screen_rect.origin.y; - - content_rect = NSMakeRect (nx, ny - window->height, - window->width, - window->height); - - if (window->window_type == GDK_SURFACE_TEMP) - { - style_mask = NSBorderlessWindowMask; - } - else - { - style_mask = (NSTitledWindowMask | - NSClosableWindowMask | - NSMiniaturizableWindowMask | - NSResizableWindowMask); - } - - impl->toplevel = [[GdkQuartzNSWindow alloc] initWithContentRect:content_rect - styleMask:style_mask - backing:NSBackingStoreBuffered - defer:NO - screen:screen]; - - gdk_surface_set_title (window, get_default_title ()); - - [impl->toplevel setOpaque:NO]; - [impl->toplevel setBackgroundColor:[NSColor clearColor]]; - - content_rect.origin.x = 0; - content_rect.origin.y = 0; - - impl->view = [[GdkQuartzView alloc] initWithFrame:content_rect]; - [impl->view setGdkSurface:window]; - [impl->toplevel setContentView:impl->view]; - [impl->view release]; - } - break; - - case GDK_SURFACE_CHILD: - { - GdkSurfaceImplQuartz *parent_impl = GDK_SURFACE_IMPL_QUARTZ (window->parent->impl); - - if (!window->input_only) - { - NSRect frame_rect = NSMakeRect (window->x + window->parent->abs_x, - window->y + window->parent->abs_y, - window->width, - window->height); - - impl->view = [[GdkQuartzView alloc] initWithFrame:frame_rect]; - - [impl->view setGdkSurface:window]; - - /* GdkSurfaces should be hidden by default */ - [impl->view setHidden:YES]; - [parent_impl->view addSubview:impl->view]; - [impl->view release]; - } - } - break; - - default: - g_assert_not_reached (); - } - - GDK_QUARTZ_RELEASE_POOL; -} - -void -_gdk_quartz_surface_update_position (GdkSurface *window) -{ - NSRect frame_rect; - NSRect content_rect; - GdkSurfaceImplQuartz *impl = GDK_SURFACE_IMPL_QUARTZ (window->impl); - - GDK_QUARTZ_ALLOC_POOL; - - frame_rect = [impl->toplevel frame]; - content_rect = [impl->toplevel contentRectForFrameRect:frame_rect]; - - _gdk_quartz_surface_xy_to_gdk_xy (content_rect.origin.x, - content_rect.origin.y + content_rect.size.height, - &window->x, &window->y); - - - GDK_QUARTZ_RELEASE_POOL; -} - -void -_gdk_quartz_surface_init_windowing (GdkDisplay *display) -{ - GdkSurfaceImplQuartz *impl; - - g_assert (_gdk_root == NULL); - - _gdk_root = _gdk_display_create_window (display); - - _gdk_root->impl = g_object_new (_gdk_root_window_impl_quartz_get_type (), NULL); - _gdk_root->impl_window = _gdk_root; - - impl = GDK_SURFACE_IMPL_QUARTZ (_gdk_root->impl); - - _gdk_quartz_screen_update_window_sizes (screen); - - _gdk_root->state = 0; /* We don't want GDK_SURFACE_STATE_WITHDRAWN here */ - _gdk_root->window_type = GDK_SURFACE_ROOT; - _gdk_root->viewable = TRUE; - - impl->wrapper = _gdk_root; -} - -static void -gdk_quartz_surface_destroy (GdkSurface *window, - gboolean recursing, - gboolean foreign_destroy) -{ - GdkSurfaceImplQuartz *impl; - GdkSurface *parent; - - impl = GDK_SURFACE_IMPL_QUARTZ (window->impl); - - main_window_stack = g_slist_remove (main_window_stack, window); - - g_list_free (impl->sorted_children); - impl->sorted_children = NULL; - - parent = window->parent; - if (parent) - { - GdkSurfaceImplQuartz *parent_impl = GDK_SURFACE_IMPL_QUARTZ (parent->impl); - - parent_impl->sorted_children = g_list_remove (parent_impl->sorted_children, window); - } - - if (impl->cairo_surface) - { - cairo_surface_finish (impl->cairo_surface); - cairo_surface_set_user_data (impl->cairo_surface, &gdk_quartz_cairo_key, - NULL, NULL); - impl->cairo_surface = NULL; - } - - if (!recursing && !foreign_destroy) - { - GDK_QUARTZ_ALLOC_POOL; - - if (impl->toplevel) - [impl->toplevel close]; - else if (impl->view) - [impl->view removeFromSuperview]; - - GDK_QUARTZ_RELEASE_POOL; - } -} - -/* FIXME: This might be possible to simplify with client-side windows. Also - * note that already_mapped is not used yet, see the x11 backend. -*/ -static void -gdk_surface_quartz_show (GdkSurface *window, gboolean already_mapped) -{ - GdkSurfaceImplQuartz *impl = GDK_SURFACE_IMPL_QUARTZ (window->impl); - gboolean focus_on_map; - - GDK_QUARTZ_ALLOC_POOL; - - if (!GDK_SURFACE_IS_MAPPED (window)) - focus_on_map = window->focus_on_map; - else - focus_on_map = TRUE; - - if (WINDOW_IS_TOPLEVEL (window) && impl->toplevel) - { - gboolean make_key; - - make_key = (window->accept_focus && focus_on_map && - window->window_type != GDK_SURFACE_TEMP); - - [(GdkQuartzNSWindow*)impl->toplevel showAndMakeKey:make_key]; - clear_toplevel_order (); - - _gdk_quartz_events_send_map_event (window); - } - else - { - [impl->view setHidden:NO]; - } - - [impl->view setNeedsDisplay:YES]; - - gdk_synthesize_window_state (window, GDK_SURFACE_STATE_WITHDRAWN, 0); - - if (window->state & GDK_SURFACE_STATE_MAXIMIZED) - gdk_surface_maximize (window); - - if (window->state & GDK_SURFACE_STATE_ICONIFIED) - gdk_surface_iconify (window); - - if (impl->transient_for && !GDK_SURFACE_DESTROYED (impl->transient_for)) - _gdk_quartz_surface_attach_to_parent (window); - - GDK_QUARTZ_RELEASE_POOL; -} - -/* Temporarily unsets the parent window, if the window is a - * transient. - */ -void -_gdk_quartz_surface_detach_from_parent (GdkSurface *window) -{ - GdkSurfaceImplQuartz *impl; - - g_return_if_fail (GDK_IS_SURFACE (window)); - - impl = GDK_SURFACE_IMPL_QUARTZ (window->impl); - - g_return_if_fail (impl->toplevel != NULL); - - if (impl->transient_for && !GDK_SURFACE_DESTROYED (impl->transient_for)) - { - GdkSurfaceImplQuartz *parent_impl; - - parent_impl = GDK_SURFACE_IMPL_QUARTZ (impl->transient_for->impl); - [parent_impl->toplevel removeChildWindow:impl->toplevel]; - clear_toplevel_order (); - } -} - -/* Re-sets the parent window, if the window is a transient. */ -void -_gdk_quartz_surface_attach_to_parent (GdkSurface *window) -{ - GdkSurfaceImplQuartz *impl; - - g_return_if_fail (GDK_IS_SURFACE (window)); - - impl = GDK_SURFACE_IMPL_QUARTZ (window->impl); - - g_return_if_fail (impl->toplevel != NULL); - - if (impl->transient_for && !GDK_SURFACE_DESTROYED (impl->transient_for)) - { - GdkSurfaceImplQuartz *parent_impl; - - parent_impl = GDK_SURFACE_IMPL_QUARTZ (impl->transient_for->impl); - [parent_impl->toplevel addChildWindow:impl->toplevel ordered:NSWindowAbove]; - clear_toplevel_order (); - } -} - -void -gdk_surface_quartz_hide (GdkSurface *window) -{ - GdkSurfaceImplQuartz *impl; - - /* Make sure we're not stuck in fullscreen mode. */ -#ifndef AVAILABLE_MAC_OS_X_VERSION_10_7_AND_LATER - if (get_fullscreen_geometry (window)) - SetSystemUIMode (kUIModeNormal, 0); -#endif - - _gdk_surface_clear_update_area (window); - - impl = GDK_SURFACE_IMPL_QUARTZ (window->impl); - - if (WINDOW_IS_TOPLEVEL (window)) - { - /* Update main window. */ - main_window_stack = g_slist_remove (main_window_stack, window); - if ([NSApp mainWindow] == impl->toplevel) - _gdk_quartz_surface_did_resign_main (window); - - if (impl->transient_for) - _gdk_quartz_surface_detach_from_parent (window); - - [(GdkQuartzNSWindow*)impl->toplevel hide]; - } - else if (impl->view) - { - [impl->view setHidden:YES]; - } -} - -void -gdk_surface_quartz_withdraw (GdkSurface *window) -{ - gdk_surface_hide (window); -} - -static void -move_resize_window_internal (GdkSurface *window, - gint x, - gint y, - gint width, - gint height) -{ - GdkSurfaceImplQuartz *impl; - GdkRectangle old_visible; - GdkRectangle new_visible; - GdkRectangle scroll_rect; - cairo_region_t *old_region; - cairo_region_t *expose_region; - NSSize delta; - - if (GDK_SURFACE_DESTROYED (window)) - return; - - impl = GDK_SURFACE_IMPL_QUARTZ (window->impl); - - if ((x == -1 || (x == window->x)) && - (y == -1 || (y == window->y)) && - (width == -1 || (width == window->width)) && - (height == -1 || (height == window->height))) - { - return; - } - - if (!impl->toplevel) - { - /* The previously visible area of this window in a coordinate - * system rooted at the origin of this window. - */ - old_visible.x = -window->x; - old_visible.y = -window->y; - - old_visible.width = window->width; - old_visible.height = window->height; - } - - if (x != -1) - { - delta.width = x - window->x; - window->x = x; - } - else - { - delta.width = 0; - } - - if (y != -1) - { - delta.height = y - window->y; - window->y = y; - } - else - { - delta.height = 0; - } - - if (width != -1) - window->width = width; - - if (height != -1) - window->height = height; - - GDK_QUARTZ_ALLOC_POOL; - - if (impl->toplevel) - { - NSRect content_rect; - NSRect frame_rect; - gint gx, gy; - - _gdk_quartz_surface_gdk_xy_to_xy (window->x, window->y + window->height, - &gx, &gy); - - content_rect = NSMakeRect (gx, gy, window->width, window->height); - - frame_rect = [impl->toplevel frameRectForContentRect:content_rect]; - [impl->toplevel setFrame:frame_rect display:YES]; - } - else - { - if (!window->input_only) - { - NSRect nsrect; - - nsrect = NSMakeRect (window->x, window->y, window->width, window->height); - - /* The newly visible area of this window in a coordinate - * system rooted at the origin of this window. - */ - new_visible.x = -window->x; - new_visible.y = -window->y; - new_visible.width = old_visible.width; /* parent has not changed size */ - new_visible.height = old_visible.height; /* parent has not changed size */ - - expose_region = cairo_region_create_rectangle (&new_visible); - old_region = cairo_region_create_rectangle (&old_visible); - cairo_region_subtract (expose_region, old_region); - - /* Determine what (if any) part of the previously visible - * part of the window can be copied without a redraw - */ - scroll_rect = old_visible; - scroll_rect.x -= delta.width; - scroll_rect.y -= delta.height; - gdk_rectangle_intersect (&scroll_rect, &old_visible, &scroll_rect); - - if (!cairo_region_is_empty (expose_region)) - { - if (scroll_rect.width != 0 && scroll_rect.height != 0) - { - [impl->view scrollRect:NSMakeRect (scroll_rect.x, - scroll_rect.y, - scroll_rect.width, - scroll_rect.height) - by:delta]; - } - - [impl->view setFrame:nsrect]; - - gdk_quartz_surface_set_needs_display_in_region (window, expose_region); - } - else - { - [impl->view setFrame:nsrect]; - [impl->view setNeedsDisplay:YES]; - } - - cairo_region_destroy (expose_region); - cairo_region_destroy (old_region); - } - } - - GDK_QUARTZ_RELEASE_POOL; -} - -static inline void -window_quartz_move (GdkSurface *window, - gint x, - gint y) -{ - g_return_if_fail (GDK_IS_SURFACE (window)); - - if (window->state & GDK_SURFACE_STATE_FULLSCREEN) - return; - - move_resize_window_internal (window, x, y, -1, -1); -} - -static inline void -window_quartz_resize (GdkSurface *window, - gint width, - gint height) -{ - g_return_if_fail (GDK_IS_SURFACE (window)); - - if (window->state & GDK_SURFACE_STATE_FULLSCREEN) - return; - - if (width < 1) - width = 1; - if (height < 1) - height = 1; - - move_resize_window_internal (window, -1, -1, width, height); -} - -static inline void -window_quartz_move_resize (GdkSurface *window, - gint x, - gint y, - gint width, - gint height) -{ - if (width < 1) - width = 1; - if (height < 1) - height = 1; - - move_resize_window_internal (window, x, y, width, height); -} - -static void -gdk_surface_quartz_move_resize (GdkSurface *window, - gboolean with_move, - gint x, - gint y, - gint width, - gint height) -{ - if (with_move && (width < 0 && height < 0)) - window_quartz_move (window, x, y); - else - { - if (with_move) - window_quartz_move_resize (window, x, y, width, height); - else - window_quartz_resize (window, width, height); - } -} - -/* Get the toplevel ordering from NSApp and update our own list. We do - * this on demand since the NSApp’s list is not up to date directly - * after we get windowDidBecomeMain. - */ -static void -update_toplevel_order (void) -{ - GdkSurfaceImplQuartz *root_impl; - NSEnumerator *enumerator; - id nswindow; - GList *toplevels = NULL; - - root_impl = GDK_SURFACE_IMPL_QUARTZ (_gdk_root->impl); - - if (root_impl->sorted_children) - return; - - GDK_QUARTZ_ALLOC_POOL; - - enumerator = [[NSApp orderedWindows] objectEnumerator]; - while ((nswindow = [enumerator nextObject])) - { - GdkSurface *window; - - if (![[nswindow contentView] isKindOfClass:[GdkQuartzView class]]) - continue; - - window = [(GdkQuartzView *)[nswindow contentView] gdkWindow]; - toplevels = g_list_prepend (toplevels, window); - } - - GDK_QUARTZ_RELEASE_POOL; - - root_impl->sorted_children = g_list_reverse (toplevels); -} - -static void -clear_toplevel_order (void) -{ - GdkSurfaceImplQuartz *root_impl; - - root_impl = GDK_SURFACE_IMPL_QUARTZ (_gdk_root->impl); - - g_list_free (root_impl->sorted_children); - root_impl->sorted_children = NULL; -} - -static void -gdk_surface_quartz_raise (GdkSurface *window) -{ - if (GDK_SURFACE_DESTROYED (window)) - return; - - if (WINDOW_IS_TOPLEVEL (window)) - { - GdkSurfaceImplQuartz *impl; - - impl = GDK_SURFACE_IMPL_QUARTZ (window->impl); - [impl->toplevel orderFront:impl->toplevel]; - - clear_toplevel_order (); - } - else - { - GdkSurface *parent = window->parent; - - if (parent) - { - GdkSurfaceImplQuartz *impl; - - impl = (GdkSurfaceImplQuartz *)parent->impl; - - impl->sorted_children = g_list_remove (impl->sorted_children, window); - impl->sorted_children = g_list_prepend (impl->sorted_children, window); - } - } -} - -static void -gdk_surface_quartz_lower (GdkSurface *window) -{ - if (GDK_SURFACE_DESTROYED (window)) - return; - - if (WINDOW_IS_TOPLEVEL (window)) - { - GdkSurfaceImplQuartz *impl; - - impl = GDK_SURFACE_IMPL_QUARTZ (window->impl); - [impl->toplevel orderBack:impl->toplevel]; - - clear_toplevel_order (); - } - else - { - GdkSurface *parent = window->parent; - - if (parent) - { - GdkSurfaceImplQuartz *impl; - - impl = (GdkSurfaceImplQuartz *)parent->impl; - - impl->sorted_children = g_list_remove (impl->sorted_children, window); - impl->sorted_children = g_list_append (impl->sorted_children, window); - } - } -} - -static void -gdk_surface_quartz_restack_toplevel (GdkSurface *window, - GdkSurface *sibling, - gboolean above) -{ - GdkSurfaceImplQuartz *impl; - gint sibling_num; - - impl = GDK_SURFACE_IMPL_QUARTZ (sibling->impl); - sibling_num = [impl->toplevel windowNumber]; - - impl = GDK_SURFACE_IMPL_QUARTZ (window->impl); - - if (above) - [impl->toplevel orderWindow:NSWindowAbove relativeTo:sibling_num]; - else - [impl->toplevel orderWindow:NSWindowBelow relativeTo:sibling_num]; -} - -static void -gdk_surface_quartz_get_geometry (GdkSurface *window, - gint *x, - gint *y, - gint *width, - gint *height) -{ - GdkSurfaceImplQuartz *impl; - NSRect ns_rect; - - if (GDK_SURFACE_DESTROYED (window)) - return; - - impl = GDK_SURFACE_IMPL_QUARTZ (window->impl); - if (window == _gdk_root) - { - if (x) - *x = 0; - if (y) - *y = 0; - - if (width) - *width = window->width; - if (height) - *height = window->height; - } - else if (WINDOW_IS_TOPLEVEL (window)) - { - ns_rect = [impl->toplevel contentRectForFrameRect:[impl->toplevel frame]]; - - /* This doesn't work exactly as in X. There doesn't seem to be a - * way to get the coords relative to the parent window (usually - * the window frame), but that seems useless except for - * borderless windows where it's relative to the root window. So - * we return (0, 0) (should be something like (0, 22)) for - * windows with borders and the root relative coordinates - * otherwise. - */ - if ([impl->toplevel styleMask] == NSBorderlessWindowMask) - { - _gdk_quartz_surface_xy_to_gdk_xy (ns_rect.origin.x, - ns_rect.origin.y + ns_rect.size.height, - x, y); - } - else - { - if (x) - *x = 0; - if (y) - *y = 0; - } - - if (width) - *width = ns_rect.size.width; - if (height) - *height = ns_rect.size.height; - } - else - { - ns_rect = [impl->view frame]; - - if (x) - *x = ns_rect.origin.x; - if (y) - *y = ns_rect.origin.y; - if (width) - *width = ns_rect.size.width; - if (height) - *height = ns_rect.size.height; - } -} - -static void -gdk_surface_quartz_get_root_coords (GdkSurface *window, - gint x, - gint y, - gint *root_x, - gint *root_y) -{ - int tmp_x = 0, tmp_y = 0; - GdkSurface *toplevel; - NSRect content_rect; - GdkSurfaceImplQuartz *impl; - - if (GDK_SURFACE_DESTROYED (window)) - { - if (root_x) - *root_x = 0; - if (root_y) - *root_y = 0; - - return; - } - - if (window == _gdk_root) - { - if (root_x) - *root_x = x; - if (root_y) - *root_y = y; - - return; - } - - toplevel = gdk_surface_get_toplevel (window); - impl = GDK_SURFACE_IMPL_QUARTZ (toplevel->impl); - - content_rect = [impl->toplevel contentRectForFrameRect:[impl->toplevel frame]]; - - _gdk_quartz_surface_xy_to_gdk_xy (content_rect.origin.x, - content_rect.origin.y + content_rect.size.height, - &tmp_x, &tmp_y); - - tmp_x += x; - tmp_y += y; - - while (window != toplevel) - { - if (_gdk_surface_has_impl ((GdkSurface *)window)) - { - tmp_x += window->x; - tmp_y += window->y; - } - - window = window->parent; - } - - if (root_x) - *root_x = tmp_x; - if (root_y) - *root_y = tmp_y; -} - -/* Returns coordinates relative to the passed in window. */ -static GdkSurface * -gdk_surface_quartz_get_device_state_helper (GdkSurface *window, - GdkDevice *device, - gdouble *x, - gdouble *y, - GdkModifierType *mask) -{ - NSPoint point; - gint x_tmp, y_tmp; - GdkSurface *toplevel; - GdkSurface *found_window; - - g_return_val_if_fail (window == NULL || GDK_IS_SURFACE (window), NULL); - - if (GDK_SURFACE_DESTROYED (window)) - { - *x = 0; - *y = 0; - *mask = 0; - return NULL; - } - - toplevel = gdk_surface_get_toplevel (window); - - *mask = _gdk_quartz_events_get_current_keyboard_modifiers () | - _gdk_quartz_events_get_current_mouse_modifiers (); - - /* Get the y coordinate, needs to be flipped. */ - if (window == _gdk_root) - { - point = [NSEvent mouseLocation]; - _gdk_quartz_surface_nspoint_to_gdk_xy (point, &x_tmp, &y_tmp); - } - else - { - GdkSurfaceImplQuartz *impl; - NSWindow *nswindow; - - impl = GDK_SURFACE_IMPL_QUARTZ (toplevel->impl); - nswindow = impl->toplevel; - - point = [nswindow mouseLocationOutsideOfEventStream]; - - x_tmp = point.x; - y_tmp = toplevel->height - point.y; - - window = (GdkSurface *)toplevel; - } - - found_window = _gdk_quartz_surface_find_child (window, x_tmp, y_tmp, - FALSE); - - /* We never return the root window. */ - if (found_window == _gdk_root) - found_window = NULL; - - *x = x_tmp; - *y = y_tmp; - - return found_window; -} - -static gboolean -gdk_surface_quartz_get_device_state (GdkSurface *window, - GdkDevice *device, - gdouble *x, - gdouble *y, - GdkModifierType *mask) -{ - return gdk_surface_quartz_get_device_state_helper (window, - device, - x, y, mask) != NULL; -} - -static GdkEventMask -gdk_surface_quartz_get_events (GdkSurface *window) -{ - if (GDK_SURFACE_DESTROYED (window)) - return 0; - else - return window->event_mask; -} - -static void -gdk_surface_quartz_set_events (GdkSurface *window, - GdkEventMask event_mask) -{ - /* The mask is set in the common code. */ -} - -static void -gdk_quartz_surface_set_urgency_hint (GdkSurface *window, - gboolean urgent) -{ - if (GDK_SURFACE_DESTROYED (window) || - !WINDOW_IS_TOPLEVEL (window)) - return; - - /* FIXME: Implement */ -} - -static void -gdk_quartz_surface_set_geometry_hints (GdkSurface *window, - const GdkGeometry *geometry, - GdkSurfaceHints geom_mask) -{ - GdkSurfaceImplQuartz *impl; - - g_return_if_fail (geometry != NULL); - - if (GDK_SURFACE_DESTROYED (window) || - !WINDOW_IS_TOPLEVEL (window)) - return; - - impl = GDK_SURFACE_IMPL_QUARTZ (window->impl); - if (!impl->toplevel) - return; - - if (geom_mask & GDK_HINT_POS) - { - /* FIXME: Implement */ - } - - if (geom_mask & GDK_HINT_USER_POS) - { - /* FIXME: Implement */ - } - - if (geom_mask & GDK_HINT_USER_SIZE) - { - /* FIXME: Implement */ - } - - if (geom_mask & GDK_HINT_MIN_SIZE) - { - NSSize size; - - size.width = geometry->min_width; - size.height = geometry->min_height; - - [impl->toplevel setContentMinSize:size]; - } - - if (geom_mask & GDK_HINT_MAX_SIZE) - { - NSSize size; - - size.width = geometry->max_width; - size.height = geometry->max_height; - - [impl->toplevel setContentMaxSize:size]; - } - - if (geom_mask & GDK_HINT_BASE_SIZE) - { - /* FIXME: Implement */ - } - - if (geom_mask & GDK_HINT_RESIZE_INC) - { - NSSize size; - - size.width = geometry->width_inc; - size.height = geometry->height_inc; - - [impl->toplevel setContentResizeIncrements:size]; - } - - if (geom_mask & GDK_HINT_ASPECT) - { - NSSize size; - - if (geometry->min_aspect != geometry->max_aspect) - { - g_warning ("Only equal minimum and maximum aspect ratios are supported on Mac OS. Using minimum aspect ratio..."); - } - - size.width = geometry->min_aspect; - size.height = 1.0; - - [impl->toplevel setContentAspectRatio:size]; - } - - if (geom_mask & GDK_HINT_WIN_GRAVITY) - { - /* FIXME: Implement */ - } -} - -static void -gdk_quartz_surface_set_title (GdkSurface *window, - const gchar *title) -{ - GdkSurfaceImplQuartz *impl; - - g_return_if_fail (title != NULL); - - if (GDK_SURFACE_DESTROYED (window) || - !WINDOW_IS_TOPLEVEL (window)) - return; - - impl = GDK_SURFACE_IMPL_QUARTZ (window->impl); - - if (impl->toplevel) - { - GDK_QUARTZ_ALLOC_POOL; - [impl->toplevel setTitle:[NSString stringWithUTF8String:title]]; - GDK_QUARTZ_RELEASE_POOL; - } -} - -static void -gdk_quartz_surface_set_role (GdkSurface *window, - const gchar *role) -{ - if (GDK_SURFACE_DESTROYED (window) || - WINDOW_IS_TOPLEVEL (window)) - return; - - /* FIXME: Implement */ -} - -static void -gdk_quartz_surface_set_startup_id (GdkSurface *window, - const gchar *startup_id) -{ - /* FIXME: Implement? */ -} - -static void -gdk_quartz_surface_set_transient_for (GdkSurface *window, - GdkSurface *parent) -{ - GdkSurfaceImplQuartz *window_impl; - GdkSurfaceImplQuartz *parent_impl; - - if (GDK_SURFACE_DESTROYED (window) || GDK_SURFACE_DESTROYED (parent) || - !WINDOW_IS_TOPLEVEL (window)) - return; - - window_impl = GDK_SURFACE_IMPL_QUARTZ (window->impl); - if (!window_impl->toplevel) - return; - - GDK_QUARTZ_ALLOC_POOL; - - if (window_impl->transient_for) - { - _gdk_quartz_surface_detach_from_parent (window); - - g_object_unref (window_impl->transient_for); - window_impl->transient_for = NULL; - } - - parent_impl = GDK_SURFACE_IMPL_QUARTZ (parent->impl); - if (parent_impl->toplevel) - { - /* We save the parent because it needs to be unset/reset when - * hiding and showing the window. - */ - - /* We don't set transients for tooltips, they are already - * handled by the window level being the top one. If we do, then - * the parent window will be brought to the top just because the - * tooltip is, which is not what we want. - */ - if (gdk_surface_get_type_hint (window) != GDK_SURFACE_TYPE_HINT_TOOLTIP) - { - window_impl->transient_for = g_object_ref (parent); - - /* We only add the window if it is shown, otherwise it will - * be shown unconditionally here. If it is not shown, the - * window will be added in show() instead. - */ - if (!(window->state & GDK_SURFACE_STATE_WITHDRAWN)) - _gdk_quartz_surface_attach_to_parent (window); - } - } - - GDK_QUARTZ_RELEASE_POOL; -} - -static void -gdk_surface_quartz_shape_combine_region (GdkSurface *window, - const cairo_region_t *shape, - gint x, - gint y) -{ - /* FIXME: Implement */ -} - -static void -gdk_surface_quartz_input_shape_combine_region (GdkSurface *window, - const cairo_region_t *shape_region, - gint offset_x, - gint offset_y) -{ - /* FIXME: Implement */ -} - -static void -gdk_quartz_surface_set_accept_focus (GdkSurface *window, - gboolean accept_focus) -{ - window->accept_focus = accept_focus != FALSE; -} - -static void -gdk_quartz_surface_set_focus_on_map (GdkSurface *window, - gboolean focus_on_map) -{ - window->focus_on_map = focus_on_map != FALSE; -} - -static void -gdk_quartz_surface_set_icon_name (GdkSurface *window, - const gchar *name) -{ - /* FIXME: Implement */ -} - -static void -gdk_quartz_surface_focus (GdkSurface *window, - guint32 timestamp) -{ - GdkSurfaceImplQuartz *impl; - - impl = GDK_SURFACE_IMPL_QUARTZ (window->impl); - - if (GDK_SURFACE_DESTROYED (window) || - !WINDOW_IS_TOPLEVEL (window)) - return; - - if (window->accept_focus && window->window_type != GDK_SURFACE_TEMP) - { - GDK_QUARTZ_ALLOC_POOL; - [impl->toplevel makeKeyAndOrderFront:impl->toplevel]; - clear_toplevel_order (); - GDK_QUARTZ_RELEASE_POOL; - } -} - -static gint -window_type_hint_to_level (GdkSurfaceTypeHint hint) -{ - /* the order in this switch statement corresponds to the actual - * stacking order: the first group is top, the last group is bottom - */ - switch (hint) - { - case GDK_SURFACE_TYPE_HINT_POPUP_MENU: - case GDK_SURFACE_TYPE_HINT_COMBO: - case GDK_SURFACE_TYPE_HINT_DND: - case GDK_SURFACE_TYPE_HINT_TOOLTIP: - return NSPopUpMenuWindowLevel; - - case GDK_SURFACE_TYPE_HINT_NOTIFICATION: - case GDK_SURFACE_TYPE_HINT_SPLASHSCREEN: - return NSStatusWindowLevel; - - case GDK_SURFACE_TYPE_HINT_MENU: /* Torn-off menu */ - case GDK_SURFACE_TYPE_HINT_DROPDOWN_MENU: /* Menu from menubar */ - return NSTornOffMenuWindowLevel; - - case GDK_SURFACE_TYPE_HINT_DOCK: - return NSFloatingWindowLevel; /* NSDockWindowLevel is deprecated, and not replaced */ - - case GDK_SURFACE_TYPE_HINT_UTILITY: - case GDK_SURFACE_TYPE_HINT_DIALOG: /* Dialog window */ - case GDK_SURFACE_TYPE_HINT_NORMAL: /* Normal toplevel window */ - case GDK_SURFACE_TYPE_HINT_TOOLBAR: /* Window used to implement toolbars */ - return NSNormalWindowLevel; - - case GDK_SURFACE_TYPE_HINT_DESKTOP: - return kCGDesktopWindowLevelKey; /* doesn't map to any real Cocoa model */ - - default: - break; - } - - return NSNormalWindowLevel; -} - -static gboolean -window_type_hint_to_shadow (GdkSurfaceTypeHint hint) -{ - switch (hint) - { - case GDK_SURFACE_TYPE_HINT_NORMAL: /* Normal toplevel window */ - case GDK_SURFACE_TYPE_HINT_DIALOG: /* Dialog window */ - case GDK_SURFACE_TYPE_HINT_DOCK: - case GDK_SURFACE_TYPE_HINT_UTILITY: - case GDK_SURFACE_TYPE_HINT_MENU: /* Torn-off menu */ - case GDK_SURFACE_TYPE_HINT_DROPDOWN_MENU: /* Menu from menubar */ - case GDK_SURFACE_TYPE_HINT_SPLASHSCREEN: - case GDK_SURFACE_TYPE_HINT_POPUP_MENU: - case GDK_SURFACE_TYPE_HINT_COMBO: - case GDK_SURFACE_TYPE_HINT_NOTIFICATION: - case GDK_SURFACE_TYPE_HINT_TOOLTIP: - return TRUE; - - case GDK_SURFACE_TYPE_HINT_TOOLBAR: /* Window used to implement toolbars */ - case GDK_SURFACE_TYPE_HINT_DESKTOP: /* N/A */ - case GDK_SURFACE_TYPE_HINT_DND: - break; - - default: - break; - } - - return FALSE; -} - -static gboolean -window_type_hint_to_hides_on_deactivate (GdkSurfaceTypeHint hint) -{ - switch (hint) - { - case GDK_SURFACE_TYPE_HINT_UTILITY: - case GDK_SURFACE_TYPE_HINT_MENU: /* Torn-off menu */ - case GDK_SURFACE_TYPE_HINT_SPLASHSCREEN: - case GDK_SURFACE_TYPE_HINT_NOTIFICATION: - case GDK_SURFACE_TYPE_HINT_TOOLTIP: - return TRUE; - - default: - break; - } - - return FALSE; -} - -static void -_gdk_quartz_surface_update_has_shadow (GdkSurfaceImplQuartz *impl) -{ - gboolean has_shadow; - - /* In case there is any shadow set we have to turn off the - * NSWindow setHasShadow as the system drawn ones wont match our - * window boundary anymore */ - has_shadow = (window_type_hint_to_shadow (impl->type_hint) && !impl->shadow_max); - - [impl->toplevel setHasShadow: has_shadow]; -} - -static void -gdk_quartz_surface_set_type_hint (GdkSurface *window, - GdkSurfaceTypeHint hint) -{ - GdkSurfaceImplQuartz *impl; - - if (GDK_SURFACE_DESTROYED (window) || - !WINDOW_IS_TOPLEVEL (window)) - return; - - impl = GDK_SURFACE_IMPL_QUARTZ (window->impl); - - impl->type_hint = hint; - - /* Match the documentation, only do something if we're not mapped yet. */ - if (GDK_SURFACE_IS_MAPPED (window)) - return; - - _gdk_quartz_surface_update_has_shadow (impl); - [impl->toplevel setLevel: window_type_hint_to_level (hint)]; - [impl->toplevel setHidesOnDeactivate: window_type_hint_to_hides_on_deactivate (hint)]; -} - -static GdkSurfaceTypeHint -gdk_quartz_surface_get_type_hint (GdkSurface *window) -{ - if (GDK_SURFACE_DESTROYED (window) || - !WINDOW_IS_TOPLEVEL (window)) - return GDK_SURFACE_TYPE_HINT_NORMAL; - - return GDK_SURFACE_IMPL_QUARTZ (window->impl)->type_hint; -} - -static void -gdk_quartz_surface_set_modal_hint (GdkSurface *window, - gboolean modal) -{ - if (GDK_SURFACE_DESTROYED (window) || - !WINDOW_IS_TOPLEVEL (window)) - return; - - /* FIXME: Implement */ -} - -static void -gdk_quartz_surface_set_skip_taskbar_hint (GdkSurface *window, - gboolean skips_taskbar) -{ - if (GDK_SURFACE_DESTROYED (window) || - !WINDOW_IS_TOPLEVEL (window)) - return; - - /* FIXME: Implement */ -} - -static void -gdk_quartz_surface_set_skip_pager_hint (GdkSurface *window, - gboolean skips_pager) -{ - if (GDK_SURFACE_DESTROYED (window) || - !WINDOW_IS_TOPLEVEL (window)) - return; - - /* FIXME: Implement */ -} - -static void -gdk_quartz_surface_begin_resize_drag (GdkSurface *window, - GdkSurfaceEdge edge, - GdkDevice *device, - gint button, - gint root_x, - gint root_y, - guint32 timestamp) -{ - GdkSurfaceImplQuartz *impl; - - g_return_if_fail (GDK_IS_SURFACE (window)); - - if (GDK_SURFACE_DESTROYED (window)) - return; - - impl = GDK_SURFACE_IMPL_QUARTZ (window->impl); - - if (!impl->toplevel) - { - g_warning ("Can't call gdk_surface_begin_resize_drag on non-toplevel window"); - return; - } - - [(GdkQuartzNSWindow *)impl->toplevel beginManualResize:edge]; -} - -static void -gdk_quartz_surface_begin_move_drag (GdkSurface *window, - GdkDevice *device, - gint button, - gint root_x, - gint root_y, - guint32 timestamp) -{ - GdkSurfaceImplQuartz *impl; - - if (GDK_SURFACE_DESTROYED (window) || - !WINDOW_IS_TOPLEVEL (window)) - return; - - impl = GDK_SURFACE_IMPL_QUARTZ (window->impl); - - if (!impl->toplevel) - { - g_warning ("Can't call gdk_surface_begin_move_drag on non-toplevel window"); - return; - } - - [(GdkQuartzNSWindow *)impl->toplevel beginManualMove]; -} - -static void -gdk_quartz_surface_set_icon_list (GdkSurface *window, - GList *surfaces) -{ - /* FIXME: Implement */ -} - -static void -gdk_quartz_surface_get_frame_extents (GdkSurface *window, - GdkRectangle *rect) -{ - GdkSurface *toplevel; - GdkSurfaceImplQuartz *impl; - NSRect ns_rect; - - g_return_if_fail (rect != NULL); - - - rect->x = 0; - rect->y = 0; - rect->width = 1; - rect->height = 1; - - toplevel = gdk_surface_get_toplevel (window); - impl = GDK_SURFACE_IMPL_QUARTZ (toplevel->impl); - - ns_rect = [impl->toplevel frame]; - - _gdk_quartz_surface_xy_to_gdk_xy (ns_rect.origin.x, - ns_rect.origin.y + ns_rect.size.height, - &rect->x, &rect->y); - - rect->width = ns_rect.size.width; - rect->height = ns_rect.size.height; -} - -/* Fake protocol to make gcc think that it's OK to call setStyleMask - even if it isn't. We check to make sure before actually calling - it. */ - -@protocol CanSetStyleMask -- (void)setStyleMask:(int)mask; -@end - -static void -gdk_quartz_surface_set_decorations (GdkSurface *window, - GdkWMDecoration decorations) -{ - GdkSurfaceImplQuartz *impl; - NSUInteger old_mask, new_mask; - NSView *old_view; - - if (GDK_SURFACE_DESTROYED (window) || - !WINDOW_IS_TOPLEVEL (window)) - return; - - impl = GDK_SURFACE_IMPL_QUARTZ (window->impl); - - if (decorations == 0 || GDK_SURFACE_TYPE (window) == GDK_SURFACE_TEMP || - impl->type_hint == GDK_SURFACE_TYPE_HINT_SPLASHSCREEN ) - { - new_mask = NSBorderlessWindowMask; - } - else - { - /* FIXME: Honor other GDK_DECOR_* flags. */ - new_mask = (NSTitledWindowMask | NSClosableWindowMask | - NSMiniaturizableWindowMask | NSResizableWindowMask); - } - - GDK_QUARTZ_ALLOC_POOL; - - old_mask = [impl->toplevel styleMask]; - - if (old_mask != new_mask) - { - NSRect rect; - - old_view = [[impl->toplevel contentView] retain]; - - rect = [impl->toplevel frame]; - - /* Properly update the size of the window when the titlebar is - * added or removed. - */ - if (old_mask == NSBorderlessWindowMask && - new_mask != NSBorderlessWindowMask) - { - rect = [NSWindow frameRectForContentRect:rect styleMask:new_mask]; - - } - else if (old_mask != NSBorderlessWindowMask && - new_mask == NSBorderlessWindowMask) - { - rect = [NSWindow contentRectForFrameRect:rect styleMask:old_mask]; - } - - /* Note, before OS 10.6 there doesn't seem to be a way to change this - * without recreating the toplevel. From 10.6 onward, a simple call to - * setStyleMask takes care of most of this, except for ensuring that the - * title is set. - */ - if ([impl->toplevel respondsToSelector:@selector(setStyleMask:)]) - { - NSString *title = [impl->toplevel title]; - - [(id)impl->toplevel setStyleMask:new_mask]; - - /* It appears that unsetting and then resetting NSTitledWindowMask - * does not reset the title in the title bar as might be expected. - * - * In theory we only need to set this if new_mask includes - * NSTitledWindowMask. This behaved extremely oddly when - * conditionalized upon that and since it has no side effects (i.e. - * if NSTitledWindowMask is not requested, the title will not be - * displayed) just do it unconditionally. We also must null check - * 'title' before setting it to avoid crashing. - */ - if (title) - [impl->toplevel setTitle:title]; - } - else - { - NSString *title = [impl->toplevel title]; - NSColor *bg = [impl->toplevel backgroundColor]; - NSScreen *screen = [impl->toplevel screen]; - - /* Make sure the old window is closed, recall that releasedWhenClosed - * is set on GdkQuartzSurfaces. - */ - [impl->toplevel close]; - - impl->toplevel = [[GdkQuartzNSWindow alloc] initWithContentRect:rect - styleMask:new_mask - backing:NSBackingStoreBuffered - defer:NO - screen:screen]; - _gdk_quartz_surface_update_has_shadow (impl); - - [impl->toplevel setLevel: window_type_hint_to_level (impl->type_hint)]; - if (title) - [impl->toplevel setTitle:title]; - [impl->toplevel setBackgroundColor:bg]; - [impl->toplevel setHidesOnDeactivate: window_type_hint_to_hides_on_deactivate (impl->type_hint)]; - [impl->toplevel setContentView:old_view]; - } - - if (new_mask == NSBorderlessWindowMask) - { - [impl->toplevel setContentSize:rect.size]; - [impl->toplevel setCollectionBehavior:NSWindowCollectionBehaviorFullScreenPrimary]; - } - else - [impl->toplevel setFrame:rect display:YES]; - - /* Invalidate the window shadow for non-opaque views that have shadow - * enabled, to get the shadow shape updated. - */ - if (![old_view isOpaque] && [impl->toplevel hasShadow]) - [(GdkQuartzView*)old_view setNeedsInvalidateShadow:YES]; - - [old_view release]; - } - - GDK_QUARTZ_RELEASE_POOL; -} - -static gboolean -gdk_quartz_surface_get_decorations (GdkSurface *window, - GdkWMDecoration *decorations) -{ - GdkSurfaceImplQuartz *impl; - - if (GDK_SURFACE_DESTROYED (window) || - !WINDOW_IS_TOPLEVEL (window)) - return FALSE; - - impl = GDK_SURFACE_IMPL_QUARTZ (window->impl); - - if (decorations) - { - /* Borderless is 0, so we can't check it as a bit being set. */ - if ([impl->toplevel styleMask] == NSBorderlessWindowMask) - { - *decorations = 0; - } - else - { - /* FIXME: Honor the other GDK_DECOR_* flags. */ - *decorations = GDK_DECOR_ALL; - } - } - - return TRUE; -} - -static void -gdk_quartz_surface_set_functions (GdkSurface *window, - GdkWMFunction functions) -{ - GdkSurfaceImplQuartz *impl; - gboolean min, max, close; - - g_return_if_fail (GDK_IS_SURFACE (window)); - - impl = GDK_SURFACE_IMPL_QUARTZ (window->impl); - - if (functions & GDK_FUNC_ALL) - { - min = !(functions & GDK_FUNC_MINIMIZE); - max = !(functions & GDK_FUNC_MAXIMIZE); - close = !(functions & GDK_FUNC_CLOSE); - } - else - { - min = (functions & GDK_FUNC_MINIMIZE); - max = (functions & GDK_FUNC_MAXIMIZE); - close = (functions & GDK_FUNC_CLOSE); - } - - if (impl->toplevel) - { - NSUInteger mask = [impl->toplevel styleMask]; - - if (min) - mask = mask | NSMiniaturizableWindowMask; - else - mask = mask & ~NSMiniaturizableWindowMask; - - if (max) - mask = mask | NSResizableWindowMask; - else - mask = mask & ~NSResizableWindowMask; - - if (close) - mask = mask | NSClosableWindowMask; - else - mask = mask & ~NSClosableWindowMask; - - [impl->toplevel setStyleMask:mask]; - } -} - -static void -gdk_quartz_surface_stick (GdkSurface *window) -{ - if (GDK_SURFACE_DESTROYED (window) || - !WINDOW_IS_TOPLEVEL (window)) - return; -} - -static void -gdk_quartz_surface_unstick (GdkSurface *window) -{ - if (GDK_SURFACE_DESTROYED (window) || - !WINDOW_IS_TOPLEVEL (window)) - return; -} - -static void -gdk_quartz_surface_maximize (GdkSurface *window) -{ - GdkSurfaceImplQuartz *impl; - gboolean maximized; - - if (GDK_SURFACE_DESTROYED (window) || - !WINDOW_IS_TOPLEVEL (window)) - return; - - impl = GDK_SURFACE_IMPL_QUARTZ (window->impl); - maximized = gdk_surface_get_state (window) & GDK_SURFACE_STATE_MAXIMIZED; - - if (GDK_SURFACE_IS_MAPPED (window)) - { - GDK_QUARTZ_ALLOC_POOL; - - if (impl->toplevel && !maximized) - [impl->toplevel zoom:nil]; - - GDK_QUARTZ_RELEASE_POOL; - } -} - -static void -gdk_quartz_surface_unmaximize (GdkSurface *window) -{ - GdkSurfaceImplQuartz *impl; - gboolean maximized; - - if (GDK_SURFACE_DESTROYED (window) || - !WINDOW_IS_TOPLEVEL (window)) - return; - - impl = GDK_SURFACE_IMPL_QUARTZ (window->impl); - maximized = gdk_surface_get_state (window) & GDK_SURFACE_STATE_MAXIMIZED; - - if (GDK_SURFACE_IS_MAPPED (window)) - { - GDK_QUARTZ_ALLOC_POOL; - - if (impl->toplevel && maximized) - [impl->toplevel zoom:nil]; - - GDK_QUARTZ_RELEASE_POOL; - } -} - -static void -gdk_quartz_surface_iconify (GdkSurface *window) -{ - GdkSurfaceImplQuartz *impl; - - if (GDK_SURFACE_DESTROYED (window) || - !WINDOW_IS_TOPLEVEL (window)) - return; - - impl = GDK_SURFACE_IMPL_QUARTZ (window->impl); - - if (GDK_SURFACE_IS_MAPPED (window)) - { - GDK_QUARTZ_ALLOC_POOL; - - if (impl->toplevel) - [impl->toplevel miniaturize:nil]; - - GDK_QUARTZ_RELEASE_POOL; - } - else - { - gdk_synthesize_window_state (window, - 0, - GDK_SURFACE_STATE_ICONIFIED); - } -} - -static void -gdk_quartz_surface_deiconify (GdkSurface *window) -{ - GdkSurfaceImplQuartz *impl; - - if (GDK_SURFACE_DESTROYED (window) || - !WINDOW_IS_TOPLEVEL (window)) - return; - - impl = GDK_SURFACE_IMPL_QUARTZ (window->impl); - - if (GDK_SURFACE_IS_MAPPED (window)) - { - GDK_QUARTZ_ALLOC_POOL; - - if (impl->toplevel) - [impl->toplevel deminiaturize:nil]; - - GDK_QUARTZ_RELEASE_POOL; - } - else - { - gdk_synthesize_window_state (window, - GDK_SURFACE_STATE_ICONIFIED, - 0); - } -} - -#ifdef AVAILABLE_MAC_OS_X_VERSION_10_7_AND_LATER - -static gboolean -window_is_fullscreen (GdkSurface *window) -{ - GdkSurfaceImplQuartz *impl = GDK_SURFACE_IMPL_QUARTZ (window->impl); - - return ([impl->toplevel styleMask] & NSFullScreenWindowMask) != 0; -} - -static void -gdk_quartz_surface_fullscreen (GdkSurface *window) -{ - GdkSurfaceImplQuartz *impl; - - if (GDK_SURFACE_DESTROYED (window) || - !WINDOW_IS_TOPLEVEL (window)) - return; - - impl = GDK_SURFACE_IMPL_QUARTZ (window->impl); - - if (!window_is_fullscreen (window)) - [impl->toplevel toggleFullScreen:nil]; -} - -static void -gdk_quartz_surface_unfullscreen (GdkSurface *window) -{ - GdkSurfaceImplQuartz *impl; - - if (GDK_SURFACE_DESTROYED (window) || - !WINDOW_IS_TOPLEVEL (window)) - return; - - impl = GDK_SURFACE_IMPL_QUARTZ (window->impl); - - if (window_is_fullscreen (window)) - [impl->toplevel toggleFullScreen:nil]; -} - -void -_gdk_quartz_surface_update_fullscreen_state (GdkSurface *window) -{ - gboolean is_fullscreen; - gboolean was_fullscreen; - - is_fullscreen = window_is_fullscreen (window); - was_fullscreen = (gdk_surface_get_state (window) & GDK_SURFACE_STATE_FULLSCREEN) != 0; - - if (is_fullscreen != was_fullscreen) - { - if (is_fullscreen) - gdk_synthesize_window_state (window, 0, GDK_SURFACE_STATE_FULLSCREEN); - else - gdk_synthesize_window_state (window, GDK_SURFACE_STATE_FULLSCREEN, 0); - } -} - -#else - -static FullscreenSavedGeometry * -get_fullscreen_geometry (GdkSurface *window) -{ - return g_object_get_data (G_OBJECT (window), FULLSCREEN_DATA); -} - -static void -gdk_quartz_surface_fullscreen (GdkSurface *window) -{ - FullscreenSavedGeometry *geometry; - GdkSurfaceImplQuartz *impl = GDK_SURFACE_IMPL_QUARTZ (window->impl); - NSRect frame; - - if (GDK_SURFACE_DESTROYED (window) || - !WINDOW_IS_TOPLEVEL (window)) - return; - - geometry = get_fullscreen_geometry (window); - if (!geometry) - { - geometry = g_new (FullscreenSavedGeometry, 1); - - geometry->x = window->x; - geometry->y = window->y; - geometry->width = window->width; - geometry->height = window->height; - - if (!gdk_surface_get_decorations (window, &geometry->decor)) - geometry->decor = GDK_DECOR_ALL; - - g_object_set_data_full (G_OBJECT (window), - FULLSCREEN_DATA, geometry, - g_free); - - gdk_surface_set_decorations (window, 0); - - frame = [[impl->toplevel screen] frame]; - move_resize_window_internal (window, - 0, 0, - frame.size.width, frame.size.height); - [impl->toplevel setContentSize:frame.size]; - [impl->toplevel makeKeyAndOrderFront:impl->toplevel]; - - clear_toplevel_order (); - } - - SetSystemUIMode (kUIModeAllHidden, kUIOptionAutoShowMenuBar); - - gdk_synthesize_window_state (window, 0, GDK_SURFACE_STATE_FULLSCREEN); -} - -static void -gdk_quartz_surface_unfullscreen (GdkSurface *window) -{ - GdkSurfaceImplQuartz *impl = GDK_SURFACE_IMPL_QUARTZ (window->impl); - FullscreenSavedGeometry *geometry; - - if (GDK_SURFACE_DESTROYED (window) || - !WINDOW_IS_TOPLEVEL (window)) - return; - - geometry = get_fullscreen_geometry (window); - if (geometry) - { - SetSystemUIMode (kUIModeNormal, 0); - - move_resize_window_internal (window, - geometry->x, - geometry->y, - geometry->width, - geometry->height); - - gdk_surface_set_decorations (window, geometry->decor); - - g_object_set_data (G_OBJECT (window), FULLSCREEN_DATA, NULL); - - [impl->toplevel makeKeyAndOrderFront:impl->toplevel]; - clear_toplevel_order (); - - gdk_synthesize_window_state (window, GDK_SURFACE_STATE_FULLSCREEN, 0); - } -} - -#endif - -static void -gdk_quartz_surface_set_keep_above (GdkSurface *window, - gboolean setting) -{ - GdkSurfaceImplQuartz *impl = GDK_SURFACE_IMPL_QUARTZ (window->impl); - gint level; - - g_return_if_fail (GDK_IS_SURFACE (window)); - - if (GDK_SURFACE_DESTROYED (window) || - !WINDOW_IS_TOPLEVEL (window)) - return; - - level = window_type_hint_to_level (gdk_surface_get_type_hint (window)); - - /* Adjust normal window level by one if necessary. */ - [impl->toplevel setLevel: level + (setting ? 1 : 0)]; -} - -static void -gdk_quartz_surface_set_keep_below (GdkSurface *window, - gboolean setting) -{ - GdkSurfaceImplQuartz *impl = GDK_SURFACE_IMPL_QUARTZ (window->impl); - gint level; - - g_return_if_fail (GDK_IS_SURFACE (window)); - - if (GDK_SURFACE_DESTROYED (window) || - !WINDOW_IS_TOPLEVEL (window)) - return; - - level = window_type_hint_to_level (gdk_surface_get_type_hint (window)); - - /* Adjust normal window level by one if necessary. */ - [impl->toplevel setLevel: level - (setting ? 1 : 0)]; -} - -static GdkSurface * -gdk_quartz_surface_get_group (GdkSurface *window) -{ - g_return_val_if_fail (GDK_SURFACE_TYPE (window) != GDK_SURFACE_CHILD, NULL); - - if (GDK_SURFACE_DESTROYED (window) || - !WINDOW_IS_TOPLEVEL (window)) - return NULL; - - /* FIXME: Implement */ - - return NULL; -} - -static void -gdk_quartz_surface_set_group (GdkSurface *window, - GdkSurface *leader) -{ - /* FIXME: Implement */ -} - -static void -gdk_quartz_surface_destroy_notify (GdkSurface *window) -{ - check_grab_destroy (window); -} - -static void -gdk_quartz_surface_set_opacity (GdkSurface *window, - gdouble opacity) -{ - GdkSurfaceImplQuartz *impl = GDK_SURFACE_IMPL_QUARTZ (window->impl); - - g_return_if_fail (GDK_IS_SURFACE (window)); - g_return_if_fail (WINDOW_IS_TOPLEVEL (window)); - - if (GDK_SURFACE_DESTROYED (window) || - !WINDOW_IS_TOPLEVEL (window)) - return; - - if (opacity < 0) - opacity = 0; - else if (opacity > 1) - opacity = 1; - - [impl->toplevel setAlphaValue: opacity]; -} - -static void -gdk_quartz_surface_set_shadow_width (GdkSurface *window, - gint left, - gint right, - gint top, - gint bottom) -{ - GdkSurfaceImplQuartz *impl = GDK_SURFACE_IMPL_QUARTZ (window->impl); - - g_return_if_fail (GDK_IS_SURFACE (window)); - g_return_if_fail (WINDOW_IS_TOPLEVEL (window)); - - if (GDK_SURFACE_DESTROYED (window) || - !WINDOW_IS_TOPLEVEL (window)) - return; - - impl->shadow_top = top; - impl->shadow_max = MAX (MAX (left, right), MAX (top, bottom)); - _gdk_quartz_surface_update_has_shadow (impl); -} - -/* Protocol to build cleanly for OSX < 10.7 */ -@protocol ScaleFactor -- (CGFloat) backingScaleFactor; -@end - -static gint -gdk_quartz_surface_get_scale_factor (GdkSurface *window) -{ - GdkSurfaceImplQuartz *impl; - - if (GDK_SURFACE_DESTROYED (window)) - return 1; - - impl = GDK_SURFACE_IMPL_QUARTZ (window->impl); - - if (impl->toplevel != NULL && gdk_quartz_osx_version() >= GDK_OSX_LION) - return [(id ) impl->toplevel backingScaleFactor]; - - return 1; -} - -static void -gdk_surface_impl_quartz_class_init (GdkSurfaceImplQuartzClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - GdkSurfaceImplClass *impl_class = GDK_SURFACE_IMPL_CLASS (klass); - GdkSurfaceImplQuartzClass *impl_quartz_class = GDK_SURFACE_IMPL_QUARTZ_CLASS (klass); - - parent_class = g_type_class_peek_parent (klass); - - object_class->finalize = gdk_surface_impl_quartz_finalize; - - impl_class->ref_cairo_surface = gdk_quartz_ref_cairo_surface; - impl_class->show = gdk_surface_quartz_show; - impl_class->hide = gdk_surface_quartz_hide; - impl_class->withdraw = gdk_surface_quartz_withdraw; - impl_class->set_events = gdk_surface_quartz_set_events; - impl_class->get_events = gdk_surface_quartz_get_events; - impl_class->raise = gdk_surface_quartz_raise; - impl_class->lower = gdk_surface_quartz_lower; - impl_class->restack_toplevel = gdk_surface_quartz_restack_toplevel; - impl_class->move_resize = gdk_surface_quartz_move_resize; - impl_class->get_geometry = gdk_surface_quartz_get_geometry; - impl_class->get_root_coords = gdk_surface_quartz_get_root_coords; - impl_class->get_device_state = gdk_surface_quartz_get_device_state; - impl_class->shape_combine_region = gdk_surface_quartz_shape_combine_region; - impl_class->input_shape_combine_region = gdk_surface_quartz_input_shape_combine_region; - impl_class->destroy = gdk_quartz_surface_destroy; - impl_class->begin_paint = gdk_surface_impl_quartz_begin_paint; - impl_class->get_scale_factor = gdk_quartz_surface_get_scale_factor; - - impl_class->focus = gdk_quartz_surface_focus; - impl_class->set_type_hint = gdk_quartz_surface_set_type_hint; - impl_class->get_type_hint = gdk_quartz_surface_get_type_hint; - impl_class->set_modal_hint = gdk_quartz_surface_set_modal_hint; - impl_class->set_skip_taskbar_hint = gdk_quartz_surface_set_skip_taskbar_hint; - impl_class->set_skip_pager_hint = gdk_quartz_surface_set_skip_pager_hint; - impl_class->set_urgency_hint = gdk_quartz_surface_set_urgency_hint; - impl_class->set_geometry_hints = gdk_quartz_surface_set_geometry_hints; - impl_class->set_title = gdk_quartz_surface_set_title; - impl_class->set_role = gdk_quartz_surface_set_role; - impl_class->set_startup_id = gdk_quartz_surface_set_startup_id; - impl_class->set_transient_for = gdk_quartz_surface_set_transient_for; - impl_class->get_frame_extents = gdk_quartz_surface_get_frame_extents; - impl_class->set_accept_focus = gdk_quartz_surface_set_accept_focus; - impl_class->set_focus_on_map = gdk_quartz_surface_set_focus_on_map; - impl_class->set_icon_list = gdk_quartz_surface_set_icon_list; - impl_class->set_icon_name = gdk_quartz_surface_set_icon_name; - impl_class->iconify = gdk_quartz_surface_iconify; - impl_class->deiconify = gdk_quartz_surface_deiconify; - impl_class->stick = gdk_quartz_surface_stick; - impl_class->unstick = gdk_quartz_surface_unstick; - impl_class->maximize = gdk_quartz_surface_maximize; - impl_class->unmaximize = gdk_quartz_surface_unmaximize; - impl_class->fullscreen = gdk_quartz_surface_fullscreen; - impl_class->unfullscreen = gdk_quartz_surface_unfullscreen; - impl_class->set_keep_above = gdk_quartz_surface_set_keep_above; - impl_class->set_keep_below = gdk_quartz_surface_set_keep_below; - impl_class->get_group = gdk_quartz_surface_get_group; - impl_class->set_group = gdk_quartz_surface_set_group; - impl_class->set_decorations = gdk_quartz_surface_set_decorations; - impl_class->get_decorations = gdk_quartz_surface_get_decorations; - impl_class->set_functions = gdk_quartz_surface_set_functions; - impl_class->set_functions = gdk_quartz_surface_set_functions; - impl_class->begin_resize_drag = gdk_quartz_surface_begin_resize_drag; - impl_class->begin_move_drag = gdk_quartz_surface_begin_move_drag; - impl_class->set_opacity = gdk_quartz_surface_set_opacity; - impl_class->set_shadow_width = gdk_quartz_surface_set_shadow_width; - impl_class->destroy_notify = gdk_quartz_surface_destroy_notify; - impl_class->register_dnd = _gdk_quartz_surface_register_dnd; - impl_class->drag_begin = _gdk_quartz_surface_drag_begin; - impl_class->process_updates_recurse = _gdk_quartz_surface_process_updates_recurse; - - impl_class->create_gl_context = gdk_quartz_surface_create_gl_context; - - impl_quartz_class->get_context = gdk_surface_impl_quartz_get_context; - impl_quartz_class->release_context = gdk_surface_impl_quartz_release_context; -} - -GType -_gdk_surface_impl_quartz_get_type (void) -{ - static GType object_type = 0; - - if (!object_type) - { - const GTypeInfo object_info = - { - sizeof (GdkSurfaceImplQuartzClass), - (GBaseInitFunc) NULL, - (GBaseFinalizeFunc) NULL, - (GClassInitFunc) gdk_surface_impl_quartz_class_init, - NULL, /* class_finalize */ - NULL, /* class_data */ - sizeof (GdkSurfaceImplQuartz), - 0, /* n_preallocs */ - (GInstanceInitFunc) gdk_surface_impl_quartz_init, - }; - - object_type = g_type_register_static (GDK_TYPE_SURFACE_IMPL, - "GdkSurfaceImplQuartz", - &object_info, 0); - } - - return object_type; -} - -CGContextRef -gdk_quartz_surface_get_context (GdkSurfaceImplQuartz *window, - gboolean antialias) -{ - if (!GDK_SURFACE_IMPL_QUARTZ_GET_CLASS (window)->get_context) - { - g_warning ("%s doesn't implement GdkSurfaceImplQuartzClass::get_context()", - G_OBJECT_TYPE_NAME (window)); - return NULL; - } - - return GDK_SURFACE_IMPL_QUARTZ_GET_CLASS (window)->get_context (window, antialias); -} - -void -gdk_quartz_surface_release_context (GdkSurfaceImplQuartz *window, - CGContextRef cg_context) -{ - if (!GDK_SURFACE_IMPL_QUARTZ_GET_CLASS (window)->release_context) - { - g_warning ("%s doesn't implement GdkSurfaceImplQuartzClass::release_context()", - G_OBJECT_TYPE_NAME (window)); - return; - } - - GDK_SURFACE_IMPL_QUARTZ_GET_CLASS (window)->release_context (window, cg_context); -} - - - -static CGContextRef -gdk_root_window_impl_quartz_get_context (GdkSurfaceImplQuartz *window, - gboolean antialias) -{ - CGColorSpaceRef colorspace; - CGContextRef cg_context; - GdkSurfaceImplQuartz *window_impl = GDK_SURFACE_IMPL_QUARTZ (window); - - if (GDK_SURFACE_DESTROYED (window_impl->wrapper)) - return NULL; - - /* We do not have the notion of a root window on OS X. We fake this - * by creating a 1x1 bitmap and return a context to that. - */ - colorspace = CGColorSpaceCreateWithName (kCGColorSpaceGenericRGB); - cg_context = CGBitmapContextCreate (NULL, - 1, 1, 8, 4, colorspace, - kCGImageAlphaPremultipliedLast); - CGColorSpaceRelease (colorspace); - - return cg_context; -} - -static void -gdk_root_window_impl_quartz_release_context (GdkSurfaceImplQuartz *window, - CGContextRef cg_context) -{ - CGContextRelease (cg_context); -} - -static void -gdk_root_window_impl_quartz_class_init (GdkRootWindowImplQuartzClass *klass) -{ - GdkSurfaceImplQuartzClass *window_quartz_class = GDK_SURFACE_IMPL_QUARTZ_CLASS (klass); - - root_window_parent_class = g_type_class_peek_parent (klass); - - window_quartz_class->get_context = gdk_root_window_impl_quartz_get_context; - window_quartz_class->release_context = gdk_root_window_impl_quartz_release_context; -} - -static void -gdk_root_window_impl_quartz_init (GdkRootWindowImplQuartz *impl) -{ -} - -GType -_gdk_root_window_impl_quartz_get_type (void) -{ - static GType object_type = 0; - - if (!object_type) - { - const GTypeInfo object_info = - { - sizeof (GdkRootWindowImplQuartzClass), - (GBaseInitFunc) NULL, - (GBaseFinalizeFunc) NULL, - (GClassInitFunc) gdk_root_window_impl_quartz_class_init, - NULL, /* class_finalize */ - NULL, /* class_data */ - sizeof (GdkRootWindowImplQuartz), - 0, /* n_preallocs */ - (GInstanceInitFunc) gdk_root_window_impl_quartz_init, - }; - - object_type = g_type_register_static (GDK_TYPE_SURFACE_IMPL_QUARTZ, - "GdkRootWindowQuartz", - &object_info, 0); - } - - return object_type; -} - -GList * -get_toplevels (void) -{ - update_toplevel_order (); - return GDK_SURFACE_IMPL_QUARTZ (_gdk_root->impl)->sorted_children; -} diff --git a/gdk/quartz/gdkwindow-quartz.h b/gdk/quartz/gdkwindow-quartz.h deleted file mode 100644 index cbadd75153..0000000000 --- a/gdk/quartz/gdkwindow-quartz.h +++ /dev/null @@ -1,115 +0,0 @@ -/* gdkdrawable-quartz.h - * - * Copyright (C) 2005 Imendio AB - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * 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, see . - */ - -#ifndef __GDK_SURFACE_QUARTZ_H__ -#define __GDK_SURFACE_QUARTZ_H__ - -#import -#import -#include "gdk/gdkwindowimpl.h" - -G_BEGIN_DECLS - -/* Window implementation for Quartz - */ - -typedef struct _GdkSurfaceImplQuartz GdkSurfaceImplQuartz; -typedef struct _GdkSurfaceImplQuartzClass GdkSurfaceImplQuartzClass; - -#define GDK_TYPE_SURFACE_IMPL_QUARTZ (_gdk_surface_impl_quartz_get_type ()) -#define GDK_SURFACE_IMPL_QUARTZ(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), GDK_TYPE_SURFACE_IMPL_QUARTZ, GdkSurfaceImplQuartz)) -#define GDK_SURFACE_IMPL_QUARTZ_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GDK_TYPE_SURFACE_IMPL_QUARTZ, GdkSurfaceImplQuartzClass)) -#define GDK_IS_SURFACE_IMPL_QUARTZ(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), GDK_TYPE_SURFACE_IMPL_QUARTZ)) -#define GDK_IS_SURFACE_IMPL_QUARTZ_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GDK_TYPE_SURFACE_IMPL_QUARTZ)) -#define GDK_SURFACE_IMPL_QUARTZ_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GDK_TYPE_SURFACE_IMPL_QUARTZ, GdkSurfaceImplQuartzClass)) - -struct _GdkSurfaceImplQuartz -{ - GdkSurfaceImpl parent_instance; - - GdkSurface *wrapper; - - NSWindow *toplevel; - NSTrackingRectTag tracking_rect; - GdkQuartzView *view; - - GdkSurfaceTypeHint type_hint; - - gint in_paint_rect_count; - - GdkSurface *transient_for; - - /* Sorted by z-order */ - GList *sorted_children; - - cairo_region_t *needs_display_region; - - cairo_surface_t *cairo_surface; - - gint shadow_top; - - gint shadow_max; -}; - -struct _GdkSurfaceImplQuartzClass -{ - GdkSurfaceImplClass parent_class; - - CGContextRef (* get_context) (GdkSurfaceImplQuartz *window, - gboolean antialias); - void (* release_context) (GdkSurfaceImplQuartz *window, - CGContextRef cg_context); -}; - -GType _gdk_surface_impl_quartz_get_type (void); - -CGContextRef gdk_quartz_surface_get_context (GdkSurfaceImplQuartz *window, - gboolean antialias); -void gdk_quartz_surface_release_context (GdkSurfaceImplQuartz *window, - CGContextRef context); - -/* Root window implementation for Quartz - */ - -typedef struct _GdkRootWindowImplQuartz GdkRootWindowImplQuartz; -typedef struct _GdkRootWindowImplQuartzClass GdkRootWindowImplQuartzClass; - -#define GDK_TYPE_ROOT_WINDOW_IMPL_QUARTZ (_gdk_root_window_impl_quartz_get_type ()) -#define GDK_ROOT_WINDOW_IMPL_QUARTZ(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), GDK_TYPE_ROOT_WINDOW_IMPL_QUARTZ, GdkRootWindowImplQuartz)) -#define GDK_ROOT_WINDOW_IMPL_QUARTZ_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GDK_TYPE_ROOT_WINDOW_IMPL_QUARTZ, GdkRootWindowImplQuartzClass)) -#define GDK_IS_ROOT_WINDOW_IMPL_QUARTZ(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), GDK_TYPE_ROOT_WINDOW_IMPL_QUARTZ)) -#define GDK_IS_ROOT_WINDOW_IMPL_QUARTZ_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GDK_TYPE_ROOT_WINDOW_IMPL_QUARTZ)) -#define GDK_ROOT_WINDOW_IMPL_QUARTZ_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GDK_TYPE_ROOT_WINDOW_IMPL_QUARTZ, GdkRootWindowImplQuartzClass)) - -struct _GdkRootWindowImplQuartz -{ - GdkSurfaceImplQuartz parent_instance; -}; - -struct _GdkRootWindowImplQuartzClass -{ - GdkSurfaceImplQuartzClass parent_class; -}; - -GType _gdk_root_window_impl_quartz_get_type (void); - -GList *get_toplevels (void); - -G_END_DECLS - -#endif /* __GDK_SURFACE_QUARTZ_H__ */ diff --git a/gdk/quartz/meson.build b/gdk/quartz/meson.build index acacd791a8..8d2e0fc2c3 100644 --- a/gdk/quartz/meson.build +++ b/gdk/quartz/meson.build @@ -16,7 +16,7 @@ gdk_quartz_sources = files([ 'gdkscreen-quartz.c', 'gdkselection-quartz.c', 'gdkutils-quartz.c', - 'gdkwindow-quartz.c', + 'gdksurface-quartz.c', ]) gdk_quartz_public_headers = files([ @@ -29,7 +29,7 @@ gdk_quartz_public_headers = files([ 'gdkquartzkeys.h', 'gdkquartzscreen.h', 'gdkquartzutils.h', - 'gdkquartzwindow.h', + 'gdkquartzsurface.h', ]) install_headers(gdk_quartz_public_headers, subdir: 'gtk-4.0/gdk/quartz/') diff --git a/gdk/wayland/gdkdevice-wayland.c b/gdk/wayland/gdkdevice-wayland.c index 7e9de9b08b..d4bbd2ef0d 100644 --- a/gdk/wayland/gdkdevice-wayland.c +++ b/gdk/wayland/gdkdevice-wayland.c @@ -22,7 +22,7 @@ #include #include -#include +#include #include #include "gdkclipboard-wayland.h" #include "gdkclipboardprivate.h" diff --git a/gdk/wayland/gdkdisplay-wayland.h b/gdk/wayland/gdkdisplay-wayland.h index 5b0fa7918b..625b5e2f4a 100644 --- a/gdk/wayland/gdkdisplay-wayland.h +++ b/gdk/wayland/gdkdisplay-wayland.h @@ -37,7 +37,7 @@ #include #include -#include +#include #include #include /* For gdk_get_program_class() */ diff --git a/gdk/wayland/gdkglcontext-wayland.c b/gdk/wayland/gdkglcontext-wayland.c index 6df899156a..9fe7d8447e 100644 --- a/gdk/wayland/gdkglcontext-wayland.c +++ b/gdk/wayland/gdkglcontext-wayland.c @@ -26,7 +26,7 @@ #include "gdkwaylanddisplay.h" #include "gdkwaylandglcontext.h" -#include "gdkwaylandwindow.h" +#include "gdkwaylandsurface.h" #include "gdkprivate-wayland.h" #include "gdkinternals.h" diff --git a/gdk/wayland/gdkglcontext-wayland.h b/gdk/wayland/gdkglcontext-wayland.h index 3b98c3b7cf..e402994979 100644 --- a/gdk/wayland/gdkglcontext-wayland.h +++ b/gdk/wayland/gdkglcontext-wayland.h @@ -24,7 +24,7 @@ #include "gdkglcontextprivate.h" #include "gdkdisplayprivate.h" -#include "gdkwindow.h" +#include "gdksurface.h" #include "gdkinternals.h" #include diff --git a/gdk/wayland/gdksurface-wayland.c b/gdk/wayland/gdksurface-wayland.c new file mode 100644 index 0000000000..99d02bf971 --- /dev/null +++ b/gdk/wayland/gdksurface-wayland.c @@ -0,0 +1,4206 @@ +/* + * Copyright © 2010 Intel Corporation + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * 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, see . + */ + +#include "config.h" + +#include +#include + +#include "gdk.h" +#include "gdkwayland.h" + +#include "gdksurface.h" +#include "gdksurfaceimpl.h" +#include "gdkdisplay-wayland.h" +#include "gdkglcontext-wayland.h" +#include "gdkframeclockprivate.h" +#include "gdkprivate-wayland.h" +#include "gdkinternals.h" +#include "gdkdeviceprivate.h" +#include "gdkprivate-wayland.h" +#include "gdkmonitor-wayland.h" +#include + +#include +#include +#include +#include + +enum { + COMMITTED, + + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL]; + +#define WINDOW_IS_TOPLEVEL(window) \ + (GDK_SURFACE_TYPE (window) != GDK_SURFACE_CHILD) + +#define MAX_WL_BUFFER_SIZE (4083) /* 4096 minus header, string argument length and NUL byte */ + +typedef struct _GdkWaylandSurface GdkWaylandSurface; +typedef struct _GdkWaylandSurfaceClass GdkWaylandSurfaceClass; + +struct _GdkWaylandSurface +{ + GdkSurface parent; +}; + +struct _GdkWaylandSurfaceClass +{ + GdkSurfaceClass parent_class; +}; + +G_DEFINE_TYPE (GdkWaylandSurface, gdk_wayland_surface, GDK_TYPE_SURFACE) + +static void +gdk_wayland_surface_class_init (GdkWaylandSurfaceClass *wayland_surface_class) +{ +} + +static void +gdk_wayland_surface_init (GdkWaylandSurface *wayland_surface) +{ +} + +#define GDK_TYPE_SURFACE_IMPL_WAYLAND (_gdk_surface_impl_wayland_get_type ()) +#define GDK_SURFACE_IMPL_WAYLAND(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), GDK_TYPE_SURFACE_IMPL_WAYLAND, GdkSurfaceImplWayland)) +#define GDK_SURFACE_IMPL_WAYLAND_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GDK_TYPE_SURFACE_IMPL_WAYLAND, GdkSurfaceImplWaylandClass)) +#define GDK_IS_SURFACE_IMPL_WAYLAND(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), GDK_TYPE_SURFACE_IMPL_WAYLAND)) +#define GDK_IS_SURFACE_IMPL_WAYLAND_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GDK_TYPE_SURFACE_IMPL_WAYLAND)) +#define GDK_SURFACE_IMPL_WAYLAND_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GDK_TYPE_SURFACE_IMPL_WAYLAND, GdkSurfaceImplWaylandClass)) + +typedef struct _GdkSurfaceImplWayland GdkSurfaceImplWayland; +typedef struct _GdkSurfaceImplWaylandClass GdkSurfaceImplWaylandClass; + +typedef enum _PositionMethod +{ + POSITION_METHOD_NONE, + POSITION_METHOD_MOVE_RESIZE, + POSITION_METHOD_MOVE_TO_RECT +} PositionMethod; + +struct _GdkSurfaceImplWayland +{ + GdkSurfaceImpl parent_instance; + + GdkSurface *wrapper; + + struct { + /* The wl_outputs that this window currently touches */ + GSList *outputs; + + struct wl_surface *wl_surface; + struct zxdg_surface_v6 *xdg_surface; + struct zxdg_toplevel_v6 *xdg_toplevel; + struct zxdg_popup_v6 *xdg_popup; + struct gtk_surface1 *gtk_surface; + struct wl_subsurface *wl_subsurface; + struct wl_egl_window *egl_window; + struct wl_egl_window *dummy_egl_window; + struct zxdg_exported_v1 *xdg_exported; + struct org_kde_kwin_server_decoration *server_decoration; + } display_server; + + EGLSurface egl_surface; + EGLSurface dummy_egl_surface; + + unsigned int initial_configure_received : 1; + unsigned int mapped : 1; + unsigned int use_custom_surface : 1; + unsigned int pending_buffer_attached : 1; + unsigned int pending_commit : 1; + unsigned int awaiting_frame : 1; + GdkSurfaceTypeHint hint; + GdkSurface *transient_for; + GdkSurface *popup_parent; + PositionMethod position_method; + + cairo_surface_t *staging_cairo_surface; + cairo_surface_t *committed_cairo_surface; + cairo_surface_t *backfill_cairo_surface; + + int pending_buffer_offset_x; + int pending_buffer_offset_y; + + gchar *title; + + struct { + gboolean was_set; + + gchar *application_id; + gchar *app_menu_path; + gchar *menubar_path; + gchar *window_object_path; + gchar *application_object_path; + gchar *unique_bus_name; + } application; + + GdkGeometry geometry_hints; + GdkSurfaceHints geometry_mask; + + GdkSeat *grab_input_seat; + + gint64 pending_frame_counter; + guint32 scale; + + int margin_left; + int margin_right; + int margin_top; + int margin_bottom; + gboolean margin_dirty; + + struct wl_output *initial_fullscreen_output; + + cairo_region_t *opaque_region; + gboolean opaque_region_dirty; + + cairo_region_t *input_region; + gboolean input_region_dirty; + + cairo_region_t *staged_updates_region; + + int saved_width; + int saved_height; + + gulong parent_surface_committed_handler; + + struct { + GdkRectangle rect; + GdkGravity rect_anchor; + GdkGravity window_anchor; + GdkAnchorHints anchor_hints; + gint rect_anchor_dx; + gint rect_anchor_dy; + } pending_move_to_rect; + + struct { + int width; + int height; + GdkSurfaceState state; + } pending; + + struct { + GdkWaylandSurfaceExported callback; + gpointer user_data; + GDestroyNotify destroy_func; + } exported; + + struct zxdg_imported_v1 *imported_transient_for; + GHashTable *shortcuts_inhibitors; +}; + +struct _GdkSurfaceImplWaylandClass +{ + GdkSurfaceImplClass parent_class; +}; + +static void gdk_wayland_surface_maybe_configure (GdkSurface *window, + int width, + int height, + int scale); + +static void maybe_set_gtk_surface_dbus_properties (GdkSurface *window); +static void maybe_set_gtk_surface_modal (GdkSurface *window); + +static void gdk_surface_request_transient_parent_commit (GdkSurface *window); + +static void gdk_wayland_surface_sync_margin (GdkSurface *window); +static void gdk_wayland_surface_sync_input_region (GdkSurface *window); +static void gdk_wayland_surface_sync_opaque_region (GdkSurface *window); + +static void unset_transient_for_exported (GdkSurface *window); + +static void calculate_moved_to_rect_result (GdkSurface *window, + int x, + int y, + int width, + int height, + GdkRectangle *flipped_rect, + GdkRectangle *final_rect, + gboolean *flipped_x, + gboolean *flipped_y); + +static gboolean gdk_wayland_surface_is_exported (GdkSurface *window); + +GType _gdk_surface_impl_wayland_get_type (void); + +G_DEFINE_TYPE (GdkSurfaceImplWayland, _gdk_surface_impl_wayland, GDK_TYPE_SURFACE_IMPL) + +static void +_gdk_surface_impl_wayland_init (GdkSurfaceImplWayland *impl) +{ + impl->scale = 1; + impl->initial_fullscreen_output = NULL; + impl->saved_width = -1; + impl->saved_height = -1; +} + +static void +_gdk_wayland_screen_add_orphan_dialog (GdkSurface *window) +{ + GdkWaylandDisplay *display_wayland = + GDK_WAYLAND_DISPLAY (gdk_surface_get_display (window)); + + if (!g_list_find (display_wayland->orphan_dialogs, window)) + display_wayland->orphan_dialogs = + g_list_prepend (display_wayland->orphan_dialogs, window); +} + +static void +drop_cairo_surfaces (GdkSurface *window) +{ + GdkSurfaceImplWayland *impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); + + g_clear_pointer (&impl->staging_cairo_surface, cairo_surface_destroy); + g_clear_pointer (&impl->backfill_cairo_surface, cairo_surface_destroy); + + /* We nullify this so if a buffer release comes in later, we won't + * try to reuse that buffer since it's no longer suitable + */ + impl->committed_cairo_surface = NULL; +} + +static void +_gdk_wayland_surface_save_size (GdkSurface *window) +{ + GdkSurfaceImplWayland *impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); + + if (window->state & (GDK_SURFACE_STATE_FULLSCREEN | GDK_SURFACE_STATE_MAXIMIZED)) + return; + + impl->saved_width = window->width - impl->margin_left - impl->margin_right; + impl->saved_height = window->height - impl->margin_top - impl->margin_bottom; +} + +static void +_gdk_wayland_surface_clear_saved_size (GdkSurface *window) +{ + GdkSurfaceImplWayland *impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); + + if (window->state & (GDK_SURFACE_STATE_FULLSCREEN | GDK_SURFACE_STATE_MAXIMIZED)) + return; + + impl->saved_width = -1; + impl->saved_height = -1; +} + +/* + * gdk_wayland_surface_update_size: + * @drawable: a #GdkDrawableImplWayland. + * + * Updates the state of the drawable (in particular the drawable's + * cairo surface) when its size has changed. + */ +static void +gdk_wayland_surface_update_size (GdkSurface *window, + int32_t width, + int32_t height, + int scale) +{ + GdkSurfaceImplWayland *impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); + GdkRectangle area; + cairo_region_t *region; + + if ((window->width == width) && + (window->height == height) && + (impl->scale == scale)) + return; + + drop_cairo_surfaces (window); + + window->width = width; + window->height = height; + impl->scale = scale; + + if (impl->display_server.egl_window) + wl_egl_window_resize (impl->display_server.egl_window, width * scale, height * scale, 0, 0); + if (impl->display_server.wl_surface) + wl_surface_set_buffer_scale (impl->display_server.wl_surface, scale); + + area.x = 0; + area.y = 0; + area.width = window->width; + area.height = window->height; + + region = cairo_region_create_rectangle (&area); + _gdk_surface_invalidate_for_expose (window, region); + cairo_region_destroy (region); +} + +static const gchar * +get_default_title (void) +{ + const char *title; + + title = g_get_application_name (); + if (!title) + title = g_get_prgname (); + if (!title) + title = ""; + + return title; +} + +static void +fill_presentation_time_from_frame_time (GdkFrameTimings *timings, + guint32 frame_time) +{ + /* The timestamp in a wayland frame is a msec time value that in some + * way reflects the time at which the server started drawing the frame. + * This is not useful from our perspective. + * + * However, for the DRM backend of Weston, on reasonably recent + * Linux, we know that the time is the + * clock_gettime (CLOCK_MONOTONIC) value at the vblank, and that + * backend starts drawing immediately after receiving the vblank + * notification. If we detect this, and make the assumption that the + * compositor will finish drawing before the next vblank, we can + * then determine the presentation time as the frame time we + * received plus one refresh interval. + * + * If a backend is using clock_gettime(CLOCK_MONOTONIC), but not + * picking values right at the vblank, then the presentation times + * we compute won't be accurate, but not really worse than then + * the alternative of not providing presentation times at all. + * + * The complexity here is dealing with the fact that we receive + * only the low 32 bits of the CLOCK_MONOTONIC value in milliseconds. + */ + gint64 now_monotonic = g_get_monotonic_time (); + gint64 now_monotonic_msec = now_monotonic / 1000; + uint32_t now_monotonic_low = (uint32_t)now_monotonic_msec; + + if (frame_time - now_monotonic_low < 1000 || + frame_time - now_monotonic_low > (uint32_t)-1000) + { + /* Timestamp we received is within one second of the current time. + */ + gint64 last_frame_time = now_monotonic + (gint64)1000 * (gint32)(frame_time - now_monotonic_low); + if ((gint32)now_monotonic_low < 0 && (gint32)frame_time > 0) + last_frame_time += (gint64)1000 * G_GINT64_CONSTANT(0x100000000); + else if ((gint32)now_monotonic_low > 0 && (gint32)frame_time < 0) + last_frame_time -= (gint64)1000 * G_GINT64_CONSTANT(0x100000000); + + timings->presentation_time = last_frame_time + timings->refresh_interval; + } +} + +static void +read_back_cairo_surface (GdkSurface *window) +{ + GdkSurfaceImplWayland *impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); + cairo_t *cr; + cairo_region_t *paint_region = NULL; + + if (!impl->backfill_cairo_surface) + goto out; + + paint_region = cairo_region_copy (window->clip_region); + cairo_region_subtract (paint_region, impl->staged_updates_region); + + if (cairo_region_is_empty (paint_region)) + goto out; + + cr = cairo_create (impl->staging_cairo_surface); + cairo_set_source_surface (cr, impl->backfill_cairo_surface, 0, 0); + gdk_cairo_region (cr, paint_region); + cairo_clip (cr); + cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE); + cairo_paint (cr); + cairo_destroy (cr); + cairo_surface_flush (impl->staging_cairo_surface); + +out: + g_clear_pointer (&paint_region, cairo_region_destroy); + g_clear_pointer (&impl->staged_updates_region, cairo_region_destroy); + g_clear_pointer (&impl->backfill_cairo_surface, cairo_surface_destroy); +} + +static void +frame_callback (void *data, + struct wl_callback *callback, + uint32_t time) +{ + GdkSurface *window = data; + GdkSurfaceImplWayland *impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); + GdkWaylandDisplay *display_wayland = + GDK_WAYLAND_DISPLAY (gdk_surface_get_display (window)); + GdkFrameClock *clock = gdk_surface_get_frame_clock (window); + GdkFrameTimings *timings; + + GDK_DISPLAY_NOTE (GDK_DISPLAY (display_wayland), EVENTS, g_message ("frame %p", window)); + + wl_callback_destroy (callback); + + if (GDK_SURFACE_DESTROYED (window)) + return; + + if (!impl->awaiting_frame) + return; + + impl->awaiting_frame = FALSE; + _gdk_frame_clock_thaw (clock); + + timings = gdk_frame_clock_get_timings (clock, impl->pending_frame_counter); + impl->pending_frame_counter = 0; + + if (timings == NULL) + return; + + timings->refresh_interval = 16667; /* default to 1/60th of a second */ + if (impl->display_server.outputs) + { + /* We pick a random output out of the outputs that the window touches + * The rate here is in milli-hertz */ + int refresh_rate = + gdk_wayland_display_get_output_refresh_rate (display_wayland, + impl->display_server.outputs->data); + if (refresh_rate != 0) + timings->refresh_interval = G_GINT64_CONSTANT(1000000000) / refresh_rate; + } + + fill_presentation_time_from_frame_time (timings, time); + + timings->complete = TRUE; + +#ifdef G_ENABLE_DEBUG + if ((_gdk_debug_flags & GDK_DEBUG_FRAMES) != 0) + _gdk_frame_clock_debug_print_timings (clock, timings); +#endif +} + +static const struct wl_callback_listener frame_listener = { + frame_callback +}; + +static void +on_frame_clock_before_paint (GdkFrameClock *clock, + GdkSurface *window) +{ + GdkFrameTimings *timings = gdk_frame_clock_get_current_timings (clock); + gint64 presentation_time; + gint64 refresh_interval; + + if (window->update_freeze_count > 0) + return; + + gdk_frame_clock_get_refresh_info (clock, + timings->frame_time, + &refresh_interval, &presentation_time); + + if (presentation_time != 0) + { + /* Assume the algorithm used by the DRM backend of Weston - it + * starts drawing at the next vblank after receiving the commit + * for this frame, and presentation occurs at the vblank + * after that. + */ + timings->predicted_presentation_time = presentation_time + refresh_interval; + } + else + { + /* As above, but we don't actually know the phase of the vblank, + * so just assume that we're half way through a refresh cycle. + */ + timings->predicted_presentation_time = timings->frame_time + refresh_interval / 2 + refresh_interval; + } +} + +static void +on_frame_clock_after_paint (GdkFrameClock *clock, + GdkSurface *window) +{ + GdkSurfaceImplWayland *impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); + struct wl_callback *callback; + + if (!impl->pending_commit) + return; + + if (window->update_freeze_count > 0) + return; + + callback = wl_surface_frame (impl->display_server.wl_surface); + wl_callback_add_listener (callback, &frame_listener, window); + _gdk_frame_clock_freeze (clock); + + /* Before we commit a new buffer, make sure we've backfilled + * undrawn parts from any old committed buffer + */ + if (impl->pending_buffer_attached) + read_back_cairo_surface (window); + + /* From this commit forward, we can't write to the buffer, + * it's "live". In the future, if we need to stage more changes + * we have to allocate a new staging buffer and draw to it instead. + * + * Our one saving grace is if the compositor releases the buffer + * before we need to stage any changes, then we can take it back and + * use it again. + */ + wl_surface_commit (impl->display_server.wl_surface); + + if (impl->pending_buffer_attached) + impl->committed_cairo_surface = g_steal_pointer (&impl->staging_cairo_surface); + + impl->pending_buffer_attached = FALSE; + impl->pending_commit = FALSE; + impl->pending_frame_counter = gdk_frame_clock_get_frame_counter (clock); + impl->awaiting_frame = TRUE; + + g_signal_emit (impl, signals[COMMITTED], 0); +} + +void +gdk_wayland_surface_update_scale (GdkSurface *window) +{ + GdkSurfaceImplWayland *impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); + GdkWaylandDisplay *display_wayland = GDK_WAYLAND_DISPLAY (gdk_surface_get_display (window)); + guint32 scale; + GSList *l; + GList *children, *c; + + if (display_wayland->compositor_version < WL_SURFACE_HAS_BUFFER_SCALE) + { + /* We can't set the scale on this surface */ + return; + } + + scale = 1; + for (l = impl->display_server.outputs; l != NULL; l = l->next) + { + guint32 output_scale = gdk_wayland_display_get_output_scale (display_wayland, l->data); + scale = MAX (scale, output_scale); + } + + /* Notify app that scale changed */ + gdk_wayland_surface_maybe_configure (window, window->width, window->height, scale); + + children = gdk_surface_get_children (window); + for (c = children; c; c = c->next) + { + GdkSurface *child = c->data; + gdk_wayland_surface_update_scale (child); + } + g_list_free (children); +} + +static void gdk_wayland_surface_create_surface (GdkSurface *window); + +void +_gdk_wayland_display_create_window_impl (GdkDisplay *display, + GdkSurface *window, + GdkSurface *real_parent, + GdkEventMask event_mask, + GdkSurfaceAttr *attributes) +{ + GdkWaylandDisplay *display_wayland = GDK_WAYLAND_DISPLAY (display); + GdkSurfaceImplWayland *impl; + GdkFrameClock *frame_clock; + + impl = g_object_new (GDK_TYPE_SURFACE_IMPL_WAYLAND, NULL); + window->impl = GDK_SURFACE_IMPL (impl); + impl->wrapper = GDK_SURFACE (window); + impl->shortcuts_inhibitors = g_hash_table_new (NULL, NULL); + + if (window->width > 65535) + { + g_warning ("Native Windows wider than 65535 pixels are not supported"); + window->width = 65535; + } + if (window->height > 65535) + { + g_warning ("Native Windows taller than 65535 pixels are not supported"); + window->height = 65535; + } + + g_object_ref (window); + + /* More likely to be right than just assuming 1 */ + if (display_wayland->compositor_version >= WL_SURFACE_HAS_BUFFER_SCALE && + gdk_display_get_n_monitors (display) > 0) + impl->scale = gdk_monitor_get_scale_factor (gdk_display_get_monitor (display, 0)); + + impl->title = NULL; + + switch (GDK_SURFACE_TYPE (window)) + { + case GDK_SURFACE_TOPLEVEL: + case GDK_SURFACE_TEMP: + gdk_surface_set_title (window, get_default_title ()); + break; + + case GDK_SURFACE_CHILD: + default: + break; + } + + if (real_parent == NULL) + display_wayland->toplevels = g_list_prepend (display_wayland->toplevels, window); + + gdk_wayland_surface_create_surface (window); + + frame_clock = gdk_surface_get_frame_clock (window); + g_signal_connect (frame_clock, "before-paint", G_CALLBACK (on_frame_clock_before_paint), window); + g_signal_connect (frame_clock, "after-paint", G_CALLBACK (on_frame_clock_after_paint), window); +} + +static void +gdk_wayland_surface_attach_image (GdkSurface *window) +{ + GdkWaylandDisplay *display; + GdkSurfaceImplWayland *impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); + + if (GDK_SURFACE_DESTROYED (window)) + return; + + g_assert (_gdk_wayland_is_shm_surface (impl->staging_cairo_surface)); + + /* Attach this new buffer to the surface */ + wl_surface_attach (impl->display_server.wl_surface, + _gdk_wayland_shm_surface_get_wl_buffer (impl->staging_cairo_surface), + impl->pending_buffer_offset_x, + impl->pending_buffer_offset_y); + impl->pending_buffer_offset_x = 0; + impl->pending_buffer_offset_y = 0; + + /* Only set the buffer scale if supported by the compositor */ + display = GDK_WAYLAND_DISPLAY (gdk_surface_get_display (window)); + if (display->compositor_version >= WL_SURFACE_HAS_BUFFER_SCALE) + wl_surface_set_buffer_scale (impl->display_server.wl_surface, impl->scale); + + impl->pending_buffer_attached = TRUE; + impl->pending_commit = TRUE; +} + +static const cairo_user_data_key_t gdk_wayland_surface_cairo_key; + +static void +buffer_release_callback (void *_data, + struct wl_buffer *wl_buffer) +{ + cairo_surface_t *cairo_surface = _data; + GdkSurfaceImplWayland *impl = cairo_surface_get_user_data (cairo_surface, &gdk_wayland_surface_cairo_key); + + g_return_if_fail (GDK_IS_SURFACE_IMPL_WAYLAND (impl)); + + /* The released buffer isn't the latest committed one, we have no further + * use for it, so clean it up. + */ + if (impl->committed_cairo_surface != cairo_surface) + { + /* If this fails, then the surface buffer got reused before it was + * released from the compositor + */ + g_warn_if_fail (impl->staging_cairo_surface != cairo_surface); + + cairo_surface_destroy (cairo_surface); + return; + } + + if (impl->staged_updates_region != NULL) + { + /* If this fails, then we're tracking staged updates on a staging surface + * that doesn't exist. + */ + g_warn_if_fail (impl->staging_cairo_surface != NULL); + + /* If we've staged updates into a new buffer before the release for this + * buffer came in, then we can't reuse this buffer, so unref it. It may still + * be alive as a readback buffer though (via impl->backfill_cairo_surface). + * + * It's possible a staging surface was allocated but no updates were staged. + * If that happened, clean up that staging surface now, since the old commit + * buffer is available again, and reusing the old commit buffer for future + * updates will save having to do a read back later. + */ + if (!cairo_region_is_empty (impl->staged_updates_region)) + { + g_clear_pointer (&impl->committed_cairo_surface, cairo_surface_destroy); + return; + } + else + { + g_clear_pointer (&impl->staged_updates_region, cairo_region_destroy); + g_clear_pointer (&impl->staging_cairo_surface, cairo_surface_destroy); + } + } + + /* Release came in, we haven't done any interim updates, so we can just use + * the old committed buffer again. + */ + impl->staging_cairo_surface = g_steal_pointer (&impl->committed_cairo_surface); +} + +static const struct wl_buffer_listener buffer_listener = { + buffer_release_callback +}; + +static void +gdk_wayland_surface_ensure_cairo_surface (GdkSurface *window) +{ + GdkSurfaceImplWayland *impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); + + /* If we are drawing using OpenGL then we only need a logical 1x1 surface. */ + if (impl->display_server.egl_window) + { + if (impl->staging_cairo_surface && + _gdk_wayland_is_shm_surface (impl->staging_cairo_surface)) + g_clear_pointer (&impl->staging_cairo_surface, cairo_surface_destroy); + + if (!impl->staging_cairo_surface) + { + impl->staging_cairo_surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, + impl->scale, + impl->scale); + cairo_surface_set_device_scale (impl->staging_cairo_surface, + impl->scale, impl->scale); + } + } + else if (!impl->staging_cairo_surface) + { + GdkWaylandDisplay *display_wayland = GDK_WAYLAND_DISPLAY (gdk_surface_get_display (impl->wrapper)); + struct wl_buffer *buffer; + + impl->staging_cairo_surface = _gdk_wayland_display_create_shm_surface (display_wayland, + impl->wrapper->width, + impl->wrapper->height, + impl->scale); + cairo_surface_set_user_data (impl->staging_cairo_surface, + &gdk_wayland_surface_cairo_key, + g_object_ref (impl), + (cairo_destroy_func_t) + g_object_unref); + buffer = _gdk_wayland_shm_surface_get_wl_buffer (impl->staging_cairo_surface); + wl_buffer_add_listener (buffer, &buffer_listener, impl->staging_cairo_surface); + } +} + +/* The cairo surface returned here uses a memory segment that's shared + * with the display server. This is not a temporary buffer that gets + * copied to the display server, but the actual buffer the display server + * will ultimately end up sending to the GPU. At the time this happens + * impl->committed_cairo_surface gets set to impl->staging_cairo_surface, and + * impl->staging_cairo_surface gets nullified. + */ +static cairo_surface_t * +gdk_wayland_surface_ref_cairo_surface (GdkSurface *window) +{ + GdkSurfaceImplWayland *impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); + + if (GDK_SURFACE_DESTROYED (impl->wrapper)) + return NULL; + + gdk_wayland_surface_ensure_cairo_surface (window); + + cairo_surface_reference (impl->staging_cairo_surface); + + return impl->staging_cairo_surface; +} + +static cairo_surface_t * +gdk_wayland_surface_create_similar_image_surface (GdkSurface * window, + cairo_format_t format, + int width, + int height) +{ + return cairo_image_surface_create (format, width, height); +} + +static gboolean +gdk_surface_impl_wayland_begin_paint (GdkSurface *window) +{ + gdk_wayland_surface_ensure_cairo_surface (window); + + return FALSE; +} + +static void +gdk_surface_impl_wayland_end_paint (GdkSurface *window) +{ + GdkSurfaceImplWayland *impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); + cairo_rectangle_int_t rect; + int i, n; + + if (impl->staging_cairo_surface && + _gdk_wayland_is_shm_surface (impl->staging_cairo_surface) && + !cairo_region_is_empty (window->current_paint.region)) + { + gdk_wayland_surface_attach_image (window); + + /* If there's a committed buffer pending, then track which + * updates are staged until the next frame, so we can back + * fill the unstaged parts of the staging buffer with the + * last frame. + */ + if (impl->committed_cairo_surface != NULL) + { + if (impl->staged_updates_region == NULL) + { + impl->staged_updates_region = cairo_region_copy (window->current_paint.region); + impl->backfill_cairo_surface = cairo_surface_reference (impl->committed_cairo_surface); + } + else + { + cairo_region_union (impl->staged_updates_region, window->current_paint.region); + } + } + + n = cairo_region_num_rectangles (window->current_paint.region); + for (i = 0; i < n; i++) + { + cairo_region_get_rectangle (window->current_paint.region, i, &rect); + wl_surface_damage (impl->display_server.wl_surface, rect.x, rect.y, rect.width, rect.height); + } + + impl->pending_commit = TRUE; + } + + gdk_wayland_surface_sync (window); +} + +void +gdk_wayland_surface_sync (GdkSurface *window) +{ + gdk_wayland_surface_sync_margin (window); + gdk_wayland_surface_sync_opaque_region (window); + gdk_wayland_surface_sync_input_region (window); +} + +static gboolean +gdk_surface_impl_wayland_beep (GdkSurface *window) +{ + gdk_wayland_display_system_bell (gdk_surface_get_display (window), + window); + + return TRUE; +} + +static void +gdk_surface_impl_wayland_finalize (GObject *object) +{ + GdkSurface *window = GDK_SURFACE (object); + GdkSurfaceImplWayland *impl; + + g_return_if_fail (GDK_IS_SURFACE_IMPL_WAYLAND (object)); + + impl = GDK_SURFACE_IMPL_WAYLAND (object); + + if (gdk_wayland_surface_is_exported (window)) + gdk_wayland_surface_unexport_handle (window); + + g_free (impl->title); + + g_free (impl->application.application_id); + g_free (impl->application.app_menu_path); + g_free (impl->application.menubar_path); + g_free (impl->application.window_object_path); + g_free (impl->application.application_object_path); + g_free (impl->application.unique_bus_name); + + g_clear_pointer (&impl->opaque_region, cairo_region_destroy); + g_clear_pointer (&impl->input_region, cairo_region_destroy); + g_clear_pointer (&impl->staged_updates_region, cairo_region_destroy); + + g_hash_table_destroy (impl->shortcuts_inhibitors); + + G_OBJECT_CLASS (_gdk_surface_impl_wayland_parent_class)->finalize (object); +} + +static void +gdk_wayland_surface_configure (GdkSurface *window, + int width, + int height, + int scale) +{ + GdkDisplay *display; + GdkEvent *event; + + event = gdk_event_new (GDK_CONFIGURE); + event->any.window = g_object_ref (window); + event->any.send_event = FALSE; + event->configure.width = width; + event->configure.height = height; + + gdk_wayland_surface_update_size (window, width, height, scale); + _gdk_surface_update_size (window); + + display = gdk_surface_get_display (window); + _gdk_wayland_display_deliver_event (display, event); +} + +static void +gdk_wayland_surface_maybe_configure (GdkSurface *window, + int width, + int height, + int scale) +{ + GdkSurfaceImplWayland *impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); + gboolean is_xdg_popup; + gboolean is_visible; + + if (window->width == width && + window->height == height && + impl->scale == scale) + return; + + /* For xdg_popup using an xdg_positioner, there is a race condition if + * the application tries to change the size after it's mapped, but before + * the initial configure is received, so hide and show the surface again + * force the new size onto the compositor. See bug #772505. + */ + + is_xdg_popup = (impl->display_server.xdg_popup != NULL); + is_visible = gdk_surface_is_visible (window); + + if (is_xdg_popup && is_visible && !impl->initial_configure_received) + gdk_surface_hide (window); + + gdk_wayland_surface_configure (window, width, height, scale); + + if (is_xdg_popup && is_visible && !impl->initial_configure_received) + gdk_surface_show (window); +} + +static void +gdk_wayland_surface_sync_parent (GdkSurface *window, + GdkSurface *parent) +{ + GdkSurfaceImplWayland *impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); + GdkSurfaceImplWayland *impl_parent = NULL; + struct zxdg_toplevel_v6 *parent_toplevel; + + g_assert (parent == NULL || + gdk_surface_get_display (window) == gdk_surface_get_display (parent)); + + if (!impl->display_server.xdg_toplevel) + return; + + if (impl->transient_for) + impl_parent = GDK_SURFACE_IMPL_WAYLAND (impl->transient_for->impl); + else if (parent) + impl_parent = GDK_SURFACE_IMPL_WAYLAND (parent->impl); + + if (impl_parent) + { + /* XXX: Is this correct? */ + if (!impl_parent->display_server.wl_surface) + return; + + parent_toplevel = impl_parent->display_server.xdg_toplevel; + } + else + parent_toplevel = NULL; + + zxdg_toplevel_v6_set_parent (impl->display_server.xdg_toplevel, + parent_toplevel); +} + +static void +gdk_wayland_surface_sync_parent_of_imported (GdkSurface *window) +{ + GdkSurfaceImplWayland *impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); + + if (!impl->display_server.wl_surface) + return; + + if (!impl->imported_transient_for) + return; + + if (!impl->display_server.xdg_toplevel) + return; + + zxdg_imported_v1_set_parent_of (impl->imported_transient_for, + impl->display_server.wl_surface); +} + +static void +gdk_wayland_surface_update_dialogs (GdkSurface *window) +{ + GdkWaylandDisplay *display_wayland = + GDK_WAYLAND_DISPLAY (gdk_surface_get_display (window)); + GList *l; + + if (!display_wayland->orphan_dialogs) + return; + + for (l = display_wayland->orphan_dialogs; l; l = l->next) + { + GdkSurface *w = l->data; + GdkSurfaceImplWayland *impl; + + if (!GDK_IS_SURFACE_IMPL_WAYLAND(w->impl)) + continue; + + impl = GDK_SURFACE_IMPL_WAYLAND (w->impl); + if (w == window) + continue; + if (impl->hint != GDK_SURFACE_TYPE_HINT_DIALOG) + continue; + if (impl->transient_for) + continue; + + /* Update the parent relationship only for dialogs without transients */ + gdk_wayland_surface_sync_parent (w, window); + } +} + +static void +gdk_wayland_surface_sync_title (GdkSurface *window) +{ + GdkSurfaceImplWayland *impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); + + if (!impl->display_server.xdg_toplevel) + return; + + if (!impl->title) + return; + + zxdg_toplevel_v6_set_title (impl->display_server.xdg_toplevel, impl->title); +} + +static void +gdk_wayland_surface_get_window_geometry (GdkSurface *window, + GdkRectangle *geometry) +{ + GdkSurfaceImplWayland *impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); + + *geometry = (GdkRectangle) { + .x = impl->margin_left, + .y = impl->margin_top, + .width = window->width - (impl->margin_left + impl->margin_right), + .height = window->height - (impl->margin_top + impl->margin_bottom) + }; +} + +static void +gdk_wayland_surface_sync_margin (GdkSurface *window) +{ + GdkSurfaceImplWayland *impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); + GdkRectangle geometry; + + if (!impl->display_server.xdg_surface) + return; + + gdk_wayland_surface_get_window_geometry (window, &geometry); + gdk_surface_set_geometry_hints (window, + &impl->geometry_hints, + impl->geometry_mask); + zxdg_surface_v6_set_window_geometry (impl->display_server.xdg_surface, + geometry.x, + geometry.y, + geometry.width, + geometry.height); +} + +static struct wl_region * +wl_region_from_cairo_region (GdkWaylandDisplay *display, + cairo_region_t *region) +{ + struct wl_region *wl_region; + int i, n_rects; + + wl_region = wl_compositor_create_region (display->compositor); + if (wl_region == NULL) + return NULL; + + n_rects = cairo_region_num_rectangles (region); + for (i = 0; i < n_rects; i++) + { + cairo_rectangle_int_t rect; + cairo_region_get_rectangle (region, i, &rect); + wl_region_add (wl_region, rect.x, rect.y, rect.width, rect.height); + } + + return wl_region; +} + +static void +gdk_wayland_surface_sync_opaque_region (GdkSurface *window) +{ + GdkSurfaceImplWayland *impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); + struct wl_region *wl_region = NULL; + + if (!impl->display_server.wl_surface) + return; + + if (!impl->opaque_region_dirty) + return; + + if (impl->opaque_region != NULL) + wl_region = wl_region_from_cairo_region (GDK_WAYLAND_DISPLAY (gdk_surface_get_display (window)), + impl->opaque_region); + + wl_surface_set_opaque_region (impl->display_server.wl_surface, wl_region); + + if (wl_region != NULL) + wl_region_destroy (wl_region); + + impl->opaque_region_dirty = FALSE; +} + +static void +gdk_wayland_surface_sync_input_region (GdkSurface *window) +{ + GdkSurfaceImplWayland *impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); + struct wl_region *wl_region = NULL; + + if (!impl->display_server.wl_surface) + return; + + if (!impl->input_region_dirty) + return; + + if (impl->input_region != NULL) + wl_region = wl_region_from_cairo_region (GDK_WAYLAND_DISPLAY (gdk_surface_get_display (window)), + impl->input_region); + + wl_surface_set_input_region (impl->display_server.wl_surface, wl_region); + + if (wl_region != NULL) + wl_region_destroy (wl_region); + + impl->input_region_dirty = FALSE; +} + +static void +gdk_wayland_set_input_region_if_empty (GdkSurface *window) +{ + GdkSurfaceImplWayland *impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); + GdkWaylandDisplay *display; + struct wl_region *empty; + + if (!impl->input_region_dirty) + return; + + if (impl->input_region == NULL) + return; + + if (!cairo_region_is_empty (impl->input_region)) + return; + + display = GDK_WAYLAND_DISPLAY (gdk_surface_get_display (window)); + empty = wl_compositor_create_region (display->compositor); + + wl_surface_set_input_region (impl->display_server.wl_surface, empty); + wl_region_destroy (empty); + + impl->input_region_dirty = FALSE; +} + +static void +surface_enter (void *data, + struct wl_surface *wl_surface, + struct wl_output *output) +{ + GdkSurface *window = GDK_SURFACE (data); + GdkSurfaceImplWayland *impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); + + GDK_DISPLAY_NOTE (gdk_surface_get_display (window), EVENTS, + g_message ("surface enter, window %p output %p", window, output)); + + impl->display_server.outputs = g_slist_prepend (impl->display_server.outputs, output); + + gdk_wayland_surface_update_scale (window); +} + +static void +surface_leave (void *data, + struct wl_surface *wl_surface, + struct wl_output *output) +{ + GdkSurface *window = GDK_SURFACE (data); + GdkSurfaceImplWayland *impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); + + GDK_DISPLAY_NOTE (gdk_surface_get_display (window), EVENTS, + g_message ("surface leave, window %p output %p", window, output)); + + impl->display_server.outputs = g_slist_remove (impl->display_server.outputs, output); + + if (impl->display_server.outputs) + gdk_wayland_surface_update_scale (window); +} + +static const struct wl_surface_listener surface_listener = { + surface_enter, + surface_leave +}; + +static void +on_parent_surface_committed (GdkSurfaceImplWayland *parent_impl, + GdkSurface *window) +{ + GdkSurfaceImplWayland *impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); + + g_signal_handler_disconnect (parent_impl, + impl->parent_surface_committed_handler); + impl->parent_surface_committed_handler = 0; + + wl_subsurface_set_desync (impl->display_server.wl_subsurface); + + /* Special case if the input region is empty, it won't change on resize */ + gdk_wayland_set_input_region_if_empty (window); +} + +static void +gdk_wayland_surface_create_subsurface (GdkSurface *window) +{ + GdkSurfaceImplWayland *impl, *parent_impl = NULL; + GdkWaylandDisplay *display_wayland; + + impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); + + if (!impl->display_server.wl_surface) + return; /* Bail out, surface and subsurface will be created later when shown */ + + if (impl->display_server.wl_subsurface) + return; + + if (impl->transient_for) + parent_impl = GDK_SURFACE_IMPL_WAYLAND (impl->transient_for->impl); + + if (parent_impl && parent_impl->display_server.wl_surface) + { + display_wayland = GDK_WAYLAND_DISPLAY (gdk_surface_get_display (window)); + impl->display_server.wl_subsurface = + wl_subcompositor_get_subsurface (display_wayland->subcompositor, + impl->display_server.wl_surface, parent_impl->display_server.wl_surface); + wl_subsurface_set_position (impl->display_server.wl_subsurface, + window->x + window->abs_x, + window->y + window->abs_y); + + /* In order to synchronize the initial position with the initial frame + * content, wait with making the subsurface desynchronized until after + * the parent was committed. + */ + impl->parent_surface_committed_handler = + g_signal_connect_object (parent_impl, "committed", + G_CALLBACK (on_parent_surface_committed), + window, 0); + gdk_surface_request_transient_parent_commit (window); + } +} + +static void +gdk_wayland_surface_create_surface (GdkSurface *window) +{ + GdkSurfaceImplWayland *impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); + GdkWaylandDisplay *display_wayland = GDK_WAYLAND_DISPLAY (gdk_surface_get_display (window)); + + impl->display_server.wl_surface = wl_compositor_create_surface (display_wayland->compositor); + wl_surface_add_listener (impl->display_server.wl_surface, &surface_listener, window); +} + +static void +xdg_surface_configure (void *data, + struct zxdg_surface_v6 *xdg_surface, + uint32_t serial) +{ + GdkSurface *window = GDK_SURFACE (data); + GdkSurfaceImplWayland *impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); + GdkSurfaceState new_state; + int width = impl->pending.width; + int height = impl->pending.height; + gboolean fixed_size; + gboolean saved_size; + + if (!impl->initial_configure_received) + { + gdk_surface_thaw_updates (window); + impl->initial_configure_received = TRUE; + } + + if (impl->display_server.xdg_popup) + { + zxdg_surface_v6_ack_configure (xdg_surface, serial); + return; + } + + new_state = impl->pending.state; + impl->pending.state = 0; + + fixed_size = + new_state & (GDK_SURFACE_STATE_MAXIMIZED | GDK_SURFACE_STATE_FULLSCREEN | GDK_SURFACE_STATE_TILED); + + saved_size = (width == 0 && height == 0); + /* According to xdg_shell, an xdg_surface.configure with size 0x0 + * should be interpreted as that it is up to the client to set a + * size. + * + * When transitioning from maximize or fullscreen state, this means + * the client should configure its size back to what it was before + * being maximize or fullscreen. + */ + if (saved_size && !fixed_size) + { + width = impl->saved_width; + height = impl->saved_height; + } + + if (width > 0 && height > 0) + { + GdkSurfaceHints geometry_mask = impl->geometry_mask; + + /* Ignore size increments for maximized/fullscreen windows */ + if (fixed_size) + geometry_mask &= ~GDK_HINT_RESIZE_INC; + if (!saved_size) + { + /* Do not reapply contrains if we are restoring original size */ + gdk_surface_constrain_size (&impl->geometry_hints, + geometry_mask, + width + impl->margin_left + impl->margin_right, + height + impl->margin_top + impl->margin_bottom, + &width, + &height); + + /* Save size for next time we get 0x0 */ + _gdk_wayland_surface_save_size (window); + } + + gdk_wayland_surface_configure (window, width, height, impl->scale); + } + + GDK_DISPLAY_NOTE (gdk_surface_get_display (window), EVENTS, + g_message ("configure, window %p %dx%d,%s%s%s%s", + window, width, height, + (new_state & GDK_SURFACE_STATE_FULLSCREEN) ? " fullscreen" : "", + (new_state & GDK_SURFACE_STATE_MAXIMIZED) ? " maximized" : "", + (new_state & GDK_SURFACE_STATE_FOCUSED) ? " focused" : "", + (new_state & GDK_SURFACE_STATE_TILED) ? " tiled" : "")); + + gdk_surface_set_state (window, new_state); + zxdg_surface_v6_ack_configure (xdg_surface, serial); + if (impl->hint != GDK_SURFACE_TYPE_HINT_DIALOG && + new_state & GDK_SURFACE_STATE_FOCUSED) + gdk_wayland_surface_update_dialogs (window); +} + +static const struct zxdg_surface_v6_listener xdg_surface_listener = { + xdg_surface_configure, +}; + +static void +xdg_toplevel_configure (void *data, + struct zxdg_toplevel_v6 *xdg_toplevel, + int32_t width, + int32_t height, + struct wl_array *states) +{ + GdkSurface *window = GDK_SURFACE (data); + GdkSurfaceImplWayland *impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); + uint32_t *p; + + wl_array_for_each (p, states) + { + uint32_t state = *p; + switch (state) + { + case ZXDG_TOPLEVEL_V6_STATE_FULLSCREEN: + impl->pending.state |= GDK_SURFACE_STATE_FULLSCREEN; + break; + case ZXDG_TOPLEVEL_V6_STATE_MAXIMIZED: + impl->pending.state |= GDK_SURFACE_STATE_MAXIMIZED; + break; + case ZXDG_TOPLEVEL_V6_STATE_ACTIVATED: + impl->pending.state |= GDK_SURFACE_STATE_FOCUSED; + break; + case ZXDG_TOPLEVEL_V6_STATE_RESIZING: + break; + default: + /* Unknown state */ + break; + } + } + + impl->pending.width = width; + impl->pending.height = height; +} + +static void +xdg_toplevel_close (void *data, + struct zxdg_toplevel_v6 *xdg_toplevel) +{ + GdkSurface *window = GDK_SURFACE (data); + GdkDisplay *display; + GdkEvent *event; + + display = gdk_surface_get_display (window); + + GDK_DISPLAY_NOTE (display, EVENTS, g_message ("close %p", window)); + + event = gdk_event_new (GDK_DELETE); + event->any.window = g_object_ref (window); + event->any.send_event = TRUE; + + _gdk_wayland_display_deliver_event (display, event); +} + +static const struct zxdg_toplevel_v6_listener xdg_toplevel_listener = { + xdg_toplevel_configure, + xdg_toplevel_close, +}; + +static void +gdk_wayland_surface_create_xdg_toplevel (GdkSurface *window) +{ + GdkWaylandDisplay *display_wayland = GDK_WAYLAND_DISPLAY (gdk_surface_get_display (window)); + GdkSurfaceImplWayland *impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); + const gchar *app_id; + + impl->display_server.xdg_surface = + zxdg_shell_v6_get_xdg_surface (display_wayland->xdg_shell, + impl->display_server.wl_surface); + zxdg_surface_v6_add_listener (impl->display_server.xdg_surface, + &xdg_surface_listener, + window); + gdk_surface_freeze_updates (window); + + impl->display_server.xdg_toplevel = + zxdg_surface_v6_get_toplevel (impl->display_server.xdg_surface); + zxdg_toplevel_v6_add_listener (impl->display_server.xdg_toplevel, + &xdg_toplevel_listener, + window); + + gdk_wayland_surface_sync_parent (window, NULL); + gdk_wayland_surface_sync_parent_of_imported (window); + gdk_wayland_surface_sync_title (window); + + if (window->state & GDK_SURFACE_STATE_MAXIMIZED) + zxdg_toplevel_v6_set_maximized (impl->display_server.xdg_toplevel); + if (window->state & GDK_SURFACE_STATE_FULLSCREEN) + zxdg_toplevel_v6_set_fullscreen (impl->display_server.xdg_toplevel, + impl->initial_fullscreen_output); + + impl->initial_fullscreen_output = NULL; + + app_id = g_get_prgname (); + + if (app_id == NULL) + app_id = "GTK+ Application"; + + zxdg_toplevel_v6_set_app_id (impl->display_server.xdg_toplevel, app_id); + + maybe_set_gtk_surface_dbus_properties (window); + maybe_set_gtk_surface_modal (window); + + if (impl->hint == GDK_SURFACE_TYPE_HINT_DIALOG) + _gdk_wayland_screen_add_orphan_dialog (window); + + wl_surface_commit (impl->display_server.wl_surface); +} + +static void +xdg_popup_configure (void *data, + struct zxdg_popup_v6 *xdg_popup, + int32_t x, + int32_t y, + int32_t width, + int32_t height) +{ + GdkSurface *window = GDK_SURFACE (data); + GdkSurfaceImplWayland *impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); + GdkRectangle flipped_rect; + GdkRectangle final_rect; + gboolean flipped_x; + gboolean flipped_y; + + g_return_if_fail (impl->transient_for); + + if (impl->position_method != POSITION_METHOD_MOVE_TO_RECT) + return; + + calculate_moved_to_rect_result (window, x, y, width, height, + &flipped_rect, + &final_rect, + &flipped_x, + &flipped_y); + + g_signal_emit_by_name (window, + "moved-to-rect", + &flipped_rect, + &final_rect, + flipped_x, + flipped_y); +} + +static void +xdg_popup_done (void *data, + struct zxdg_popup_v6 *xdg_popup) +{ + GdkSurface *window = GDK_SURFACE (data); + + GDK_DISPLAY_NOTE (gdk_surface_get_display (window), EVENTS, g_message ("done %p", window)); + + gdk_surface_hide (window); +} + +static const struct zxdg_popup_v6_listener xdg_popup_listener = { + xdg_popup_configure, + xdg_popup_done, +}; + +static enum zxdg_positioner_v6_anchor +rect_anchor_to_anchor (GdkGravity rect_anchor) +{ + switch (rect_anchor) + { + case GDK_GRAVITY_NORTH_WEST: + case GDK_GRAVITY_STATIC: + return (ZXDG_POSITIONER_V6_ANCHOR_TOP | + ZXDG_POSITIONER_V6_ANCHOR_LEFT); + case GDK_GRAVITY_NORTH: + return ZXDG_POSITIONER_V6_ANCHOR_TOP; + case GDK_GRAVITY_NORTH_EAST: + return (ZXDG_POSITIONER_V6_ANCHOR_TOP | + ZXDG_POSITIONER_V6_ANCHOR_RIGHT); + case GDK_GRAVITY_WEST: + return ZXDG_POSITIONER_V6_ANCHOR_LEFT; + case GDK_GRAVITY_CENTER: + return ZXDG_POSITIONER_V6_ANCHOR_NONE; + case GDK_GRAVITY_EAST: + return ZXDG_POSITIONER_V6_ANCHOR_RIGHT; + case GDK_GRAVITY_SOUTH_WEST: + return (ZXDG_POSITIONER_V6_ANCHOR_BOTTOM | + ZXDG_POSITIONER_V6_ANCHOR_LEFT); + case GDK_GRAVITY_SOUTH: + return ZXDG_POSITIONER_V6_ANCHOR_BOTTOM; + case GDK_GRAVITY_SOUTH_EAST: + return (ZXDG_POSITIONER_V6_ANCHOR_BOTTOM | + ZXDG_POSITIONER_V6_ANCHOR_RIGHT); + default: + g_assert_not_reached (); + } + + return (ZXDG_POSITIONER_V6_ANCHOR_TOP | + ZXDG_POSITIONER_V6_ANCHOR_LEFT); +} + +static enum zxdg_positioner_v6_gravity +window_anchor_to_gravity (GdkGravity rect_anchor) +{ + switch (rect_anchor) + { + case GDK_GRAVITY_NORTH_WEST: + case GDK_GRAVITY_STATIC: + return (ZXDG_POSITIONER_V6_GRAVITY_BOTTOM | + ZXDG_POSITIONER_V6_GRAVITY_RIGHT); + case GDK_GRAVITY_NORTH: + return ZXDG_POSITIONER_V6_GRAVITY_BOTTOM; + case GDK_GRAVITY_NORTH_EAST: + return (ZXDG_POSITIONER_V6_GRAVITY_BOTTOM | + ZXDG_POSITIONER_V6_GRAVITY_LEFT); + case GDK_GRAVITY_WEST: + return ZXDG_POSITIONER_V6_GRAVITY_RIGHT; + case GDK_GRAVITY_CENTER: + return ZXDG_POSITIONER_V6_GRAVITY_NONE; + case GDK_GRAVITY_EAST: + return ZXDG_POSITIONER_V6_GRAVITY_LEFT; + case GDK_GRAVITY_SOUTH_WEST: + return (ZXDG_POSITIONER_V6_GRAVITY_TOP | + ZXDG_POSITIONER_V6_GRAVITY_RIGHT); + case GDK_GRAVITY_SOUTH: + return ZXDG_POSITIONER_V6_GRAVITY_TOP; + case GDK_GRAVITY_SOUTH_EAST: + return (ZXDG_POSITIONER_V6_GRAVITY_TOP | + ZXDG_POSITIONER_V6_GRAVITY_LEFT); + default: + g_assert_not_reached (); + } + + return (ZXDG_POSITIONER_V6_GRAVITY_BOTTOM | + ZXDG_POSITIONER_V6_GRAVITY_RIGHT); +} + +void +gdk_wayland_surface_announce_csd (GdkSurface *window) +{ + GdkWaylandDisplay *display_wayland = GDK_WAYLAND_DISPLAY (gdk_surface_get_display (window)); + GdkSurfaceImplWayland *impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); + if (!display_wayland->server_decoration_manager) + return; + impl->display_server.server_decoration = + org_kde_kwin_server_decoration_manager_create (display_wayland->server_decoration_manager, + impl->display_server.wl_surface); + if (impl->display_server.server_decoration) + org_kde_kwin_server_decoration_request_mode (impl->display_server.server_decoration, + ORG_KDE_KWIN_SERVER_DECORATION_MANAGER_MODE_CLIENT); +} + +static GdkSurface * +get_real_parent_and_translate (GdkSurface *window, + gint *x, + gint *y) +{ + GdkSurfaceImplWayland *impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); + GdkSurface *parent = impl->transient_for; + + while (parent) + { + GdkSurfaceImplWayland *parent_impl = + GDK_SURFACE_IMPL_WAYLAND (parent->impl); + GdkSurface *effective_parent = gdk_surface_get_parent (parent); + + if ((gdk_surface_has_native (parent) && + !parent_impl->display_server.wl_subsurface) || + !effective_parent) + break; + + *x += parent->x; + *y += parent->y; + + if (gdk_surface_has_native (parent) && + parent_impl->display_server.wl_subsurface) + parent = parent->transient_for; + else + parent = effective_parent; + } + + return parent; +} + +static void +translate_to_real_parent_window_geometry (GdkSurface *window, + gint *x, + gint *y) +{ + GdkSurface *parent; + + parent = get_real_parent_and_translate (window, x, y); + + *x -= parent->shadow_left; + *y -= parent->shadow_top; +} + +static GdkSurface * +translate_from_real_parent_window_geometry (GdkSurface *window, + gint *x, + gint *y) +{ + GdkSurface *parent; + gint dx = 0; + gint dy = 0; + + parent = get_real_parent_and_translate (window, &dx, &dy); + + *x -= dx - parent->shadow_left; + *y -= dy - parent->shadow_top; + + return parent; +} + +static void +calculate_popup_rect (GdkSurface *window, + GdkGravity rect_anchor, + GdkGravity window_anchor, + GdkRectangle *out_rect) +{ + GdkSurfaceImplWayland *impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); + GdkRectangle geometry; + GdkRectangle anchor_rect; + int x = 0, y = 0; + + gdk_wayland_surface_get_window_geometry (window, &geometry); + + anchor_rect = (GdkRectangle) { + .x = (impl->pending_move_to_rect.rect.x + + impl->pending_move_to_rect.rect_anchor_dx), + .y = (impl->pending_move_to_rect.rect.y + + impl->pending_move_to_rect.rect_anchor_dy), + .width = impl->pending_move_to_rect.rect.width, + .height = impl->pending_move_to_rect.rect.height + }; + + switch (rect_anchor) + { + default: + case GDK_GRAVITY_STATIC: + case GDK_GRAVITY_NORTH_WEST: + x = anchor_rect.x; + y = anchor_rect.y; + break; + case GDK_GRAVITY_NORTH: + x = anchor_rect.x + (anchor_rect.width / 2); + y = anchor_rect.y; + break; + case GDK_GRAVITY_NORTH_EAST: + x = anchor_rect.x + anchor_rect.width; + y = anchor_rect.y; + break; + case GDK_GRAVITY_WEST: + x = anchor_rect.x; + y = anchor_rect.y + (anchor_rect.height / 2); + break; + case GDK_GRAVITY_CENTER: + x = anchor_rect.x + (anchor_rect.width / 2); + y = anchor_rect.y + (anchor_rect.height / 2); + break; + case GDK_GRAVITY_EAST: + x = anchor_rect.x + anchor_rect.width; + y = anchor_rect.y + (anchor_rect.height / 2); + break; + case GDK_GRAVITY_SOUTH_WEST: + x = anchor_rect.x; + y = anchor_rect.y + anchor_rect.height; + break; + case GDK_GRAVITY_SOUTH: + x = anchor_rect.x + (anchor_rect.width / 2); + y = anchor_rect.y + anchor_rect.height; + break; + case GDK_GRAVITY_SOUTH_EAST: + x = anchor_rect.x + anchor_rect.width; + y = anchor_rect.y + anchor_rect.height; + break; + } + + switch (window_anchor) + { + default: + case GDK_GRAVITY_STATIC: + case GDK_GRAVITY_NORTH_WEST: + break; + case GDK_GRAVITY_NORTH: + x -= geometry.width / 2; + break; + case GDK_GRAVITY_NORTH_EAST: + x -= geometry.width; + break; + case GDK_GRAVITY_WEST: + y -= geometry.height / 2; + break; + case GDK_GRAVITY_CENTER: + x -= geometry.width / 2; + y -= geometry.height / 2; + break; + case GDK_GRAVITY_EAST: + x -= geometry.width; + y -= geometry.height / 2; + break; + case GDK_GRAVITY_SOUTH_WEST: + y -= geometry.height; + break; + case GDK_GRAVITY_SOUTH: + x -= geometry.width / 2; + y -= geometry.height; + break; + case GDK_GRAVITY_SOUTH_EAST: + x -= geometry.width; + y -= geometry.height; + break; + } + + *out_rect = (GdkRectangle) { + .x = x, + .y = y, + .width = geometry.width, + .height = geometry.height + }; +} + +static GdkGravity +flip_anchor_horizontally (GdkGravity anchor) +{ + switch (anchor) + { + default: + case GDK_GRAVITY_STATIC: + case GDK_GRAVITY_NORTH_WEST: + return GDK_GRAVITY_NORTH_EAST; + case GDK_GRAVITY_NORTH: + return GDK_GRAVITY_NORTH; + case GDK_GRAVITY_NORTH_EAST: + return GDK_GRAVITY_NORTH_WEST; + case GDK_GRAVITY_WEST: + return GDK_GRAVITY_EAST; + case GDK_GRAVITY_CENTER: + return GDK_GRAVITY_CENTER; + case GDK_GRAVITY_EAST: + return GDK_GRAVITY_WEST; + case GDK_GRAVITY_SOUTH_WEST: + return GDK_GRAVITY_SOUTH_EAST; + case GDK_GRAVITY_SOUTH: + return GDK_GRAVITY_SOUTH; + case GDK_GRAVITY_SOUTH_EAST: + return GDK_GRAVITY_SOUTH_WEST; + } + + g_assert_not_reached (); +} + +static GdkGravity +flip_anchor_vertically (GdkGravity anchor) +{ + switch (anchor) + { + default: + case GDK_GRAVITY_STATIC: + case GDK_GRAVITY_NORTH_WEST: + return GDK_GRAVITY_SOUTH_WEST; + case GDK_GRAVITY_NORTH: + return GDK_GRAVITY_SOUTH; + case GDK_GRAVITY_NORTH_EAST: + return GDK_GRAVITY_SOUTH_EAST; + case GDK_GRAVITY_WEST: + return GDK_GRAVITY_WEST; + case GDK_GRAVITY_CENTER: + return GDK_GRAVITY_CENTER; + case GDK_GRAVITY_EAST: + return GDK_GRAVITY_EAST; + case GDK_GRAVITY_SOUTH_WEST: + return GDK_GRAVITY_NORTH_WEST; + case GDK_GRAVITY_SOUTH: + return GDK_GRAVITY_NORTH; + case GDK_GRAVITY_SOUTH_EAST: + return GDK_GRAVITY_NORTH_EAST; + } + + g_assert_not_reached (); +} + +static void +calculate_moved_to_rect_result (GdkSurface *window, + int x, + int y, + int width, + int height, + GdkRectangle *flipped_rect, + GdkRectangle *final_rect, + gboolean *flipped_x, + gboolean *flipped_y) +{ + GdkSurfaceImplWayland *impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); + GdkSurface *parent; + gint window_x, window_y; + gint window_width, window_height; + GdkRectangle best_rect; + + parent = translate_from_real_parent_window_geometry (window, &x, &y); + *final_rect = (GdkRectangle) { + .x = x, + .y = y, + .width = width, + .height = height, + }; + + window_x = parent->x + x; + window_y = parent->y + y; + window_width = width + window->shadow_left + window->shadow_right; + window_height = height + window->shadow_top + window->shadow_bottom; + + gdk_surface_move_resize (window, + window_x, window_y, + window_width, window_height); + + calculate_popup_rect (window, + impl->pending_move_to_rect.rect_anchor, + impl->pending_move_to_rect.window_anchor, + &best_rect); + + *flipped_rect = best_rect; + + if (x != best_rect.x && + impl->pending_move_to_rect.anchor_hints & GDK_ANCHOR_FLIP_X) + { + GdkRectangle flipped_x_rect; + GdkGravity flipped_rect_anchor; + GdkGravity flipped_window_anchor; + + flipped_rect_anchor = + flip_anchor_horizontally (impl->pending_move_to_rect.rect_anchor); + flipped_window_anchor = + flip_anchor_horizontally (impl->pending_move_to_rect.window_anchor), + calculate_popup_rect (window, + flipped_rect_anchor, + flipped_window_anchor, + &flipped_x_rect); + + if (flipped_x_rect.x == x) + flipped_rect->x = x; + } + if (y != best_rect.y && + impl->pending_move_to_rect.anchor_hints & GDK_ANCHOR_FLIP_Y) + { + GdkRectangle flipped_y_rect; + GdkGravity flipped_rect_anchor; + GdkGravity flipped_window_anchor; + + flipped_rect_anchor = + flip_anchor_vertically (impl->pending_move_to_rect.rect_anchor); + flipped_window_anchor = + flip_anchor_vertically (impl->pending_move_to_rect.window_anchor), + calculate_popup_rect (window, + flipped_rect_anchor, + flipped_window_anchor, + &flipped_y_rect); + + if (flipped_y_rect.y == y) + flipped_rect->y = y; + } + + *flipped_x = flipped_rect->x != best_rect.x; + *flipped_y = flipped_rect->y != best_rect.y; +} + +static struct zxdg_positioner_v6 * +create_dynamic_positioner (GdkSurface *window) +{ + GdkSurfaceImplWayland *impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); + GdkWaylandDisplay *display = + GDK_WAYLAND_DISPLAY (gdk_surface_get_display (window)); + struct zxdg_positioner_v6 *positioner; + GdkRectangle geometry; + enum zxdg_positioner_v6_anchor anchor; + enum zxdg_positioner_v6_gravity gravity; + uint32_t constraint_adjustment = ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_NONE; + gint real_anchor_rect_x, real_anchor_rect_y; + gint anchor_rect_width, anchor_rect_height; + + positioner = zxdg_shell_v6_create_positioner (display->xdg_shell); + + gdk_wayland_surface_get_window_geometry (window, &geometry); + zxdg_positioner_v6_set_size (positioner, geometry.width, geometry.height); + + real_anchor_rect_x = impl->pending_move_to_rect.rect.x; + real_anchor_rect_y = impl->pending_move_to_rect.rect.y; + translate_to_real_parent_window_geometry (window, + &real_anchor_rect_x, + &real_anchor_rect_y); + + anchor_rect_width = impl->pending_move_to_rect.rect.width; + anchor_rect_height = impl->pending_move_to_rect.rect.height; + zxdg_positioner_v6_set_anchor_rect (positioner, + real_anchor_rect_x, + real_anchor_rect_y, + anchor_rect_width, + anchor_rect_height); + + zxdg_positioner_v6_set_offset (positioner, + impl->pending_move_to_rect.rect_anchor_dx, + impl->pending_move_to_rect.rect_anchor_dy); + + anchor = rect_anchor_to_anchor (impl->pending_move_to_rect.rect_anchor); + zxdg_positioner_v6_set_anchor (positioner, anchor); + + gravity = window_anchor_to_gravity (impl->pending_move_to_rect.window_anchor); + zxdg_positioner_v6_set_gravity (positioner, gravity); + + if (impl->pending_move_to_rect.anchor_hints & GDK_ANCHOR_FLIP_X) + constraint_adjustment |= ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_FLIP_X; + if (impl->pending_move_to_rect.anchor_hints & GDK_ANCHOR_FLIP_Y) + constraint_adjustment |= ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_FLIP_Y; + if (impl->pending_move_to_rect.anchor_hints & GDK_ANCHOR_SLIDE_X) + constraint_adjustment |= ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_SLIDE_X; + if (impl->pending_move_to_rect.anchor_hints & GDK_ANCHOR_SLIDE_Y) + constraint_adjustment |= ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_SLIDE_Y; + if (impl->pending_move_to_rect.anchor_hints & GDK_ANCHOR_RESIZE_X) + constraint_adjustment |= ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_RESIZE_X; + if (impl->pending_move_to_rect.anchor_hints & GDK_ANCHOR_RESIZE_Y) + constraint_adjustment |= ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_RESIZE_Y; + + zxdg_positioner_v6_set_constraint_adjustment (positioner, + constraint_adjustment); + + return positioner; +} + +static struct zxdg_positioner_v6 * +create_simple_positioner (GdkSurface *window, + GdkSurface *parent) +{ + GdkWaylandDisplay *display = + GDK_WAYLAND_DISPLAY (gdk_surface_get_display (window)); + struct zxdg_positioner_v6 *positioner; + GdkRectangle geometry; + GdkRectangle parent_geometry; + int parent_x, parent_y; + + positioner = zxdg_shell_v6_create_positioner (display->xdg_shell); + + gdk_wayland_surface_get_window_geometry (window, &geometry); + zxdg_positioner_v6_set_size (positioner, geometry.width, geometry.height); + + parent_x = parent->x; + parent_y = parent->y; + + gdk_wayland_surface_get_window_geometry (parent, &parent_geometry); + parent_x += parent_geometry.x; + parent_y += parent_geometry.y; + + zxdg_positioner_v6_set_anchor_rect (positioner, + (window->x + geometry.x) - parent_x, + (window->y + geometry.y) - parent_y, + 1, 1); + zxdg_positioner_v6_set_anchor (positioner, + (ZXDG_POSITIONER_V6_ANCHOR_TOP | + ZXDG_POSITIONER_V6_ANCHOR_LEFT)); + zxdg_positioner_v6_set_gravity (positioner, + (ZXDG_POSITIONER_V6_GRAVITY_BOTTOM | + ZXDG_POSITIONER_V6_GRAVITY_RIGHT)); + + return positioner; +} + +static void +gdk_wayland_surface_create_xdg_popup (GdkSurface *window, + GdkSurface *parent, + struct wl_seat *seat) +{ + GdkWaylandDisplay *display = GDK_WAYLAND_DISPLAY (gdk_surface_get_display (window)); + GdkSurfaceImplWayland *impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); + GdkSurfaceImplWayland *parent_impl = GDK_SURFACE_IMPL_WAYLAND (parent->impl); + struct zxdg_positioner_v6 *positioner; + GdkSeat *gdk_seat; + guint32 serial; + + if (!impl->display_server.wl_surface) + return; + + if (!parent_impl->display_server.xdg_surface) + return; + + if (impl->display_server.xdg_toplevel) + { + g_warning ("Can't map popup, already mapped as toplevel"); + return; + } + if (impl->display_server.xdg_popup) + { + g_warning ("Can't map popup, already mapped"); + return; + } + if ((display->current_popups && + g_list_last (display->current_popups)->data != parent) || + (!display->current_popups && + !parent_impl->display_server.xdg_toplevel)) + { + g_warning ("Tried to map a popup with a non-top most parent"); + return; + } + + impl->display_server.xdg_surface = + zxdg_shell_v6_get_xdg_surface (display->xdg_shell, + impl->display_server.wl_surface); + zxdg_surface_v6_add_listener (impl->display_server.xdg_surface, + &xdg_surface_listener, + window); + gdk_surface_freeze_updates (window); + + if (impl->position_method == POSITION_METHOD_MOVE_TO_RECT) + positioner = create_dynamic_positioner (window); + else + positioner = create_simple_positioner (window, parent); + + impl->display_server.xdg_popup = + zxdg_surface_v6_get_popup (impl->display_server.xdg_surface, + parent_impl->display_server.xdg_surface, + positioner); + zxdg_popup_v6_add_listener (impl->display_server.xdg_popup, + &xdg_popup_listener, + window); + + zxdg_positioner_v6_destroy (positioner); + + if (seat) + { + gdk_seat = gdk_display_get_default_seat (GDK_DISPLAY (display)); + serial = _gdk_wayland_seat_get_last_implicit_grab_serial (gdk_seat, NULL); + zxdg_popup_v6_grab (impl->display_server.xdg_popup, seat, serial); + } + + wl_surface_commit (impl->display_server.wl_surface); + + impl->popup_parent = parent; + display->current_popups = g_list_append (display->current_popups, window); +} + +static struct wl_seat * +find_grab_input_seat (GdkSurface *window, GdkSurface *transient_for) +{ + GdkSurface *attached_grab_window; + GdkSurfaceImplWayland *impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); + GdkSurfaceImplWayland *tmp_impl; + + /* Use the device that was used for the grab as the device for + * the popup window setup - so this relies on GTK+ taking the + * grab before showing the popup window. + */ + if (impl->grab_input_seat) + return gdk_wayland_seat_get_wl_seat (impl->grab_input_seat); + + /* HACK: GtkMenu grabs a special window known as the "grab transfer window" + * and then transfers the grab over to the correct window later. Look for + * this window when taking the grab to know it's correct. + * + * See: associate_menu_grab_transfer_window in gtkmenu.c + */ + attached_grab_window = g_object_get_data (G_OBJECT (window), "gdk-attached-grab-window"); + if (attached_grab_window) + { + tmp_impl = GDK_SURFACE_IMPL_WAYLAND (attached_grab_window->impl); + if (tmp_impl->grab_input_seat) + return gdk_wayland_seat_get_wl_seat (tmp_impl->grab_input_seat); + } + + while (transient_for) + { + tmp_impl = GDK_SURFACE_IMPL_WAYLAND (transient_for->impl); + + if (tmp_impl->grab_input_seat) + return gdk_wayland_seat_get_wl_seat (tmp_impl->grab_input_seat); + + transient_for = tmp_impl->transient_for; + } + + return NULL; +} + +static gboolean +should_be_mapped (GdkSurface *window) +{ + GdkSurfaceImplWayland *impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); + + /* Don't map crazy temp that GTK+ uses for internal X11 shenanigans. */ + if (window->window_type == GDK_SURFACE_TEMP && window->x < 0 && window->y < 0) + return FALSE; + + if (impl->hint == GDK_SURFACE_TYPE_HINT_DND) + return FALSE; + + return TRUE; +} + +static gboolean +should_map_as_popup (GdkSurface *window) +{ + GdkSurfaceImplWayland *impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); + + /* Ideally, popup would be temp windows with a parent and grab */ + if (GDK_SURFACE_TYPE (window) == GDK_SURFACE_TEMP) + { + /* If a temp window has a parent and a grab, we can use a popup */ + if (impl->transient_for) + { + if (impl->grab_input_seat) + return TRUE; + } + else + g_message ("Window %p is a temporary window without parent, " + "application will not be able to position it on screen.", + window); + } + + /* Yet we need to keep the window type hint tests for compatibility */ + switch ((guint) impl->hint) + { + case GDK_SURFACE_TYPE_HINT_POPUP_MENU: + case GDK_SURFACE_TYPE_HINT_DROPDOWN_MENU: + case GDK_SURFACE_TYPE_HINT_COMBO: + return TRUE; + + default: + break; + } + + return FALSE; +} + +static gboolean +should_map_as_subsurface (GdkSurface *window) +{ + GdkSurfaceImplWayland *impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); + + if (GDK_SURFACE_TYPE (window) == GDK_SURFACE_SUBSURFACE) + return TRUE; + + if (GDK_SURFACE_TYPE (window) != GDK_SURFACE_TEMP) + return FALSE; + + /* if we want a popup, we do not want a subsurface */ + if (should_map_as_popup (window)) + return FALSE; + + if (impl->transient_for) + { + GdkSurfaceImplWayland *impl_parent; + + impl_parent = GDK_SURFACE_IMPL_WAYLAND (impl->transient_for->impl); + /* subsurface require that the parent is mapped */ + if (impl_parent->mapped) + return TRUE; + else + g_warning ("Couldn't map window %p as subsurface because its parent is not mapped.", + window); + + } + + return FALSE; +} + +/* Get the window that can be used as a parent for a popup, i.e. a xdg_toplevel + * or xdg_popup. If the window is not, traverse up the transiency parents until + * we find one. + */ +static GdkSurface * +get_popup_parent (GdkSurface *window) +{ + while (window) + { + GdkSurfaceImplWayland *impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); + + if (impl->display_server.xdg_popup || impl->display_server.xdg_toplevel) + return window; + + window = impl->transient_for; + } + + return NULL; +} + +static void +gdk_wayland_surface_map (GdkSurface *window) +{ + GdkSurfaceImplWayland *impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); + GdkSurface *transient_for = NULL; + + if (!should_be_mapped (window)) + return; + + if (impl->mapped || impl->use_custom_surface) + return; + + if (should_map_as_subsurface (window)) + { + if (impl->transient_for) + gdk_wayland_surface_create_subsurface (window); + else + g_warning ("Couldn't map window %p as susburface yet because it doesn't have a parent", + window); + } + else if (should_map_as_popup (window)) + { + gboolean create_fallback = FALSE; + struct wl_seat *grab_input_seat; + + /* Popup menus can appear without a transient parent, which means they + * cannot be positioned properly on Wayland. This attempts to guess the + * surface they should be positioned with by finding the surface beneath + * the device that created the grab for the popup window. + */ + if (!impl->transient_for && impl->hint == GDK_SURFACE_TYPE_HINT_POPUP_MENU) + { + GdkDevice *grab_device = NULL; + + /* The popup menu window is not the grabbed window. This may mean + * that a "transfer window" (see gtkmenu.c) is used, and we need + * to find that window to get the grab device. If so is the case + * the "transfer window" can be retrieved via the + * "gdk-attached-grab-window" associated data field. + */ + if (!impl->grab_input_seat) + { + GdkSurface *attached_grab_window = + g_object_get_data (G_OBJECT (window), + "gdk-attached-grab-window"); + if (attached_grab_window) + { + GdkSurfaceImplWayland *attached_impl = + GDK_SURFACE_IMPL_WAYLAND (attached_grab_window->impl); + grab_device = gdk_seat_get_pointer (attached_impl->grab_input_seat); + transient_for = + gdk_device_get_window_at_position (grab_device, + NULL, NULL); + } + } + else + { + grab_device = gdk_seat_get_pointer (impl->grab_input_seat); + transient_for = + gdk_device_get_window_at_position (grab_device, NULL, NULL); + } + + if (transient_for) + transient_for = get_popup_parent (gdk_surface_get_toplevel (transient_for)); + + /* If the position was not explicitly set, start the popup at the + * position of the device that holds the grab. + */ + if (impl->position_method == POSITION_METHOD_NONE && grab_device) + gdk_surface_get_device_position (transient_for, grab_device, + &window->x, &window->y, NULL); + } + else + { + transient_for = gdk_surface_get_toplevel (impl->transient_for); + transient_for = get_popup_parent (transient_for); + } + + if (!transient_for) + { + g_warning ("Couldn't map as window %p as popup because it doesn't have a parent", + window); + + create_fallback = TRUE; + } + else + { + grab_input_seat = find_grab_input_seat (window, transient_for); + } + + if (!create_fallback) + { + gdk_wayland_surface_create_xdg_popup (window, + transient_for, + grab_input_seat); + } + else + { + gdk_wayland_surface_create_xdg_toplevel (window); + } + } + else + { + gdk_wayland_surface_create_xdg_toplevel (window); + } + + impl->mapped = TRUE; +} + +static void +gdk_wayland_surface_show (GdkSurface *window, + gboolean already_mapped) +{ + GdkSurfaceImplWayland *impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); + + if (!impl->display_server.wl_surface) + gdk_wayland_surface_create_surface (window); + + gdk_wayland_surface_map (window); + + _gdk_make_event (window, GDK_MAP, NULL, FALSE); + + if (impl->staging_cairo_surface && + _gdk_wayland_is_shm_surface (impl->staging_cairo_surface)) + gdk_wayland_surface_attach_image (window); +} + +static void +unmap_subsurface (GdkSurface *window) +{ + GdkSurfaceImplWayland *impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); + GdkSurfaceImplWayland *parent_impl; + + g_return_if_fail (impl->display_server.wl_subsurface); + g_return_if_fail (impl->transient_for); + + parent_impl = GDK_SURFACE_IMPL_WAYLAND (impl->transient_for->impl); + wl_subsurface_destroy (impl->display_server.wl_subsurface); + if (impl->parent_surface_committed_handler) + { + g_signal_handler_disconnect (parent_impl, + impl->parent_surface_committed_handler); + impl->parent_surface_committed_handler = 0; + } + impl->display_server.wl_subsurface = NULL; +} + +static void +unmap_popups_for_window (GdkSurface *window) +{ + GdkWaylandDisplay *display_wayland; + GList *l; + + display_wayland = GDK_WAYLAND_DISPLAY (gdk_surface_get_display (window)); + for (l = display_wayland->current_popups; l; l = l->next) + { + GdkSurface *popup = l->data; + GdkSurfaceImplWayland *popup_impl = GDK_SURFACE_IMPL_WAYLAND (popup->impl); + + if (popup_impl->popup_parent == window) + { + g_warning ("Tried to unmap the parent of a popup"); + gdk_surface_hide (popup); + + return; + } + } +} + +static void +gdk_wayland_surface_hide_surface (GdkSurface *window) +{ + GdkWaylandDisplay *display_wayland = GDK_WAYLAND_DISPLAY (gdk_surface_get_display (window)); + GdkSurfaceImplWayland *impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); + + unmap_popups_for_window (window); + + if (impl->display_server.wl_surface) + { + if (impl->dummy_egl_surface) + { + eglDestroySurface (display_wayland->egl_display, impl->dummy_egl_surface); + impl->dummy_egl_surface = NULL; + } + + if (impl->display_server.dummy_egl_window) + { + wl_egl_window_destroy (impl->display_server.dummy_egl_window); + impl->display_server.dummy_egl_window = NULL; + } + + if (impl->egl_surface) + { + eglDestroySurface (display_wayland->egl_display, impl->egl_surface); + impl->egl_surface = NULL; + } + + if (impl->display_server.egl_window) + { + wl_egl_window_destroy (impl->display_server.egl_window); + impl->display_server.egl_window = NULL; + } + + if (impl->display_server.xdg_toplevel) + { + zxdg_toplevel_v6_destroy (impl->display_server.xdg_toplevel); + impl->display_server.xdg_toplevel = NULL; + } + else if (impl->display_server.xdg_popup) + { + zxdg_popup_v6_destroy (impl->display_server.xdg_popup); + impl->display_server.xdg_popup = NULL; + display_wayland->current_popups = + g_list_remove (display_wayland->current_popups, window); + } + if (impl->display_server.xdg_surface) + { + zxdg_surface_v6_destroy (impl->display_server.xdg_surface); + impl->display_server.xdg_surface = NULL; + if (!impl->initial_configure_received) + gdk_surface_thaw_updates (window); + else + impl->initial_configure_received = FALSE; + } + + if (impl->display_server.wl_subsurface) + unmap_subsurface (window); + + if (impl->awaiting_frame) + { + GdkFrameClock *frame_clock; + + impl->awaiting_frame = FALSE; + frame_clock = gdk_surface_get_frame_clock (window); + if (frame_clock) + _gdk_frame_clock_thaw (frame_clock); + } + + if (impl->display_server.gtk_surface) + { + gtk_surface1_destroy (impl->display_server.gtk_surface); + impl->display_server.gtk_surface = NULL; + impl->application.was_set = FALSE; + } + + wl_surface_destroy (impl->display_server.wl_surface); + impl->display_server.wl_surface = NULL; + + g_slist_free (impl->display_server.outputs); + impl->display_server.outputs = NULL; + + if (impl->hint == GDK_SURFACE_TYPE_HINT_DIALOG && !impl->transient_for) + display_wayland->orphan_dialogs = + g_list_remove (display_wayland->orphan_dialogs, window); + } + + unset_transient_for_exported (window); + + _gdk_wayland_surface_clear_saved_size (window); + impl->pending_commit = FALSE; + impl->mapped = FALSE; +} + +static void +gdk_wayland_surface_hide (GdkSurface *window) +{ + gdk_wayland_surface_hide_surface (window); + _gdk_surface_clear_update_area (window); +} + +static void +gdk_surface_wayland_withdraw (GdkSurface *window) +{ + if (!window->destroyed) + { + if (GDK_SURFACE_IS_MAPPED (window)) + gdk_synthesize_window_state (window, 0, GDK_SURFACE_STATE_WITHDRAWN); + + g_assert (!GDK_SURFACE_IS_MAPPED (window)); + + gdk_wayland_surface_hide_surface (window); + } +} + +static void +gdk_surface_wayland_set_events (GdkSurface *window, + GdkEventMask event_mask) +{ + GDK_SURFACE (window)->event_mask = event_mask; +} + +static GdkEventMask +gdk_surface_wayland_get_events (GdkSurface *window) +{ + if (GDK_SURFACE_DESTROYED (window)) + return 0; + else + return GDK_SURFACE (window)->event_mask; +} + +static void +gdk_surface_wayland_raise (GdkSurface *window) +{ +} + +static void +gdk_surface_wayland_lower (GdkSurface *window) +{ +} + +static void +gdk_surface_wayland_restack_toplevel (GdkSurface *window, + GdkSurface *sibling, + gboolean above) +{ +} + +static void +gdk_surface_request_transient_parent_commit (GdkSurface *window) +{ + GdkSurfaceImplWayland *window_impl, *impl; + GdkFrameClock *frame_clock; + + window_impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); + + if (!window_impl->transient_for) + return; + + impl = GDK_SURFACE_IMPL_WAYLAND (window_impl->transient_for->impl); + + if (!impl->display_server.wl_surface || impl->pending_commit) + return; + + frame_clock = gdk_surface_get_frame_clock (window_impl->transient_for); + + if (!frame_clock) + return; + + impl->pending_commit = TRUE; + gdk_frame_clock_request_phase (frame_clock, + GDK_FRAME_CLOCK_PHASE_AFTER_PAINT); +} + +static void +gdk_surface_wayland_move_resize (GdkSurface *window, + gboolean with_move, + gint x, + gint y, + gint width, + gint height) +{ + GdkSurfaceImplWayland *impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); + + if (with_move) + { + /* Each toplevel has in its own "root" coordinate system */ + if (GDK_SURFACE_TYPE (window) != GDK_SURFACE_TOPLEVEL) + { + window->x = x; + window->y = y; + impl->position_method = POSITION_METHOD_MOVE_RESIZE; + + if (impl->display_server.wl_subsurface) + { + wl_subsurface_set_position (impl->display_server.wl_subsurface, + window->x + window->abs_x, + window->y + window->abs_y); + gdk_surface_request_transient_parent_commit (window); + } + } + } + + /* If this function is called with width and height = -1 then that means + * just move the window - don't update its size + */ + if (width > 0 && height > 0) + gdk_wayland_surface_maybe_configure (window, width, height, impl->scale); +} + +/* Avoid zero width/height as this is a protocol error */ +static void +sanitize_anchor_rect (GdkSurface *window, + GdkRectangle *rect) +{ + gint original_width = rect->width; + gint original_height = rect->height; + + rect->width = MAX (1, rect->width); + rect->height = MAX (1, rect->height); + rect->x = MAX (rect->x + original_width - rect->width, 0); + rect->y = MAX (rect->y + original_height - rect->height, 0); +} + +static void +gdk_surface_wayland_move_to_rect (GdkSurface *window, + const GdkRectangle *rect, + GdkGravity rect_anchor, + GdkGravity window_anchor, + GdkAnchorHints anchor_hints, + gint rect_anchor_dx, + gint rect_anchor_dy) +{ + GdkSurfaceImplWayland *impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); + + impl->pending_move_to_rect.rect = *rect; + sanitize_anchor_rect (window, &impl->pending_move_to_rect.rect); + + impl->pending_move_to_rect.rect_anchor = rect_anchor; + impl->pending_move_to_rect.window_anchor = window_anchor; + impl->pending_move_to_rect.anchor_hints = anchor_hints; + impl->pending_move_to_rect.rect_anchor_dx = rect_anchor_dx; + impl->pending_move_to_rect.rect_anchor_dy = rect_anchor_dy; + + impl->position_method = POSITION_METHOD_MOVE_TO_RECT; +} + +static void +gdk_surface_wayland_get_geometry (GdkSurface *window, + gint *x, + gint *y, + gint *width, + gint *height) +{ + if (!GDK_SURFACE_DESTROYED (window)) + { + if (x) + *x = window->x; + if (y) + *y = window->y; + if (width) + *width = window->width; + if (height) + *height = window->height; + } +} + +static void +gdk_surface_wayland_get_root_coords (GdkSurface *window, + gint x, + gint y, + gint *root_x, + gint *root_y) +{ + /* + * Wayland does not have a global coordinate space shared between surfaces. In + * fact, for regular toplevels, we have no idea where our surfaces are + * positioned, relatively. + * + * However, there are some cases like popups and subsurfaces where we do have + * some amount of control over the placement of our window, and we can + * semi-accurately control the x/y position of these windows, if they are + * relative to another surface. + * + * To pretend we have something called a root coordinate space, assume all + * parent-less windows are positioned in (0, 0), and all relative positioned + * popups and subsurfaces are placed within this fake root coordinate space. + * + * For example a 200x200 large toplevel window will have the position (0, 0). + * If a popup positioned in the middle of the toplevel will have the fake + * position (100,100). Furthermore, if a positioned is placed in the middle + * that popup, will have the fake position (150,150), even though it has the + * relative position (50,50). These three windows would make up one single + * fake root coordinate space. + */ + + if (root_x) + *root_x = window->x + x; + + if (root_y) + *root_y = window->y + y; +} + +static gboolean +gdk_surface_wayland_get_device_state (GdkSurface *window, + GdkDevice *device, + gdouble *x, + gdouble *y, + GdkModifierType *mask) +{ + gboolean return_val; + + g_return_val_if_fail (window == NULL || GDK_IS_SURFACE (window), FALSE); + + return_val = TRUE; + + if (!GDK_SURFACE_DESTROYED (window)) + { + GdkSurface *child; + + GDK_DEVICE_GET_CLASS (device)->query_state (device, window, + &child, + NULL, NULL, + x, y, mask); + return_val = (child != NULL); + } + + return return_val; +} + +static void +gdk_surface_wayland_shape_combine_region (GdkSurface *window, + const cairo_region_t *shape_region, + gint offset_x, + gint offset_y) +{ +} + +static void +gdk_surface_wayland_input_shape_combine_region (GdkSurface *window, + const cairo_region_t *shape_region, + gint offset_x, + gint offset_y) +{ + GdkSurfaceImplWayland *impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); + + if (GDK_SURFACE_DESTROYED (window)) + return; + + g_clear_pointer (&impl->input_region, cairo_region_destroy); + + if (shape_region) + { + impl->input_region = cairo_region_copy (shape_region); + cairo_region_translate (impl->input_region, offset_x, offset_y); + } + + impl->input_region_dirty = TRUE; +} + +static void +gdk_wayland_surface_destroy (GdkSurface *window, + gboolean recursing, + gboolean foreign_destroy) +{ + g_return_if_fail (GDK_IS_SURFACE (window)); + + /* Wayland windows can't be externally destroyed; we may possibly + * eventually want to use this path at display close-down + */ + g_return_if_fail (!foreign_destroy); + + gdk_wayland_surface_hide_surface (window); + drop_cairo_surfaces (window); + + if (window->parent == NULL) + { + GdkWaylandDisplay *display = GDK_WAYLAND_DISPLAY (gdk_surface_get_display (window)); + display->toplevels = g_list_remove (display->toplevels, window); + } +} + +static void +gdk_wayland_surface_focus (GdkSurface *window, + guint32 timestamp) +{ + GdkSurfaceImplWayland *impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); + + if (!impl->display_server.gtk_surface) + return; + + /* We didn't have an event to fetch a time from, meaning we have nothing valid + * to send. This should rather be translated to a 'needs-attention' request or + * something. + */ + if (timestamp == GDK_CURRENT_TIME) + return; + + gtk_surface1_present (impl->display_server.gtk_surface, timestamp); +} + +static void +gdk_wayland_surface_set_type_hint (GdkSurface *window, + GdkSurfaceTypeHint hint) +{ + GdkSurfaceImplWayland *impl; + + impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); + + if (GDK_SURFACE_DESTROYED (window)) + return; + + impl->hint = hint; +} + +static GdkSurfaceTypeHint +gdk_wayland_surface_get_type_hint (GdkSurface *window) +{ + GdkSurfaceImplWayland *impl; + + if (GDK_SURFACE_DESTROYED (window)) + return GDK_SURFACE_TYPE_HINT_NORMAL; + + impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); + + return impl->hint; +} + +static void +gtk_surface_configure (void *data, + struct gtk_surface1 *gtk_surface, + struct wl_array *states) +{ + GdkSurface *window = GDK_SURFACE (data); + GdkSurfaceImplWayland *impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); + GdkSurfaceState new_state = 0; + uint32_t *p; + + wl_array_for_each (p, states) + { + uint32_t state = *p; + + switch (state) + { + case GTK_SURFACE1_STATE_TILED: + new_state |= GDK_SURFACE_STATE_TILED; + break; + + /* Since v2 */ + case GTK_SURFACE1_STATE_TILED_TOP: + new_state |= (GDK_SURFACE_STATE_TILED | GDK_SURFACE_STATE_TOP_TILED); + break; + case GTK_SURFACE1_STATE_TILED_RIGHT: + new_state |= (GDK_SURFACE_STATE_TILED | GDK_SURFACE_STATE_RIGHT_TILED); + break; + case GTK_SURFACE1_STATE_TILED_BOTTOM: + new_state |= (GDK_SURFACE_STATE_TILED | GDK_SURFACE_STATE_BOTTOM_TILED); + break; + case GTK_SURFACE1_STATE_TILED_LEFT: + new_state |= (GDK_SURFACE_STATE_TILED | GDK_SURFACE_STATE_LEFT_TILED); + break; + default: + /* Unknown state */ + break; + } + } + + impl->pending.state |= new_state; +} + +static void +gtk_surface_configure_edges (void *data, + struct gtk_surface1 *gtk_surface, + struct wl_array *edge_constraints) +{ + GdkSurface *window = GDK_SURFACE (data); + GdkSurfaceImplWayland *impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); + GdkSurfaceState new_state = 0; + uint32_t *p; + + wl_array_for_each (p, edge_constraints) + { + uint32_t constraint = *p; + + switch (constraint) + { + case GTK_SURFACE1_EDGE_CONSTRAINT_RESIZABLE_TOP: + new_state |= GDK_SURFACE_STATE_TOP_RESIZABLE; + break; + case GTK_SURFACE1_EDGE_CONSTRAINT_RESIZABLE_RIGHT: + new_state |= GDK_SURFACE_STATE_RIGHT_RESIZABLE; + break; + case GTK_SURFACE1_EDGE_CONSTRAINT_RESIZABLE_BOTTOM: + new_state |= GDK_SURFACE_STATE_BOTTOM_RESIZABLE; + break; + case GTK_SURFACE1_EDGE_CONSTRAINT_RESIZABLE_LEFT: + new_state |= GDK_SURFACE_STATE_LEFT_RESIZABLE; + break; + default: + /* Unknown state */ + break; + } + } + + impl->pending.state |= new_state; +} + +static const struct gtk_surface1_listener gtk_surface_listener = { + gtk_surface_configure, + gtk_surface_configure_edges +}; + +static void +gdk_wayland_surface_init_gtk_surface (GdkSurface *window) +{ + GdkSurfaceImplWayland *impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); + GdkWaylandDisplay *display = + GDK_WAYLAND_DISPLAY (gdk_surface_get_display (window)); + + if (impl->display_server.gtk_surface != NULL) + return; + if (impl->display_server.xdg_surface == NULL) + return; + if (display->gtk_shell == NULL) + return; + + impl->display_server.gtk_surface = + gtk_shell1_get_gtk_surface (display->gtk_shell, + impl->display_server.wl_surface); + gdk_surface_set_geometry_hints (window, + &impl->geometry_hints, + impl->geometry_mask); + gtk_surface1_add_listener (impl->display_server.gtk_surface, + >k_surface_listener, + window); +} + +static void +maybe_set_gtk_surface_modal (GdkSurface *window) +{ + GdkSurfaceImplWayland *impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); + + gdk_wayland_surface_init_gtk_surface (window); + if (impl->display_server.gtk_surface == NULL) + return; + + if (window->modal_hint) + gtk_surface1_set_modal (impl->display_server.gtk_surface); + else + gtk_surface1_unset_modal (impl->display_server.gtk_surface); + +} + +static void +gdk_wayland_surface_set_modal_hint (GdkSurface *window, + gboolean modal) +{ + window->modal_hint = modal; + maybe_set_gtk_surface_modal (window); +} + +static void +gdk_wayland_surface_set_skip_taskbar_hint (GdkSurface *window, + gboolean skips_taskbar) +{ +} + +static void +gdk_wayland_surface_set_skip_pager_hint (GdkSurface *window, + gboolean skips_pager) +{ +} + +static void +gdk_wayland_surface_set_urgency_hint (GdkSurface *window, + gboolean urgent) +{ +} + +static void +gdk_wayland_surface_set_geometry_hints (GdkSurface *window, + const GdkGeometry *geometry, + GdkSurfaceHints geom_mask) +{ + GdkSurfaceImplWayland *impl; + int width, height; + + if (GDK_SURFACE_DESTROYED (window) || + !WINDOW_IS_TOPLEVEL (window)) + return; + + impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); + + impl->geometry_hints = *geometry; + impl->geometry_mask = geom_mask; + + if (!impl->display_server.xdg_toplevel) + return; + + if (geom_mask & GDK_HINT_MIN_SIZE) + { + width = MAX (0, geometry->min_width - (impl->margin_left + impl->margin_right)); + height = MAX (0, geometry->min_height - (impl->margin_top + impl->margin_bottom)); + } + else + { + width = 0; + height = 0; + } + + zxdg_toplevel_v6_set_min_size (impl->display_server.xdg_toplevel, width, height); + + if (geom_mask & GDK_HINT_MAX_SIZE) + { + width = MAX (0, geometry->max_width - (impl->margin_left + impl->margin_right)); + height = MAX (0, geometry->max_height - (impl->margin_top + impl->margin_bottom)); + } + else + { + width = 0; + height = 0; + } + + zxdg_toplevel_v6_set_max_size (impl->display_server.xdg_toplevel, width, height); +} + +static void +gdk_wayland_surface_set_title (GdkSurface *window, + const gchar *title) +{ + GdkSurfaceImplWayland *impl; + const char *end; + gsize title_length; + + g_return_if_fail (title != NULL); + + if (GDK_SURFACE_DESTROYED (window)) + return; + + impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); + + if (g_strcmp0 (impl->title, title) == 0) + return; + + g_free (impl->title); + + title_length = MIN (strlen (title), MAX_WL_BUFFER_SIZE); + if (g_utf8_validate (title, title_length, &end)) + { + impl->title = g_malloc (end - title + 1); + memcpy (impl->title, title, end - title); + impl->title[end - title] = '\0'; + } + else + { + impl->title = g_utf8_make_valid (title, title_length); + g_warning ("Invalid utf8 passed to gdk_surface_set_title: '%s'", title); + } + + gdk_wayland_surface_sync_title (window); +} + +static void +gdk_wayland_surface_set_role (GdkSurface *window, + const gchar *role) +{ +} + +static void +gdk_wayland_surface_set_startup_id (GdkSurface *window, + const gchar *startup_id) +{ +} + +static gboolean +check_transient_for_loop (GdkSurface *window, + GdkSurface *parent) +{ + while (parent) + { + GdkSurfaceImplWayland *impl; + + if (!GDK_IS_SURFACE_IMPL_WAYLAND(parent->impl)) + return FALSE; + + impl = GDK_SURFACE_IMPL_WAYLAND (parent->impl); + if (impl->transient_for == window) + return TRUE; + parent = impl->transient_for; + } + return FALSE; +} + +static void +gdk_wayland_surface_set_transient_for (GdkSurface *window, + GdkSurface *parent) +{ + GdkSurfaceImplWayland *impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); + GdkWaylandDisplay *display_wayland = + GDK_WAYLAND_DISPLAY (gdk_surface_get_display (window)); + GdkSurface *previous_parent; + + g_assert (parent == NULL || + gdk_surface_get_display (window) == gdk_surface_get_display (parent)); + + if (check_transient_for_loop (window, parent)) + { + g_warning ("Setting %p transient for %p would create a loop", window, parent); + return; + } + + unset_transient_for_exported (window); + + if (impl->display_server.wl_subsurface) + unmap_subsurface (window); + + previous_parent = impl->transient_for; + impl->transient_for = parent; + + if (impl->hint == GDK_SURFACE_TYPE_HINT_DIALOG) + { + if (!parent) + _gdk_wayland_screen_add_orphan_dialog (window); + else if (!previous_parent) + display_wayland->orphan_dialogs = + g_list_remove (display_wayland->orphan_dialogs, window); + } + gdk_wayland_surface_sync_parent (window, NULL); + if (should_map_as_subsurface (window) && + parent && gdk_surface_is_visible (window)) + gdk_wayland_surface_create_subsurface (window); +} + +static void +gdk_wayland_surface_get_frame_extents (GdkSurface *window, + GdkRectangle *rect) +{ + *rect = (GdkRectangle) { + .x = window->x, + .y = window->y, + .width = window->width, + .height = window->height + }; +} + +static void +gdk_wayland_surface_set_accept_focus (GdkSurface *window, + gboolean accept_focus) +{ +} + +static void +gdk_wayland_surface_set_focus_on_map (GdkSurface *window, + gboolean focus_on_map) +{ +} + +static void +gdk_wayland_surface_set_icon_list (GdkSurface *window, + GList *surfaces) +{ +} + +static void +gdk_wayland_surface_set_icon_name (GdkSurface *window, + const gchar *name) +{ + if (GDK_SURFACE_DESTROYED (window)) + return; +} + +static void +gdk_wayland_surface_iconify (GdkSurface *window) +{ + GdkSurfaceImplWayland *impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); + + if (GDK_SURFACE_DESTROYED (window) || + !WINDOW_IS_TOPLEVEL (window)) + return; + + if (!impl->display_server.xdg_toplevel) + return; + + zxdg_toplevel_v6_set_minimized (impl->display_server.xdg_toplevel); +} + +static void +gdk_wayland_surface_deiconify (GdkSurface *window) +{ + if (GDK_SURFACE_DESTROYED (window) || + !WINDOW_IS_TOPLEVEL (window)) + return; + + if (GDK_SURFACE_IS_MAPPED (window)) + gdk_surface_show (window); + else + /* Flip our client side flag, the real work happens on map. */ + gdk_synthesize_window_state (window, GDK_SURFACE_STATE_ICONIFIED, 0); +} + +static void +gdk_wayland_surface_stick (GdkSurface *window) +{ +} + +static void +gdk_wayland_surface_unstick (GdkSurface *window) +{ +} + +static void +gdk_wayland_surface_maximize (GdkSurface *window) +{ + GdkSurfaceImplWayland *impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); + + if (GDK_SURFACE_DESTROYED (window)) + return; + + _gdk_wayland_surface_save_size (window); + if (impl->display_server.xdg_toplevel) + zxdg_toplevel_v6_set_maximized (impl->display_server.xdg_toplevel); + else + gdk_synthesize_window_state (window, 0, GDK_SURFACE_STATE_MAXIMIZED); +} + +static void +gdk_wayland_surface_unmaximize (GdkSurface *window) +{ + GdkSurfaceImplWayland *impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); + + if (GDK_SURFACE_DESTROYED (window)) + return; + + if (impl->display_server.xdg_toplevel) + zxdg_toplevel_v6_unset_maximized (impl->display_server.xdg_toplevel); + else + gdk_synthesize_window_state (window, GDK_SURFACE_STATE_MAXIMIZED, 0); +} + +static void +gdk_wayland_surface_fullscreen_on_monitor (GdkSurface *window, + GdkMonitor *monitor) +{ + GdkSurfaceImplWayland *impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); + struct wl_output *output = ((GdkWaylandMonitor *)monitor)->output; + + if (GDK_SURFACE_DESTROYED (window)) + return; + + _gdk_wayland_surface_save_size (window); + if (impl->display_server.xdg_toplevel) + { + zxdg_toplevel_v6_set_fullscreen (impl->display_server.xdg_toplevel, output); + } + else + { + gdk_synthesize_window_state (window, 0, GDK_SURFACE_STATE_FULLSCREEN); + impl->initial_fullscreen_output = output; + } +} + +static void +gdk_wayland_surface_fullscreen (GdkSurface *window) +{ + GdkSurfaceImplWayland *impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); + + if (GDK_SURFACE_DESTROYED (window)) + return; + + impl->initial_fullscreen_output = NULL; + + _gdk_wayland_surface_save_size (window); + if (impl->display_server.xdg_toplevel) + zxdg_toplevel_v6_set_fullscreen (impl->display_server.xdg_toplevel, NULL); + else + gdk_synthesize_window_state (window, 0, GDK_SURFACE_STATE_FULLSCREEN); +} + +static void +gdk_wayland_surface_unfullscreen (GdkSurface *window) +{ + GdkSurfaceImplWayland *impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); + + if (GDK_SURFACE_DESTROYED (window)) + return; + + impl->initial_fullscreen_output = NULL; + + if (impl->display_server.xdg_toplevel) + zxdg_toplevel_v6_unset_fullscreen (impl->display_server.xdg_toplevel); + else + gdk_synthesize_window_state (window, GDK_SURFACE_STATE_FULLSCREEN, 0); +} + +static void +gdk_wayland_surface_set_keep_above (GdkSurface *window, gboolean setting) +{ +} + +static void +gdk_wayland_surface_set_keep_below (GdkSurface *window, gboolean setting) +{ +} + +static GdkSurface * +gdk_wayland_surface_get_group (GdkSurface *window) +{ + return NULL; +} + +static void +gdk_wayland_surface_set_group (GdkSurface *window, + GdkSurface *leader) +{ +} + +static void +gdk_wayland_surface_set_decorations (GdkSurface *window, + GdkWMDecoration decorations) +{ +} + +static gboolean +gdk_wayland_surface_get_decorations (GdkSurface *window, + GdkWMDecoration *decorations) +{ + return FALSE; +} + +static void +gdk_wayland_surface_set_functions (GdkSurface *window, + GdkWMFunction functions) +{ +} + +static void +gdk_wayland_surface_begin_resize_drag (GdkSurface *window, + GdkSurfaceEdge edge, + GdkDevice *device, + gint button, + gint root_x, + gint root_y, + guint32 timestamp) +{ + GdkSurfaceImplWayland *impl; + GdkEventSequence *sequence; + uint32_t resize_edges, serial; + + if (GDK_SURFACE_DESTROYED (window) || + !WINDOW_IS_TOPLEVEL (window)) + return; + + switch (edge) + { + case GDK_SURFACE_EDGE_NORTH_WEST: + resize_edges = ZXDG_TOPLEVEL_V6_RESIZE_EDGE_TOP_LEFT; + break; + + case GDK_SURFACE_EDGE_NORTH: + resize_edges = ZXDG_TOPLEVEL_V6_RESIZE_EDGE_TOP; + break; + + case GDK_SURFACE_EDGE_NORTH_EAST: + resize_edges = ZXDG_TOPLEVEL_V6_RESIZE_EDGE_TOP_RIGHT; + break; + + case GDK_SURFACE_EDGE_WEST: + resize_edges = ZXDG_TOPLEVEL_V6_RESIZE_EDGE_LEFT; + break; + + case GDK_SURFACE_EDGE_EAST: + resize_edges = ZXDG_TOPLEVEL_V6_RESIZE_EDGE_RIGHT; + break; + + case GDK_SURFACE_EDGE_SOUTH_WEST: + resize_edges = ZXDG_TOPLEVEL_V6_RESIZE_EDGE_BOTTOM_LEFT; + break; + + case GDK_SURFACE_EDGE_SOUTH: + resize_edges = ZXDG_TOPLEVEL_V6_RESIZE_EDGE_BOTTOM; + break; + + case GDK_SURFACE_EDGE_SOUTH_EAST: + resize_edges = ZXDG_TOPLEVEL_V6_RESIZE_EDGE_BOTTOM_RIGHT; + break; + + default: + g_warning ("gdk_surface_begin_resize_drag: bad resize edge %d!", edge); + return; + } + + impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); + + if (!impl->display_server.xdg_toplevel) + return; + + serial = _gdk_wayland_seat_get_last_implicit_grab_serial (gdk_device_get_seat (device), + &sequence); + + zxdg_toplevel_v6_resize (impl->display_server.xdg_toplevel, + gdk_wayland_device_get_wl_seat (device), + serial, resize_edges); + + if (sequence) + gdk_wayland_device_unset_touch_grab (device, sequence); + + /* This is needed since Wayland will absorb all the pointer events after the + * above function - FIXME: Is this always safe..? + */ + gdk_seat_ungrab (gdk_device_get_seat (device)); +} + +static void +gdk_wayland_surface_begin_move_drag (GdkSurface *window, + GdkDevice *device, + gint button, + gint root_x, + gint root_y, + guint32 timestamp) +{ + GdkSurfaceImplWayland *impl; + GdkEventSequence *sequence; + uint32_t serial; + + if (GDK_SURFACE_DESTROYED (window) || + !WINDOW_IS_TOPLEVEL (window)) + return; + + impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); + + if (!impl->display_server.xdg_toplevel) + return; + + serial = _gdk_wayland_seat_get_last_implicit_grab_serial (gdk_device_get_seat (device), + &sequence); + zxdg_toplevel_v6_move (impl->display_server.xdg_toplevel, + gdk_wayland_device_get_wl_seat (device), + serial); + if (sequence) + gdk_wayland_device_unset_touch_grab (device, sequence); + + /* This is needed since Wayland will absorb all the pointer events after the + * above function - FIXME: Is this always safe..? + */ + gdk_seat_ungrab (gdk_device_get_seat (device)); +} + +static void +gdk_wayland_surface_set_opacity (GdkSurface *window, + gdouble opacity) +{ +} + +static void +gdk_wayland_surface_destroy_notify (GdkSurface *window) +{ + if (!GDK_SURFACE_DESTROYED (window)) + { + g_warning ("GdkSurface %p unexpectedly destroyed", window); + _gdk_surface_destroy (window, TRUE); + } + + g_object_unref (window); +} + +static gint +gdk_wayland_surface_get_scale_factor (GdkSurface *window) +{ + GdkSurfaceImplWayland *impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); + + if (GDK_SURFACE_DESTROYED (window)) + return 1; + + return impl->scale; +} + +static void +gdk_wayland_surface_set_opaque_region (GdkSurface *window, + cairo_region_t *region) +{ + GdkSurfaceImplWayland *impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); + + if (GDK_SURFACE_DESTROYED (window)) + return; + + g_clear_pointer (&impl->opaque_region, cairo_region_destroy); + impl->opaque_region = cairo_region_reference (region); + impl->opaque_region_dirty = TRUE; +} + +static void +gdk_wayland_surface_set_shadow_width (GdkSurface *window, + int left, + int right, + int top, + int bottom) +{ + GdkSurfaceImplWayland *impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); + gint new_width, new_height; + + if (GDK_SURFACE_DESTROYED (window)) + return; + + /* Reconfigure window to keep the same window geometry */ + new_width = window->width - + (impl->margin_left + impl->margin_right) + (left + right); + new_height = window->height - + (impl->margin_top + impl->margin_bottom) + (top + bottom); + gdk_wayland_surface_maybe_configure (window, new_width, new_height, impl->scale); + + impl->margin_left = left; + impl->margin_right = right; + impl->margin_top = top; + impl->margin_bottom = bottom; +} + +static gboolean +gdk_wayland_surface_show_window_menu (GdkSurface *window, + GdkEvent *event) +{ + GdkSurfaceImplWayland *impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); + struct wl_seat *seat; + GdkWaylandDevice *device; + double x, y; + uint32_t serial; + + switch ((guint) event->any.type) + { + case GDK_BUTTON_PRESS: + case GDK_BUTTON_RELEASE: + case GDK_TOUCH_BEGIN: + case GDK_TOUCH_END: + break; + default: + return FALSE; + } + + if (!impl->display_server.xdg_surface) + return FALSE; + + device = GDK_WAYLAND_DEVICE (gdk_event_get_device (event)); + seat = gdk_wayland_device_get_wl_seat (GDK_DEVICE (device)); + gdk_event_get_coords (event, &x, &y); + + serial = _gdk_wayland_device_get_implicit_grab_serial (device, event); + zxdg_toplevel_v6_show_window_menu (impl->display_server.xdg_toplevel, + seat, serial, x, y); + return TRUE; +} + +static gboolean +gdk_wayland_surface_supports_edge_constraints (GdkSurface *window) +{ + GdkSurfaceImplWayland *impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); + struct gtk_surface1 *gtk_surface = impl->display_server.gtk_surface; + + if (!gtk_surface) + return FALSE; + + return gtk_surface1_get_version (gtk_surface) >= GTK_SURFACE1_CONFIGURE_EDGES_SINCE_VERSION; +} + +static void +_gdk_surface_impl_wayland_class_init (GdkSurfaceImplWaylandClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GdkSurfaceImplClass *impl_class = GDK_SURFACE_IMPL_CLASS (klass); + + object_class->finalize = gdk_surface_impl_wayland_finalize; + + impl_class->ref_cairo_surface = gdk_wayland_surface_ref_cairo_surface; + impl_class->create_similar_image_surface = gdk_wayland_surface_create_similar_image_surface; + impl_class->show = gdk_wayland_surface_show; + impl_class->hide = gdk_wayland_surface_hide; + impl_class->withdraw = gdk_surface_wayland_withdraw; + impl_class->set_events = gdk_surface_wayland_set_events; + impl_class->get_events = gdk_surface_wayland_get_events; + impl_class->raise = gdk_surface_wayland_raise; + impl_class->lower = gdk_surface_wayland_lower; + impl_class->restack_toplevel = gdk_surface_wayland_restack_toplevel; + impl_class->move_resize = gdk_surface_wayland_move_resize; + impl_class->move_to_rect = gdk_surface_wayland_move_to_rect; + impl_class->get_geometry = gdk_surface_wayland_get_geometry; + impl_class->get_root_coords = gdk_surface_wayland_get_root_coords; + impl_class->get_device_state = gdk_surface_wayland_get_device_state; + impl_class->shape_combine_region = gdk_surface_wayland_shape_combine_region; + impl_class->input_shape_combine_region = gdk_surface_wayland_input_shape_combine_region; + impl_class->destroy = gdk_wayland_surface_destroy; + impl_class->begin_paint = gdk_surface_impl_wayland_begin_paint; + impl_class->end_paint = gdk_surface_impl_wayland_end_paint; + impl_class->beep = gdk_surface_impl_wayland_beep; + + impl_class->focus = gdk_wayland_surface_focus; + impl_class->set_type_hint = gdk_wayland_surface_set_type_hint; + impl_class->get_type_hint = gdk_wayland_surface_get_type_hint; + impl_class->set_modal_hint = gdk_wayland_surface_set_modal_hint; + impl_class->set_skip_taskbar_hint = gdk_wayland_surface_set_skip_taskbar_hint; + impl_class->set_skip_pager_hint = gdk_wayland_surface_set_skip_pager_hint; + impl_class->set_urgency_hint = gdk_wayland_surface_set_urgency_hint; + impl_class->set_geometry_hints = gdk_wayland_surface_set_geometry_hints; + impl_class->set_title = gdk_wayland_surface_set_title; + impl_class->set_role = gdk_wayland_surface_set_role; + impl_class->set_startup_id = gdk_wayland_surface_set_startup_id; + impl_class->set_transient_for = gdk_wayland_surface_set_transient_for; + impl_class->get_frame_extents = gdk_wayland_surface_get_frame_extents; + impl_class->set_accept_focus = gdk_wayland_surface_set_accept_focus; + impl_class->set_focus_on_map = gdk_wayland_surface_set_focus_on_map; + impl_class->set_icon_list = gdk_wayland_surface_set_icon_list; + impl_class->set_icon_name = gdk_wayland_surface_set_icon_name; + impl_class->iconify = gdk_wayland_surface_iconify; + impl_class->deiconify = gdk_wayland_surface_deiconify; + impl_class->stick = gdk_wayland_surface_stick; + impl_class->unstick = gdk_wayland_surface_unstick; + impl_class->maximize = gdk_wayland_surface_maximize; + impl_class->unmaximize = gdk_wayland_surface_unmaximize; + impl_class->fullscreen = gdk_wayland_surface_fullscreen; + impl_class->fullscreen_on_monitor = gdk_wayland_surface_fullscreen_on_monitor; + impl_class->unfullscreen = gdk_wayland_surface_unfullscreen; + impl_class->set_keep_above = gdk_wayland_surface_set_keep_above; + impl_class->set_keep_below = gdk_wayland_surface_set_keep_below; + impl_class->get_group = gdk_wayland_surface_get_group; + impl_class->set_group = gdk_wayland_surface_set_group; + impl_class->set_decorations = gdk_wayland_surface_set_decorations; + impl_class->get_decorations = gdk_wayland_surface_get_decorations; + impl_class->set_functions = gdk_wayland_surface_set_functions; + impl_class->begin_resize_drag = gdk_wayland_surface_begin_resize_drag; + impl_class->begin_move_drag = gdk_wayland_surface_begin_move_drag; + impl_class->set_opacity = gdk_wayland_surface_set_opacity; + impl_class->destroy_notify = gdk_wayland_surface_destroy_notify; + impl_class->register_dnd = _gdk_wayland_surface_register_dnd; + impl_class->drag_begin = _gdk_wayland_surface_drag_begin; + impl_class->get_scale_factor = gdk_wayland_surface_get_scale_factor; + impl_class->set_opaque_region = gdk_wayland_surface_set_opaque_region; + impl_class->set_shadow_width = gdk_wayland_surface_set_shadow_width; + impl_class->show_window_menu = gdk_wayland_surface_show_window_menu; + impl_class->create_gl_context = gdk_wayland_surface_create_gl_context; + impl_class->supports_edge_constraints = gdk_wayland_surface_supports_edge_constraints; + + signals[COMMITTED] = g_signal_new (g_intern_static_string ("committed"), + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, NULL, + G_TYPE_NONE, 0); +} + +void +_gdk_wayland_surface_set_grab_seat (GdkSurface *window, + GdkSeat *seat) +{ + GdkSurfaceImplWayland *impl; + + g_return_if_fail (window != NULL); + + impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); + impl->grab_input_seat = seat; +} + +/** + * gdk_wayland_surface_new_subsurface: (constructor) + * @display: the display to create the window on + * @position: position relative to the transient window + * + * Creates a new subsurface window. + * + * Returns: (transfer full): the new #GdkSurface + **/ +GdkSurface * +gdk_wayland_surface_new_subsurface (GdkDisplay *display, + const GdkRectangle *position) +{ + GdkSurfaceAttr attr; + + g_return_val_if_fail (GDK_IS_DISPLAY (display), NULL); + g_return_val_if_fail (position != NULL, NULL); + + attr.wclass = GDK_INPUT_OUTPUT; + attr.x = position->x; + attr.y = position->y; + attr.width = position->width; + attr.height = position->height; + attr.window_type = GDK_SURFACE_SUBSURFACE; + + return gdk_surface_new (display, NULL, &attr); +} + +/** + * gdk_wayland_surface_get_wl_surface: + * @window: (type GdkWaylandSurface): a #GdkSurface + * + * Returns the Wayland surface of a #GdkSurface. + * + * Returns: (transfer none): a Wayland wl_surface + */ +struct wl_surface * +gdk_wayland_surface_get_wl_surface (GdkSurface *window) +{ + g_return_val_if_fail (GDK_IS_WAYLAND_SURFACE (window), NULL); + + return GDK_SURFACE_IMPL_WAYLAND (window->impl)->display_server.wl_surface; +} + +struct wl_output * +gdk_wayland_surface_get_wl_output (GdkSurface *window) +{ + GdkSurfaceImplWayland *impl; + + g_return_val_if_fail (GDK_IS_WAYLAND_SURFACE (window), NULL); + + impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); + /* We pick the head of the list as this is the last entered output */ + if (impl->display_server.outputs) + return (struct wl_output *) impl->display_server.outputs->data; + + return NULL; +} + +static struct wl_egl_window * +gdk_wayland_surface_get_wl_egl_window (GdkSurface *window) +{ + GdkSurfaceImplWayland *impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); + + if (impl->display_server.egl_window == NULL) + { + impl->display_server.egl_window = + wl_egl_window_create (impl->display_server.wl_surface, + impl->wrapper->width * impl->scale, + impl->wrapper->height * impl->scale); + wl_surface_set_buffer_scale (impl->display_server.wl_surface, impl->scale); + } + + return impl->display_server.egl_window; +} + +EGLSurface +gdk_wayland_surface_get_egl_surface (GdkSurface *window, + EGLConfig config) +{ + GdkWaylandDisplay *display = GDK_WAYLAND_DISPLAY (gdk_surface_get_display (window)); + GdkSurfaceImplWayland *impl; + struct wl_egl_window *egl_window; + + g_return_val_if_fail (GDK_IS_WAYLAND_SURFACE (window), NULL); + + impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); + + if (impl->egl_surface == NULL) + { + egl_window = gdk_wayland_surface_get_wl_egl_window (window); + + impl->egl_surface = + eglCreateWindowSurface (display->egl_display, config, egl_window, NULL); + } + + return impl->egl_surface; +} + +EGLSurface +gdk_wayland_surface_get_dummy_egl_surface (GdkSurface *window, + EGLConfig config) +{ + GdkWaylandDisplay *display = GDK_WAYLAND_DISPLAY (gdk_surface_get_display (window)); + GdkSurfaceImplWayland *impl; + + g_return_val_if_fail (GDK_IS_WAYLAND_SURFACE (window), NULL); + + impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); + + if (impl->dummy_egl_surface == NULL) + { + impl->display_server.dummy_egl_window = + wl_egl_window_create (impl->display_server.wl_surface, 1, 1); + + impl->dummy_egl_surface = + eglCreateWindowSurface (display->egl_display, config, impl->display_server.dummy_egl_window, NULL); + } + + return impl->dummy_egl_surface; +} + +struct gtk_surface1 * +gdk_wayland_surface_get_gtk_surface (GdkSurface *window) +{ + g_return_val_if_fail (GDK_IS_WAYLAND_SURFACE (window), NULL); + + return GDK_SURFACE_IMPL_WAYLAND (window->impl)->display_server.gtk_surface; +} + +/** + * gdk_wayland_surface_set_use_custom_surface: + * @window: (type GdkWaylandSurface): a #GdkSurface + * + * Marks a #GdkSurface as a custom Wayland surface. The application is + * expected to register the surface as some type of surface using + * some Wayland interface. + * + * A good example would be writing a panel or on-screen-keyboard as an + * out-of-process helper - as opposed to having those in the compositor + * process. In this case the underlying surface isn’t an xdg_shell + * surface and the panel or OSK client need to identify the wl_surface + * as a panel or OSK to the compositor. The assumption is that the + * compositor will expose a private interface to the special client + * that lets the client identify the wl_surface as a panel or such. + * + * This function should be called before a #GdkSurface is shown. This is + * best done by connecting to the #GtkWidget::realize signal: + * + * |[ + * static void + * widget_realize_cb (GtkWidget *widget) + * { + * GdkSurface *window; + * struct wl_surface *surface; + * struct input_panel_surface *ip_surface; + * + * window = gtk_widget_get_window (widget); + * gdk_wayland_surface_set_custom_surface (window); + * + * surface = gdk_wayland_surface_get_wl_surface (window); + * ip_surface = input_panel_get_input_panel_surface (input_panel, surface); + * input_panel_surface_set_panel (ip_surface); + * } + * + * static void + * setup_window (GtkWindow *window) + * { + * g_signal_connect (window, "realize", G_CALLBACK (widget_realize_cb), NULL); + * } + * ]| + */ +void +gdk_wayland_surface_set_use_custom_surface (GdkSurface *window) +{ + GdkSurfaceImplWayland *impl; + + g_return_if_fail (GDK_IS_WAYLAND_SURFACE (window)); + + impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); + + if (!impl->display_server.wl_surface) + gdk_wayland_surface_create_surface (window); + + impl->use_custom_surface = TRUE; +} + +static void +maybe_set_gtk_surface_dbus_properties (GdkSurface *window) +{ + GdkSurfaceImplWayland *impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); + + if (impl->application.was_set) + return; + + if (impl->application.application_id == NULL && + impl->application.app_menu_path == NULL && + impl->application.menubar_path == NULL && + impl->application.window_object_path == NULL && + impl->application.application_object_path == NULL && + impl->application.unique_bus_name == NULL) + return; + + gdk_wayland_surface_init_gtk_surface (window); + if (impl->display_server.gtk_surface == NULL) + return; + + gtk_surface1_set_dbus_properties (impl->display_server.gtk_surface, + impl->application.application_id, + impl->application.app_menu_path, + impl->application.menubar_path, + impl->application.window_object_path, + impl->application.application_object_path, + impl->application.unique_bus_name); + impl->application.was_set = TRUE; +} + +void +gdk_wayland_surface_set_dbus_properties_libgtk_only (GdkSurface *window, + const char *application_id, + const char *app_menu_path, + const char *menubar_path, + const char *window_object_path, + const char *application_object_path, + const char *unique_bus_name) +{ + GdkSurfaceImplWayland *impl; + + g_return_if_fail (GDK_IS_WAYLAND_SURFACE (window)); + + impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); + + impl->application.application_id = g_strdup (application_id); + impl->application.app_menu_path = g_strdup (app_menu_path); + impl->application.menubar_path = g_strdup (menubar_path); + impl->application.window_object_path = g_strdup (window_object_path); + impl->application.application_object_path = + g_strdup (application_object_path); + impl->application.unique_bus_name = g_strdup (unique_bus_name); + + maybe_set_gtk_surface_dbus_properties (window); +} + +void +_gdk_wayland_surface_offset_next_wl_buffer (GdkSurface *window, + int x, + int y) +{ + GdkSurfaceImplWayland *impl; + + g_return_if_fail (GDK_IS_WAYLAND_SURFACE (window)); + + impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); + + impl->pending_buffer_offset_x = x; + impl->pending_buffer_offset_y = y; +} + +static void +xdg_exported_handle (void *data, + struct zxdg_exported_v1 *zxdg_exported_v1, + const char *handle) +{ + GdkSurface *window = data; + GdkSurfaceImplWayland *impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); + + impl->exported.callback (window, handle, impl->exported.user_data); + g_clear_pointer (&impl->exported.user_data, + impl->exported.destroy_func); +} + +static const struct zxdg_exported_v1_listener xdg_exported_listener = { + xdg_exported_handle +}; + +/** + * GdkWaylandSurfaceExported: + * @window: the #GdkSurface that is exported + * @handle: the handle + * @user_data: user data that was passed to gdk_wayland_surface_export_handle() + * + * Callback that gets called when the handle for a window has been + * obtained from the Wayland compositor. The handle can be passed + * to other processes, for the purpose of marking windows as transient + * for out-of-process surfaces. + */ + +static gboolean +gdk_wayland_surface_is_exported (GdkSurface *window) +{ + GdkSurfaceImplWayland *impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); + + return !!impl->display_server.xdg_exported; +} + +/** + * gdk_wayland_surface_export_handle: + * @window: the #GdkSurface to obtain a handle for + * @callback: callback to call with the handle + * @user_data: user data for @callback + * @destroy_func: destroy notify for @user_data + * + * Asynchronously obtains a handle for a surface that can be passed + * to other processes. When the handle has been obtained, @callback + * will be called. + * + * It is an error to call this function on a window that is already + * exported. + * + * When the handle is no longer needed, gdk_wayland_surface_unexport_handle() + * should be called to clean up resources. + * + * The main purpose for obtaining a handle is to mark a surface + * from another window as transient for this one, see + * gdk_wayland_surface_set_transient_for_exported(). + * + * Note that this API depends on an unstable Wayland protocol, + * and thus may require changes in the future. + * + * Return value: %TRUE if the handle has been requested, %FALSE if + * an error occurred. + */ +gboolean +gdk_wayland_surface_export_handle (GdkSurface *window, + GdkWaylandSurfaceExported callback, + gpointer user_data, + GDestroyNotify destroy_func) +{ + GdkSurfaceImplWayland *impl; + GdkWaylandDisplay *display_wayland; + GdkDisplay *display = gdk_surface_get_display (window); + struct zxdg_exported_v1 *xdg_exported; + + g_return_val_if_fail (GDK_IS_WAYLAND_SURFACE (window), FALSE); + g_return_val_if_fail (GDK_IS_WAYLAND_DISPLAY (display), FALSE); + + impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); + display_wayland = GDK_WAYLAND_DISPLAY (display); + + g_return_val_if_fail (!impl->display_server.xdg_exported, FALSE); + + if (!display_wayland->xdg_exporter) + { + g_warning ("Server is missing xdg_foreign support"); + return FALSE; + } + + xdg_exported = zxdg_exporter_v1_export (display_wayland->xdg_exporter, + impl->display_server.wl_surface); + zxdg_exported_v1_add_listener (xdg_exported, &xdg_exported_listener, window); + + impl->display_server.xdg_exported = xdg_exported; + impl->exported.callback = callback; + impl->exported.user_data = user_data; + impl->exported.destroy_func = destroy_func; + + return TRUE; +} + +/** + * gdk_wayland_surface_unexport_handle: + * @window: the #GdkSurface to unexport + * + * Destroys the handle that was obtained with + * gdk_wayland_surface_export_handle(). + * + * It is an error to call this function on a window that + * does not have a handle. + * + * Note that this API depends on an unstable Wayland protocol, + * and thus may require changes in the future. + */ +void +gdk_wayland_surface_unexport_handle (GdkSurface *window) +{ + GdkSurfaceImplWayland *impl; + + g_return_if_fail (GDK_IS_WAYLAND_SURFACE (window)); + + impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); + + g_return_if_fail (impl->display_server.xdg_exported); + + g_clear_pointer (&impl->display_server.xdg_exported, + zxdg_exported_v1_destroy); + g_clear_pointer (&impl->exported.user_data, + impl->exported.destroy_func); +} + +static void +unset_transient_for_exported (GdkSurface *window) +{ + GdkSurfaceImplWayland *impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); + + g_clear_pointer (&impl->imported_transient_for, zxdg_imported_v1_destroy); +} + +static void +xdg_imported_destroyed (void *data, + struct zxdg_imported_v1 *zxdg_imported_v1) +{ + GdkSurface *window = data; + + unset_transient_for_exported (window); +} + +static const struct zxdg_imported_v1_listener xdg_imported_listener = { + xdg_imported_destroyed, +}; + +/** + * gdk_wayland_surface_set_transient_for_exported: + * @window: the #GdkSurface to make as transient + * @parent_handle_str: an exported handle for a surface + * + * Marks @window as transient for the surface to which the given + * @parent_handle_str refers. Typically, the handle will originate + * from a gdk_wayland_surface_export_handle() call in another process. + * + * Note that this API depends on an unstable Wayland protocol, + * and thus may require changes in the future. + * + * Return value: %TRUE if the window has been marked as transient, + * %FALSE if an error occurred. + */ +gboolean +gdk_wayland_surface_set_transient_for_exported (GdkSurface *window, + char *parent_handle_str) +{ + GdkSurfaceImplWayland *impl; + GdkWaylandDisplay *display_wayland; + GdkDisplay *display = gdk_surface_get_display (window); + + g_return_val_if_fail (GDK_IS_WAYLAND_SURFACE (window), FALSE); + g_return_val_if_fail (GDK_IS_WAYLAND_DISPLAY (display), FALSE); + g_return_val_if_fail (!should_map_as_subsurface (window) && + !should_map_as_popup (window), FALSE); + + impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); + display_wayland = GDK_WAYLAND_DISPLAY (display); + + if (!display_wayland->xdg_importer) + { + g_warning ("Server is missing xdg_foreign support"); + return FALSE; + } + + gdk_surface_set_transient_for (window, NULL); + + impl->imported_transient_for = + zxdg_importer_v1_import (display_wayland->xdg_importer, parent_handle_str); + zxdg_imported_v1_add_listener (impl->imported_transient_for, + &xdg_imported_listener, + window); + + gdk_wayland_surface_sync_parent_of_imported (window); + + return TRUE; +} + +static struct zwp_keyboard_shortcuts_inhibitor_v1 * +gdk_wayland_surface_get_inhibitor (GdkSurfaceImplWayland *impl, + struct wl_seat *seat) +{ + return g_hash_table_lookup (impl->shortcuts_inhibitors, seat); +} + +void +gdk_wayland_surface_inhibit_shortcuts (GdkSurface *window, + GdkSeat *gdk_seat) +{ + GdkSurfaceImplWayland *impl= GDK_SURFACE_IMPL_WAYLAND (window->impl); + GdkWaylandDisplay *display = GDK_WAYLAND_DISPLAY (gdk_surface_get_display (window)); + struct wl_surface *surface = impl->display_server.wl_surface; + struct wl_seat *seat = gdk_wayland_seat_get_wl_seat (gdk_seat); + struct zwp_keyboard_shortcuts_inhibitor_v1 *inhibitor; + + if (display->keyboard_shortcuts_inhibit == NULL) + return; + + if (gdk_wayland_surface_get_inhibitor (impl, seat)) + return; /* Already inhibitted */ + + inhibitor = + zwp_keyboard_shortcuts_inhibit_manager_v1_inhibit_shortcuts ( + display->keyboard_shortcuts_inhibit, surface, seat); + + g_hash_table_insert (impl->shortcuts_inhibitors, seat, inhibitor); +} + +void +gdk_wayland_surface_restore_shortcuts (GdkSurface *window, + GdkSeat *gdk_seat) +{ + GdkSurfaceImplWayland *impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); + struct wl_seat *seat = gdk_wayland_seat_get_wl_seat (gdk_seat); + struct zwp_keyboard_shortcuts_inhibitor_v1 *inhibitor; + + inhibitor = gdk_wayland_surface_get_inhibitor (impl, seat); + if (inhibitor == NULL) + return; /* Not inhibitted */ + + zwp_keyboard_shortcuts_inhibitor_v1_destroy (inhibitor); + g_hash_table_remove (impl->shortcuts_inhibitors, seat); +} + diff --git a/gdk/wayland/gdkvulkancontext-wayland.c b/gdk/wayland/gdkvulkancontext-wayland.c index cdac207635..a5f721ec64 100644 --- a/gdk/wayland/gdkvulkancontext-wayland.c +++ b/gdk/wayland/gdkvulkancontext-wayland.c @@ -28,7 +28,7 @@ #include "gdkinternals.h" #include "gdkwaylanddisplay.h" -#include "gdkwaylandwindow.h" +#include "gdkwaylandsurface.h" #include "gdkprivate-wayland.h" G_DEFINE_TYPE (GdkWaylandVulkanContext, gdk_wayland_vulkan_context, GDK_TYPE_VULKAN_CONTEXT) diff --git a/gdk/wayland/gdkwayland.h b/gdk/wayland/gdkwayland.h index 14f25d1d1d..9179d58e77 100644 --- a/gdk/wayland/gdkwayland.h +++ b/gdk/wayland/gdkwayland.h @@ -32,7 +32,7 @@ #include #include #include -#include +#include #include #undef __GDKWAYLAND_H_INSIDE__ diff --git a/gdk/wayland/gdkwaylandsurface.h b/gdk/wayland/gdkwaylandsurface.h new file mode 100644 index 0000000000..b9747b6ca8 --- /dev/null +++ b/gdk/wayland/gdkwaylandsurface.h @@ -0,0 +1,87 @@ +/* GDK - The GIMP Drawing Kit + * Copyright (C) 2013 Jan Arne Petersen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * 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, see . + */ + +#ifndef __GDK_WAYLAND_SURFACE_H__ +#define __GDK_WAYLAND_SURFACE_H__ + +#if !defined (__GDKWAYLAND_H_INSIDE__) && !defined (GDK_COMPILATION) +#error "Only can be included directly." +#endif + +#include + +#include + +G_BEGIN_DECLS + +#ifdef GDK_COMPILATION +typedef struct _GdkWaylandSurface GdkWaylandSurface; +#else +typedef GdkSurface GdkWaylandSurface; +#endif +typedef struct _GdkWaylandSurfaceClass GdkWaylandSurfaceClass; + +#define GDK_TYPE_WAYLAND_SURFACE (gdk_wayland_surface_get_type()) +#define GDK_WAYLAND_SURFACE(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), GDK_TYPE_WAYLAND_SURFACE, GdkWaylandSurface)) +#define GDK_WAYLAND_SURFACE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GDK_TYPE_WAYLAND_SURFACE, GdkWaylandSurfaceClass)) +#define GDK_IS_WAYLAND_SURFACE(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), GDK_TYPE_WAYLAND_SURFACE)) +#define GDK_IS_WAYLAND_SURFACE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GDK_TYPE_WAYLAND_SURFACE)) +#define GDK_WAYLAND_SURFACE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GDK_TYPE_WAYLAND_SURFACE, GdkWaylandSurfaceClass)) + +GDK_AVAILABLE_IN_ALL +GType gdk_wayland_surface_get_type (void); + +GDK_AVAILABLE_IN_ALL +GdkSurface * gdk_wayland_surface_new_subsurface (GdkDisplay *display, + const GdkRectangle *position); +GDK_AVAILABLE_IN_ALL +struct wl_surface *gdk_wayland_surface_get_wl_surface (GdkSurface *window); + +GDK_AVAILABLE_IN_ALL +void gdk_wayland_surface_set_use_custom_surface (GdkSurface *window); + +GDK_AVAILABLE_IN_ALL +void gdk_wayland_surface_set_dbus_properties_libgtk_only (GdkSurface *window, + const char *application_id, + const char *app_menu_path, + const char *menubar_path, + const char *window_object_path, + const char *application_object_path, + const char *unique_bus_name); + +typedef void (*GdkWaylandSurfaceExported) (GdkSurface *window, + const char *handle, + gpointer user_data); + +GDK_AVAILABLE_IN_ALL +gboolean gdk_wayland_surface_export_handle (GdkSurface *window, + GdkWaylandSurfaceExported callback, + gpointer user_data, + GDestroyNotify destroy_func); + +GDK_AVAILABLE_IN_ALL +void gdk_wayland_surface_unexport_handle (GdkSurface *window); + +GDK_AVAILABLE_IN_ALL +gboolean gdk_wayland_surface_set_transient_for_exported (GdkSurface *window, + char *parent_handle_str); + +void gdk_wayland_surface_announce_csd (GdkSurface *window); + +G_END_DECLS + +#endif /* __GDK_WAYLAND_SURFACE_H__ */ diff --git a/gdk/wayland/gdkwaylandwindow.h b/gdk/wayland/gdkwaylandwindow.h deleted file mode 100644 index b9747b6ca8..0000000000 --- a/gdk/wayland/gdkwaylandwindow.h +++ /dev/null @@ -1,87 +0,0 @@ -/* GDK - The GIMP Drawing Kit - * Copyright (C) 2013 Jan Arne Petersen - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * 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, see . - */ - -#ifndef __GDK_WAYLAND_SURFACE_H__ -#define __GDK_WAYLAND_SURFACE_H__ - -#if !defined (__GDKWAYLAND_H_INSIDE__) && !defined (GDK_COMPILATION) -#error "Only can be included directly." -#endif - -#include - -#include - -G_BEGIN_DECLS - -#ifdef GDK_COMPILATION -typedef struct _GdkWaylandSurface GdkWaylandSurface; -#else -typedef GdkSurface GdkWaylandSurface; -#endif -typedef struct _GdkWaylandSurfaceClass GdkWaylandSurfaceClass; - -#define GDK_TYPE_WAYLAND_SURFACE (gdk_wayland_surface_get_type()) -#define GDK_WAYLAND_SURFACE(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), GDK_TYPE_WAYLAND_SURFACE, GdkWaylandSurface)) -#define GDK_WAYLAND_SURFACE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GDK_TYPE_WAYLAND_SURFACE, GdkWaylandSurfaceClass)) -#define GDK_IS_WAYLAND_SURFACE(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), GDK_TYPE_WAYLAND_SURFACE)) -#define GDK_IS_WAYLAND_SURFACE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GDK_TYPE_WAYLAND_SURFACE)) -#define GDK_WAYLAND_SURFACE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GDK_TYPE_WAYLAND_SURFACE, GdkWaylandSurfaceClass)) - -GDK_AVAILABLE_IN_ALL -GType gdk_wayland_surface_get_type (void); - -GDK_AVAILABLE_IN_ALL -GdkSurface * gdk_wayland_surface_new_subsurface (GdkDisplay *display, - const GdkRectangle *position); -GDK_AVAILABLE_IN_ALL -struct wl_surface *gdk_wayland_surface_get_wl_surface (GdkSurface *window); - -GDK_AVAILABLE_IN_ALL -void gdk_wayland_surface_set_use_custom_surface (GdkSurface *window); - -GDK_AVAILABLE_IN_ALL -void gdk_wayland_surface_set_dbus_properties_libgtk_only (GdkSurface *window, - const char *application_id, - const char *app_menu_path, - const char *menubar_path, - const char *window_object_path, - const char *application_object_path, - const char *unique_bus_name); - -typedef void (*GdkWaylandSurfaceExported) (GdkSurface *window, - const char *handle, - gpointer user_data); - -GDK_AVAILABLE_IN_ALL -gboolean gdk_wayland_surface_export_handle (GdkSurface *window, - GdkWaylandSurfaceExported callback, - gpointer user_data, - GDestroyNotify destroy_func); - -GDK_AVAILABLE_IN_ALL -void gdk_wayland_surface_unexport_handle (GdkSurface *window); - -GDK_AVAILABLE_IN_ALL -gboolean gdk_wayland_surface_set_transient_for_exported (GdkSurface *window, - char *parent_handle_str); - -void gdk_wayland_surface_announce_csd (GdkSurface *window); - -G_END_DECLS - -#endif /* __GDK_WAYLAND_SURFACE_H__ */ diff --git a/gdk/wayland/gdkwindow-wayland.c b/gdk/wayland/gdkwindow-wayland.c deleted file mode 100644 index ead400fe6d..0000000000 --- a/gdk/wayland/gdkwindow-wayland.c +++ /dev/null @@ -1,4206 +0,0 @@ -/* - * Copyright © 2010 Intel Corporation - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public License - * as published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * 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, see . - */ - -#include "config.h" - -#include -#include - -#include "gdk.h" -#include "gdkwayland.h" - -#include "gdkwindow.h" -#include "gdkwindowimpl.h" -#include "gdkdisplay-wayland.h" -#include "gdkglcontext-wayland.h" -#include "gdkframeclockprivate.h" -#include "gdkprivate-wayland.h" -#include "gdkinternals.h" -#include "gdkdeviceprivate.h" -#include "gdkprivate-wayland.h" -#include "gdkmonitor-wayland.h" -#include - -#include -#include -#include -#include - -enum { - COMMITTED, - - LAST_SIGNAL -}; - -static guint signals[LAST_SIGNAL]; - -#define WINDOW_IS_TOPLEVEL(window) \ - (GDK_SURFACE_TYPE (window) != GDK_SURFACE_CHILD) - -#define MAX_WL_BUFFER_SIZE (4083) /* 4096 minus header, string argument length and NUL byte */ - -typedef struct _GdkWaylandSurface GdkWaylandSurface; -typedef struct _GdkWaylandSurfaceClass GdkWaylandSurfaceClass; - -struct _GdkWaylandSurface -{ - GdkSurface parent; -}; - -struct _GdkWaylandSurfaceClass -{ - GdkSurfaceClass parent_class; -}; - -G_DEFINE_TYPE (GdkWaylandSurface, gdk_wayland_surface, GDK_TYPE_SURFACE) - -static void -gdk_wayland_surface_class_init (GdkWaylandSurfaceClass *wayland_surface_class) -{ -} - -static void -gdk_wayland_surface_init (GdkWaylandSurface *wayland_surface) -{ -} - -#define GDK_TYPE_SURFACE_IMPL_WAYLAND (_gdk_surface_impl_wayland_get_type ()) -#define GDK_SURFACE_IMPL_WAYLAND(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), GDK_TYPE_SURFACE_IMPL_WAYLAND, GdkSurfaceImplWayland)) -#define GDK_SURFACE_IMPL_WAYLAND_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GDK_TYPE_SURFACE_IMPL_WAYLAND, GdkSurfaceImplWaylandClass)) -#define GDK_IS_SURFACE_IMPL_WAYLAND(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), GDK_TYPE_SURFACE_IMPL_WAYLAND)) -#define GDK_IS_SURFACE_IMPL_WAYLAND_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GDK_TYPE_SURFACE_IMPL_WAYLAND)) -#define GDK_SURFACE_IMPL_WAYLAND_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GDK_TYPE_SURFACE_IMPL_WAYLAND, GdkSurfaceImplWaylandClass)) - -typedef struct _GdkSurfaceImplWayland GdkSurfaceImplWayland; -typedef struct _GdkSurfaceImplWaylandClass GdkSurfaceImplWaylandClass; - -typedef enum _PositionMethod -{ - POSITION_METHOD_NONE, - POSITION_METHOD_MOVE_RESIZE, - POSITION_METHOD_MOVE_TO_RECT -} PositionMethod; - -struct _GdkSurfaceImplWayland -{ - GdkSurfaceImpl parent_instance; - - GdkSurface *wrapper; - - struct { - /* The wl_outputs that this window currently touches */ - GSList *outputs; - - struct wl_surface *wl_surface; - struct zxdg_surface_v6 *xdg_surface; - struct zxdg_toplevel_v6 *xdg_toplevel; - struct zxdg_popup_v6 *xdg_popup; - struct gtk_surface1 *gtk_surface; - struct wl_subsurface *wl_subsurface; - struct wl_egl_window *egl_window; - struct wl_egl_window *dummy_egl_window; - struct zxdg_exported_v1 *xdg_exported; - struct org_kde_kwin_server_decoration *server_decoration; - } display_server; - - EGLSurface egl_surface; - EGLSurface dummy_egl_surface; - - unsigned int initial_configure_received : 1; - unsigned int mapped : 1; - unsigned int use_custom_surface : 1; - unsigned int pending_buffer_attached : 1; - unsigned int pending_commit : 1; - unsigned int awaiting_frame : 1; - GdkSurfaceTypeHint hint; - GdkSurface *transient_for; - GdkSurface *popup_parent; - PositionMethod position_method; - - cairo_surface_t *staging_cairo_surface; - cairo_surface_t *committed_cairo_surface; - cairo_surface_t *backfill_cairo_surface; - - int pending_buffer_offset_x; - int pending_buffer_offset_y; - - gchar *title; - - struct { - gboolean was_set; - - gchar *application_id; - gchar *app_menu_path; - gchar *menubar_path; - gchar *window_object_path; - gchar *application_object_path; - gchar *unique_bus_name; - } application; - - GdkGeometry geometry_hints; - GdkSurfaceHints geometry_mask; - - GdkSeat *grab_input_seat; - - gint64 pending_frame_counter; - guint32 scale; - - int margin_left; - int margin_right; - int margin_top; - int margin_bottom; - gboolean margin_dirty; - - struct wl_output *initial_fullscreen_output; - - cairo_region_t *opaque_region; - gboolean opaque_region_dirty; - - cairo_region_t *input_region; - gboolean input_region_dirty; - - cairo_region_t *staged_updates_region; - - int saved_width; - int saved_height; - - gulong parent_surface_committed_handler; - - struct { - GdkRectangle rect; - GdkGravity rect_anchor; - GdkGravity window_anchor; - GdkAnchorHints anchor_hints; - gint rect_anchor_dx; - gint rect_anchor_dy; - } pending_move_to_rect; - - struct { - int width; - int height; - GdkSurfaceState state; - } pending; - - struct { - GdkWaylandSurfaceExported callback; - gpointer user_data; - GDestroyNotify destroy_func; - } exported; - - struct zxdg_imported_v1 *imported_transient_for; - GHashTable *shortcuts_inhibitors; -}; - -struct _GdkSurfaceImplWaylandClass -{ - GdkSurfaceImplClass parent_class; -}; - -static void gdk_wayland_surface_maybe_configure (GdkSurface *window, - int width, - int height, - int scale); - -static void maybe_set_gtk_surface_dbus_properties (GdkSurface *window); -static void maybe_set_gtk_surface_modal (GdkSurface *window); - -static void gdk_surface_request_transient_parent_commit (GdkSurface *window); - -static void gdk_wayland_surface_sync_margin (GdkSurface *window); -static void gdk_wayland_surface_sync_input_region (GdkSurface *window); -static void gdk_wayland_surface_sync_opaque_region (GdkSurface *window); - -static void unset_transient_for_exported (GdkSurface *window); - -static void calculate_moved_to_rect_result (GdkSurface *window, - int x, - int y, - int width, - int height, - GdkRectangle *flipped_rect, - GdkRectangle *final_rect, - gboolean *flipped_x, - gboolean *flipped_y); - -static gboolean gdk_wayland_surface_is_exported (GdkSurface *window); - -GType _gdk_surface_impl_wayland_get_type (void); - -G_DEFINE_TYPE (GdkSurfaceImplWayland, _gdk_surface_impl_wayland, GDK_TYPE_SURFACE_IMPL) - -static void -_gdk_surface_impl_wayland_init (GdkSurfaceImplWayland *impl) -{ - impl->scale = 1; - impl->initial_fullscreen_output = NULL; - impl->saved_width = -1; - impl->saved_height = -1; -} - -static void -_gdk_wayland_screen_add_orphan_dialog (GdkSurface *window) -{ - GdkWaylandDisplay *display_wayland = - GDK_WAYLAND_DISPLAY (gdk_surface_get_display (window)); - - if (!g_list_find (display_wayland->orphan_dialogs, window)) - display_wayland->orphan_dialogs = - g_list_prepend (display_wayland->orphan_dialogs, window); -} - -static void -drop_cairo_surfaces (GdkSurface *window) -{ - GdkSurfaceImplWayland *impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); - - g_clear_pointer (&impl->staging_cairo_surface, cairo_surface_destroy); - g_clear_pointer (&impl->backfill_cairo_surface, cairo_surface_destroy); - - /* We nullify this so if a buffer release comes in later, we won't - * try to reuse that buffer since it's no longer suitable - */ - impl->committed_cairo_surface = NULL; -} - -static void -_gdk_wayland_surface_save_size (GdkSurface *window) -{ - GdkSurfaceImplWayland *impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); - - if (window->state & (GDK_SURFACE_STATE_FULLSCREEN | GDK_SURFACE_STATE_MAXIMIZED)) - return; - - impl->saved_width = window->width - impl->margin_left - impl->margin_right; - impl->saved_height = window->height - impl->margin_top - impl->margin_bottom; -} - -static void -_gdk_wayland_surface_clear_saved_size (GdkSurface *window) -{ - GdkSurfaceImplWayland *impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); - - if (window->state & (GDK_SURFACE_STATE_FULLSCREEN | GDK_SURFACE_STATE_MAXIMIZED)) - return; - - impl->saved_width = -1; - impl->saved_height = -1; -} - -/* - * gdk_wayland_surface_update_size: - * @drawable: a #GdkDrawableImplWayland. - * - * Updates the state of the drawable (in particular the drawable's - * cairo surface) when its size has changed. - */ -static void -gdk_wayland_surface_update_size (GdkSurface *window, - int32_t width, - int32_t height, - int scale) -{ - GdkSurfaceImplWayland *impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); - GdkRectangle area; - cairo_region_t *region; - - if ((window->width == width) && - (window->height == height) && - (impl->scale == scale)) - return; - - drop_cairo_surfaces (window); - - window->width = width; - window->height = height; - impl->scale = scale; - - if (impl->display_server.egl_window) - wl_egl_window_resize (impl->display_server.egl_window, width * scale, height * scale, 0, 0); - if (impl->display_server.wl_surface) - wl_surface_set_buffer_scale (impl->display_server.wl_surface, scale); - - area.x = 0; - area.y = 0; - area.width = window->width; - area.height = window->height; - - region = cairo_region_create_rectangle (&area); - _gdk_surface_invalidate_for_expose (window, region); - cairo_region_destroy (region); -} - -static const gchar * -get_default_title (void) -{ - const char *title; - - title = g_get_application_name (); - if (!title) - title = g_get_prgname (); - if (!title) - title = ""; - - return title; -} - -static void -fill_presentation_time_from_frame_time (GdkFrameTimings *timings, - guint32 frame_time) -{ - /* The timestamp in a wayland frame is a msec time value that in some - * way reflects the time at which the server started drawing the frame. - * This is not useful from our perspective. - * - * However, for the DRM backend of Weston, on reasonably recent - * Linux, we know that the time is the - * clock_gettime (CLOCK_MONOTONIC) value at the vblank, and that - * backend starts drawing immediately after receiving the vblank - * notification. If we detect this, and make the assumption that the - * compositor will finish drawing before the next vblank, we can - * then determine the presentation time as the frame time we - * received plus one refresh interval. - * - * If a backend is using clock_gettime(CLOCK_MONOTONIC), but not - * picking values right at the vblank, then the presentation times - * we compute won't be accurate, but not really worse than then - * the alternative of not providing presentation times at all. - * - * The complexity here is dealing with the fact that we receive - * only the low 32 bits of the CLOCK_MONOTONIC value in milliseconds. - */ - gint64 now_monotonic = g_get_monotonic_time (); - gint64 now_monotonic_msec = now_monotonic / 1000; - uint32_t now_monotonic_low = (uint32_t)now_monotonic_msec; - - if (frame_time - now_monotonic_low < 1000 || - frame_time - now_monotonic_low > (uint32_t)-1000) - { - /* Timestamp we received is within one second of the current time. - */ - gint64 last_frame_time = now_monotonic + (gint64)1000 * (gint32)(frame_time - now_monotonic_low); - if ((gint32)now_monotonic_low < 0 && (gint32)frame_time > 0) - last_frame_time += (gint64)1000 * G_GINT64_CONSTANT(0x100000000); - else if ((gint32)now_monotonic_low > 0 && (gint32)frame_time < 0) - last_frame_time -= (gint64)1000 * G_GINT64_CONSTANT(0x100000000); - - timings->presentation_time = last_frame_time + timings->refresh_interval; - } -} - -static void -read_back_cairo_surface (GdkSurface *window) -{ - GdkSurfaceImplWayland *impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); - cairo_t *cr; - cairo_region_t *paint_region = NULL; - - if (!impl->backfill_cairo_surface) - goto out; - - paint_region = cairo_region_copy (window->clip_region); - cairo_region_subtract (paint_region, impl->staged_updates_region); - - if (cairo_region_is_empty (paint_region)) - goto out; - - cr = cairo_create (impl->staging_cairo_surface); - cairo_set_source_surface (cr, impl->backfill_cairo_surface, 0, 0); - gdk_cairo_region (cr, paint_region); - cairo_clip (cr); - cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE); - cairo_paint (cr); - cairo_destroy (cr); - cairo_surface_flush (impl->staging_cairo_surface); - -out: - g_clear_pointer (&paint_region, cairo_region_destroy); - g_clear_pointer (&impl->staged_updates_region, cairo_region_destroy); - g_clear_pointer (&impl->backfill_cairo_surface, cairo_surface_destroy); -} - -static void -frame_callback (void *data, - struct wl_callback *callback, - uint32_t time) -{ - GdkSurface *window = data; - GdkSurfaceImplWayland *impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); - GdkWaylandDisplay *display_wayland = - GDK_WAYLAND_DISPLAY (gdk_surface_get_display (window)); - GdkFrameClock *clock = gdk_surface_get_frame_clock (window); - GdkFrameTimings *timings; - - GDK_DISPLAY_NOTE (GDK_DISPLAY (display_wayland), EVENTS, g_message ("frame %p", window)); - - wl_callback_destroy (callback); - - if (GDK_SURFACE_DESTROYED (window)) - return; - - if (!impl->awaiting_frame) - return; - - impl->awaiting_frame = FALSE; - _gdk_frame_clock_thaw (clock); - - timings = gdk_frame_clock_get_timings (clock, impl->pending_frame_counter); - impl->pending_frame_counter = 0; - - if (timings == NULL) - return; - - timings->refresh_interval = 16667; /* default to 1/60th of a second */ - if (impl->display_server.outputs) - { - /* We pick a random output out of the outputs that the window touches - * The rate here is in milli-hertz */ - int refresh_rate = - gdk_wayland_display_get_output_refresh_rate (display_wayland, - impl->display_server.outputs->data); - if (refresh_rate != 0) - timings->refresh_interval = G_GINT64_CONSTANT(1000000000) / refresh_rate; - } - - fill_presentation_time_from_frame_time (timings, time); - - timings->complete = TRUE; - -#ifdef G_ENABLE_DEBUG - if ((_gdk_debug_flags & GDK_DEBUG_FRAMES) != 0) - _gdk_frame_clock_debug_print_timings (clock, timings); -#endif -} - -static const struct wl_callback_listener frame_listener = { - frame_callback -}; - -static void -on_frame_clock_before_paint (GdkFrameClock *clock, - GdkSurface *window) -{ - GdkFrameTimings *timings = gdk_frame_clock_get_current_timings (clock); - gint64 presentation_time; - gint64 refresh_interval; - - if (window->update_freeze_count > 0) - return; - - gdk_frame_clock_get_refresh_info (clock, - timings->frame_time, - &refresh_interval, &presentation_time); - - if (presentation_time != 0) - { - /* Assume the algorithm used by the DRM backend of Weston - it - * starts drawing at the next vblank after receiving the commit - * for this frame, and presentation occurs at the vblank - * after that. - */ - timings->predicted_presentation_time = presentation_time + refresh_interval; - } - else - { - /* As above, but we don't actually know the phase of the vblank, - * so just assume that we're half way through a refresh cycle. - */ - timings->predicted_presentation_time = timings->frame_time + refresh_interval / 2 + refresh_interval; - } -} - -static void -on_frame_clock_after_paint (GdkFrameClock *clock, - GdkSurface *window) -{ - GdkSurfaceImplWayland *impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); - struct wl_callback *callback; - - if (!impl->pending_commit) - return; - - if (window->update_freeze_count > 0) - return; - - callback = wl_surface_frame (impl->display_server.wl_surface); - wl_callback_add_listener (callback, &frame_listener, window); - _gdk_frame_clock_freeze (clock); - - /* Before we commit a new buffer, make sure we've backfilled - * undrawn parts from any old committed buffer - */ - if (impl->pending_buffer_attached) - read_back_cairo_surface (window); - - /* From this commit forward, we can't write to the buffer, - * it's "live". In the future, if we need to stage more changes - * we have to allocate a new staging buffer and draw to it instead. - * - * Our one saving grace is if the compositor releases the buffer - * before we need to stage any changes, then we can take it back and - * use it again. - */ - wl_surface_commit (impl->display_server.wl_surface); - - if (impl->pending_buffer_attached) - impl->committed_cairo_surface = g_steal_pointer (&impl->staging_cairo_surface); - - impl->pending_buffer_attached = FALSE; - impl->pending_commit = FALSE; - impl->pending_frame_counter = gdk_frame_clock_get_frame_counter (clock); - impl->awaiting_frame = TRUE; - - g_signal_emit (impl, signals[COMMITTED], 0); -} - -void -gdk_wayland_surface_update_scale (GdkSurface *window) -{ - GdkSurfaceImplWayland *impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); - GdkWaylandDisplay *display_wayland = GDK_WAYLAND_DISPLAY (gdk_surface_get_display (window)); - guint32 scale; - GSList *l; - GList *children, *c; - - if (display_wayland->compositor_version < WL_SURFACE_HAS_BUFFER_SCALE) - { - /* We can't set the scale on this surface */ - return; - } - - scale = 1; - for (l = impl->display_server.outputs; l != NULL; l = l->next) - { - guint32 output_scale = gdk_wayland_display_get_output_scale (display_wayland, l->data); - scale = MAX (scale, output_scale); - } - - /* Notify app that scale changed */ - gdk_wayland_surface_maybe_configure (window, window->width, window->height, scale); - - children = gdk_surface_get_children (window); - for (c = children; c; c = c->next) - { - GdkSurface *child = c->data; - gdk_wayland_surface_update_scale (child); - } - g_list_free (children); -} - -static void gdk_wayland_surface_create_surface (GdkSurface *window); - -void -_gdk_wayland_display_create_window_impl (GdkDisplay *display, - GdkSurface *window, - GdkSurface *real_parent, - GdkEventMask event_mask, - GdkSurfaceAttr *attributes) -{ - GdkWaylandDisplay *display_wayland = GDK_WAYLAND_DISPLAY (display); - GdkSurfaceImplWayland *impl; - GdkFrameClock *frame_clock; - - impl = g_object_new (GDK_TYPE_SURFACE_IMPL_WAYLAND, NULL); - window->impl = GDK_SURFACE_IMPL (impl); - impl->wrapper = GDK_SURFACE (window); - impl->shortcuts_inhibitors = g_hash_table_new (NULL, NULL); - - if (window->width > 65535) - { - g_warning ("Native Windows wider than 65535 pixels are not supported"); - window->width = 65535; - } - if (window->height > 65535) - { - g_warning ("Native Windows taller than 65535 pixels are not supported"); - window->height = 65535; - } - - g_object_ref (window); - - /* More likely to be right than just assuming 1 */ - if (display_wayland->compositor_version >= WL_SURFACE_HAS_BUFFER_SCALE && - gdk_display_get_n_monitors (display) > 0) - impl->scale = gdk_monitor_get_scale_factor (gdk_display_get_monitor (display, 0)); - - impl->title = NULL; - - switch (GDK_SURFACE_TYPE (window)) - { - case GDK_SURFACE_TOPLEVEL: - case GDK_SURFACE_TEMP: - gdk_surface_set_title (window, get_default_title ()); - break; - - case GDK_SURFACE_CHILD: - default: - break; - } - - if (real_parent == NULL) - display_wayland->toplevels = g_list_prepend (display_wayland->toplevels, window); - - gdk_wayland_surface_create_surface (window); - - frame_clock = gdk_surface_get_frame_clock (window); - g_signal_connect (frame_clock, "before-paint", G_CALLBACK (on_frame_clock_before_paint), window); - g_signal_connect (frame_clock, "after-paint", G_CALLBACK (on_frame_clock_after_paint), window); -} - -static void -gdk_wayland_surface_attach_image (GdkSurface *window) -{ - GdkWaylandDisplay *display; - GdkSurfaceImplWayland *impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); - - if (GDK_SURFACE_DESTROYED (window)) - return; - - g_assert (_gdk_wayland_is_shm_surface (impl->staging_cairo_surface)); - - /* Attach this new buffer to the surface */ - wl_surface_attach (impl->display_server.wl_surface, - _gdk_wayland_shm_surface_get_wl_buffer (impl->staging_cairo_surface), - impl->pending_buffer_offset_x, - impl->pending_buffer_offset_y); - impl->pending_buffer_offset_x = 0; - impl->pending_buffer_offset_y = 0; - - /* Only set the buffer scale if supported by the compositor */ - display = GDK_WAYLAND_DISPLAY (gdk_surface_get_display (window)); - if (display->compositor_version >= WL_SURFACE_HAS_BUFFER_SCALE) - wl_surface_set_buffer_scale (impl->display_server.wl_surface, impl->scale); - - impl->pending_buffer_attached = TRUE; - impl->pending_commit = TRUE; -} - -static const cairo_user_data_key_t gdk_wayland_surface_cairo_key; - -static void -buffer_release_callback (void *_data, - struct wl_buffer *wl_buffer) -{ - cairo_surface_t *cairo_surface = _data; - GdkSurfaceImplWayland *impl = cairo_surface_get_user_data (cairo_surface, &gdk_wayland_surface_cairo_key); - - g_return_if_fail (GDK_IS_SURFACE_IMPL_WAYLAND (impl)); - - /* The released buffer isn't the latest committed one, we have no further - * use for it, so clean it up. - */ - if (impl->committed_cairo_surface != cairo_surface) - { - /* If this fails, then the surface buffer got reused before it was - * released from the compositor - */ - g_warn_if_fail (impl->staging_cairo_surface != cairo_surface); - - cairo_surface_destroy (cairo_surface); - return; - } - - if (impl->staged_updates_region != NULL) - { - /* If this fails, then we're tracking staged updates on a staging surface - * that doesn't exist. - */ - g_warn_if_fail (impl->staging_cairo_surface != NULL); - - /* If we've staged updates into a new buffer before the release for this - * buffer came in, then we can't reuse this buffer, so unref it. It may still - * be alive as a readback buffer though (via impl->backfill_cairo_surface). - * - * It's possible a staging surface was allocated but no updates were staged. - * If that happened, clean up that staging surface now, since the old commit - * buffer is available again, and reusing the old commit buffer for future - * updates will save having to do a read back later. - */ - if (!cairo_region_is_empty (impl->staged_updates_region)) - { - g_clear_pointer (&impl->committed_cairo_surface, cairo_surface_destroy); - return; - } - else - { - g_clear_pointer (&impl->staged_updates_region, cairo_region_destroy); - g_clear_pointer (&impl->staging_cairo_surface, cairo_surface_destroy); - } - } - - /* Release came in, we haven't done any interim updates, so we can just use - * the old committed buffer again. - */ - impl->staging_cairo_surface = g_steal_pointer (&impl->committed_cairo_surface); -} - -static const struct wl_buffer_listener buffer_listener = { - buffer_release_callback -}; - -static void -gdk_wayland_surface_ensure_cairo_surface (GdkSurface *window) -{ - GdkSurfaceImplWayland *impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); - - /* If we are drawing using OpenGL then we only need a logical 1x1 surface. */ - if (impl->display_server.egl_window) - { - if (impl->staging_cairo_surface && - _gdk_wayland_is_shm_surface (impl->staging_cairo_surface)) - g_clear_pointer (&impl->staging_cairo_surface, cairo_surface_destroy); - - if (!impl->staging_cairo_surface) - { - impl->staging_cairo_surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, - impl->scale, - impl->scale); - cairo_surface_set_device_scale (impl->staging_cairo_surface, - impl->scale, impl->scale); - } - } - else if (!impl->staging_cairo_surface) - { - GdkWaylandDisplay *display_wayland = GDK_WAYLAND_DISPLAY (gdk_surface_get_display (impl->wrapper)); - struct wl_buffer *buffer; - - impl->staging_cairo_surface = _gdk_wayland_display_create_shm_surface (display_wayland, - impl->wrapper->width, - impl->wrapper->height, - impl->scale); - cairo_surface_set_user_data (impl->staging_cairo_surface, - &gdk_wayland_surface_cairo_key, - g_object_ref (impl), - (cairo_destroy_func_t) - g_object_unref); - buffer = _gdk_wayland_shm_surface_get_wl_buffer (impl->staging_cairo_surface); - wl_buffer_add_listener (buffer, &buffer_listener, impl->staging_cairo_surface); - } -} - -/* The cairo surface returned here uses a memory segment that's shared - * with the display server. This is not a temporary buffer that gets - * copied to the display server, but the actual buffer the display server - * will ultimately end up sending to the GPU. At the time this happens - * impl->committed_cairo_surface gets set to impl->staging_cairo_surface, and - * impl->staging_cairo_surface gets nullified. - */ -static cairo_surface_t * -gdk_wayland_surface_ref_cairo_surface (GdkSurface *window) -{ - GdkSurfaceImplWayland *impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); - - if (GDK_SURFACE_DESTROYED (impl->wrapper)) - return NULL; - - gdk_wayland_surface_ensure_cairo_surface (window); - - cairo_surface_reference (impl->staging_cairo_surface); - - return impl->staging_cairo_surface; -} - -static cairo_surface_t * -gdk_wayland_surface_create_similar_image_surface (GdkSurface * window, - cairo_format_t format, - int width, - int height) -{ - return cairo_image_surface_create (format, width, height); -} - -static gboolean -gdk_surface_impl_wayland_begin_paint (GdkSurface *window) -{ - gdk_wayland_surface_ensure_cairo_surface (window); - - return FALSE; -} - -static void -gdk_surface_impl_wayland_end_paint (GdkSurface *window) -{ - GdkSurfaceImplWayland *impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); - cairo_rectangle_int_t rect; - int i, n; - - if (impl->staging_cairo_surface && - _gdk_wayland_is_shm_surface (impl->staging_cairo_surface) && - !cairo_region_is_empty (window->current_paint.region)) - { - gdk_wayland_surface_attach_image (window); - - /* If there's a committed buffer pending, then track which - * updates are staged until the next frame, so we can back - * fill the unstaged parts of the staging buffer with the - * last frame. - */ - if (impl->committed_cairo_surface != NULL) - { - if (impl->staged_updates_region == NULL) - { - impl->staged_updates_region = cairo_region_copy (window->current_paint.region); - impl->backfill_cairo_surface = cairo_surface_reference (impl->committed_cairo_surface); - } - else - { - cairo_region_union (impl->staged_updates_region, window->current_paint.region); - } - } - - n = cairo_region_num_rectangles (window->current_paint.region); - for (i = 0; i < n; i++) - { - cairo_region_get_rectangle (window->current_paint.region, i, &rect); - wl_surface_damage (impl->display_server.wl_surface, rect.x, rect.y, rect.width, rect.height); - } - - impl->pending_commit = TRUE; - } - - gdk_wayland_surface_sync (window); -} - -void -gdk_wayland_surface_sync (GdkSurface *window) -{ - gdk_wayland_surface_sync_margin (window); - gdk_wayland_surface_sync_opaque_region (window); - gdk_wayland_surface_sync_input_region (window); -} - -static gboolean -gdk_surface_impl_wayland_beep (GdkSurface *window) -{ - gdk_wayland_display_system_bell (gdk_surface_get_display (window), - window); - - return TRUE; -} - -static void -gdk_surface_impl_wayland_finalize (GObject *object) -{ - GdkSurface *window = GDK_SURFACE (object); - GdkSurfaceImplWayland *impl; - - g_return_if_fail (GDK_IS_SURFACE_IMPL_WAYLAND (object)); - - impl = GDK_SURFACE_IMPL_WAYLAND (object); - - if (gdk_wayland_surface_is_exported (window)) - gdk_wayland_surface_unexport_handle (window); - - g_free (impl->title); - - g_free (impl->application.application_id); - g_free (impl->application.app_menu_path); - g_free (impl->application.menubar_path); - g_free (impl->application.window_object_path); - g_free (impl->application.application_object_path); - g_free (impl->application.unique_bus_name); - - g_clear_pointer (&impl->opaque_region, cairo_region_destroy); - g_clear_pointer (&impl->input_region, cairo_region_destroy); - g_clear_pointer (&impl->staged_updates_region, cairo_region_destroy); - - g_hash_table_destroy (impl->shortcuts_inhibitors); - - G_OBJECT_CLASS (_gdk_surface_impl_wayland_parent_class)->finalize (object); -} - -static void -gdk_wayland_surface_configure (GdkSurface *window, - int width, - int height, - int scale) -{ - GdkDisplay *display; - GdkEvent *event; - - event = gdk_event_new (GDK_CONFIGURE); - event->any.window = g_object_ref (window); - event->any.send_event = FALSE; - event->configure.width = width; - event->configure.height = height; - - gdk_wayland_surface_update_size (window, width, height, scale); - _gdk_surface_update_size (window); - - display = gdk_surface_get_display (window); - _gdk_wayland_display_deliver_event (display, event); -} - -static void -gdk_wayland_surface_maybe_configure (GdkSurface *window, - int width, - int height, - int scale) -{ - GdkSurfaceImplWayland *impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); - gboolean is_xdg_popup; - gboolean is_visible; - - if (window->width == width && - window->height == height && - impl->scale == scale) - return; - - /* For xdg_popup using an xdg_positioner, there is a race condition if - * the application tries to change the size after it's mapped, but before - * the initial configure is received, so hide and show the surface again - * force the new size onto the compositor. See bug #772505. - */ - - is_xdg_popup = (impl->display_server.xdg_popup != NULL); - is_visible = gdk_surface_is_visible (window); - - if (is_xdg_popup && is_visible && !impl->initial_configure_received) - gdk_surface_hide (window); - - gdk_wayland_surface_configure (window, width, height, scale); - - if (is_xdg_popup && is_visible && !impl->initial_configure_received) - gdk_surface_show (window); -} - -static void -gdk_wayland_surface_sync_parent (GdkSurface *window, - GdkSurface *parent) -{ - GdkSurfaceImplWayland *impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); - GdkSurfaceImplWayland *impl_parent = NULL; - struct zxdg_toplevel_v6 *parent_toplevel; - - g_assert (parent == NULL || - gdk_surface_get_display (window) == gdk_surface_get_display (parent)); - - if (!impl->display_server.xdg_toplevel) - return; - - if (impl->transient_for) - impl_parent = GDK_SURFACE_IMPL_WAYLAND (impl->transient_for->impl); - else if (parent) - impl_parent = GDK_SURFACE_IMPL_WAYLAND (parent->impl); - - if (impl_parent) - { - /* XXX: Is this correct? */ - if (!impl_parent->display_server.wl_surface) - return; - - parent_toplevel = impl_parent->display_server.xdg_toplevel; - } - else - parent_toplevel = NULL; - - zxdg_toplevel_v6_set_parent (impl->display_server.xdg_toplevel, - parent_toplevel); -} - -static void -gdk_wayland_surface_sync_parent_of_imported (GdkSurface *window) -{ - GdkSurfaceImplWayland *impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); - - if (!impl->display_server.wl_surface) - return; - - if (!impl->imported_transient_for) - return; - - if (!impl->display_server.xdg_toplevel) - return; - - zxdg_imported_v1_set_parent_of (impl->imported_transient_for, - impl->display_server.wl_surface); -} - -static void -gdk_wayland_surface_update_dialogs (GdkSurface *window) -{ - GdkWaylandDisplay *display_wayland = - GDK_WAYLAND_DISPLAY (gdk_surface_get_display (window)); - GList *l; - - if (!display_wayland->orphan_dialogs) - return; - - for (l = display_wayland->orphan_dialogs; l; l = l->next) - { - GdkSurface *w = l->data; - GdkSurfaceImplWayland *impl; - - if (!GDK_IS_SURFACE_IMPL_WAYLAND(w->impl)) - continue; - - impl = GDK_SURFACE_IMPL_WAYLAND (w->impl); - if (w == window) - continue; - if (impl->hint != GDK_SURFACE_TYPE_HINT_DIALOG) - continue; - if (impl->transient_for) - continue; - - /* Update the parent relationship only for dialogs without transients */ - gdk_wayland_surface_sync_parent (w, window); - } -} - -static void -gdk_wayland_surface_sync_title (GdkSurface *window) -{ - GdkSurfaceImplWayland *impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); - - if (!impl->display_server.xdg_toplevel) - return; - - if (!impl->title) - return; - - zxdg_toplevel_v6_set_title (impl->display_server.xdg_toplevel, impl->title); -} - -static void -gdk_wayland_surface_get_window_geometry (GdkSurface *window, - GdkRectangle *geometry) -{ - GdkSurfaceImplWayland *impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); - - *geometry = (GdkRectangle) { - .x = impl->margin_left, - .y = impl->margin_top, - .width = window->width - (impl->margin_left + impl->margin_right), - .height = window->height - (impl->margin_top + impl->margin_bottom) - }; -} - -static void -gdk_wayland_surface_sync_margin (GdkSurface *window) -{ - GdkSurfaceImplWayland *impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); - GdkRectangle geometry; - - if (!impl->display_server.xdg_surface) - return; - - gdk_wayland_surface_get_window_geometry (window, &geometry); - gdk_surface_set_geometry_hints (window, - &impl->geometry_hints, - impl->geometry_mask); - zxdg_surface_v6_set_window_geometry (impl->display_server.xdg_surface, - geometry.x, - geometry.y, - geometry.width, - geometry.height); -} - -static struct wl_region * -wl_region_from_cairo_region (GdkWaylandDisplay *display, - cairo_region_t *region) -{ - struct wl_region *wl_region; - int i, n_rects; - - wl_region = wl_compositor_create_region (display->compositor); - if (wl_region == NULL) - return NULL; - - n_rects = cairo_region_num_rectangles (region); - for (i = 0; i < n_rects; i++) - { - cairo_rectangle_int_t rect; - cairo_region_get_rectangle (region, i, &rect); - wl_region_add (wl_region, rect.x, rect.y, rect.width, rect.height); - } - - return wl_region; -} - -static void -gdk_wayland_surface_sync_opaque_region (GdkSurface *window) -{ - GdkSurfaceImplWayland *impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); - struct wl_region *wl_region = NULL; - - if (!impl->display_server.wl_surface) - return; - - if (!impl->opaque_region_dirty) - return; - - if (impl->opaque_region != NULL) - wl_region = wl_region_from_cairo_region (GDK_WAYLAND_DISPLAY (gdk_surface_get_display (window)), - impl->opaque_region); - - wl_surface_set_opaque_region (impl->display_server.wl_surface, wl_region); - - if (wl_region != NULL) - wl_region_destroy (wl_region); - - impl->opaque_region_dirty = FALSE; -} - -static void -gdk_wayland_surface_sync_input_region (GdkSurface *window) -{ - GdkSurfaceImplWayland *impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); - struct wl_region *wl_region = NULL; - - if (!impl->display_server.wl_surface) - return; - - if (!impl->input_region_dirty) - return; - - if (impl->input_region != NULL) - wl_region = wl_region_from_cairo_region (GDK_WAYLAND_DISPLAY (gdk_surface_get_display (window)), - impl->input_region); - - wl_surface_set_input_region (impl->display_server.wl_surface, wl_region); - - if (wl_region != NULL) - wl_region_destroy (wl_region); - - impl->input_region_dirty = FALSE; -} - -static void -gdk_wayland_set_input_region_if_empty (GdkSurface *window) -{ - GdkSurfaceImplWayland *impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); - GdkWaylandDisplay *display; - struct wl_region *empty; - - if (!impl->input_region_dirty) - return; - - if (impl->input_region == NULL) - return; - - if (!cairo_region_is_empty (impl->input_region)) - return; - - display = GDK_WAYLAND_DISPLAY (gdk_surface_get_display (window)); - empty = wl_compositor_create_region (display->compositor); - - wl_surface_set_input_region (impl->display_server.wl_surface, empty); - wl_region_destroy (empty); - - impl->input_region_dirty = FALSE; -} - -static void -surface_enter (void *data, - struct wl_surface *wl_surface, - struct wl_output *output) -{ - GdkSurface *window = GDK_SURFACE (data); - GdkSurfaceImplWayland *impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); - - GDK_DISPLAY_NOTE (gdk_surface_get_display (window), EVENTS, - g_message ("surface enter, window %p output %p", window, output)); - - impl->display_server.outputs = g_slist_prepend (impl->display_server.outputs, output); - - gdk_wayland_surface_update_scale (window); -} - -static void -surface_leave (void *data, - struct wl_surface *wl_surface, - struct wl_output *output) -{ - GdkSurface *window = GDK_SURFACE (data); - GdkSurfaceImplWayland *impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); - - GDK_DISPLAY_NOTE (gdk_surface_get_display (window), EVENTS, - g_message ("surface leave, window %p output %p", window, output)); - - impl->display_server.outputs = g_slist_remove (impl->display_server.outputs, output); - - if (impl->display_server.outputs) - gdk_wayland_surface_update_scale (window); -} - -static const struct wl_surface_listener surface_listener = { - surface_enter, - surface_leave -}; - -static void -on_parent_surface_committed (GdkSurfaceImplWayland *parent_impl, - GdkSurface *window) -{ - GdkSurfaceImplWayland *impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); - - g_signal_handler_disconnect (parent_impl, - impl->parent_surface_committed_handler); - impl->parent_surface_committed_handler = 0; - - wl_subsurface_set_desync (impl->display_server.wl_subsurface); - - /* Special case if the input region is empty, it won't change on resize */ - gdk_wayland_set_input_region_if_empty (window); -} - -static void -gdk_wayland_surface_create_subsurface (GdkSurface *window) -{ - GdkSurfaceImplWayland *impl, *parent_impl = NULL; - GdkWaylandDisplay *display_wayland; - - impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); - - if (!impl->display_server.wl_surface) - return; /* Bail out, surface and subsurface will be created later when shown */ - - if (impl->display_server.wl_subsurface) - return; - - if (impl->transient_for) - parent_impl = GDK_SURFACE_IMPL_WAYLAND (impl->transient_for->impl); - - if (parent_impl && parent_impl->display_server.wl_surface) - { - display_wayland = GDK_WAYLAND_DISPLAY (gdk_surface_get_display (window)); - impl->display_server.wl_subsurface = - wl_subcompositor_get_subsurface (display_wayland->subcompositor, - impl->display_server.wl_surface, parent_impl->display_server.wl_surface); - wl_subsurface_set_position (impl->display_server.wl_subsurface, - window->x + window->abs_x, - window->y + window->abs_y); - - /* In order to synchronize the initial position with the initial frame - * content, wait with making the subsurface desynchronized until after - * the parent was committed. - */ - impl->parent_surface_committed_handler = - g_signal_connect_object (parent_impl, "committed", - G_CALLBACK (on_parent_surface_committed), - window, 0); - gdk_surface_request_transient_parent_commit (window); - } -} - -static void -gdk_wayland_surface_create_surface (GdkSurface *window) -{ - GdkSurfaceImplWayland *impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); - GdkWaylandDisplay *display_wayland = GDK_WAYLAND_DISPLAY (gdk_surface_get_display (window)); - - impl->display_server.wl_surface = wl_compositor_create_surface (display_wayland->compositor); - wl_surface_add_listener (impl->display_server.wl_surface, &surface_listener, window); -} - -static void -xdg_surface_configure (void *data, - struct zxdg_surface_v6 *xdg_surface, - uint32_t serial) -{ - GdkSurface *window = GDK_SURFACE (data); - GdkSurfaceImplWayland *impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); - GdkSurfaceState new_state; - int width = impl->pending.width; - int height = impl->pending.height; - gboolean fixed_size; - gboolean saved_size; - - if (!impl->initial_configure_received) - { - gdk_surface_thaw_updates (window); - impl->initial_configure_received = TRUE; - } - - if (impl->display_server.xdg_popup) - { - zxdg_surface_v6_ack_configure (xdg_surface, serial); - return; - } - - new_state = impl->pending.state; - impl->pending.state = 0; - - fixed_size = - new_state & (GDK_SURFACE_STATE_MAXIMIZED | GDK_SURFACE_STATE_FULLSCREEN | GDK_SURFACE_STATE_TILED); - - saved_size = (width == 0 && height == 0); - /* According to xdg_shell, an xdg_surface.configure with size 0x0 - * should be interpreted as that it is up to the client to set a - * size. - * - * When transitioning from maximize or fullscreen state, this means - * the client should configure its size back to what it was before - * being maximize or fullscreen. - */ - if (saved_size && !fixed_size) - { - width = impl->saved_width; - height = impl->saved_height; - } - - if (width > 0 && height > 0) - { - GdkSurfaceHints geometry_mask = impl->geometry_mask; - - /* Ignore size increments for maximized/fullscreen windows */ - if (fixed_size) - geometry_mask &= ~GDK_HINT_RESIZE_INC; - if (!saved_size) - { - /* Do not reapply contrains if we are restoring original size */ - gdk_surface_constrain_size (&impl->geometry_hints, - geometry_mask, - width + impl->margin_left + impl->margin_right, - height + impl->margin_top + impl->margin_bottom, - &width, - &height); - - /* Save size for next time we get 0x0 */ - _gdk_wayland_surface_save_size (window); - } - - gdk_wayland_surface_configure (window, width, height, impl->scale); - } - - GDK_DISPLAY_NOTE (gdk_surface_get_display (window), EVENTS, - g_message ("configure, window %p %dx%d,%s%s%s%s", - window, width, height, - (new_state & GDK_SURFACE_STATE_FULLSCREEN) ? " fullscreen" : "", - (new_state & GDK_SURFACE_STATE_MAXIMIZED) ? " maximized" : "", - (new_state & GDK_SURFACE_STATE_FOCUSED) ? " focused" : "", - (new_state & GDK_SURFACE_STATE_TILED) ? " tiled" : "")); - - gdk_surface_set_state (window, new_state); - zxdg_surface_v6_ack_configure (xdg_surface, serial); - if (impl->hint != GDK_SURFACE_TYPE_HINT_DIALOG && - new_state & GDK_SURFACE_STATE_FOCUSED) - gdk_wayland_surface_update_dialogs (window); -} - -static const struct zxdg_surface_v6_listener xdg_surface_listener = { - xdg_surface_configure, -}; - -static void -xdg_toplevel_configure (void *data, - struct zxdg_toplevel_v6 *xdg_toplevel, - int32_t width, - int32_t height, - struct wl_array *states) -{ - GdkSurface *window = GDK_SURFACE (data); - GdkSurfaceImplWayland *impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); - uint32_t *p; - - wl_array_for_each (p, states) - { - uint32_t state = *p; - switch (state) - { - case ZXDG_TOPLEVEL_V6_STATE_FULLSCREEN: - impl->pending.state |= GDK_SURFACE_STATE_FULLSCREEN; - break; - case ZXDG_TOPLEVEL_V6_STATE_MAXIMIZED: - impl->pending.state |= GDK_SURFACE_STATE_MAXIMIZED; - break; - case ZXDG_TOPLEVEL_V6_STATE_ACTIVATED: - impl->pending.state |= GDK_SURFACE_STATE_FOCUSED; - break; - case ZXDG_TOPLEVEL_V6_STATE_RESIZING: - break; - default: - /* Unknown state */ - break; - } - } - - impl->pending.width = width; - impl->pending.height = height; -} - -static void -xdg_toplevel_close (void *data, - struct zxdg_toplevel_v6 *xdg_toplevel) -{ - GdkSurface *window = GDK_SURFACE (data); - GdkDisplay *display; - GdkEvent *event; - - display = gdk_surface_get_display (window); - - GDK_DISPLAY_NOTE (display, EVENTS, g_message ("close %p", window)); - - event = gdk_event_new (GDK_DELETE); - event->any.window = g_object_ref (window); - event->any.send_event = TRUE; - - _gdk_wayland_display_deliver_event (display, event); -} - -static const struct zxdg_toplevel_v6_listener xdg_toplevel_listener = { - xdg_toplevel_configure, - xdg_toplevel_close, -}; - -static void -gdk_wayland_surface_create_xdg_toplevel (GdkSurface *window) -{ - GdkWaylandDisplay *display_wayland = GDK_WAYLAND_DISPLAY (gdk_surface_get_display (window)); - GdkSurfaceImplWayland *impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); - const gchar *app_id; - - impl->display_server.xdg_surface = - zxdg_shell_v6_get_xdg_surface (display_wayland->xdg_shell, - impl->display_server.wl_surface); - zxdg_surface_v6_add_listener (impl->display_server.xdg_surface, - &xdg_surface_listener, - window); - gdk_surface_freeze_updates (window); - - impl->display_server.xdg_toplevel = - zxdg_surface_v6_get_toplevel (impl->display_server.xdg_surface); - zxdg_toplevel_v6_add_listener (impl->display_server.xdg_toplevel, - &xdg_toplevel_listener, - window); - - gdk_wayland_surface_sync_parent (window, NULL); - gdk_wayland_surface_sync_parent_of_imported (window); - gdk_wayland_surface_sync_title (window); - - if (window->state & GDK_SURFACE_STATE_MAXIMIZED) - zxdg_toplevel_v6_set_maximized (impl->display_server.xdg_toplevel); - if (window->state & GDK_SURFACE_STATE_FULLSCREEN) - zxdg_toplevel_v6_set_fullscreen (impl->display_server.xdg_toplevel, - impl->initial_fullscreen_output); - - impl->initial_fullscreen_output = NULL; - - app_id = g_get_prgname (); - - if (app_id == NULL) - app_id = "GTK+ Application"; - - zxdg_toplevel_v6_set_app_id (impl->display_server.xdg_toplevel, app_id); - - maybe_set_gtk_surface_dbus_properties (window); - maybe_set_gtk_surface_modal (window); - - if (impl->hint == GDK_SURFACE_TYPE_HINT_DIALOG) - _gdk_wayland_screen_add_orphan_dialog (window); - - wl_surface_commit (impl->display_server.wl_surface); -} - -static void -xdg_popup_configure (void *data, - struct zxdg_popup_v6 *xdg_popup, - int32_t x, - int32_t y, - int32_t width, - int32_t height) -{ - GdkSurface *window = GDK_SURFACE (data); - GdkSurfaceImplWayland *impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); - GdkRectangle flipped_rect; - GdkRectangle final_rect; - gboolean flipped_x; - gboolean flipped_y; - - g_return_if_fail (impl->transient_for); - - if (impl->position_method != POSITION_METHOD_MOVE_TO_RECT) - return; - - calculate_moved_to_rect_result (window, x, y, width, height, - &flipped_rect, - &final_rect, - &flipped_x, - &flipped_y); - - g_signal_emit_by_name (window, - "moved-to-rect", - &flipped_rect, - &final_rect, - flipped_x, - flipped_y); -} - -static void -xdg_popup_done (void *data, - struct zxdg_popup_v6 *xdg_popup) -{ - GdkSurface *window = GDK_SURFACE (data); - - GDK_DISPLAY_NOTE (gdk_surface_get_display (window), EVENTS, g_message ("done %p", window)); - - gdk_surface_hide (window); -} - -static const struct zxdg_popup_v6_listener xdg_popup_listener = { - xdg_popup_configure, - xdg_popup_done, -}; - -static enum zxdg_positioner_v6_anchor -rect_anchor_to_anchor (GdkGravity rect_anchor) -{ - switch (rect_anchor) - { - case GDK_GRAVITY_NORTH_WEST: - case GDK_GRAVITY_STATIC: - return (ZXDG_POSITIONER_V6_ANCHOR_TOP | - ZXDG_POSITIONER_V6_ANCHOR_LEFT); - case GDK_GRAVITY_NORTH: - return ZXDG_POSITIONER_V6_ANCHOR_TOP; - case GDK_GRAVITY_NORTH_EAST: - return (ZXDG_POSITIONER_V6_ANCHOR_TOP | - ZXDG_POSITIONER_V6_ANCHOR_RIGHT); - case GDK_GRAVITY_WEST: - return ZXDG_POSITIONER_V6_ANCHOR_LEFT; - case GDK_GRAVITY_CENTER: - return ZXDG_POSITIONER_V6_ANCHOR_NONE; - case GDK_GRAVITY_EAST: - return ZXDG_POSITIONER_V6_ANCHOR_RIGHT; - case GDK_GRAVITY_SOUTH_WEST: - return (ZXDG_POSITIONER_V6_ANCHOR_BOTTOM | - ZXDG_POSITIONER_V6_ANCHOR_LEFT); - case GDK_GRAVITY_SOUTH: - return ZXDG_POSITIONER_V6_ANCHOR_BOTTOM; - case GDK_GRAVITY_SOUTH_EAST: - return (ZXDG_POSITIONER_V6_ANCHOR_BOTTOM | - ZXDG_POSITIONER_V6_ANCHOR_RIGHT); - default: - g_assert_not_reached (); - } - - return (ZXDG_POSITIONER_V6_ANCHOR_TOP | - ZXDG_POSITIONER_V6_ANCHOR_LEFT); -} - -static enum zxdg_positioner_v6_gravity -window_anchor_to_gravity (GdkGravity rect_anchor) -{ - switch (rect_anchor) - { - case GDK_GRAVITY_NORTH_WEST: - case GDK_GRAVITY_STATIC: - return (ZXDG_POSITIONER_V6_GRAVITY_BOTTOM | - ZXDG_POSITIONER_V6_GRAVITY_RIGHT); - case GDK_GRAVITY_NORTH: - return ZXDG_POSITIONER_V6_GRAVITY_BOTTOM; - case GDK_GRAVITY_NORTH_EAST: - return (ZXDG_POSITIONER_V6_GRAVITY_BOTTOM | - ZXDG_POSITIONER_V6_GRAVITY_LEFT); - case GDK_GRAVITY_WEST: - return ZXDG_POSITIONER_V6_GRAVITY_RIGHT; - case GDK_GRAVITY_CENTER: - return ZXDG_POSITIONER_V6_GRAVITY_NONE; - case GDK_GRAVITY_EAST: - return ZXDG_POSITIONER_V6_GRAVITY_LEFT; - case GDK_GRAVITY_SOUTH_WEST: - return (ZXDG_POSITIONER_V6_GRAVITY_TOP | - ZXDG_POSITIONER_V6_GRAVITY_RIGHT); - case GDK_GRAVITY_SOUTH: - return ZXDG_POSITIONER_V6_GRAVITY_TOP; - case GDK_GRAVITY_SOUTH_EAST: - return (ZXDG_POSITIONER_V6_GRAVITY_TOP | - ZXDG_POSITIONER_V6_GRAVITY_LEFT); - default: - g_assert_not_reached (); - } - - return (ZXDG_POSITIONER_V6_GRAVITY_BOTTOM | - ZXDG_POSITIONER_V6_GRAVITY_RIGHT); -} - -void -gdk_wayland_surface_announce_csd (GdkSurface *window) -{ - GdkWaylandDisplay *display_wayland = GDK_WAYLAND_DISPLAY (gdk_surface_get_display (window)); - GdkSurfaceImplWayland *impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); - if (!display_wayland->server_decoration_manager) - return; - impl->display_server.server_decoration = - org_kde_kwin_server_decoration_manager_create (display_wayland->server_decoration_manager, - impl->display_server.wl_surface); - if (impl->display_server.server_decoration) - org_kde_kwin_server_decoration_request_mode (impl->display_server.server_decoration, - ORG_KDE_KWIN_SERVER_DECORATION_MANAGER_MODE_CLIENT); -} - -static GdkSurface * -get_real_parent_and_translate (GdkSurface *window, - gint *x, - gint *y) -{ - GdkSurfaceImplWayland *impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); - GdkSurface *parent = impl->transient_for; - - while (parent) - { - GdkSurfaceImplWayland *parent_impl = - GDK_SURFACE_IMPL_WAYLAND (parent->impl); - GdkSurface *effective_parent = gdk_surface_get_parent (parent); - - if ((gdk_surface_has_native (parent) && - !parent_impl->display_server.wl_subsurface) || - !effective_parent) - break; - - *x += parent->x; - *y += parent->y; - - if (gdk_surface_has_native (parent) && - parent_impl->display_server.wl_subsurface) - parent = parent->transient_for; - else - parent = effective_parent; - } - - return parent; -} - -static void -translate_to_real_parent_window_geometry (GdkSurface *window, - gint *x, - gint *y) -{ - GdkSurface *parent; - - parent = get_real_parent_and_translate (window, x, y); - - *x -= parent->shadow_left; - *y -= parent->shadow_top; -} - -static GdkSurface * -translate_from_real_parent_window_geometry (GdkSurface *window, - gint *x, - gint *y) -{ - GdkSurface *parent; - gint dx = 0; - gint dy = 0; - - parent = get_real_parent_and_translate (window, &dx, &dy); - - *x -= dx - parent->shadow_left; - *y -= dy - parent->shadow_top; - - return parent; -} - -static void -calculate_popup_rect (GdkSurface *window, - GdkGravity rect_anchor, - GdkGravity window_anchor, - GdkRectangle *out_rect) -{ - GdkSurfaceImplWayland *impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); - GdkRectangle geometry; - GdkRectangle anchor_rect; - int x = 0, y = 0; - - gdk_wayland_surface_get_window_geometry (window, &geometry); - - anchor_rect = (GdkRectangle) { - .x = (impl->pending_move_to_rect.rect.x + - impl->pending_move_to_rect.rect_anchor_dx), - .y = (impl->pending_move_to_rect.rect.y + - impl->pending_move_to_rect.rect_anchor_dy), - .width = impl->pending_move_to_rect.rect.width, - .height = impl->pending_move_to_rect.rect.height - }; - - switch (rect_anchor) - { - default: - case GDK_GRAVITY_STATIC: - case GDK_GRAVITY_NORTH_WEST: - x = anchor_rect.x; - y = anchor_rect.y; - break; - case GDK_GRAVITY_NORTH: - x = anchor_rect.x + (anchor_rect.width / 2); - y = anchor_rect.y; - break; - case GDK_GRAVITY_NORTH_EAST: - x = anchor_rect.x + anchor_rect.width; - y = anchor_rect.y; - break; - case GDK_GRAVITY_WEST: - x = anchor_rect.x; - y = anchor_rect.y + (anchor_rect.height / 2); - break; - case GDK_GRAVITY_CENTER: - x = anchor_rect.x + (anchor_rect.width / 2); - y = anchor_rect.y + (anchor_rect.height / 2); - break; - case GDK_GRAVITY_EAST: - x = anchor_rect.x + anchor_rect.width; - y = anchor_rect.y + (anchor_rect.height / 2); - break; - case GDK_GRAVITY_SOUTH_WEST: - x = anchor_rect.x; - y = anchor_rect.y + anchor_rect.height; - break; - case GDK_GRAVITY_SOUTH: - x = anchor_rect.x + (anchor_rect.width / 2); - y = anchor_rect.y + anchor_rect.height; - break; - case GDK_GRAVITY_SOUTH_EAST: - x = anchor_rect.x + anchor_rect.width; - y = anchor_rect.y + anchor_rect.height; - break; - } - - switch (window_anchor) - { - default: - case GDK_GRAVITY_STATIC: - case GDK_GRAVITY_NORTH_WEST: - break; - case GDK_GRAVITY_NORTH: - x -= geometry.width / 2; - break; - case GDK_GRAVITY_NORTH_EAST: - x -= geometry.width; - break; - case GDK_GRAVITY_WEST: - y -= geometry.height / 2; - break; - case GDK_GRAVITY_CENTER: - x -= geometry.width / 2; - y -= geometry.height / 2; - break; - case GDK_GRAVITY_EAST: - x -= geometry.width; - y -= geometry.height / 2; - break; - case GDK_GRAVITY_SOUTH_WEST: - y -= geometry.height; - break; - case GDK_GRAVITY_SOUTH: - x -= geometry.width / 2; - y -= geometry.height; - break; - case GDK_GRAVITY_SOUTH_EAST: - x -= geometry.width; - y -= geometry.height; - break; - } - - *out_rect = (GdkRectangle) { - .x = x, - .y = y, - .width = geometry.width, - .height = geometry.height - }; -} - -static GdkGravity -flip_anchor_horizontally (GdkGravity anchor) -{ - switch (anchor) - { - default: - case GDK_GRAVITY_STATIC: - case GDK_GRAVITY_NORTH_WEST: - return GDK_GRAVITY_NORTH_EAST; - case GDK_GRAVITY_NORTH: - return GDK_GRAVITY_NORTH; - case GDK_GRAVITY_NORTH_EAST: - return GDK_GRAVITY_NORTH_WEST; - case GDK_GRAVITY_WEST: - return GDK_GRAVITY_EAST; - case GDK_GRAVITY_CENTER: - return GDK_GRAVITY_CENTER; - case GDK_GRAVITY_EAST: - return GDK_GRAVITY_WEST; - case GDK_GRAVITY_SOUTH_WEST: - return GDK_GRAVITY_SOUTH_EAST; - case GDK_GRAVITY_SOUTH: - return GDK_GRAVITY_SOUTH; - case GDK_GRAVITY_SOUTH_EAST: - return GDK_GRAVITY_SOUTH_WEST; - } - - g_assert_not_reached (); -} - -static GdkGravity -flip_anchor_vertically (GdkGravity anchor) -{ - switch (anchor) - { - default: - case GDK_GRAVITY_STATIC: - case GDK_GRAVITY_NORTH_WEST: - return GDK_GRAVITY_SOUTH_WEST; - case GDK_GRAVITY_NORTH: - return GDK_GRAVITY_SOUTH; - case GDK_GRAVITY_NORTH_EAST: - return GDK_GRAVITY_SOUTH_EAST; - case GDK_GRAVITY_WEST: - return GDK_GRAVITY_WEST; - case GDK_GRAVITY_CENTER: - return GDK_GRAVITY_CENTER; - case GDK_GRAVITY_EAST: - return GDK_GRAVITY_EAST; - case GDK_GRAVITY_SOUTH_WEST: - return GDK_GRAVITY_NORTH_WEST; - case GDK_GRAVITY_SOUTH: - return GDK_GRAVITY_NORTH; - case GDK_GRAVITY_SOUTH_EAST: - return GDK_GRAVITY_NORTH_EAST; - } - - g_assert_not_reached (); -} - -static void -calculate_moved_to_rect_result (GdkSurface *window, - int x, - int y, - int width, - int height, - GdkRectangle *flipped_rect, - GdkRectangle *final_rect, - gboolean *flipped_x, - gboolean *flipped_y) -{ - GdkSurfaceImplWayland *impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); - GdkSurface *parent; - gint window_x, window_y; - gint window_width, window_height; - GdkRectangle best_rect; - - parent = translate_from_real_parent_window_geometry (window, &x, &y); - *final_rect = (GdkRectangle) { - .x = x, - .y = y, - .width = width, - .height = height, - }; - - window_x = parent->x + x; - window_y = parent->y + y; - window_width = width + window->shadow_left + window->shadow_right; - window_height = height + window->shadow_top + window->shadow_bottom; - - gdk_surface_move_resize (window, - window_x, window_y, - window_width, window_height); - - calculate_popup_rect (window, - impl->pending_move_to_rect.rect_anchor, - impl->pending_move_to_rect.window_anchor, - &best_rect); - - *flipped_rect = best_rect; - - if (x != best_rect.x && - impl->pending_move_to_rect.anchor_hints & GDK_ANCHOR_FLIP_X) - { - GdkRectangle flipped_x_rect; - GdkGravity flipped_rect_anchor; - GdkGravity flipped_window_anchor; - - flipped_rect_anchor = - flip_anchor_horizontally (impl->pending_move_to_rect.rect_anchor); - flipped_window_anchor = - flip_anchor_horizontally (impl->pending_move_to_rect.window_anchor), - calculate_popup_rect (window, - flipped_rect_anchor, - flipped_window_anchor, - &flipped_x_rect); - - if (flipped_x_rect.x == x) - flipped_rect->x = x; - } - if (y != best_rect.y && - impl->pending_move_to_rect.anchor_hints & GDK_ANCHOR_FLIP_Y) - { - GdkRectangle flipped_y_rect; - GdkGravity flipped_rect_anchor; - GdkGravity flipped_window_anchor; - - flipped_rect_anchor = - flip_anchor_vertically (impl->pending_move_to_rect.rect_anchor); - flipped_window_anchor = - flip_anchor_vertically (impl->pending_move_to_rect.window_anchor), - calculate_popup_rect (window, - flipped_rect_anchor, - flipped_window_anchor, - &flipped_y_rect); - - if (flipped_y_rect.y == y) - flipped_rect->y = y; - } - - *flipped_x = flipped_rect->x != best_rect.x; - *flipped_y = flipped_rect->y != best_rect.y; -} - -static struct zxdg_positioner_v6 * -create_dynamic_positioner (GdkSurface *window) -{ - GdkSurfaceImplWayland *impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); - GdkWaylandDisplay *display = - GDK_WAYLAND_DISPLAY (gdk_surface_get_display (window)); - struct zxdg_positioner_v6 *positioner; - GdkRectangle geometry; - enum zxdg_positioner_v6_anchor anchor; - enum zxdg_positioner_v6_gravity gravity; - uint32_t constraint_adjustment = ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_NONE; - gint real_anchor_rect_x, real_anchor_rect_y; - gint anchor_rect_width, anchor_rect_height; - - positioner = zxdg_shell_v6_create_positioner (display->xdg_shell); - - gdk_wayland_surface_get_window_geometry (window, &geometry); - zxdg_positioner_v6_set_size (positioner, geometry.width, geometry.height); - - real_anchor_rect_x = impl->pending_move_to_rect.rect.x; - real_anchor_rect_y = impl->pending_move_to_rect.rect.y; - translate_to_real_parent_window_geometry (window, - &real_anchor_rect_x, - &real_anchor_rect_y); - - anchor_rect_width = impl->pending_move_to_rect.rect.width; - anchor_rect_height = impl->pending_move_to_rect.rect.height; - zxdg_positioner_v6_set_anchor_rect (positioner, - real_anchor_rect_x, - real_anchor_rect_y, - anchor_rect_width, - anchor_rect_height); - - zxdg_positioner_v6_set_offset (positioner, - impl->pending_move_to_rect.rect_anchor_dx, - impl->pending_move_to_rect.rect_anchor_dy); - - anchor = rect_anchor_to_anchor (impl->pending_move_to_rect.rect_anchor); - zxdg_positioner_v6_set_anchor (positioner, anchor); - - gravity = window_anchor_to_gravity (impl->pending_move_to_rect.window_anchor); - zxdg_positioner_v6_set_gravity (positioner, gravity); - - if (impl->pending_move_to_rect.anchor_hints & GDK_ANCHOR_FLIP_X) - constraint_adjustment |= ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_FLIP_X; - if (impl->pending_move_to_rect.anchor_hints & GDK_ANCHOR_FLIP_Y) - constraint_adjustment |= ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_FLIP_Y; - if (impl->pending_move_to_rect.anchor_hints & GDK_ANCHOR_SLIDE_X) - constraint_adjustment |= ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_SLIDE_X; - if (impl->pending_move_to_rect.anchor_hints & GDK_ANCHOR_SLIDE_Y) - constraint_adjustment |= ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_SLIDE_Y; - if (impl->pending_move_to_rect.anchor_hints & GDK_ANCHOR_RESIZE_X) - constraint_adjustment |= ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_RESIZE_X; - if (impl->pending_move_to_rect.anchor_hints & GDK_ANCHOR_RESIZE_Y) - constraint_adjustment |= ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_RESIZE_Y; - - zxdg_positioner_v6_set_constraint_adjustment (positioner, - constraint_adjustment); - - return positioner; -} - -static struct zxdg_positioner_v6 * -create_simple_positioner (GdkSurface *window, - GdkSurface *parent) -{ - GdkWaylandDisplay *display = - GDK_WAYLAND_DISPLAY (gdk_surface_get_display (window)); - struct zxdg_positioner_v6 *positioner; - GdkRectangle geometry; - GdkRectangle parent_geometry; - int parent_x, parent_y; - - positioner = zxdg_shell_v6_create_positioner (display->xdg_shell); - - gdk_wayland_surface_get_window_geometry (window, &geometry); - zxdg_positioner_v6_set_size (positioner, geometry.width, geometry.height); - - parent_x = parent->x; - parent_y = parent->y; - - gdk_wayland_surface_get_window_geometry (parent, &parent_geometry); - parent_x += parent_geometry.x; - parent_y += parent_geometry.y; - - zxdg_positioner_v6_set_anchor_rect (positioner, - (window->x + geometry.x) - parent_x, - (window->y + geometry.y) - parent_y, - 1, 1); - zxdg_positioner_v6_set_anchor (positioner, - (ZXDG_POSITIONER_V6_ANCHOR_TOP | - ZXDG_POSITIONER_V6_ANCHOR_LEFT)); - zxdg_positioner_v6_set_gravity (positioner, - (ZXDG_POSITIONER_V6_GRAVITY_BOTTOM | - ZXDG_POSITIONER_V6_GRAVITY_RIGHT)); - - return positioner; -} - -static void -gdk_wayland_surface_create_xdg_popup (GdkSurface *window, - GdkSurface *parent, - struct wl_seat *seat) -{ - GdkWaylandDisplay *display = GDK_WAYLAND_DISPLAY (gdk_surface_get_display (window)); - GdkSurfaceImplWayland *impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); - GdkSurfaceImplWayland *parent_impl = GDK_SURFACE_IMPL_WAYLAND (parent->impl); - struct zxdg_positioner_v6 *positioner; - GdkSeat *gdk_seat; - guint32 serial; - - if (!impl->display_server.wl_surface) - return; - - if (!parent_impl->display_server.xdg_surface) - return; - - if (impl->display_server.xdg_toplevel) - { - g_warning ("Can't map popup, already mapped as toplevel"); - return; - } - if (impl->display_server.xdg_popup) - { - g_warning ("Can't map popup, already mapped"); - return; - } - if ((display->current_popups && - g_list_last (display->current_popups)->data != parent) || - (!display->current_popups && - !parent_impl->display_server.xdg_toplevel)) - { - g_warning ("Tried to map a popup with a non-top most parent"); - return; - } - - impl->display_server.xdg_surface = - zxdg_shell_v6_get_xdg_surface (display->xdg_shell, - impl->display_server.wl_surface); - zxdg_surface_v6_add_listener (impl->display_server.xdg_surface, - &xdg_surface_listener, - window); - gdk_surface_freeze_updates (window); - - if (impl->position_method == POSITION_METHOD_MOVE_TO_RECT) - positioner = create_dynamic_positioner (window); - else - positioner = create_simple_positioner (window, parent); - - impl->display_server.xdg_popup = - zxdg_surface_v6_get_popup (impl->display_server.xdg_surface, - parent_impl->display_server.xdg_surface, - positioner); - zxdg_popup_v6_add_listener (impl->display_server.xdg_popup, - &xdg_popup_listener, - window); - - zxdg_positioner_v6_destroy (positioner); - - if (seat) - { - gdk_seat = gdk_display_get_default_seat (GDK_DISPLAY (display)); - serial = _gdk_wayland_seat_get_last_implicit_grab_serial (gdk_seat, NULL); - zxdg_popup_v6_grab (impl->display_server.xdg_popup, seat, serial); - } - - wl_surface_commit (impl->display_server.wl_surface); - - impl->popup_parent = parent; - display->current_popups = g_list_append (display->current_popups, window); -} - -static struct wl_seat * -find_grab_input_seat (GdkSurface *window, GdkSurface *transient_for) -{ - GdkSurface *attached_grab_window; - GdkSurfaceImplWayland *impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); - GdkSurfaceImplWayland *tmp_impl; - - /* Use the device that was used for the grab as the device for - * the popup window setup - so this relies on GTK+ taking the - * grab before showing the popup window. - */ - if (impl->grab_input_seat) - return gdk_wayland_seat_get_wl_seat (impl->grab_input_seat); - - /* HACK: GtkMenu grabs a special window known as the "grab transfer window" - * and then transfers the grab over to the correct window later. Look for - * this window when taking the grab to know it's correct. - * - * See: associate_menu_grab_transfer_window in gtkmenu.c - */ - attached_grab_window = g_object_get_data (G_OBJECT (window), "gdk-attached-grab-window"); - if (attached_grab_window) - { - tmp_impl = GDK_SURFACE_IMPL_WAYLAND (attached_grab_window->impl); - if (tmp_impl->grab_input_seat) - return gdk_wayland_seat_get_wl_seat (tmp_impl->grab_input_seat); - } - - while (transient_for) - { - tmp_impl = GDK_SURFACE_IMPL_WAYLAND (transient_for->impl); - - if (tmp_impl->grab_input_seat) - return gdk_wayland_seat_get_wl_seat (tmp_impl->grab_input_seat); - - transient_for = tmp_impl->transient_for; - } - - return NULL; -} - -static gboolean -should_be_mapped (GdkSurface *window) -{ - GdkSurfaceImplWayland *impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); - - /* Don't map crazy temp that GTK+ uses for internal X11 shenanigans. */ - if (window->window_type == GDK_SURFACE_TEMP && window->x < 0 && window->y < 0) - return FALSE; - - if (impl->hint == GDK_SURFACE_TYPE_HINT_DND) - return FALSE; - - return TRUE; -} - -static gboolean -should_map_as_popup (GdkSurface *window) -{ - GdkSurfaceImplWayland *impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); - - /* Ideally, popup would be temp windows with a parent and grab */ - if (GDK_SURFACE_TYPE (window) == GDK_SURFACE_TEMP) - { - /* If a temp window has a parent and a grab, we can use a popup */ - if (impl->transient_for) - { - if (impl->grab_input_seat) - return TRUE; - } - else - g_message ("Window %p is a temporary window without parent, " - "application will not be able to position it on screen.", - window); - } - - /* Yet we need to keep the window type hint tests for compatibility */ - switch ((guint) impl->hint) - { - case GDK_SURFACE_TYPE_HINT_POPUP_MENU: - case GDK_SURFACE_TYPE_HINT_DROPDOWN_MENU: - case GDK_SURFACE_TYPE_HINT_COMBO: - return TRUE; - - default: - break; - } - - return FALSE; -} - -static gboolean -should_map_as_subsurface (GdkSurface *window) -{ - GdkSurfaceImplWayland *impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); - - if (GDK_SURFACE_TYPE (window) == GDK_SURFACE_SUBSURFACE) - return TRUE; - - if (GDK_SURFACE_TYPE (window) != GDK_SURFACE_TEMP) - return FALSE; - - /* if we want a popup, we do not want a subsurface */ - if (should_map_as_popup (window)) - return FALSE; - - if (impl->transient_for) - { - GdkSurfaceImplWayland *impl_parent; - - impl_parent = GDK_SURFACE_IMPL_WAYLAND (impl->transient_for->impl); - /* subsurface require that the parent is mapped */ - if (impl_parent->mapped) - return TRUE; - else - g_warning ("Couldn't map window %p as subsurface because its parent is not mapped.", - window); - - } - - return FALSE; -} - -/* Get the window that can be used as a parent for a popup, i.e. a xdg_toplevel - * or xdg_popup. If the window is not, traverse up the transiency parents until - * we find one. - */ -static GdkSurface * -get_popup_parent (GdkSurface *window) -{ - while (window) - { - GdkSurfaceImplWayland *impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); - - if (impl->display_server.xdg_popup || impl->display_server.xdg_toplevel) - return window; - - window = impl->transient_for; - } - - return NULL; -} - -static void -gdk_wayland_surface_map (GdkSurface *window) -{ - GdkSurfaceImplWayland *impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); - GdkSurface *transient_for = NULL; - - if (!should_be_mapped (window)) - return; - - if (impl->mapped || impl->use_custom_surface) - return; - - if (should_map_as_subsurface (window)) - { - if (impl->transient_for) - gdk_wayland_surface_create_subsurface (window); - else - g_warning ("Couldn't map window %p as susburface yet because it doesn't have a parent", - window); - } - else if (should_map_as_popup (window)) - { - gboolean create_fallback = FALSE; - struct wl_seat *grab_input_seat; - - /* Popup menus can appear without a transient parent, which means they - * cannot be positioned properly on Wayland. This attempts to guess the - * surface they should be positioned with by finding the surface beneath - * the device that created the grab for the popup window. - */ - if (!impl->transient_for && impl->hint == GDK_SURFACE_TYPE_HINT_POPUP_MENU) - { - GdkDevice *grab_device = NULL; - - /* The popup menu window is not the grabbed window. This may mean - * that a "transfer window" (see gtkmenu.c) is used, and we need - * to find that window to get the grab device. If so is the case - * the "transfer window" can be retrieved via the - * "gdk-attached-grab-window" associated data field. - */ - if (!impl->grab_input_seat) - { - GdkSurface *attached_grab_window = - g_object_get_data (G_OBJECT (window), - "gdk-attached-grab-window"); - if (attached_grab_window) - { - GdkSurfaceImplWayland *attached_impl = - GDK_SURFACE_IMPL_WAYLAND (attached_grab_window->impl); - grab_device = gdk_seat_get_pointer (attached_impl->grab_input_seat); - transient_for = - gdk_device_get_window_at_position (grab_device, - NULL, NULL); - } - } - else - { - grab_device = gdk_seat_get_pointer (impl->grab_input_seat); - transient_for = - gdk_device_get_window_at_position (grab_device, NULL, NULL); - } - - if (transient_for) - transient_for = get_popup_parent (gdk_surface_get_toplevel (transient_for)); - - /* If the position was not explicitly set, start the popup at the - * position of the device that holds the grab. - */ - if (impl->position_method == POSITION_METHOD_NONE && grab_device) - gdk_surface_get_device_position (transient_for, grab_device, - &window->x, &window->y, NULL); - } - else - { - transient_for = gdk_surface_get_toplevel (impl->transient_for); - transient_for = get_popup_parent (transient_for); - } - - if (!transient_for) - { - g_warning ("Couldn't map as window %p as popup because it doesn't have a parent", - window); - - create_fallback = TRUE; - } - else - { - grab_input_seat = find_grab_input_seat (window, transient_for); - } - - if (!create_fallback) - { - gdk_wayland_surface_create_xdg_popup (window, - transient_for, - grab_input_seat); - } - else - { - gdk_wayland_surface_create_xdg_toplevel (window); - } - } - else - { - gdk_wayland_surface_create_xdg_toplevel (window); - } - - impl->mapped = TRUE; -} - -static void -gdk_wayland_surface_show (GdkSurface *window, - gboolean already_mapped) -{ - GdkSurfaceImplWayland *impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); - - if (!impl->display_server.wl_surface) - gdk_wayland_surface_create_surface (window); - - gdk_wayland_surface_map (window); - - _gdk_make_event (window, GDK_MAP, NULL, FALSE); - - if (impl->staging_cairo_surface && - _gdk_wayland_is_shm_surface (impl->staging_cairo_surface)) - gdk_wayland_surface_attach_image (window); -} - -static void -unmap_subsurface (GdkSurface *window) -{ - GdkSurfaceImplWayland *impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); - GdkSurfaceImplWayland *parent_impl; - - g_return_if_fail (impl->display_server.wl_subsurface); - g_return_if_fail (impl->transient_for); - - parent_impl = GDK_SURFACE_IMPL_WAYLAND (impl->transient_for->impl); - wl_subsurface_destroy (impl->display_server.wl_subsurface); - if (impl->parent_surface_committed_handler) - { - g_signal_handler_disconnect (parent_impl, - impl->parent_surface_committed_handler); - impl->parent_surface_committed_handler = 0; - } - impl->display_server.wl_subsurface = NULL; -} - -static void -unmap_popups_for_window (GdkSurface *window) -{ - GdkWaylandDisplay *display_wayland; - GList *l; - - display_wayland = GDK_WAYLAND_DISPLAY (gdk_surface_get_display (window)); - for (l = display_wayland->current_popups; l; l = l->next) - { - GdkSurface *popup = l->data; - GdkSurfaceImplWayland *popup_impl = GDK_SURFACE_IMPL_WAYLAND (popup->impl); - - if (popup_impl->popup_parent == window) - { - g_warning ("Tried to unmap the parent of a popup"); - gdk_surface_hide (popup); - - return; - } - } -} - -static void -gdk_wayland_surface_hide_surface (GdkSurface *window) -{ - GdkWaylandDisplay *display_wayland = GDK_WAYLAND_DISPLAY (gdk_surface_get_display (window)); - GdkSurfaceImplWayland *impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); - - unmap_popups_for_window (window); - - if (impl->display_server.wl_surface) - { - if (impl->dummy_egl_surface) - { - eglDestroySurface (display_wayland->egl_display, impl->dummy_egl_surface); - impl->dummy_egl_surface = NULL; - } - - if (impl->display_server.dummy_egl_window) - { - wl_egl_window_destroy (impl->display_server.dummy_egl_window); - impl->display_server.dummy_egl_window = NULL; - } - - if (impl->egl_surface) - { - eglDestroySurface (display_wayland->egl_display, impl->egl_surface); - impl->egl_surface = NULL; - } - - if (impl->display_server.egl_window) - { - wl_egl_window_destroy (impl->display_server.egl_window); - impl->display_server.egl_window = NULL; - } - - if (impl->display_server.xdg_toplevel) - { - zxdg_toplevel_v6_destroy (impl->display_server.xdg_toplevel); - impl->display_server.xdg_toplevel = NULL; - } - else if (impl->display_server.xdg_popup) - { - zxdg_popup_v6_destroy (impl->display_server.xdg_popup); - impl->display_server.xdg_popup = NULL; - display_wayland->current_popups = - g_list_remove (display_wayland->current_popups, window); - } - if (impl->display_server.xdg_surface) - { - zxdg_surface_v6_destroy (impl->display_server.xdg_surface); - impl->display_server.xdg_surface = NULL; - if (!impl->initial_configure_received) - gdk_surface_thaw_updates (window); - else - impl->initial_configure_received = FALSE; - } - - if (impl->display_server.wl_subsurface) - unmap_subsurface (window); - - if (impl->awaiting_frame) - { - GdkFrameClock *frame_clock; - - impl->awaiting_frame = FALSE; - frame_clock = gdk_surface_get_frame_clock (window); - if (frame_clock) - _gdk_frame_clock_thaw (frame_clock); - } - - if (impl->display_server.gtk_surface) - { - gtk_surface1_destroy (impl->display_server.gtk_surface); - impl->display_server.gtk_surface = NULL; - impl->application.was_set = FALSE; - } - - wl_surface_destroy (impl->display_server.wl_surface); - impl->display_server.wl_surface = NULL; - - g_slist_free (impl->display_server.outputs); - impl->display_server.outputs = NULL; - - if (impl->hint == GDK_SURFACE_TYPE_HINT_DIALOG && !impl->transient_for) - display_wayland->orphan_dialogs = - g_list_remove (display_wayland->orphan_dialogs, window); - } - - unset_transient_for_exported (window); - - _gdk_wayland_surface_clear_saved_size (window); - impl->pending_commit = FALSE; - impl->mapped = FALSE; -} - -static void -gdk_wayland_surface_hide (GdkSurface *window) -{ - gdk_wayland_surface_hide_surface (window); - _gdk_surface_clear_update_area (window); -} - -static void -gdk_surface_wayland_withdraw (GdkSurface *window) -{ - if (!window->destroyed) - { - if (GDK_SURFACE_IS_MAPPED (window)) - gdk_synthesize_window_state (window, 0, GDK_SURFACE_STATE_WITHDRAWN); - - g_assert (!GDK_SURFACE_IS_MAPPED (window)); - - gdk_wayland_surface_hide_surface (window); - } -} - -static void -gdk_surface_wayland_set_events (GdkSurface *window, - GdkEventMask event_mask) -{ - GDK_SURFACE (window)->event_mask = event_mask; -} - -static GdkEventMask -gdk_surface_wayland_get_events (GdkSurface *window) -{ - if (GDK_SURFACE_DESTROYED (window)) - return 0; - else - return GDK_SURFACE (window)->event_mask; -} - -static void -gdk_surface_wayland_raise (GdkSurface *window) -{ -} - -static void -gdk_surface_wayland_lower (GdkSurface *window) -{ -} - -static void -gdk_surface_wayland_restack_toplevel (GdkSurface *window, - GdkSurface *sibling, - gboolean above) -{ -} - -static void -gdk_surface_request_transient_parent_commit (GdkSurface *window) -{ - GdkSurfaceImplWayland *window_impl, *impl; - GdkFrameClock *frame_clock; - - window_impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); - - if (!window_impl->transient_for) - return; - - impl = GDK_SURFACE_IMPL_WAYLAND (window_impl->transient_for->impl); - - if (!impl->display_server.wl_surface || impl->pending_commit) - return; - - frame_clock = gdk_surface_get_frame_clock (window_impl->transient_for); - - if (!frame_clock) - return; - - impl->pending_commit = TRUE; - gdk_frame_clock_request_phase (frame_clock, - GDK_FRAME_CLOCK_PHASE_AFTER_PAINT); -} - -static void -gdk_surface_wayland_move_resize (GdkSurface *window, - gboolean with_move, - gint x, - gint y, - gint width, - gint height) -{ - GdkSurfaceImplWayland *impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); - - if (with_move) - { - /* Each toplevel has in its own "root" coordinate system */ - if (GDK_SURFACE_TYPE (window) != GDK_SURFACE_TOPLEVEL) - { - window->x = x; - window->y = y; - impl->position_method = POSITION_METHOD_MOVE_RESIZE; - - if (impl->display_server.wl_subsurface) - { - wl_subsurface_set_position (impl->display_server.wl_subsurface, - window->x + window->abs_x, - window->y + window->abs_y); - gdk_surface_request_transient_parent_commit (window); - } - } - } - - /* If this function is called with width and height = -1 then that means - * just move the window - don't update its size - */ - if (width > 0 && height > 0) - gdk_wayland_surface_maybe_configure (window, width, height, impl->scale); -} - -/* Avoid zero width/height as this is a protocol error */ -static void -sanitize_anchor_rect (GdkSurface *window, - GdkRectangle *rect) -{ - gint original_width = rect->width; - gint original_height = rect->height; - - rect->width = MAX (1, rect->width); - rect->height = MAX (1, rect->height); - rect->x = MAX (rect->x + original_width - rect->width, 0); - rect->y = MAX (rect->y + original_height - rect->height, 0); -} - -static void -gdk_surface_wayland_move_to_rect (GdkSurface *window, - const GdkRectangle *rect, - GdkGravity rect_anchor, - GdkGravity window_anchor, - GdkAnchorHints anchor_hints, - gint rect_anchor_dx, - gint rect_anchor_dy) -{ - GdkSurfaceImplWayland *impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); - - impl->pending_move_to_rect.rect = *rect; - sanitize_anchor_rect (window, &impl->pending_move_to_rect.rect); - - impl->pending_move_to_rect.rect_anchor = rect_anchor; - impl->pending_move_to_rect.window_anchor = window_anchor; - impl->pending_move_to_rect.anchor_hints = anchor_hints; - impl->pending_move_to_rect.rect_anchor_dx = rect_anchor_dx; - impl->pending_move_to_rect.rect_anchor_dy = rect_anchor_dy; - - impl->position_method = POSITION_METHOD_MOVE_TO_RECT; -} - -static void -gdk_surface_wayland_get_geometry (GdkSurface *window, - gint *x, - gint *y, - gint *width, - gint *height) -{ - if (!GDK_SURFACE_DESTROYED (window)) - { - if (x) - *x = window->x; - if (y) - *y = window->y; - if (width) - *width = window->width; - if (height) - *height = window->height; - } -} - -static void -gdk_surface_wayland_get_root_coords (GdkSurface *window, - gint x, - gint y, - gint *root_x, - gint *root_y) -{ - /* - * Wayland does not have a global coordinate space shared between surfaces. In - * fact, for regular toplevels, we have no idea where our surfaces are - * positioned, relatively. - * - * However, there are some cases like popups and subsurfaces where we do have - * some amount of control over the placement of our window, and we can - * semi-accurately control the x/y position of these windows, if they are - * relative to another surface. - * - * To pretend we have something called a root coordinate space, assume all - * parent-less windows are positioned in (0, 0), and all relative positioned - * popups and subsurfaces are placed within this fake root coordinate space. - * - * For example a 200x200 large toplevel window will have the position (0, 0). - * If a popup positioned in the middle of the toplevel will have the fake - * position (100,100). Furthermore, if a positioned is placed in the middle - * that popup, will have the fake position (150,150), even though it has the - * relative position (50,50). These three windows would make up one single - * fake root coordinate space. - */ - - if (root_x) - *root_x = window->x + x; - - if (root_y) - *root_y = window->y + y; -} - -static gboolean -gdk_surface_wayland_get_device_state (GdkSurface *window, - GdkDevice *device, - gdouble *x, - gdouble *y, - GdkModifierType *mask) -{ - gboolean return_val; - - g_return_val_if_fail (window == NULL || GDK_IS_SURFACE (window), FALSE); - - return_val = TRUE; - - if (!GDK_SURFACE_DESTROYED (window)) - { - GdkSurface *child; - - GDK_DEVICE_GET_CLASS (device)->query_state (device, window, - &child, - NULL, NULL, - x, y, mask); - return_val = (child != NULL); - } - - return return_val; -} - -static void -gdk_surface_wayland_shape_combine_region (GdkSurface *window, - const cairo_region_t *shape_region, - gint offset_x, - gint offset_y) -{ -} - -static void -gdk_surface_wayland_input_shape_combine_region (GdkSurface *window, - const cairo_region_t *shape_region, - gint offset_x, - gint offset_y) -{ - GdkSurfaceImplWayland *impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); - - if (GDK_SURFACE_DESTROYED (window)) - return; - - g_clear_pointer (&impl->input_region, cairo_region_destroy); - - if (shape_region) - { - impl->input_region = cairo_region_copy (shape_region); - cairo_region_translate (impl->input_region, offset_x, offset_y); - } - - impl->input_region_dirty = TRUE; -} - -static void -gdk_wayland_surface_destroy (GdkSurface *window, - gboolean recursing, - gboolean foreign_destroy) -{ - g_return_if_fail (GDK_IS_SURFACE (window)); - - /* Wayland windows can't be externally destroyed; we may possibly - * eventually want to use this path at display close-down - */ - g_return_if_fail (!foreign_destroy); - - gdk_wayland_surface_hide_surface (window); - drop_cairo_surfaces (window); - - if (window->parent == NULL) - { - GdkWaylandDisplay *display = GDK_WAYLAND_DISPLAY (gdk_surface_get_display (window)); - display->toplevels = g_list_remove (display->toplevels, window); - } -} - -static void -gdk_wayland_surface_focus (GdkSurface *window, - guint32 timestamp) -{ - GdkSurfaceImplWayland *impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); - - if (!impl->display_server.gtk_surface) - return; - - /* We didn't have an event to fetch a time from, meaning we have nothing valid - * to send. This should rather be translated to a 'needs-attention' request or - * something. - */ - if (timestamp == GDK_CURRENT_TIME) - return; - - gtk_surface1_present (impl->display_server.gtk_surface, timestamp); -} - -static void -gdk_wayland_surface_set_type_hint (GdkSurface *window, - GdkSurfaceTypeHint hint) -{ - GdkSurfaceImplWayland *impl; - - impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); - - if (GDK_SURFACE_DESTROYED (window)) - return; - - impl->hint = hint; -} - -static GdkSurfaceTypeHint -gdk_wayland_surface_get_type_hint (GdkSurface *window) -{ - GdkSurfaceImplWayland *impl; - - if (GDK_SURFACE_DESTROYED (window)) - return GDK_SURFACE_TYPE_HINT_NORMAL; - - impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); - - return impl->hint; -} - -static void -gtk_surface_configure (void *data, - struct gtk_surface1 *gtk_surface, - struct wl_array *states) -{ - GdkSurface *window = GDK_SURFACE (data); - GdkSurfaceImplWayland *impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); - GdkSurfaceState new_state = 0; - uint32_t *p; - - wl_array_for_each (p, states) - { - uint32_t state = *p; - - switch (state) - { - case GTK_SURFACE1_STATE_TILED: - new_state |= GDK_SURFACE_STATE_TILED; - break; - - /* Since v2 */ - case GTK_SURFACE1_STATE_TILED_TOP: - new_state |= (GDK_SURFACE_STATE_TILED | GDK_SURFACE_STATE_TOP_TILED); - break; - case GTK_SURFACE1_STATE_TILED_RIGHT: - new_state |= (GDK_SURFACE_STATE_TILED | GDK_SURFACE_STATE_RIGHT_TILED); - break; - case GTK_SURFACE1_STATE_TILED_BOTTOM: - new_state |= (GDK_SURFACE_STATE_TILED | GDK_SURFACE_STATE_BOTTOM_TILED); - break; - case GTK_SURFACE1_STATE_TILED_LEFT: - new_state |= (GDK_SURFACE_STATE_TILED | GDK_SURFACE_STATE_LEFT_TILED); - break; - default: - /* Unknown state */ - break; - } - } - - impl->pending.state |= new_state; -} - -static void -gtk_surface_configure_edges (void *data, - struct gtk_surface1 *gtk_surface, - struct wl_array *edge_constraints) -{ - GdkSurface *window = GDK_SURFACE (data); - GdkSurfaceImplWayland *impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); - GdkSurfaceState new_state = 0; - uint32_t *p; - - wl_array_for_each (p, edge_constraints) - { - uint32_t constraint = *p; - - switch (constraint) - { - case GTK_SURFACE1_EDGE_CONSTRAINT_RESIZABLE_TOP: - new_state |= GDK_SURFACE_STATE_TOP_RESIZABLE; - break; - case GTK_SURFACE1_EDGE_CONSTRAINT_RESIZABLE_RIGHT: - new_state |= GDK_SURFACE_STATE_RIGHT_RESIZABLE; - break; - case GTK_SURFACE1_EDGE_CONSTRAINT_RESIZABLE_BOTTOM: - new_state |= GDK_SURFACE_STATE_BOTTOM_RESIZABLE; - break; - case GTK_SURFACE1_EDGE_CONSTRAINT_RESIZABLE_LEFT: - new_state |= GDK_SURFACE_STATE_LEFT_RESIZABLE; - break; - default: - /* Unknown state */ - break; - } - } - - impl->pending.state |= new_state; -} - -static const struct gtk_surface1_listener gtk_surface_listener = { - gtk_surface_configure, - gtk_surface_configure_edges -}; - -static void -gdk_wayland_surface_init_gtk_surface (GdkSurface *window) -{ - GdkSurfaceImplWayland *impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); - GdkWaylandDisplay *display = - GDK_WAYLAND_DISPLAY (gdk_surface_get_display (window)); - - if (impl->display_server.gtk_surface != NULL) - return; - if (impl->display_server.xdg_surface == NULL) - return; - if (display->gtk_shell == NULL) - return; - - impl->display_server.gtk_surface = - gtk_shell1_get_gtk_surface (display->gtk_shell, - impl->display_server.wl_surface); - gdk_surface_set_geometry_hints (window, - &impl->geometry_hints, - impl->geometry_mask); - gtk_surface1_add_listener (impl->display_server.gtk_surface, - >k_surface_listener, - window); -} - -static void -maybe_set_gtk_surface_modal (GdkSurface *window) -{ - GdkSurfaceImplWayland *impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); - - gdk_wayland_surface_init_gtk_surface (window); - if (impl->display_server.gtk_surface == NULL) - return; - - if (window->modal_hint) - gtk_surface1_set_modal (impl->display_server.gtk_surface); - else - gtk_surface1_unset_modal (impl->display_server.gtk_surface); - -} - -static void -gdk_wayland_surface_set_modal_hint (GdkSurface *window, - gboolean modal) -{ - window->modal_hint = modal; - maybe_set_gtk_surface_modal (window); -} - -static void -gdk_wayland_surface_set_skip_taskbar_hint (GdkSurface *window, - gboolean skips_taskbar) -{ -} - -static void -gdk_wayland_surface_set_skip_pager_hint (GdkSurface *window, - gboolean skips_pager) -{ -} - -static void -gdk_wayland_surface_set_urgency_hint (GdkSurface *window, - gboolean urgent) -{ -} - -static void -gdk_wayland_surface_set_geometry_hints (GdkSurface *window, - const GdkGeometry *geometry, - GdkSurfaceHints geom_mask) -{ - GdkSurfaceImplWayland *impl; - int width, height; - - if (GDK_SURFACE_DESTROYED (window) || - !WINDOW_IS_TOPLEVEL (window)) - return; - - impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); - - impl->geometry_hints = *geometry; - impl->geometry_mask = geom_mask; - - if (!impl->display_server.xdg_toplevel) - return; - - if (geom_mask & GDK_HINT_MIN_SIZE) - { - width = MAX (0, geometry->min_width - (impl->margin_left + impl->margin_right)); - height = MAX (0, geometry->min_height - (impl->margin_top + impl->margin_bottom)); - } - else - { - width = 0; - height = 0; - } - - zxdg_toplevel_v6_set_min_size (impl->display_server.xdg_toplevel, width, height); - - if (geom_mask & GDK_HINT_MAX_SIZE) - { - width = MAX (0, geometry->max_width - (impl->margin_left + impl->margin_right)); - height = MAX (0, geometry->max_height - (impl->margin_top + impl->margin_bottom)); - } - else - { - width = 0; - height = 0; - } - - zxdg_toplevel_v6_set_max_size (impl->display_server.xdg_toplevel, width, height); -} - -static void -gdk_wayland_surface_set_title (GdkSurface *window, - const gchar *title) -{ - GdkSurfaceImplWayland *impl; - const char *end; - gsize title_length; - - g_return_if_fail (title != NULL); - - if (GDK_SURFACE_DESTROYED (window)) - return; - - impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); - - if (g_strcmp0 (impl->title, title) == 0) - return; - - g_free (impl->title); - - title_length = MIN (strlen (title), MAX_WL_BUFFER_SIZE); - if (g_utf8_validate (title, title_length, &end)) - { - impl->title = g_malloc (end - title + 1); - memcpy (impl->title, title, end - title); - impl->title[end - title] = '\0'; - } - else - { - impl->title = g_utf8_make_valid (title, title_length); - g_warning ("Invalid utf8 passed to gdk_surface_set_title: '%s'", title); - } - - gdk_wayland_surface_sync_title (window); -} - -static void -gdk_wayland_surface_set_role (GdkSurface *window, - const gchar *role) -{ -} - -static void -gdk_wayland_surface_set_startup_id (GdkSurface *window, - const gchar *startup_id) -{ -} - -static gboolean -check_transient_for_loop (GdkSurface *window, - GdkSurface *parent) -{ - while (parent) - { - GdkSurfaceImplWayland *impl; - - if (!GDK_IS_SURFACE_IMPL_WAYLAND(parent->impl)) - return FALSE; - - impl = GDK_SURFACE_IMPL_WAYLAND (parent->impl); - if (impl->transient_for == window) - return TRUE; - parent = impl->transient_for; - } - return FALSE; -} - -static void -gdk_wayland_surface_set_transient_for (GdkSurface *window, - GdkSurface *parent) -{ - GdkSurfaceImplWayland *impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); - GdkWaylandDisplay *display_wayland = - GDK_WAYLAND_DISPLAY (gdk_surface_get_display (window)); - GdkSurface *previous_parent; - - g_assert (parent == NULL || - gdk_surface_get_display (window) == gdk_surface_get_display (parent)); - - if (check_transient_for_loop (window, parent)) - { - g_warning ("Setting %p transient for %p would create a loop", window, parent); - return; - } - - unset_transient_for_exported (window); - - if (impl->display_server.wl_subsurface) - unmap_subsurface (window); - - previous_parent = impl->transient_for; - impl->transient_for = parent; - - if (impl->hint == GDK_SURFACE_TYPE_HINT_DIALOG) - { - if (!parent) - _gdk_wayland_screen_add_orphan_dialog (window); - else if (!previous_parent) - display_wayland->orphan_dialogs = - g_list_remove (display_wayland->orphan_dialogs, window); - } - gdk_wayland_surface_sync_parent (window, NULL); - if (should_map_as_subsurface (window) && - parent && gdk_surface_is_visible (window)) - gdk_wayland_surface_create_subsurface (window); -} - -static void -gdk_wayland_surface_get_frame_extents (GdkSurface *window, - GdkRectangle *rect) -{ - *rect = (GdkRectangle) { - .x = window->x, - .y = window->y, - .width = window->width, - .height = window->height - }; -} - -static void -gdk_wayland_surface_set_accept_focus (GdkSurface *window, - gboolean accept_focus) -{ -} - -static void -gdk_wayland_surface_set_focus_on_map (GdkSurface *window, - gboolean focus_on_map) -{ -} - -static void -gdk_wayland_surface_set_icon_list (GdkSurface *window, - GList *surfaces) -{ -} - -static void -gdk_wayland_surface_set_icon_name (GdkSurface *window, - const gchar *name) -{ - if (GDK_SURFACE_DESTROYED (window)) - return; -} - -static void -gdk_wayland_surface_iconify (GdkSurface *window) -{ - GdkSurfaceImplWayland *impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); - - if (GDK_SURFACE_DESTROYED (window) || - !WINDOW_IS_TOPLEVEL (window)) - return; - - if (!impl->display_server.xdg_toplevel) - return; - - zxdg_toplevel_v6_set_minimized (impl->display_server.xdg_toplevel); -} - -static void -gdk_wayland_surface_deiconify (GdkSurface *window) -{ - if (GDK_SURFACE_DESTROYED (window) || - !WINDOW_IS_TOPLEVEL (window)) - return; - - if (GDK_SURFACE_IS_MAPPED (window)) - gdk_surface_show (window); - else - /* Flip our client side flag, the real work happens on map. */ - gdk_synthesize_window_state (window, GDK_SURFACE_STATE_ICONIFIED, 0); -} - -static void -gdk_wayland_surface_stick (GdkSurface *window) -{ -} - -static void -gdk_wayland_surface_unstick (GdkSurface *window) -{ -} - -static void -gdk_wayland_surface_maximize (GdkSurface *window) -{ - GdkSurfaceImplWayland *impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); - - if (GDK_SURFACE_DESTROYED (window)) - return; - - _gdk_wayland_surface_save_size (window); - if (impl->display_server.xdg_toplevel) - zxdg_toplevel_v6_set_maximized (impl->display_server.xdg_toplevel); - else - gdk_synthesize_window_state (window, 0, GDK_SURFACE_STATE_MAXIMIZED); -} - -static void -gdk_wayland_surface_unmaximize (GdkSurface *window) -{ - GdkSurfaceImplWayland *impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); - - if (GDK_SURFACE_DESTROYED (window)) - return; - - if (impl->display_server.xdg_toplevel) - zxdg_toplevel_v6_unset_maximized (impl->display_server.xdg_toplevel); - else - gdk_synthesize_window_state (window, GDK_SURFACE_STATE_MAXIMIZED, 0); -} - -static void -gdk_wayland_surface_fullscreen_on_monitor (GdkSurface *window, - GdkMonitor *monitor) -{ - GdkSurfaceImplWayland *impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); - struct wl_output *output = ((GdkWaylandMonitor *)monitor)->output; - - if (GDK_SURFACE_DESTROYED (window)) - return; - - _gdk_wayland_surface_save_size (window); - if (impl->display_server.xdg_toplevel) - { - zxdg_toplevel_v6_set_fullscreen (impl->display_server.xdg_toplevel, output); - } - else - { - gdk_synthesize_window_state (window, 0, GDK_SURFACE_STATE_FULLSCREEN); - impl->initial_fullscreen_output = output; - } -} - -static void -gdk_wayland_surface_fullscreen (GdkSurface *window) -{ - GdkSurfaceImplWayland *impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); - - if (GDK_SURFACE_DESTROYED (window)) - return; - - impl->initial_fullscreen_output = NULL; - - _gdk_wayland_surface_save_size (window); - if (impl->display_server.xdg_toplevel) - zxdg_toplevel_v6_set_fullscreen (impl->display_server.xdg_toplevel, NULL); - else - gdk_synthesize_window_state (window, 0, GDK_SURFACE_STATE_FULLSCREEN); -} - -static void -gdk_wayland_surface_unfullscreen (GdkSurface *window) -{ - GdkSurfaceImplWayland *impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); - - if (GDK_SURFACE_DESTROYED (window)) - return; - - impl->initial_fullscreen_output = NULL; - - if (impl->display_server.xdg_toplevel) - zxdg_toplevel_v6_unset_fullscreen (impl->display_server.xdg_toplevel); - else - gdk_synthesize_window_state (window, GDK_SURFACE_STATE_FULLSCREEN, 0); -} - -static void -gdk_wayland_surface_set_keep_above (GdkSurface *window, gboolean setting) -{ -} - -static void -gdk_wayland_surface_set_keep_below (GdkSurface *window, gboolean setting) -{ -} - -static GdkSurface * -gdk_wayland_surface_get_group (GdkSurface *window) -{ - return NULL; -} - -static void -gdk_wayland_surface_set_group (GdkSurface *window, - GdkSurface *leader) -{ -} - -static void -gdk_wayland_surface_set_decorations (GdkSurface *window, - GdkWMDecoration decorations) -{ -} - -static gboolean -gdk_wayland_surface_get_decorations (GdkSurface *window, - GdkWMDecoration *decorations) -{ - return FALSE; -} - -static void -gdk_wayland_surface_set_functions (GdkSurface *window, - GdkWMFunction functions) -{ -} - -static void -gdk_wayland_surface_begin_resize_drag (GdkSurface *window, - GdkSurfaceEdge edge, - GdkDevice *device, - gint button, - gint root_x, - gint root_y, - guint32 timestamp) -{ - GdkSurfaceImplWayland *impl; - GdkEventSequence *sequence; - uint32_t resize_edges, serial; - - if (GDK_SURFACE_DESTROYED (window) || - !WINDOW_IS_TOPLEVEL (window)) - return; - - switch (edge) - { - case GDK_SURFACE_EDGE_NORTH_WEST: - resize_edges = ZXDG_TOPLEVEL_V6_RESIZE_EDGE_TOP_LEFT; - break; - - case GDK_SURFACE_EDGE_NORTH: - resize_edges = ZXDG_TOPLEVEL_V6_RESIZE_EDGE_TOP; - break; - - case GDK_SURFACE_EDGE_NORTH_EAST: - resize_edges = ZXDG_TOPLEVEL_V6_RESIZE_EDGE_TOP_RIGHT; - break; - - case GDK_SURFACE_EDGE_WEST: - resize_edges = ZXDG_TOPLEVEL_V6_RESIZE_EDGE_LEFT; - break; - - case GDK_SURFACE_EDGE_EAST: - resize_edges = ZXDG_TOPLEVEL_V6_RESIZE_EDGE_RIGHT; - break; - - case GDK_SURFACE_EDGE_SOUTH_WEST: - resize_edges = ZXDG_TOPLEVEL_V6_RESIZE_EDGE_BOTTOM_LEFT; - break; - - case GDK_SURFACE_EDGE_SOUTH: - resize_edges = ZXDG_TOPLEVEL_V6_RESIZE_EDGE_BOTTOM; - break; - - case GDK_SURFACE_EDGE_SOUTH_EAST: - resize_edges = ZXDG_TOPLEVEL_V6_RESIZE_EDGE_BOTTOM_RIGHT; - break; - - default: - g_warning ("gdk_surface_begin_resize_drag: bad resize edge %d!", edge); - return; - } - - impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); - - if (!impl->display_server.xdg_toplevel) - return; - - serial = _gdk_wayland_seat_get_last_implicit_grab_serial (gdk_device_get_seat (device), - &sequence); - - zxdg_toplevel_v6_resize (impl->display_server.xdg_toplevel, - gdk_wayland_device_get_wl_seat (device), - serial, resize_edges); - - if (sequence) - gdk_wayland_device_unset_touch_grab (device, sequence); - - /* This is needed since Wayland will absorb all the pointer events after the - * above function - FIXME: Is this always safe..? - */ - gdk_seat_ungrab (gdk_device_get_seat (device)); -} - -static void -gdk_wayland_surface_begin_move_drag (GdkSurface *window, - GdkDevice *device, - gint button, - gint root_x, - gint root_y, - guint32 timestamp) -{ - GdkSurfaceImplWayland *impl; - GdkEventSequence *sequence; - uint32_t serial; - - if (GDK_SURFACE_DESTROYED (window) || - !WINDOW_IS_TOPLEVEL (window)) - return; - - impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); - - if (!impl->display_server.xdg_toplevel) - return; - - serial = _gdk_wayland_seat_get_last_implicit_grab_serial (gdk_device_get_seat (device), - &sequence); - zxdg_toplevel_v6_move (impl->display_server.xdg_toplevel, - gdk_wayland_device_get_wl_seat (device), - serial); - if (sequence) - gdk_wayland_device_unset_touch_grab (device, sequence); - - /* This is needed since Wayland will absorb all the pointer events after the - * above function - FIXME: Is this always safe..? - */ - gdk_seat_ungrab (gdk_device_get_seat (device)); -} - -static void -gdk_wayland_surface_set_opacity (GdkSurface *window, - gdouble opacity) -{ -} - -static void -gdk_wayland_surface_destroy_notify (GdkSurface *window) -{ - if (!GDK_SURFACE_DESTROYED (window)) - { - g_warning ("GdkSurface %p unexpectedly destroyed", window); - _gdk_surface_destroy (window, TRUE); - } - - g_object_unref (window); -} - -static gint -gdk_wayland_surface_get_scale_factor (GdkSurface *window) -{ - GdkSurfaceImplWayland *impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); - - if (GDK_SURFACE_DESTROYED (window)) - return 1; - - return impl->scale; -} - -static void -gdk_wayland_surface_set_opaque_region (GdkSurface *window, - cairo_region_t *region) -{ - GdkSurfaceImplWayland *impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); - - if (GDK_SURFACE_DESTROYED (window)) - return; - - g_clear_pointer (&impl->opaque_region, cairo_region_destroy); - impl->opaque_region = cairo_region_reference (region); - impl->opaque_region_dirty = TRUE; -} - -static void -gdk_wayland_surface_set_shadow_width (GdkSurface *window, - int left, - int right, - int top, - int bottom) -{ - GdkSurfaceImplWayland *impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); - gint new_width, new_height; - - if (GDK_SURFACE_DESTROYED (window)) - return; - - /* Reconfigure window to keep the same window geometry */ - new_width = window->width - - (impl->margin_left + impl->margin_right) + (left + right); - new_height = window->height - - (impl->margin_top + impl->margin_bottom) + (top + bottom); - gdk_wayland_surface_maybe_configure (window, new_width, new_height, impl->scale); - - impl->margin_left = left; - impl->margin_right = right; - impl->margin_top = top; - impl->margin_bottom = bottom; -} - -static gboolean -gdk_wayland_surface_show_window_menu (GdkSurface *window, - GdkEvent *event) -{ - GdkSurfaceImplWayland *impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); - struct wl_seat *seat; - GdkWaylandDevice *device; - double x, y; - uint32_t serial; - - switch ((guint) event->any.type) - { - case GDK_BUTTON_PRESS: - case GDK_BUTTON_RELEASE: - case GDK_TOUCH_BEGIN: - case GDK_TOUCH_END: - break; - default: - return FALSE; - } - - if (!impl->display_server.xdg_surface) - return FALSE; - - device = GDK_WAYLAND_DEVICE (gdk_event_get_device (event)); - seat = gdk_wayland_device_get_wl_seat (GDK_DEVICE (device)); - gdk_event_get_coords (event, &x, &y); - - serial = _gdk_wayland_device_get_implicit_grab_serial (device, event); - zxdg_toplevel_v6_show_window_menu (impl->display_server.xdg_toplevel, - seat, serial, x, y); - return TRUE; -} - -static gboolean -gdk_wayland_surface_supports_edge_constraints (GdkSurface *window) -{ - GdkSurfaceImplWayland *impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); - struct gtk_surface1 *gtk_surface = impl->display_server.gtk_surface; - - if (!gtk_surface) - return FALSE; - - return gtk_surface1_get_version (gtk_surface) >= GTK_SURFACE1_CONFIGURE_EDGES_SINCE_VERSION; -} - -static void -_gdk_surface_impl_wayland_class_init (GdkSurfaceImplWaylandClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - GdkSurfaceImplClass *impl_class = GDK_SURFACE_IMPL_CLASS (klass); - - object_class->finalize = gdk_surface_impl_wayland_finalize; - - impl_class->ref_cairo_surface = gdk_wayland_surface_ref_cairo_surface; - impl_class->create_similar_image_surface = gdk_wayland_surface_create_similar_image_surface; - impl_class->show = gdk_wayland_surface_show; - impl_class->hide = gdk_wayland_surface_hide; - impl_class->withdraw = gdk_surface_wayland_withdraw; - impl_class->set_events = gdk_surface_wayland_set_events; - impl_class->get_events = gdk_surface_wayland_get_events; - impl_class->raise = gdk_surface_wayland_raise; - impl_class->lower = gdk_surface_wayland_lower; - impl_class->restack_toplevel = gdk_surface_wayland_restack_toplevel; - impl_class->move_resize = gdk_surface_wayland_move_resize; - impl_class->move_to_rect = gdk_surface_wayland_move_to_rect; - impl_class->get_geometry = gdk_surface_wayland_get_geometry; - impl_class->get_root_coords = gdk_surface_wayland_get_root_coords; - impl_class->get_device_state = gdk_surface_wayland_get_device_state; - impl_class->shape_combine_region = gdk_surface_wayland_shape_combine_region; - impl_class->input_shape_combine_region = gdk_surface_wayland_input_shape_combine_region; - impl_class->destroy = gdk_wayland_surface_destroy; - impl_class->begin_paint = gdk_surface_impl_wayland_begin_paint; - impl_class->end_paint = gdk_surface_impl_wayland_end_paint; - impl_class->beep = gdk_surface_impl_wayland_beep; - - impl_class->focus = gdk_wayland_surface_focus; - impl_class->set_type_hint = gdk_wayland_surface_set_type_hint; - impl_class->get_type_hint = gdk_wayland_surface_get_type_hint; - impl_class->set_modal_hint = gdk_wayland_surface_set_modal_hint; - impl_class->set_skip_taskbar_hint = gdk_wayland_surface_set_skip_taskbar_hint; - impl_class->set_skip_pager_hint = gdk_wayland_surface_set_skip_pager_hint; - impl_class->set_urgency_hint = gdk_wayland_surface_set_urgency_hint; - impl_class->set_geometry_hints = gdk_wayland_surface_set_geometry_hints; - impl_class->set_title = gdk_wayland_surface_set_title; - impl_class->set_role = gdk_wayland_surface_set_role; - impl_class->set_startup_id = gdk_wayland_surface_set_startup_id; - impl_class->set_transient_for = gdk_wayland_surface_set_transient_for; - impl_class->get_frame_extents = gdk_wayland_surface_get_frame_extents; - impl_class->set_accept_focus = gdk_wayland_surface_set_accept_focus; - impl_class->set_focus_on_map = gdk_wayland_surface_set_focus_on_map; - impl_class->set_icon_list = gdk_wayland_surface_set_icon_list; - impl_class->set_icon_name = gdk_wayland_surface_set_icon_name; - impl_class->iconify = gdk_wayland_surface_iconify; - impl_class->deiconify = gdk_wayland_surface_deiconify; - impl_class->stick = gdk_wayland_surface_stick; - impl_class->unstick = gdk_wayland_surface_unstick; - impl_class->maximize = gdk_wayland_surface_maximize; - impl_class->unmaximize = gdk_wayland_surface_unmaximize; - impl_class->fullscreen = gdk_wayland_surface_fullscreen; - impl_class->fullscreen_on_monitor = gdk_wayland_surface_fullscreen_on_monitor; - impl_class->unfullscreen = gdk_wayland_surface_unfullscreen; - impl_class->set_keep_above = gdk_wayland_surface_set_keep_above; - impl_class->set_keep_below = gdk_wayland_surface_set_keep_below; - impl_class->get_group = gdk_wayland_surface_get_group; - impl_class->set_group = gdk_wayland_surface_set_group; - impl_class->set_decorations = gdk_wayland_surface_set_decorations; - impl_class->get_decorations = gdk_wayland_surface_get_decorations; - impl_class->set_functions = gdk_wayland_surface_set_functions; - impl_class->begin_resize_drag = gdk_wayland_surface_begin_resize_drag; - impl_class->begin_move_drag = gdk_wayland_surface_begin_move_drag; - impl_class->set_opacity = gdk_wayland_surface_set_opacity; - impl_class->destroy_notify = gdk_wayland_surface_destroy_notify; - impl_class->register_dnd = _gdk_wayland_surface_register_dnd; - impl_class->drag_begin = _gdk_wayland_surface_drag_begin; - impl_class->get_scale_factor = gdk_wayland_surface_get_scale_factor; - impl_class->set_opaque_region = gdk_wayland_surface_set_opaque_region; - impl_class->set_shadow_width = gdk_wayland_surface_set_shadow_width; - impl_class->show_window_menu = gdk_wayland_surface_show_window_menu; - impl_class->create_gl_context = gdk_wayland_surface_create_gl_context; - impl_class->supports_edge_constraints = gdk_wayland_surface_supports_edge_constraints; - - signals[COMMITTED] = g_signal_new (g_intern_static_string ("committed"), - G_TYPE_FROM_CLASS (object_class), - G_SIGNAL_RUN_LAST, - 0, - NULL, NULL, NULL, - G_TYPE_NONE, 0); -} - -void -_gdk_wayland_surface_set_grab_seat (GdkSurface *window, - GdkSeat *seat) -{ - GdkSurfaceImplWayland *impl; - - g_return_if_fail (window != NULL); - - impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); - impl->grab_input_seat = seat; -} - -/** - * gdk_wayland_surface_new_subsurface: (constructor) - * @display: the display to create the window on - * @position: position relative to the transient window - * - * Creates a new subsurface window. - * - * Returns: (transfer full): the new #GdkSurface - **/ -GdkSurface * -gdk_wayland_surface_new_subsurface (GdkDisplay *display, - const GdkRectangle *position) -{ - GdkSurfaceAttr attr; - - g_return_val_if_fail (GDK_IS_DISPLAY (display), NULL); - g_return_val_if_fail (position != NULL, NULL); - - attr.wclass = GDK_INPUT_OUTPUT; - attr.x = position->x; - attr.y = position->y; - attr.width = position->width; - attr.height = position->height; - attr.window_type = GDK_SURFACE_SUBSURFACE; - - return gdk_surface_new (display, NULL, &attr); -} - -/** - * gdk_wayland_surface_get_wl_surface: - * @window: (type GdkWaylandSurface): a #GdkSurface - * - * Returns the Wayland surface of a #GdkSurface. - * - * Returns: (transfer none): a Wayland wl_surface - */ -struct wl_surface * -gdk_wayland_surface_get_wl_surface (GdkSurface *window) -{ - g_return_val_if_fail (GDK_IS_WAYLAND_SURFACE (window), NULL); - - return GDK_SURFACE_IMPL_WAYLAND (window->impl)->display_server.wl_surface; -} - -struct wl_output * -gdk_wayland_surface_get_wl_output (GdkSurface *window) -{ - GdkSurfaceImplWayland *impl; - - g_return_val_if_fail (GDK_IS_WAYLAND_SURFACE (window), NULL); - - impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); - /* We pick the head of the list as this is the last entered output */ - if (impl->display_server.outputs) - return (struct wl_output *) impl->display_server.outputs->data; - - return NULL; -} - -static struct wl_egl_window * -gdk_wayland_surface_get_wl_egl_window (GdkSurface *window) -{ - GdkSurfaceImplWayland *impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); - - if (impl->display_server.egl_window == NULL) - { - impl->display_server.egl_window = - wl_egl_window_create (impl->display_server.wl_surface, - impl->wrapper->width * impl->scale, - impl->wrapper->height * impl->scale); - wl_surface_set_buffer_scale (impl->display_server.wl_surface, impl->scale); - } - - return impl->display_server.egl_window; -} - -EGLSurface -gdk_wayland_surface_get_egl_surface (GdkSurface *window, - EGLConfig config) -{ - GdkWaylandDisplay *display = GDK_WAYLAND_DISPLAY (gdk_surface_get_display (window)); - GdkSurfaceImplWayland *impl; - struct wl_egl_window *egl_window; - - g_return_val_if_fail (GDK_IS_WAYLAND_SURFACE (window), NULL); - - impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); - - if (impl->egl_surface == NULL) - { - egl_window = gdk_wayland_surface_get_wl_egl_window (window); - - impl->egl_surface = - eglCreateWindowSurface (display->egl_display, config, egl_window, NULL); - } - - return impl->egl_surface; -} - -EGLSurface -gdk_wayland_surface_get_dummy_egl_surface (GdkSurface *window, - EGLConfig config) -{ - GdkWaylandDisplay *display = GDK_WAYLAND_DISPLAY (gdk_surface_get_display (window)); - GdkSurfaceImplWayland *impl; - - g_return_val_if_fail (GDK_IS_WAYLAND_SURFACE (window), NULL); - - impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); - - if (impl->dummy_egl_surface == NULL) - { - impl->display_server.dummy_egl_window = - wl_egl_window_create (impl->display_server.wl_surface, 1, 1); - - impl->dummy_egl_surface = - eglCreateWindowSurface (display->egl_display, config, impl->display_server.dummy_egl_window, NULL); - } - - return impl->dummy_egl_surface; -} - -struct gtk_surface1 * -gdk_wayland_surface_get_gtk_surface (GdkSurface *window) -{ - g_return_val_if_fail (GDK_IS_WAYLAND_SURFACE (window), NULL); - - return GDK_SURFACE_IMPL_WAYLAND (window->impl)->display_server.gtk_surface; -} - -/** - * gdk_wayland_surface_set_use_custom_surface: - * @window: (type GdkWaylandSurface): a #GdkSurface - * - * Marks a #GdkSurface as a custom Wayland surface. The application is - * expected to register the surface as some type of surface using - * some Wayland interface. - * - * A good example would be writing a panel or on-screen-keyboard as an - * out-of-process helper - as opposed to having those in the compositor - * process. In this case the underlying surface isn’t an xdg_shell - * surface and the panel or OSK client need to identify the wl_surface - * as a panel or OSK to the compositor. The assumption is that the - * compositor will expose a private interface to the special client - * that lets the client identify the wl_surface as a panel or such. - * - * This function should be called before a #GdkSurface is shown. This is - * best done by connecting to the #GtkWidget::realize signal: - * - * |[ - * static void - * widget_realize_cb (GtkWidget *widget) - * { - * GdkSurface *window; - * struct wl_surface *surface; - * struct input_panel_surface *ip_surface; - * - * window = gtk_widget_get_window (widget); - * gdk_wayland_surface_set_custom_surface (window); - * - * surface = gdk_wayland_surface_get_wl_surface (window); - * ip_surface = input_panel_get_input_panel_surface (input_panel, surface); - * input_panel_surface_set_panel (ip_surface); - * } - * - * static void - * setup_window (GtkWindow *window) - * { - * g_signal_connect (window, "realize", G_CALLBACK (widget_realize_cb), NULL); - * } - * ]| - */ -void -gdk_wayland_surface_set_use_custom_surface (GdkSurface *window) -{ - GdkSurfaceImplWayland *impl; - - g_return_if_fail (GDK_IS_WAYLAND_SURFACE (window)); - - impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); - - if (!impl->display_server.wl_surface) - gdk_wayland_surface_create_surface (window); - - impl->use_custom_surface = TRUE; -} - -static void -maybe_set_gtk_surface_dbus_properties (GdkSurface *window) -{ - GdkSurfaceImplWayland *impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); - - if (impl->application.was_set) - return; - - if (impl->application.application_id == NULL && - impl->application.app_menu_path == NULL && - impl->application.menubar_path == NULL && - impl->application.window_object_path == NULL && - impl->application.application_object_path == NULL && - impl->application.unique_bus_name == NULL) - return; - - gdk_wayland_surface_init_gtk_surface (window); - if (impl->display_server.gtk_surface == NULL) - return; - - gtk_surface1_set_dbus_properties (impl->display_server.gtk_surface, - impl->application.application_id, - impl->application.app_menu_path, - impl->application.menubar_path, - impl->application.window_object_path, - impl->application.application_object_path, - impl->application.unique_bus_name); - impl->application.was_set = TRUE; -} - -void -gdk_wayland_surface_set_dbus_properties_libgtk_only (GdkSurface *window, - const char *application_id, - const char *app_menu_path, - const char *menubar_path, - const char *window_object_path, - const char *application_object_path, - const char *unique_bus_name) -{ - GdkSurfaceImplWayland *impl; - - g_return_if_fail (GDK_IS_WAYLAND_SURFACE (window)); - - impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); - - impl->application.application_id = g_strdup (application_id); - impl->application.app_menu_path = g_strdup (app_menu_path); - impl->application.menubar_path = g_strdup (menubar_path); - impl->application.window_object_path = g_strdup (window_object_path); - impl->application.application_object_path = - g_strdup (application_object_path); - impl->application.unique_bus_name = g_strdup (unique_bus_name); - - maybe_set_gtk_surface_dbus_properties (window); -} - -void -_gdk_wayland_surface_offset_next_wl_buffer (GdkSurface *window, - int x, - int y) -{ - GdkSurfaceImplWayland *impl; - - g_return_if_fail (GDK_IS_WAYLAND_SURFACE (window)); - - impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); - - impl->pending_buffer_offset_x = x; - impl->pending_buffer_offset_y = y; -} - -static void -xdg_exported_handle (void *data, - struct zxdg_exported_v1 *zxdg_exported_v1, - const char *handle) -{ - GdkSurface *window = data; - GdkSurfaceImplWayland *impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); - - impl->exported.callback (window, handle, impl->exported.user_data); - g_clear_pointer (&impl->exported.user_data, - impl->exported.destroy_func); -} - -static const struct zxdg_exported_v1_listener xdg_exported_listener = { - xdg_exported_handle -}; - -/** - * GdkWaylandSurfaceExported: - * @window: the #GdkSurface that is exported - * @handle: the handle - * @user_data: user data that was passed to gdk_wayland_surface_export_handle() - * - * Callback that gets called when the handle for a window has been - * obtained from the Wayland compositor. The handle can be passed - * to other processes, for the purpose of marking windows as transient - * for out-of-process surfaces. - */ - -static gboolean -gdk_wayland_surface_is_exported (GdkSurface *window) -{ - GdkSurfaceImplWayland *impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); - - return !!impl->display_server.xdg_exported; -} - -/** - * gdk_wayland_surface_export_handle: - * @window: the #GdkSurface to obtain a handle for - * @callback: callback to call with the handle - * @user_data: user data for @callback - * @destroy_func: destroy notify for @user_data - * - * Asynchronously obtains a handle for a surface that can be passed - * to other processes. When the handle has been obtained, @callback - * will be called. - * - * It is an error to call this function on a window that is already - * exported. - * - * When the handle is no longer needed, gdk_wayland_surface_unexport_handle() - * should be called to clean up resources. - * - * The main purpose for obtaining a handle is to mark a surface - * from another window as transient for this one, see - * gdk_wayland_surface_set_transient_for_exported(). - * - * Note that this API depends on an unstable Wayland protocol, - * and thus may require changes in the future. - * - * Return value: %TRUE if the handle has been requested, %FALSE if - * an error occurred. - */ -gboolean -gdk_wayland_surface_export_handle (GdkSurface *window, - GdkWaylandSurfaceExported callback, - gpointer user_data, - GDestroyNotify destroy_func) -{ - GdkSurfaceImplWayland *impl; - GdkWaylandDisplay *display_wayland; - GdkDisplay *display = gdk_surface_get_display (window); - struct zxdg_exported_v1 *xdg_exported; - - g_return_val_if_fail (GDK_IS_WAYLAND_SURFACE (window), FALSE); - g_return_val_if_fail (GDK_IS_WAYLAND_DISPLAY (display), FALSE); - - impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); - display_wayland = GDK_WAYLAND_DISPLAY (display); - - g_return_val_if_fail (!impl->display_server.xdg_exported, FALSE); - - if (!display_wayland->xdg_exporter) - { - g_warning ("Server is missing xdg_foreign support"); - return FALSE; - } - - xdg_exported = zxdg_exporter_v1_export (display_wayland->xdg_exporter, - impl->display_server.wl_surface); - zxdg_exported_v1_add_listener (xdg_exported, &xdg_exported_listener, window); - - impl->display_server.xdg_exported = xdg_exported; - impl->exported.callback = callback; - impl->exported.user_data = user_data; - impl->exported.destroy_func = destroy_func; - - return TRUE; -} - -/** - * gdk_wayland_surface_unexport_handle: - * @window: the #GdkSurface to unexport - * - * Destroys the handle that was obtained with - * gdk_wayland_surface_export_handle(). - * - * It is an error to call this function on a window that - * does not have a handle. - * - * Note that this API depends on an unstable Wayland protocol, - * and thus may require changes in the future. - */ -void -gdk_wayland_surface_unexport_handle (GdkSurface *window) -{ - GdkSurfaceImplWayland *impl; - - g_return_if_fail (GDK_IS_WAYLAND_SURFACE (window)); - - impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); - - g_return_if_fail (impl->display_server.xdg_exported); - - g_clear_pointer (&impl->display_server.xdg_exported, - zxdg_exported_v1_destroy); - g_clear_pointer (&impl->exported.user_data, - impl->exported.destroy_func); -} - -static void -unset_transient_for_exported (GdkSurface *window) -{ - GdkSurfaceImplWayland *impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); - - g_clear_pointer (&impl->imported_transient_for, zxdg_imported_v1_destroy); -} - -static void -xdg_imported_destroyed (void *data, - struct zxdg_imported_v1 *zxdg_imported_v1) -{ - GdkSurface *window = data; - - unset_transient_for_exported (window); -} - -static const struct zxdg_imported_v1_listener xdg_imported_listener = { - xdg_imported_destroyed, -}; - -/** - * gdk_wayland_surface_set_transient_for_exported: - * @window: the #GdkSurface to make as transient - * @parent_handle_str: an exported handle for a surface - * - * Marks @window as transient for the surface to which the given - * @parent_handle_str refers. Typically, the handle will originate - * from a gdk_wayland_surface_export_handle() call in another process. - * - * Note that this API depends on an unstable Wayland protocol, - * and thus may require changes in the future. - * - * Return value: %TRUE if the window has been marked as transient, - * %FALSE if an error occurred. - */ -gboolean -gdk_wayland_surface_set_transient_for_exported (GdkSurface *window, - char *parent_handle_str) -{ - GdkSurfaceImplWayland *impl; - GdkWaylandDisplay *display_wayland; - GdkDisplay *display = gdk_surface_get_display (window); - - g_return_val_if_fail (GDK_IS_WAYLAND_SURFACE (window), FALSE); - g_return_val_if_fail (GDK_IS_WAYLAND_DISPLAY (display), FALSE); - g_return_val_if_fail (!should_map_as_subsurface (window) && - !should_map_as_popup (window), FALSE); - - impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); - display_wayland = GDK_WAYLAND_DISPLAY (display); - - if (!display_wayland->xdg_importer) - { - g_warning ("Server is missing xdg_foreign support"); - return FALSE; - } - - gdk_surface_set_transient_for (window, NULL); - - impl->imported_transient_for = - zxdg_importer_v1_import (display_wayland->xdg_importer, parent_handle_str); - zxdg_imported_v1_add_listener (impl->imported_transient_for, - &xdg_imported_listener, - window); - - gdk_wayland_surface_sync_parent_of_imported (window); - - return TRUE; -} - -static struct zwp_keyboard_shortcuts_inhibitor_v1 * -gdk_wayland_surface_get_inhibitor (GdkSurfaceImplWayland *impl, - struct wl_seat *seat) -{ - return g_hash_table_lookup (impl->shortcuts_inhibitors, seat); -} - -void -gdk_wayland_surface_inhibit_shortcuts (GdkSurface *window, - GdkSeat *gdk_seat) -{ - GdkSurfaceImplWayland *impl= GDK_SURFACE_IMPL_WAYLAND (window->impl); - GdkWaylandDisplay *display = GDK_WAYLAND_DISPLAY (gdk_surface_get_display (window)); - struct wl_surface *surface = impl->display_server.wl_surface; - struct wl_seat *seat = gdk_wayland_seat_get_wl_seat (gdk_seat); - struct zwp_keyboard_shortcuts_inhibitor_v1 *inhibitor; - - if (display->keyboard_shortcuts_inhibit == NULL) - return; - - if (gdk_wayland_surface_get_inhibitor (impl, seat)) - return; /* Already inhibitted */ - - inhibitor = - zwp_keyboard_shortcuts_inhibit_manager_v1_inhibit_shortcuts ( - display->keyboard_shortcuts_inhibit, surface, seat); - - g_hash_table_insert (impl->shortcuts_inhibitors, seat, inhibitor); -} - -void -gdk_wayland_surface_restore_shortcuts (GdkSurface *window, - GdkSeat *gdk_seat) -{ - GdkSurfaceImplWayland *impl = GDK_SURFACE_IMPL_WAYLAND (window->impl); - struct wl_seat *seat = gdk_wayland_seat_get_wl_seat (gdk_seat); - struct zwp_keyboard_shortcuts_inhibitor_v1 *inhibitor; - - inhibitor = gdk_wayland_surface_get_inhibitor (impl, seat); - if (inhibitor == NULL) - return; /* Not inhibitted */ - - zwp_keyboard_shortcuts_inhibitor_v1_destroy (inhibitor); - g_hash_table_remove (impl->shortcuts_inhibitors, seat); -} - diff --git a/gdk/wayland/meson.build b/gdk/wayland/meson.build index fec3d2c74e..6390e57c19 100644 --- a/gdk/wayland/meson.build +++ b/gdk/wayland/meson.build @@ -12,7 +12,7 @@ gdk_wayland_sources = files([ 'gdkprimary-wayland.c', 'gdkselection-wayland.c', 'gdkvulkancontext-wayland.c', - 'gdkwindow-wayland.c', + 'gdksurface-wayland.c', 'wm-button-layout-translation.c', ]) @@ -21,7 +21,7 @@ gdk_wayland_public_headers = files([ 'gdkwaylanddisplay.h', 'gdkwaylandglcontext.h', 'gdkwaylandmonitor.h', - 'gdkwaylandwindow.h' + 'gdkwaylandsurface.h' ]) install_headers(gdk_wayland_public_headers, subdir: 'gtk-4.0/gdk/wayland/') diff --git a/gdk/win32/gdkcursor-win32.c b/gdk/win32/gdkcursor-win32.c index adeec0d8b0..8e83cfae30 100644 --- a/gdk/win32/gdkcursor-win32.c +++ b/gdk/win32/gdkcursor-win32.c @@ -934,7 +934,7 @@ _gdk_win32_display_get_maximal_cursor_size (GdkDisplay *display, /* Convert a pixbuf to an HICON (or HCURSOR). Supports alpha under * Windows XP, thresholds alpha otherwise. Also used from - * gdkwindow-win32.c for creating application icons. + * gdksurface-win32.c for creating application icons. */ static HBITMAP diff --git a/gdk/win32/gdkdevice-virtual.c b/gdk/win32/gdkdevice-virtual.c index f5658e137b..95f6fdd871 100644 --- a/gdk/win32/gdkdevice-virtual.c +++ b/gdk/win32/gdkdevice-virtual.c @@ -17,7 +17,7 @@ #include "config.h" -#include +#include #include #include diff --git a/gdk/win32/gdkdevice-win32.c b/gdk/win32/gdkdevice-win32.c index 580506a971..86218dc02c 100644 --- a/gdk/win32/gdkdevice-win32.c +++ b/gdk/win32/gdkdevice-win32.c @@ -17,7 +17,7 @@ #include "config.h" -#include +#include #include #include diff --git a/gdk/win32/gdkdevice-wintab.c b/gdk/win32/gdkdevice-wintab.c index a8a41785c7..73bf2b1773 100644 --- a/gdk/win32/gdkdevice-wintab.c +++ b/gdk/win32/gdkdevice-wintab.c @@ -17,7 +17,7 @@ #include "config.h" -#include +#include #include #include diff --git a/gdk/win32/gdkdisplay-win32.c b/gdk/win32/gdkdisplay-win32.c index 47891e6a0f..e546e4b1be 100644 --- a/gdk/win32/gdkdisplay-win32.c +++ b/gdk/win32/gdkdisplay-win32.c @@ -28,7 +28,7 @@ #include "gdkglcontext-win32.h" #include "gdkwin32display.h" #include "gdkwin32screen.h" -#include "gdkwin32window.h" +#include "gdkwin32surface.h" #include "gdkmonitor-win32.h" #include "gdkwin32.h" #include "gdkvulkancontext-win32.h" diff --git a/gdk/win32/gdkglcontext-win32.c b/gdk/win32/gdkglcontext-win32.c index d406080968..ed3db37eac 100644 --- a/gdk/win32/gdkglcontext-win32.c +++ b/gdk/win32/gdkglcontext-win32.c @@ -23,7 +23,7 @@ #include "config.h" #include "gdkprivate-win32.h" -#include "gdkwindow-win32.h" +#include "gdksurface-win32.h" #include "gdkglcontext-win32.h" #include "gdkdisplay-win32.h" @@ -31,10 +31,10 @@ #include "gdkwin32glcontext.h" #include "gdkwin32misc.h" #include "gdkwin32screen.h" -#include "gdkwin32window.h" +#include "gdkwin32surface.h" #include "gdkglcontext.h" -#include "gdkwindow.h" +#include "gdksurface.h" #include "gdkinternals.h" #include "gdkintl.h" diff --git a/gdk/win32/gdkglcontext-win32.h b/gdk/win32/gdkglcontext-win32.h index 77b656592a..8549679559 100644 --- a/gdk/win32/gdkglcontext-win32.h +++ b/gdk/win32/gdkglcontext-win32.h @@ -26,7 +26,7 @@ #include "gdkglcontextprivate.h" #include "gdkdisplayprivate.h" -#include "gdkwindow.h" +#include "gdksurface.h" #include "gdkinternals.h" G_BEGIN_DECLS diff --git a/gdk/win32/gdkprivate-win32.h b/gdk/win32/gdkprivate-win32.h index 8249a60b65..f22fa69634 100644 --- a/gdk/win32/gdkprivate-win32.h +++ b/gdk/win32/gdkprivate-win32.h @@ -35,7 +35,7 @@ #endif #include -#include +#include #include #include #include diff --git a/gdk/win32/gdksurface-win32.c b/gdk/win32/gdksurface-win32.c new file mode 100644 index 0000000000..f41534f451 --- /dev/null +++ b/gdk/win32/gdksurface-win32.c @@ -0,0 +1,5959 @@ +/* GDK - The GIMP Drawing Kit + * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald + * Copyright (C) 1998-2004 Tor Lillqvist + * Copyright (C) 2001-2011 Hans Breuer + * Copyright (C) 2007-2009 Cody Russell + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * 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, see . + */ + +/* + * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS + * file for a list of people on the GTK+ Team. See the ChangeLog + * files for a list of changes. These files are distributed with + * GTK+ at ftp://ftp.gtk.org/pub/gtk/. + */ + +#include "config.h" +#include + +#include "gdk.h" +#include "gdksurfaceimpl.h" +#include "gdkprivate-win32.h" +#include "gdkdeviceprivate.h" +#include "gdkdevicemanager-win32.h" +#include "gdkenumtypes.h" +#include "gdkwin32.h" +#include "gdkdisplayprivate.h" +#include "gdkmonitorprivate.h" +#include "gdkwin32surface.h" +#include "gdkglcontext-win32.h" +#include "gdkdisplay-win32.h" + +#include +#include +#include +#include "fallback-c89.c" + +static void gdk_surface_impl_win32_init (GdkSurfaceImplWin32 *window); +static void gdk_surface_impl_win32_class_init (GdkSurfaceImplWin32Class *klass); +static void gdk_surface_impl_win32_finalize (GObject *object); + +static gpointer parent_class = NULL; +static GSList *modal_window_stack = NULL; + +static const cairo_user_data_key_t gdk_win32_cairo_key; +typedef struct _FullscreenInfo FullscreenInfo; + +struct _FullscreenInfo +{ + RECT r; + guint hint_flags; + LONG style; +}; + +struct _AeroSnapEdgeRegion +{ + /* The rectangle along the edge of the desktop + * that allows application of the snap transformation. + */ + GdkRectangle edge; + + /* A subregion of the "edge". When the pointer hits + * this region, the transformation is revealed. + * Usually it is 1-pixel thick and is located at the + * very edge of the screen. When there's a toolbar + * at that edge, the "trigger" and the "edge" regions + * are extended to cover that toolbar. + */ + GdkRectangle trigger; +}; + +typedef struct _AeroSnapEdgeRegion AeroSnapEdgeRegion; + +/* Use this for hWndInsertAfter (2nd argument to SetWindowPos()) if + * SWP_NOZORDER flag is used. Otherwise it's unobvious why a particular + * argument is used. Using NULL is misleading, because + * NULL is equivalent to HWND_TOP. + */ +#define SWP_NOZORDER_SPECIFIED HWND_TOP + +/* Size of the regions at the edges of the desktop where + * snapping can take place (in pixels) + */ +#define AEROSNAP_REGION_THICKNESS (20) +/* Size of the subregions that actually trigger the snapping prompt + * (in pixels). + */ +#define AEROSNAP_REGION_TRIGGER_THICKNESS (1) + +/* The gap between the snap indicator and the edge of the work area + * (in pixels). + */ +#define AEROSNAP_INDICATOR_EDGE_GAP (10) + +/* Width of the outline of the snap indicator + * (in pixels). + */ +#define AEROSNAP_INDICATOR_LINE_WIDTH (3.0) + +/* Corner radius of the snap indicator. + */ +#define AEROSNAP_INDICATOR_CORNER_RADIUS (3.0) + +/* The time it takes for snap indicator to expand/shrink + * from current window size to future position of the + * snapped window (in microseconds). + */ +#define AEROSNAP_INDICATOR_ANIMATION_DURATION (200 * 1000) + +/* Opacity if the snap indicator. */ +#define AEROSNAP_INDICATOR_OPACITY (0.5) + +/* The interval between snap indicator redraws (in milliseconds). + * 16 is ~ 1/60 of a second, for ~60 FPS. + */ +#define AEROSNAP_INDICATOR_ANIMATION_TICK (16) + +static gboolean _gdk_surface_get_functions (GdkSurface *window, + GdkWMFunction *functions); +static HDC _gdk_win32_impl_acquire_dc (GdkSurfaceImplWin32 *impl); +static void _gdk_win32_impl_release_dc (GdkSurfaceImplWin32 *impl); + +#define WINDOW_IS_TOPLEVEL(window) \ + (GDK_SURFACE_TYPE (window) != GDK_SURFACE_FOREIGN) + +struct _GdkWin32Surface { + GdkSurface parent; +}; + +struct _GdkWin32SurfaceClass { + GdkSurfaceClass parent_class; +}; + +G_DEFINE_TYPE (GdkWin32Surface, gdk_win32_surface, GDK_TYPE_SURFACE) + +static void +gdk_win32_surface_class_init (GdkWin32SurfaceClass *window_class) +{ +} + +static void +gdk_win32_surface_init (GdkWin32Surface *window) +{ +} + + +G_DEFINE_TYPE (GdkSurfaceImplWin32, gdk_surface_impl_win32, GDK_TYPE_SURFACE_IMPL) + +GType +_gdk_surface_impl_win32_get_type (void) +{ + static GType object_type = 0; + + if (!object_type) + { + const GTypeInfo object_info = + { + sizeof (GdkSurfaceImplWin32Class), + (GBaseInitFunc) NULL, + (GBaseFinalizeFunc) NULL, + (GClassInitFunc) gdk_surface_impl_win32_class_init, + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (GdkSurfaceImplWin32), + 0, /* n_preallocs */ + (GInstanceInitFunc) gdk_surface_impl_win32_init, + }; + + object_type = g_type_register_static (GDK_TYPE_SURFACE_IMPL, + "GdkSurfaceImplWin32", + &object_info, 0); + } + + return object_type; +} + +static void +gdk_surface_impl_win32_init (GdkSurfaceImplWin32 *impl) +{ + GdkDisplay *display = gdk_display_get_default (); + + impl->toplevel_window_type = -1; + impl->hicon_big = NULL; + impl->hicon_small = NULL; + impl->hint_flags = 0; + impl->type_hint = GDK_SURFACE_TYPE_HINT_NORMAL; + impl->transient_owner = NULL; + impl->transient_children = NULL; + impl->num_transients = 0; + impl->changing_state = FALSE; + impl->window_scale = 1; +} + +static void +gdk_surface_impl_win32_finalize (GObject *object) +{ + GdkSurface *wrapper; + GdkSurfaceImplWin32 *window_impl; + + g_return_if_fail (GDK_IS_SURFACE_IMPL_WIN32 (object)); + + window_impl = GDK_SURFACE_IMPL_WIN32 (object); + + wrapper = window_impl->wrapper; + + if (!GDK_SURFACE_DESTROYED (wrapper)) + { + gdk_win32_handle_table_remove (window_impl->handle); + } + + g_clear_pointer (&window_impl->snap_stash, g_free); + g_clear_pointer (&window_impl->snap_stash_int, g_free); + + if (window_impl->hicon_big != NULL) + { + GDI_CALL (DestroyIcon, (window_impl->hicon_big)); + window_impl->hicon_big = NULL; + } + + if (window_impl->hicon_small != NULL) + { + GDI_CALL (DestroyIcon, (window_impl->hicon_small)); + window_impl->hicon_small = NULL; + } + + g_free (window_impl->decorations); + + if (window_impl->cache_surface) + { + cairo_surface_destroy (window_impl->cache_surface); + window_impl->cache_surface = NULL; + } + + if (window_impl->cairo_surface) + { + cairo_surface_destroy (window_impl->cairo_surface); + window_impl->cairo_surface = NULL; + } + + g_assert (window_impl->transient_owner == NULL); + g_assert (window_impl->transient_children == NULL); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +gdk_win32_get_window_client_area_rect (GdkSurface *window, + gint scale, + RECT *rect) +{ + gint x, y, width, height; + + gdk_surface_get_position (window, &x, &y); + width = gdk_surface_get_width (window); + height = gdk_surface_get_height (window); + rect->left = x * scale; + rect->top = y * scale; + rect->right = rect->left + width * scale; + rect->bottom = rect->top + height * scale; +} + +static void +gdk_win32_surface_get_queued_window_rect (GdkSurface *window, + RECT *return_window_rect) +{ + RECT window_rect; + GdkSurfaceImplWin32 *impl = GDK_SURFACE_IMPL_WIN32 (window->impl); + + gdk_win32_get_window_client_area_rect (window, impl->window_scale, &window_rect); + + /* Turn client area into window area */ + _gdk_win32_adjust_client_rect (window, &window_rect); + + /* Convert GDK screen coordinates to W32 desktop coordinates */ + window_rect.left -= _gdk_offset_x * impl->window_scale; + window_rect.right -= _gdk_offset_x * impl->window_scale; + window_rect.top -= _gdk_offset_y * impl->window_scale; + window_rect.bottom -= _gdk_offset_y * impl->window_scale; + + *return_window_rect = window_rect; +} + +static void +gdk_win32_surface_apply_queued_move_resize (GdkSurface *window, + RECT window_rect) +{ + if (!IsIconic (GDK_SURFACE_HWND (window))) + { + GdkSurfaceImplWin32 *impl = GDK_SURFACE_IMPL_WIN32 (window->impl); + GDK_NOTE (EVENTS, g_print ("Setting window position ... ")); + + API_CALL (SetWindowPos, (GDK_SURFACE_HWND (window), + SWP_NOZORDER_SPECIFIED, + window_rect.left, window_rect.top, + window_rect.right - window_rect.left, + window_rect.bottom - window_rect.top, + SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOREDRAW)); + + GDK_NOTE (EVENTS, g_print (" ... set window position\n")); + + return; + } + + /* Don't move iconic windows */ + /* TODO: use SetWindowPlacement() to change non-minimized window position */ +} + +static gboolean +gdk_win32_surface_begin_paint (GdkSurface *window) +{ + GdkSurfaceImplWin32 *impl; + RECT window_rect; + + if (window == NULL || GDK_SURFACE_DESTROYED (window)) + return TRUE; + + impl = GDK_SURFACE_IMPL_WIN32 (window->impl); + + /* Layered windows are moved *after* repaint. + * We supply our own surface, return FALSE to make GDK use it. + */ + if (impl->layered) + return FALSE; + + /* FIXME: Possibly remove the following lines when we transition to GL + * drawing fully. This will probably mean that we won't + * be able to use layered windows, as layered windows seem + * to support only up to OpenGL 1.1, which is not enough for our + * needs here. + */ + + /* Non-GL windows are moved *after* repaint. + * We don't supply our own surface, return TRUE to make GDK create + * one by itself. + *//* + if (!window->current_paint.use_gl) + return TRUE;*/ + + /* GL windows are moved *before* repaint (otherwise + * repainting doesn't work), but if there's no move queued up, + * return immediately. Doesn't matter what we return, GDK + * will create a surface anyway, as if we returned TRUE. + */ + if (!impl->drag_move_resize_context.native_move_resize_pending) + return TRUE; + + impl->drag_move_resize_context.native_move_resize_pending = FALSE; + + /* Get the position/size of the window that GDK wants, + * apply it. + */ + gdk_win32_surface_get_queued_window_rect (window, &window_rect); + gdk_win32_surface_apply_queued_move_resize (window, window_rect); + + return TRUE; +} + +static void +gdk_win32_surface_end_paint (GdkSurface *window) +{ + /* FIXME: Possibly make gdk_win32_surface_end_paint() a + * no-op stub, like what is done in Wayland, as + * the items here rely on layered window usage, + * when we transition to full GL drawing, as + * layered windows do not support enough GL + * for our needs here + */ + GdkSurfaceImplWin32 *impl; + RECT window_rect; + HDC hdc; + POINT window_position; + SIZE window_size; + POINT source_point; + BLENDFUNCTION blender; + cairo_t *cr; + + if (window == NULL || GDK_SURFACE_DESTROYED (window)) + return; + + impl = GDK_SURFACE_IMPL_WIN32 (window->impl); + + /* GL windows are moved *before* repaint */ + /*if (window->current_paint.use_gl) + return;*/ + + /* No move/resize is queued up, and we don't need to update + * the contents of a layered window, so return immediately. + */ + if (!impl->layered && + !impl->drag_move_resize_context.native_move_resize_pending) + return; + + impl->drag_move_resize_context.native_move_resize_pending = FALSE; + + /* Get the position/size of the window that GDK wants. */ + gdk_win32_surface_get_queued_window_rect (window, &window_rect); + + if (!impl->layered) + { + gdk_win32_surface_apply_queued_move_resize (window, window_rect); + + return; + } + + window_position.x = window_rect.left; + window_position.y = window_rect.top; + + window_size.cx = window_rect.right - window_rect.left; + window_size.cy = window_rect.bottom - window_rect.top; + + cairo_surface_flush (impl->cairo_surface); + + /* we always draw in the top-left corner of the surface */ + source_point.x = source_point.y = 0; + + blender.BlendOp = AC_SRC_OVER; + blender.BlendFlags = 0; + blender.AlphaFormat = AC_SRC_ALPHA; + blender.SourceConstantAlpha = impl->layered_opacity * 255; + + /* Update cache surface contents */ + cr = cairo_create (impl->cache_surface); + + cairo_set_source_surface (cr, window->current_paint.surface, 0, 0); + gdk_cairo_region (cr, window->current_paint.region); + cairo_clip (cr); + + cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE); + cairo_paint (cr); + + cairo_destroy (cr); + + cairo_surface_flush (impl->cache_surface); + hdc = cairo_win32_surface_get_dc (impl->cache_surface); + + /* Don't use UpdateLayeredWindow on minimized windows */ + if (IsIconic (GDK_SURFACE_HWND (window))) + { + gdk_win32_surface_apply_queued_move_resize (window, window_rect); + + return; + } + + /* Move, resize and redraw layered window in one call */ + API_CALL (UpdateLayeredWindow, (GDK_SURFACE_HWND (window), NULL, + &window_position, &window_size, + hdc, &source_point, + 0, &blender, ULW_ALPHA)); +} + +void +_gdk_win32_adjust_client_rect (GdkSurface *window, + RECT *rect) +{ + LONG style, exstyle; + + style = GetWindowLong (GDK_SURFACE_HWND (window), GWL_STYLE); + exstyle = GetWindowLong (GDK_SURFACE_HWND (window), GWL_EXSTYLE); + API_CALL (AdjustWindowRectEx, (rect, style, FALSE, exstyle)); +} + +gboolean +_gdk_win32_surface_enable_transparency (GdkSurface *window) +{ + GdkSurfaceImplWin32 *impl; + DWM_BLURBEHIND blur_behind; + HRGN empty_region; + HRESULT call_result; + HWND parent, thiswindow; + + if (window == NULL || GDK_SURFACE_HWND (window) == NULL) + return FALSE; + + impl = GDK_SURFACE_IMPL_WIN32 (window->impl); + + /* layered windows don't need blurbehind for transparency */ + if (impl->layered) + return TRUE; + + if (!gdk_display_is_composited (gdk_surface_get_display (window))) + return FALSE; + + thiswindow = GDK_SURFACE_HWND (window); + + /* Blurbehind only works on toplevel windows */ + parent = GetAncestor (thiswindow, GA_PARENT); + if (!(GetWindowLong (thiswindow, GWL_STYLE) & WS_POPUP) && + (parent == NULL || parent != GetDesktopWindow ())) + return FALSE; + + empty_region = CreateRectRgn (0, 0, -1, -1); + + if (empty_region == NULL) + return FALSE; + + memset (&blur_behind, 0, sizeof (blur_behind)); + blur_behind.dwFlags = DWM_BB_ENABLE | DWM_BB_BLURREGION; + blur_behind.hRgnBlur = empty_region; + blur_behind.fEnable = TRUE; + call_result = DwmEnableBlurBehindWindow (thiswindow, &blur_behind); + + if (!SUCCEEDED (call_result)) + g_warning ("%s: %s (%p) failed: %" G_GINT32_MODIFIER "x", + G_STRLOC, "DwmEnableBlurBehindWindow", thiswindow, (guint32) call_result); + + DeleteObject (empty_region); + + return SUCCEEDED (call_result); +} + +static const gchar * +get_default_title (void) +{ + const char *title; + title = g_get_application_name (); + if (!title) + title = g_get_prgname (); + + return title; +} + +/* RegisterGdkClass + * is a wrapper function for RegisterWindowClassEx. + * It creates at least one unique class for every + * GdkSurfaceType. If support for single window-specific icons + * is ever needed (e.g Dialog specific), every such window should + * get its own class + */ +static ATOM +RegisterGdkClass (GdkSurfaceType wtype, GdkSurfaceTypeHint wtype_hint) +{ + static ATOM klassTOPLEVEL = 0; + static ATOM klassCHILD = 0; + static ATOM klassTEMP = 0; + static ATOM klassTEMPSHADOW = 0; + static HICON hAppIcon = NULL; + static HICON hAppIconSm = NULL; + static WNDCLASSEXW wcl; + ATOM klass = 0; + + wcl.cbSize = sizeof (WNDCLASSEX); + wcl.style = 0; /* DON'T set CS_REDRAW. It causes total redraw + * on WM_SIZE and WM_MOVE. Flicker, Performance! + */ + wcl.lpfnWndProc = _gdk_win32_surface_procedure; + wcl.cbClsExtra = 0; + wcl.cbWndExtra = 0; + wcl.hInstance = _gdk_app_hmodule; + wcl.hIcon = 0; + wcl.hIconSm = 0; + + /* initialize once! */ + if (0 == hAppIcon && 0 == hAppIconSm) + { + gchar sLoc [MAX_PATH+1]; + + if (0 != GetModuleFileName (_gdk_app_hmodule, sLoc, MAX_PATH)) + { + ExtractIconEx (sLoc, 0, &hAppIcon, &hAppIconSm, 1); + + if (0 == hAppIcon && 0 == hAppIconSm) + { + if (0 != GetModuleFileName (_gdk_dll_hinstance, sLoc, MAX_PATH)) + { + ExtractIconEx (sLoc, 0, &hAppIcon, &hAppIconSm, 1); + } + } + } + + if (0 == hAppIcon && 0 == hAppIconSm) + { + hAppIcon = LoadImage (NULL, IDI_APPLICATION, IMAGE_ICON, + GetSystemMetrics (SM_CXICON), + GetSystemMetrics (SM_CYICON), 0); + hAppIconSm = LoadImage (NULL, IDI_APPLICATION, IMAGE_ICON, + GetSystemMetrics (SM_CXSMICON), + GetSystemMetrics (SM_CYSMICON), 0); + } + } + + if (0 == hAppIcon) + hAppIcon = hAppIconSm; + else if (0 == hAppIconSm) + hAppIconSm = hAppIcon; + + wcl.lpszMenuName = NULL; + + /* initialize once per class */ + /* + * HB: Setting the background brush leads to flicker, because we + * don't get asked how to clear the background. This is not what + * we want, at least not for input_only windows ... + */ +#define ONCE_PER_CLASS() \ + wcl.hIcon = CopyIcon (hAppIcon); \ + wcl.hIconSm = CopyIcon (hAppIconSm); \ + wcl.hbrBackground = NULL; \ + wcl.hCursor = LoadCursor (NULL, IDC_ARROW); + + switch (wtype) + { + case GDK_SURFACE_TOPLEVEL: + /* MSDN: CS_OWNDC is needed for OpenGL contexts */ + wcl.style |= CS_OWNDC; + if (0 == klassTOPLEVEL) + { + wcl.lpszClassName = L"gdkWindowToplevel"; + + ONCE_PER_CLASS (); + klassTOPLEVEL = RegisterClassExW (&wcl); + } + klass = klassTOPLEVEL; + break; + + case GDK_SURFACE_TEMP: + if ((wtype_hint == GDK_SURFACE_TYPE_HINT_MENU) || + (wtype_hint == GDK_SURFACE_TYPE_HINT_DROPDOWN_MENU) || + (wtype_hint == GDK_SURFACE_TYPE_HINT_POPUP_MENU)) + { + if (klassTEMPSHADOW == 0) + { + wcl.lpszClassName = L"gdkWindowTempShadow"; + wcl.style |= CS_SAVEBITS; + wcl.style |= 0x00020000; /* CS_DROPSHADOW */ + + ONCE_PER_CLASS (); + klassTEMPSHADOW = RegisterClassExW (&wcl); + } + + klass = klassTEMPSHADOW; + } + else + { + if (klassTEMP == 0) + { + wcl.lpszClassName = L"gdkWindowTemp"; + wcl.style |= CS_SAVEBITS; + ONCE_PER_CLASS (); + klassTEMP = RegisterClassExW (&wcl); + } + + klass = klassTEMP; + } + break; + + case GDK_SURFACE_CHILD: + default: + g_assert_not_reached (); + break; + } + + if (klass == 0) + { + WIN32_API_FAILED ("RegisterClassExW"); + g_error ("That is a fatal error"); + } + return klass; +} + +/* + * Create native windows. + * + * With the default Gdk the created windows are mostly toplevel windows. + * + * Placement of the window is derived from the passed in window, + * except for toplevel window where OS/Window Manager placement + * is used. + * + * From attributes the only things used is: colormap, title, + * wmclass and type_hint. [1]. We are checking redundant information + * and complain if that changes, which would break this implementation + * again. + * + * [1] http://mail.gnome.org/archives/gtk-devel-list/2010-August/msg00214.html + */ +void +_gdk_win32_display_create_window_impl (GdkDisplay *display, + GdkSurface *window, + GdkSurface *real_parent, + GdkEventMask event_mask, + GdkSurfaceAttr *attributes) +{ + HWND hwndNew; + HANDLE hparent; + ATOM klass = 0; + DWORD dwStyle = 0, dwExStyle; + RECT rect; + GdkSurfaceImplWin32 *impl; + GdkWin32Display *display_win32; + const gchar *title; + wchar_t *wtitle; + gint window_width, window_height; + gint offset_x = 0, offset_y = 0; + gint x, y, real_x = 0, real_y = 0; + + g_return_if_fail (display == _gdk_display); + + GDK_NOTE (MISC, + g_print ("_gdk_surface_impl_new: %s %s\n", (window->window_type == GDK_SURFACE_TOPLEVEL ? "TOPLEVEL" : + (window->window_type == GDK_SURFACE_TEMP ? "TEMP" : "???")), + (attributes->wclass == GDK_INPUT_OUTPUT ? "" : "input-only"))); + + hparent = (real_parent != NULL) ? GDK_SURFACE_HWND (real_parent) : NULL; + + impl = g_object_new (GDK_TYPE_SURFACE_IMPL_WIN32, NULL); + impl->wrapper = GDK_SURFACE (window); + window->impl = GDK_SURFACE_IMPL (impl); + + impl->layered = FALSE; + impl->layered_opacity = 1.0; + + display_win32 = GDK_WIN32_DISPLAY (display); + impl->window_scale = _gdk_win32_display_get_monitor_scale_factor (display_win32, NULL, NULL, NULL); + impl->unscaled_width = window->width * impl->window_scale; + impl->unscaled_height = window->height * impl->window_scale; + + if (!window->input_only) + { + dwExStyle = 0; + } + else + { + /* I very much doubt using WS_EX_TRANSPARENT actually + * corresponds to how X11 InputOnly windows work, but it appears + * to work well enough for the actual use cases in gtk. + */ + dwExStyle = WS_EX_TRANSPARENT; + GDK_NOTE (MISC, g_print ("... GDK_INPUT_ONLY\n")); + } + + switch (window->window_type) + { + case GDK_SURFACE_TOPLEVEL: + if (window->parent) + { + /* The common code warns for this case. */ + hparent = GetDesktopWindow (); + } + /* Children of foreign windows aren't toplevel windows */ + if (real_parent != NULL && GDK_SURFACE_TYPE (real_parent) == GDK_SURFACE_FOREIGN) + { + dwStyle = WS_CHILDWINDOW | WS_CLIPCHILDREN; + } + else + { + /* MSDN: We need WS_CLIPCHILDREN and WS_CLIPSIBLINGS for GL Context Creation */ + if (window->window_type == GDK_SURFACE_TOPLEVEL) + dwStyle = WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN | WS_CLIPSIBLINGS; + else + dwStyle = WS_OVERLAPPED | WS_MINIMIZEBOX | WS_SYSMENU | WS_CAPTION | WS_THICKFRAME | WS_CLIPCHILDREN; + + offset_x = _gdk_offset_x; + offset_y = _gdk_offset_y; + } + break; + + case GDK_SURFACE_TEMP: + /* A temp window is not necessarily a top level window */ + dwStyle = real_parent == NULL ? WS_POPUP : WS_CHILDWINDOW; + dwStyle |= WS_CLIPCHILDREN | WS_CLIPSIBLINGS; + dwExStyle |= WS_EX_TOOLWINDOW | WS_EX_TOPMOST; + offset_x = _gdk_offset_x; + offset_y = _gdk_offset_y; + break; + + + case GDK_SURFACE_CHILD: + default: + g_assert_not_reached (); + } + + rect.left = window->x * impl->window_scale; + rect.top = window->y * impl->window_scale; + rect.right = rect.left + window->width * impl->window_scale; + rect.bottom = rect.top + window->height * impl->window_scale; + + AdjustWindowRectEx (&rect, dwStyle, FALSE, dwExStyle); + + real_x = (window->x - offset_x) * impl->window_scale; + real_y = (window->y - offset_y) * impl->window_scale; + + if (window->window_type == GDK_SURFACE_TOPLEVEL) + { + /* We initially place it at default so that we can get the + default window positioning if we want */ + x = y = CW_USEDEFAULT; + } + else + { + /* TEMP, FOREIGN: Put these where requested */ + x = real_x; + y = real_y; + } + + window_width = rect.right - rect.left; + window_height = rect.bottom - rect.top; + + title = get_default_title (); + if (!title || !*title) + title = ""; + + impl->native_event_mask = GDK_STRUCTURE_MASK | event_mask; + + if (impl->type_hint == GDK_SURFACE_TYPE_HINT_UTILITY) + dwExStyle |= WS_EX_TOOLWINDOW; + + /* WS_EX_TRANSPARENT means "try draw this window last, and ignore input". + * It's the last part we're after. We don't want DND indicator to accept + * input, because that will make it a potential drop target, and if it's + * under the mouse cursor, this will kill any DND. + */ + if (impl->type_hint == GDK_SURFACE_TYPE_HINT_DND) + dwExStyle |= WS_EX_TRANSPARENT; + + klass = RegisterGdkClass (window->window_type, impl->type_hint); + + wtitle = g_utf8_to_utf16 (title, -1, NULL, NULL, NULL); + + hwndNew = CreateWindowExW (dwExStyle, + MAKEINTRESOURCEW (klass), + wtitle, + dwStyle, + x, + y, + window_width, window_height, + hparent, + NULL, + _gdk_app_hmodule, + window); + if (GDK_SURFACE_HWND (window) != hwndNew) + { + g_warning ("gdk_surface_new: gdk_event_translate::WM_CREATE (%p, %p) HWND mismatch.", + GDK_SURFACE_HWND (window), + hwndNew); + + /* HB: IHMO due to a race condition the handle was increased by + * one, which causes much trouble. Because I can't find the + * real bug, try to workaround it ... + * To reproduce: compile with MSVC 5, DEBUG=1 + */ +# if 0 + gdk_win32_handle_table_remove (GDK_SURFACE_HWND (window)); + GDK_SURFACE_HWND (window) = hwndNew; + gdk_win32_handle_table_insert (&GDK_SURFACE_HWND (window), window); +# else + /* the old behaviour, but with warning */ + impl->handle = hwndNew; +# endif + + } + + GetWindowRect (GDK_SURFACE_HWND (window), &rect); + impl->initial_x = rect.left; + impl->initial_y = rect.top; + + /* Now we know the initial position, move to actually specified position */ + if (real_x != x || real_y != y) + { + API_CALL (SetWindowPos, (GDK_SURFACE_HWND (window), + SWP_NOZORDER_SPECIFIED, + real_x, real_y, 0, 0, + SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOZORDER)); + } + + g_object_ref (window); + gdk_win32_handle_table_insert (&GDK_SURFACE_HWND (window), window); + + GDK_NOTE (MISC, g_print ("... \"%s\" %dx%d@%+d%+d %p = %p\n", + title, + window_width, window_height, + window->x - offset_x, + window->y - offset_y, + hparent, + GDK_SURFACE_HWND (window))); + + /* Add window handle to title */ + GDK_NOTE (MISC_OR_EVENTS, gdk_surface_set_title (window, title)); + + g_free (wtitle); + + if (impl->handle == NULL) + { + WIN32_API_FAILED ("CreateWindowExW"); + g_object_unref (window); + return; + } + +// if (!from_set_skip_taskbar_hint && window->window_type == GDK_SURFACE_TEMP) +// gdk_surface_set_skip_taskbar_hint (window, TRUE); + + _gdk_win32_surface_enable_transparency (window); +} + +GdkSurface * +gdk_win32_surface_foreign_new_for_display (GdkDisplay *display, + HWND anid) +{ + GdkSurface *window; + GdkSurfaceImplWin32 *impl; + + HANDLE parent; + RECT rect; + POINT point; + + if ((window = gdk_win32_surface_lookup_for_display (display, anid)) != NULL) + return g_object_ref (window); + + window = _gdk_display_create_window (display); + window->impl = g_object_new (GDK_TYPE_SURFACE_IMPL_WIN32, NULL); + window->impl_window = window; + impl = GDK_SURFACE_IMPL_WIN32 (window->impl); + impl->wrapper = window; + parent = GetParent (anid); + + /* Always treat foreigns as toplevels */ + window->parent = NULL; + + GetClientRect ((HWND) anid, &rect); + point.x = rect.left; + point.y = rect.right; + ClientToScreen ((HWND) anid, &point); + if (parent != GetDesktopWindow ()) + ScreenToClient (parent, &point); + window->x = point.x / impl->window_scale; + window->y = point.y / impl->window_scale; + impl->unscaled_width = rect.right - rect.left; + impl->unscaled_height = rect.bottom - rect.top; + window->width = (impl->unscaled_width + impl->window_scale - 1) / impl->window_scale; + window->height = (impl->unscaled_height + impl->window_scale - 1) / impl->window_scale; + window->window_type = GDK_SURFACE_FOREIGN; + window->destroyed = FALSE; + window->event_mask = GDK_ALL_EVENTS_MASK; /* XXX */ + if (IsWindowVisible ((HWND) anid)) + window->state &= (~GDK_SURFACE_STATE_WITHDRAWN); + else + window->state |= GDK_SURFACE_STATE_WITHDRAWN; + if (GetWindowLong ((HWND)anid, GWL_EXSTYLE) & WS_EX_TOPMOST) + window->state |= GDK_SURFACE_STATE_ABOVE; + else + window->state &= (~GDK_SURFACE_STATE_ABOVE); + window->state &= (~GDK_SURFACE_STATE_BELOW); + window->viewable = TRUE; + + GDK_SURFACE_HWND (window) = anid; + + g_object_ref (window); + gdk_win32_handle_table_insert (&GDK_SURFACE_HWND (window), window); + + GDK_NOTE (MISC, g_print ("gdk_win32_surface_foreign_new_for_display: %p: %s@%+d%+d\n", + (HWND) anid, + _gdk_win32_surface_description (window), + window->x, window->y)); + + return window; +} + +static void +gdk_win32_surface_destroy (GdkSurface *window, + gboolean recursing, + gboolean foreign_destroy) +{ + GdkSurfaceImplWin32 *window_impl = GDK_SURFACE_IMPL_WIN32 (window->impl); + GSList *tmp; + + g_return_if_fail (GDK_IS_SURFACE (window)); + + GDK_NOTE (MISC, g_print ("gdk_win32_surface_destroy: %p\n", + GDK_SURFACE_HWND (window))); + + /* Remove ourself from the modal stack */ + _gdk_remove_modal_window (window); + + /* Remove all our transient children */ + tmp = window_impl->transient_children; + while (tmp != NULL) + { + GdkSurface *child = tmp->data; + GdkSurfaceImplWin32 *child_impl = GDK_SURFACE_IMPL_WIN32 (GDK_SURFACE (child)->impl); + + child_impl->transient_owner = NULL; + tmp = tmp->next; + } + g_slist_free (window_impl->transient_children); + window_impl->transient_children = NULL; + + /* Remove ourself from our transient owner */ + if (window_impl->transient_owner != NULL) + { + gdk_surface_set_transient_for (window, NULL); + } + + if (!recursing && !foreign_destroy) + { + window->destroyed = TRUE; + DestroyWindow (GDK_SURFACE_HWND (window)); + } +} + +/* This function is called when the window really gone. + */ +static void +gdk_win32_surface_destroy_notify (GdkSurface *window) +{ + g_return_if_fail (GDK_IS_SURFACE (window)); + + GDK_NOTE (EVENTS, + g_print ("gdk_surface_destroy_notify: %p%s\n", + GDK_SURFACE_HWND (window), + (GDK_SURFACE_DESTROYED (window) ? " (destroyed)" : ""))); + + if (!GDK_SURFACE_DESTROYED (window)) + { + if (GDK_SURFACE_TYPE (window) != GDK_SURFACE_FOREIGN) + g_warning ("window %p unexpectedly destroyed", + GDK_SURFACE_HWND (window)); + + _gdk_surface_destroy (window, TRUE); + } + + gdk_win32_handle_table_remove (GDK_SURFACE_HWND (window)); + g_object_unref (window); +} + +static void +get_outer_rect (GdkSurface *window, + gint width, + gint height, + RECT *rect) +{ + GdkSurfaceImplWin32 *impl = GDK_SURFACE_IMPL_WIN32 (window->impl); + + rect->left = rect->top = 0; + rect->right = width * impl->window_scale; + rect->bottom = height * impl->window_scale; + + _gdk_win32_adjust_client_rect (window, rect); +} + +static void +adjust_for_gravity_hints (GdkSurface *window, + RECT *outer_rect, + gint *x, + gint *y) +{ + GdkSurfaceImplWin32 *impl = GDK_SURFACE_IMPL_WIN32 (window->impl); + + if (impl->hint_flags & GDK_HINT_WIN_GRAVITY) + { +#ifdef G_ENABLE_DEBUG + gint orig_x = *x, orig_y = *y; +#endif + + switch (impl->hints.win_gravity) + { + case GDK_GRAVITY_NORTH: + case GDK_GRAVITY_CENTER: + case GDK_GRAVITY_SOUTH: + *x -= (outer_rect->right - outer_rect->left / 2) / impl->window_scale; + *x += window->width / 2; + break; + + case GDK_GRAVITY_SOUTH_EAST: + case GDK_GRAVITY_EAST: + case GDK_GRAVITY_NORTH_EAST: + *x -= (outer_rect->right - outer_rect->left) / impl->window_scale; + *x += window->width; + break; + + case GDK_GRAVITY_STATIC: + *x += outer_rect->left / impl->window_scale; + break; + + default: + break; + } + + switch (impl->hints.win_gravity) + { + case GDK_GRAVITY_WEST: + case GDK_GRAVITY_CENTER: + case GDK_GRAVITY_EAST: + *y -= ((outer_rect->bottom - outer_rect->top) / 2) / impl->window_scale; + *y += window->height / 2; + break; + + case GDK_GRAVITY_SOUTH_WEST: + case GDK_GRAVITY_SOUTH: + case GDK_GRAVITY_SOUTH_EAST: + *y -= (outer_rect->bottom - outer_rect->top) / impl->window_scale; + *y += window->height; + break; + + case GDK_GRAVITY_STATIC: + *y += outer_rect->top * impl->window_scale; + break; + + default: + break; + } + GDK_NOTE (MISC, + (orig_x != *x || orig_y != *y) ? + g_print ("adjust_for_gravity_hints: x: %d->%d, y: %d->%d\n", + orig_x, *x, orig_y, *y) + : (void) 0); + } +} + +static void +show_window_internal (GdkSurface *window, + gboolean already_mapped, + gboolean deiconify) +{ + GdkSurfaceImplWin32 *window_impl; + gboolean focus_on_map = FALSE; + DWORD exstyle; + + if (window->destroyed) + return; + + GDK_NOTE (MISC, g_print ("show_window_internal: %p: %s%s\n", + GDK_SURFACE_HWND (window), + _gdk_win32_surface_state_to_string (window->state), + (deiconify ? " deiconify" : ""))); + + /* If asked to show (not deiconify) an withdrawn and iconified + * window, do that. + */ + if (!deiconify && + !already_mapped && + (window->state & GDK_SURFACE_STATE_ICONIFIED)) + { + GtkShowWindow (window, SW_SHOWMINNOACTIVE); + return; + } + + /* If asked to just show an iconified window, do nothing. */ + if (!deiconify && (window->state & GDK_SURFACE_STATE_ICONIFIED)) + return; + + /* If asked to deiconify an already noniconified window, do + * nothing. (Especially, don't cause the window to rise and + * activate. There are different calls for that.) + */ + if (deiconify && !(window->state & GDK_SURFACE_STATE_ICONIFIED)) + return; + + /* If asked to show (but not raise) a window that is already + * visible, do nothing. + */ + if (!deiconify && !already_mapped && IsWindowVisible (GDK_SURFACE_HWND (window))) + return; + + /* Other cases */ + + if (!already_mapped) + focus_on_map = window->focus_on_map; + + exstyle = GetWindowLong (GDK_SURFACE_HWND (window), GWL_EXSTYLE); + + /* Use SetWindowPos to show transparent windows so automatic redraws + * in other windows can be suppressed. + */ + if (exstyle & WS_EX_TRANSPARENT) + { + UINT flags = SWP_SHOWWINDOW | SWP_NOREDRAW | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER; + + if (GDK_SURFACE_TYPE (window) == GDK_SURFACE_TEMP || !focus_on_map) + flags |= SWP_NOACTIVATE; + + SetWindowPos (GDK_SURFACE_HWND (window), + SWP_NOZORDER_SPECIFIED, 0, 0, 0, 0, flags); + + return; + } + + /* For initial map of "normal" windows we want to emulate WM window + * positioning behaviour, which means: + * + Use user specified position if GDK_HINT_POS or GDK_HINT_USER_POS + * otherwise: + * + default to the initial CW_USEDEFAULT placement, + * no matter if the user moved the window before showing it. + * + Certain window types and hints have more elaborate positioning + * schemes. + */ + window_impl = GDK_SURFACE_IMPL_WIN32 (window->impl); + if (!already_mapped && + GDK_SURFACE_TYPE (window) == GDK_SURFACE_TOPLEVEL && + (window_impl->hint_flags & (GDK_HINT_POS | GDK_HINT_USER_POS)) == 0) + { + gboolean center = FALSE; + RECT window_rect, center_on_rect; + int x, y; + + x = window_impl->initial_x; + y = window_impl->initial_y; + + if (window_impl->type_hint == GDK_SURFACE_TYPE_HINT_SPLASHSCREEN) + { + HMONITOR monitor; + MONITORINFO mi; + + monitor = MonitorFromWindow (GDK_SURFACE_HWND (window), MONITOR_DEFAULTTONEAREST); + mi.cbSize = sizeof (mi); + if (monitor && GetMonitorInfo (monitor, &mi)) + center_on_rect = mi.rcMonitor; + else + { + center_on_rect.left = 0; + center_on_rect.top = 0; + center_on_rect.right = GetSystemMetrics (SM_CXSCREEN); + center_on_rect.bottom = GetSystemMetrics (SM_CYSCREEN); + } + center = TRUE; + } + else if (window_impl->transient_owner != NULL && + GDK_SURFACE_IS_MAPPED (window_impl->transient_owner)) + { + GdkSurface *owner = window_impl->transient_owner; + /* Center on transient parent */ + center_on_rect.left = (owner->x - _gdk_offset_x) * window_impl->window_scale; + center_on_rect.top = (owner->y - _gdk_offset_y) * window_impl->window_scale; + center_on_rect.right = center_on_rect.left + owner->width * window_impl->window_scale; + center_on_rect.bottom = center_on_rect.top + owner->height * window_impl->window_scale; + + _gdk_win32_adjust_client_rect (GDK_SURFACE (owner), ¢er_on_rect); + center = TRUE; + } + + if (center) + { + window_rect.left = 0; + window_rect.top = 0; + window_rect.right = window->width * window_impl->window_scale; + window_rect.bottom = window->height * window_impl->window_scale; + _gdk_win32_adjust_client_rect (window, &window_rect); + + x = center_on_rect.left + ((center_on_rect.right - center_on_rect.left) - (window_rect.right - window_rect.left)) / 2; + y = center_on_rect.top + ((center_on_rect.bottom - center_on_rect.top) - (window_rect.bottom - window_rect.top)) / 2; + } + + API_CALL (SetWindowPos, (GDK_SURFACE_HWND (window), + SWP_NOZORDER_SPECIFIED, + x, y, 0, 0, + SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOZORDER)); + } + + if (!already_mapped && + GDK_SURFACE_TYPE (window) == GDK_SURFACE_TOPLEVEL) + { + /* Ensure new windows are fully onscreen */ + RECT window_rect; + HMONITOR monitor; + MONITORINFO mi; + int x, y; + + GetWindowRect (GDK_SURFACE_HWND (window), &window_rect); + + monitor = MonitorFromWindow (GDK_SURFACE_HWND (window), MONITOR_DEFAULTTONEAREST); + mi.cbSize = sizeof (mi); + if (monitor && GetMonitorInfo (monitor, &mi)) + { + x = window_rect.left; + y = window_rect.top; + + if (window_rect.right > mi.rcWork.right) + { + window_rect.left -= (window_rect.right - mi.rcWork.right); + window_rect.right -= (window_rect.right - mi.rcWork.right); + } + + if (window_rect.bottom > mi.rcWork.bottom) + { + window_rect.top -= (window_rect.bottom - mi.rcWork.bottom); + window_rect.bottom -= (window_rect.bottom - mi.rcWork.bottom); + } + + if (window_rect.left < mi.rcWork.left) + { + window_rect.right += (mi.rcWork.left - window_rect.left); + window_rect.left += (mi.rcWork.left - window_rect.left); + } + + if (window_rect.top < mi.rcWork.top) + { + window_rect.bottom += (mi.rcWork.top - window_rect.top); + window_rect.top += (mi.rcWork.top - window_rect.top); + } + + if (x != window_rect.left || y != window_rect.top) + API_CALL (SetWindowPos, (GDK_SURFACE_HWND (window), + SWP_NOZORDER_SPECIFIED, + window_rect.left, window_rect.top, 0, 0, + SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOZORDER)); + } + } + + + if (window->state & GDK_SURFACE_STATE_FULLSCREEN) + { + gdk_surface_fullscreen (window); + } + else if (window->state & GDK_SURFACE_STATE_MAXIMIZED) + { + GtkShowWindow (window, SW_MAXIMIZE); + } + else if (window->state & GDK_SURFACE_STATE_ICONIFIED) + { + if (focus_on_map) + GtkShowWindow (window, SW_RESTORE); + else + GtkShowWindow (window, SW_SHOWNOACTIVATE); + } + else if (GDK_SURFACE_TYPE (window) == GDK_SURFACE_TEMP || !focus_on_map) + { + if (!IsWindowVisible (GDK_SURFACE_HWND (window))) + GtkShowWindow (window, SW_SHOWNOACTIVATE); + else + GtkShowWindow (window, SW_SHOWNA); + } + else if (!IsWindowVisible (GDK_SURFACE_HWND (window))) + { + GtkShowWindow (window, SW_SHOWNORMAL); + } + else + { + GtkShowWindow (window, SW_SHOW); + } + + /* Sync STATE_ABOVE to TOPMOST */ + if (GDK_SURFACE_TYPE (window) != GDK_SURFACE_TEMP && + (((window->state & GDK_SURFACE_STATE_ABOVE) && + !(exstyle & WS_EX_TOPMOST)) || + (!(window->state & GDK_SURFACE_STATE_ABOVE) && + (exstyle & WS_EX_TOPMOST)))) + { + API_CALL (SetWindowPos, (GDK_SURFACE_HWND (window), + (window->state & GDK_SURFACE_STATE_ABOVE)?HWND_TOPMOST:HWND_NOTOPMOST, + 0, 0, 0, 0, + SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE)); + } +} + +static void +gdk_win32_surface_show (GdkSurface *window, + gboolean already_mapped) +{ + show_window_internal (window, FALSE, FALSE); +} + +static void +gdk_win32_surface_hide (GdkSurface *window) +{ + if (window->destroyed) + return; + + GDK_NOTE (MISC, g_print ("gdk_win32_surface_hide: %p: %s\n", + GDK_SURFACE_HWND (window), + _gdk_win32_surface_state_to_string (window->state))); + + if (GDK_SURFACE_IS_MAPPED (window)) + gdk_synthesize_window_state (window, + 0, + GDK_SURFACE_STATE_WITHDRAWN); + + _gdk_surface_clear_update_area (window); + + if (GDK_SURFACE_TYPE (window) == GDK_SURFACE_TOPLEVEL) + ShowOwnedPopups (GDK_SURFACE_HWND (window), FALSE); + + /* Use SetWindowPos to hide transparent windows so automatic redraws + * in other windows can be suppressed. + */ + if (GetWindowLong (GDK_SURFACE_HWND (window), GWL_EXSTYLE) & WS_EX_TRANSPARENT) + { + SetWindowPos (GDK_SURFACE_HWND (window), SWP_NOZORDER_SPECIFIED, + 0, 0, 0, 0, + SWP_HIDEWINDOW | SWP_NOREDRAW | SWP_NOZORDER | SWP_NOMOVE | SWP_NOSIZE); + } + else + { + GtkShowWindow (window, SW_HIDE); + } +} + +static void +gdk_win32_surface_withdraw (GdkSurface *window) +{ + if (window->destroyed) + return; + + GDK_NOTE (MISC, g_print ("gdk_win32_surface_withdraw: %p: %s\n", + GDK_SURFACE_HWND (window), + _gdk_win32_surface_state_to_string (window->state))); + + gdk_surface_hide (window); /* ??? */ +} + +static void +gdk_win32_surface_move (GdkSurface *window, + gint x, gint y) +{ + g_return_if_fail (GDK_IS_SURFACE (window)); + + if (GDK_SURFACE_DESTROYED (window)) + return; + + GDK_NOTE (MISC, g_print ("gdk_win32_surface_move: %p: %+d%+d\n", + GDK_SURFACE_HWND (window), x, y)); + + if (window->state & GDK_SURFACE_STATE_FULLSCREEN) + return; + + /* Don't check GDK_SURFACE_TYPE (window) == GDK_SURFACE_CHILD. + * Foreign windows (another app's windows) might be children of our + * windows! Especially in the case of gtkplug/socket. + */ + if (GetAncestor (GDK_SURFACE_HWND (window), GA_PARENT) != GetDesktopWindow ()) + { + _gdk_surface_move_resize_child (window, x, y, window->width, window->height); + } + else + { + RECT outer_rect; + GdkSurfaceImplWin32 *impl = GDK_SURFACE_IMPL_WIN32 (window->impl); + + get_outer_rect (window, window->width, window->height, &outer_rect); + + adjust_for_gravity_hints (window, &outer_rect, &x, &y); + + GDK_NOTE (MISC, g_print ("... SetWindowPos(%p,NULL,%d,%d,0,0," + "NOACTIVATE|NOSIZE|NOZORDER)\n", + GDK_SURFACE_HWND (window), + (x - _gdk_offset_x) * impl->window_scale, + (y - _gdk_offset_y) * impl->window_scale)); + + API_CALL (SetWindowPos, (GDK_SURFACE_HWND (window), + SWP_NOZORDER_SPECIFIED, + (x - _gdk_offset_x) * impl->window_scale, + (y - _gdk_offset_y) * impl->window_scale, + 0, 0, + SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOZORDER)); + } +} + +static void +gdk_win32_surface_resize (GdkSurface *window, + gint width, gint height) +{ + g_return_if_fail (GDK_IS_SURFACE (window)); + + if (GDK_SURFACE_DESTROYED (window)) + return; + + if (width < 1) + width = 1; + if (height < 1) + height = 1; + + GDK_NOTE (MISC, g_print ("gdk_win32_surface_resize: %p: %dx%d\n", + GDK_SURFACE_HWND (window), width, height)); + + if (window->state & GDK_SURFACE_STATE_FULLSCREEN) + return; + + if (GetAncestor (GDK_SURFACE_HWND (window), GA_PARENT) != GetDesktopWindow ()) + { + _gdk_surface_move_resize_child (window, window->x, window->y, width, height); + } + else + { + RECT outer_rect; + GdkSurfaceImplWin32 *impl = GDK_SURFACE_IMPL_WIN32 (window->impl); + + get_outer_rect (window, width, height, &outer_rect); + + GDK_NOTE (MISC, g_print ("... SetWindowPos(%p,NULL,0,0,%ld,%ld," + "NOACTIVATE|NOMOVE|NOZORDER)\n", + GDK_SURFACE_HWND (window), + outer_rect.right - outer_rect.left, + outer_rect.bottom - outer_rect.top)); + + API_CALL (SetWindowPos, (GDK_SURFACE_HWND (window), + SWP_NOZORDER_SPECIFIED, + 0, 0, + outer_rect.right - outer_rect.left, + outer_rect.bottom - outer_rect.top, + SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER)); + window->resize_count += 1; + } +} + +static void +gdk_win32_surface_move_resize_internal (GdkSurface *window, + gint x, + gint y, + gint width, + gint height) +{ + g_return_if_fail (GDK_IS_SURFACE (window)); + + if (GDK_SURFACE_DESTROYED (window)) + return; + + if (width < 1) + width = 1; + if (height < 1) + height = 1; + + if (window->state & GDK_SURFACE_STATE_FULLSCREEN) + return; + + GDK_NOTE (MISC, g_print ("gdk_win32_surface_move_resize: %p: %dx%d@%+d%+d\n", + GDK_SURFACE_HWND (window), + width, height, x, y)); + + if (GetAncestor (GDK_SURFACE_HWND (window), GA_PARENT) != GetDesktopWindow ()) + { + _gdk_surface_move_resize_child (window, x, y, width, height); + } + else + { + RECT outer_rect; + GdkSurfaceImplWin32 *impl = GDK_SURFACE_IMPL_WIN32 (window->impl); + + get_outer_rect (window, width, height, &outer_rect); + + adjust_for_gravity_hints (window, &outer_rect, &x, &y); + + GDK_NOTE (MISC, g_print ("... SetWindowPos(%p,NULL,%d,%d,%ld,%ld," + "NOACTIVATE|NOZORDER)\n", + GDK_SURFACE_HWND (window), + (x - _gdk_offset_x) * impl->window_scale, + (y - _gdk_offset_y) * impl->window_scale, + outer_rect.right - outer_rect.left, + outer_rect.bottom - outer_rect.top)); + + API_CALL (SetWindowPos, (GDK_SURFACE_HWND (window), + SWP_NOZORDER_SPECIFIED, + (x - _gdk_offset_x) * impl->window_scale, + (y - _gdk_offset_y) * impl->window_scale, + outer_rect.right - outer_rect.left, + outer_rect.bottom - outer_rect.top, + SWP_NOACTIVATE | SWP_NOZORDER)); + } +} + +static void +gdk_win32_surface_move_resize (GdkSurface *window, + gboolean with_move, + gint x, + gint y, + gint width, + gint height) +{ + GdkSurfaceImplWin32 *window_impl; + + window_impl = GDK_SURFACE_IMPL_WIN32 (window->impl); + window_impl->inhibit_configure = TRUE; + + /* We ignore changes to the window being moved or resized by the + user, as we don't want to fight the user */ + if (GDK_SURFACE_HWND (window) == _modal_move_resize_window) + goto out; + + if (with_move && (width < 0 && height < 0)) + { + gdk_win32_surface_move (window, x, y); + } + else + { + if (with_move) + { + gdk_win32_surface_move_resize_internal (window, x, y, width, height); + } + else + { + gdk_win32_surface_resize (window, width, height); + } + } + + out: + window_impl->inhibit_configure = FALSE; + + if (WINDOW_IS_TOPLEVEL (window)) + _gdk_win32_emit_configure_event (window); +} + +static void +gdk_win32_surface_raise (GdkSurface *window) +{ + if (!GDK_SURFACE_DESTROYED (window)) + { + GDK_NOTE (MISC, g_print ("gdk_win32_surface_raise: %p\n", + GDK_SURFACE_HWND (window))); + + if (GDK_SURFACE_TYPE (window) == GDK_SURFACE_TEMP) + API_CALL (SetWindowPos, (GDK_SURFACE_HWND (window), HWND_TOPMOST, + 0, 0, 0, 0, + SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE)); + else if (window->accept_focus) + /* Do not wrap this in an API_CALL macro as SetForegroundWindow might + * fail when for example dragging a window belonging to a different + * application at the time of a gtk_window_present() call due to focus + * stealing prevention. */ + SetForegroundWindow (GDK_SURFACE_HWND (window)); + else + API_CALL (SetWindowPos, (GDK_SURFACE_HWND (window), HWND_TOP, + 0, 0, 0, 0, + SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE)); + } +} + +static void +gdk_win32_surface_lower (GdkSurface *window) +{ + if (!GDK_SURFACE_DESTROYED (window)) + { + GDK_NOTE (MISC, g_print ("gdk_win32_surface_lower: %p\n" + "... SetWindowPos(%p,HWND_BOTTOM,0,0,0,0," + "NOACTIVATE|NOMOVE|NOSIZE)\n", + GDK_SURFACE_HWND (window), + GDK_SURFACE_HWND (window))); + + API_CALL (SetWindowPos, (GDK_SURFACE_HWND (window), HWND_BOTTOM, + 0, 0, 0, 0, + SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE)); + } +} + +static void +gdk_win32_surface_set_urgency_hint (GdkSurface *window, + gboolean urgent) +{ + FLASHWINFO flashwinfo; + typedef BOOL (WINAPI *PFN_FlashWindowEx) (FLASHWINFO*); + PFN_FlashWindowEx flashWindowEx = NULL; + + g_return_if_fail (GDK_IS_SURFACE (window)); + + if (GDK_SURFACE_DESTROYED (window)) + return; + + flashWindowEx = (PFN_FlashWindowEx) GetProcAddress (GetModuleHandle ("user32.dll"), "FlashWindowEx"); + + if (flashWindowEx) + { + flashwinfo.cbSize = sizeof (flashwinfo); + flashwinfo.hwnd = GDK_SURFACE_HWND (window); + if (urgent) + flashwinfo.dwFlags = FLASHW_ALL | FLASHW_TIMER; + else + flashwinfo.dwFlags = FLASHW_STOP; + flashwinfo.uCount = 0; + flashwinfo.dwTimeout = 0; + + flashWindowEx (&flashwinfo); + } + else + { + FlashWindow (GDK_SURFACE_HWND (window), urgent); + } +} + +static gboolean +get_effective_window_decorations (GdkSurface *window, + GdkWMDecoration *decoration) +{ + GdkSurfaceImplWin32 *impl; + + impl = (GdkSurfaceImplWin32 *)window->impl; + + if (gdk_surface_get_decorations (window, decoration)) + return TRUE; + + if (window->window_type != GDK_SURFACE_TOPLEVEL) + { + return FALSE; + } + + if ((impl->hint_flags & GDK_HINT_MIN_SIZE) && + (impl->hint_flags & GDK_HINT_MAX_SIZE) && + impl->hints.min_width == impl->hints.max_width && + impl->hints.min_height == impl->hints.max_height) + { + *decoration = GDK_DECOR_ALL | GDK_DECOR_RESIZEH | GDK_DECOR_MAXIMIZE; + + if (impl->type_hint == GDK_SURFACE_TYPE_HINT_DIALOG || + impl->type_hint == GDK_SURFACE_TYPE_HINT_MENU || + impl->type_hint == GDK_SURFACE_TYPE_HINT_TOOLBAR) + { + *decoration |= GDK_DECOR_MINIMIZE; + } + else if (impl->type_hint == GDK_SURFACE_TYPE_HINT_SPLASHSCREEN) + { + *decoration |= GDK_DECOR_MENU | GDK_DECOR_MINIMIZE; + } + + return TRUE; + } + else if (impl->hint_flags & GDK_HINT_MAX_SIZE) + { + *decoration = GDK_DECOR_ALL | GDK_DECOR_MAXIMIZE; + if (impl->type_hint == GDK_SURFACE_TYPE_HINT_DIALOG || + impl->type_hint == GDK_SURFACE_TYPE_HINT_MENU || + impl->type_hint == GDK_SURFACE_TYPE_HINT_TOOLBAR) + { + *decoration |= GDK_DECOR_MINIMIZE; + } + + return TRUE; + } + else + { + switch (impl->type_hint) + { + case GDK_SURFACE_TYPE_HINT_DIALOG: + *decoration = (GDK_DECOR_ALL | GDK_DECOR_MINIMIZE | GDK_DECOR_MAXIMIZE); + return TRUE; + + case GDK_SURFACE_TYPE_HINT_MENU: + *decoration = (GDK_DECOR_ALL | GDK_DECOR_RESIZEH | GDK_DECOR_MINIMIZE | GDK_DECOR_MAXIMIZE); + return TRUE; + + case GDK_SURFACE_TYPE_HINT_TOOLBAR: + case GDK_SURFACE_TYPE_HINT_UTILITY: + gdk_surface_set_skip_taskbar_hint (window, TRUE); + gdk_surface_set_skip_pager_hint (window, TRUE); + *decoration = (GDK_DECOR_ALL | GDK_DECOR_MINIMIZE | GDK_DECOR_MAXIMIZE); + return TRUE; + + case GDK_SURFACE_TYPE_HINT_SPLASHSCREEN: + *decoration = (GDK_DECOR_ALL | GDK_DECOR_RESIZEH | GDK_DECOR_MENU | + GDK_DECOR_MINIMIZE | GDK_DECOR_MAXIMIZE); + return TRUE; + + case GDK_SURFACE_TYPE_HINT_DOCK: + return FALSE; + + case GDK_SURFACE_TYPE_HINT_DESKTOP: + return FALSE; + + default: + /* Fall thru */ + case GDK_SURFACE_TYPE_HINT_NORMAL: + *decoration = GDK_DECOR_ALL; + return TRUE; + } + } + + return FALSE; +} + +static void +gdk_win32_surface_set_geometry_hints (GdkSurface *window, + const GdkGeometry *geometry, + GdkSurfaceHints geom_mask) +{ + GdkSurfaceImplWin32 *impl; + FullscreenInfo *fi; + + g_return_if_fail (GDK_IS_SURFACE (window)); + + if (GDK_SURFACE_DESTROYED (window)) + return; + + GDK_NOTE (MISC, g_print ("gdk_surface_set_geometry_hints: %p\n", + GDK_SURFACE_HWND (window))); + + impl = GDK_SURFACE_IMPL_WIN32 (window->impl); + + fi = g_object_get_data (G_OBJECT (window), "fullscreen-info"); + if (fi) + fi->hint_flags = geom_mask; + else + impl->hint_flags = geom_mask; + impl->hints = *geometry; + + if (geom_mask & GDK_HINT_POS) + { + /* even the X11 mplementation doesn't care */ + } + + if (geom_mask & GDK_HINT_MIN_SIZE) + { + GDK_NOTE (MISC, g_print ("... MIN_SIZE: %dx%d\n", + geometry->min_width, geometry->min_height)); + } + + if (geom_mask & GDK_HINT_MAX_SIZE) + { + GDK_NOTE (MISC, g_print ("... MAX_SIZE: %dx%d\n", + geometry->max_width, geometry->max_height)); + } + + if (geom_mask & GDK_HINT_BASE_SIZE) + { + GDK_NOTE (MISC, g_print ("... BASE_SIZE: %dx%d\n", + geometry->base_width, geometry->base_height)); + } + + if (geom_mask & GDK_HINT_RESIZE_INC) + { + GDK_NOTE (MISC, g_print ("... RESIZE_INC: (%d,%d)\n", + geometry->width_inc, geometry->height_inc)); + } + + if (geom_mask & GDK_HINT_ASPECT) + { + GDK_NOTE (MISC, g_print ("... ASPECT: %g--%g\n", + geometry->min_aspect, geometry->max_aspect)); + } + + if (geom_mask & GDK_HINT_WIN_GRAVITY) + { + GDK_NOTE (MISC, g_print ("... GRAVITY: %d\n", geometry->win_gravity)); + } + + _gdk_win32_surface_update_style_bits (window); +} + +static void +gdk_win32_surface_set_title (GdkSurface *window, + const gchar *title) +{ + wchar_t *wtitle; + + g_return_if_fail (GDK_IS_SURFACE (window)); + g_return_if_fail (title != NULL); + + if (GDK_SURFACE_DESTROYED (window)) + return; + + /* Empty window titles not allowed, so set it to just a period. */ + if (!title[0]) + title = "."; + + GDK_NOTE (MISC, g_print ("gdk_surface_set_title: %p: %s\n", + GDK_SURFACE_HWND (window), title)); + + GDK_NOTE (MISC_OR_EVENTS, title = g_strdup_printf ("%p %s", GDK_SURFACE_HWND (window), title)); + + wtitle = g_utf8_to_utf16 (title, -1, NULL, NULL, NULL); + API_CALL (SetWindowTextW, (GDK_SURFACE_HWND (window), wtitle)); + g_free (wtitle); + + GDK_NOTE (MISC_OR_EVENTS, g_free ((char *) title)); +} + +static void +gdk_win32_surface_set_role (GdkSurface *window, + const gchar *role) +{ + g_return_if_fail (GDK_IS_SURFACE (window)); + + GDK_NOTE (MISC, g_print ("gdk_surface_set_role: %p: %s\n", + GDK_SURFACE_HWND (window), + (role ? role : "NULL"))); + /* XXX */ +} + +static void +gdk_win32_surface_set_transient_for (GdkSurface *window, + GdkSurface *parent) +{ + HWND window_id, parent_id; + LONG_PTR old_ptr; + DWORD w32_error; + GdkSurfaceImplWin32 *window_impl = GDK_SURFACE_IMPL_WIN32 (window->impl); + GdkSurfaceImplWin32 *parent_impl = NULL; + GSList *item; + + g_return_if_fail (GDK_IS_SURFACE (window)); + + window_id = GDK_SURFACE_HWND (window); + parent_id = parent != NULL ? GDK_SURFACE_HWND (parent) : NULL; + + GDK_NOTE (MISC, g_print ("gdk_surface_set_transient_for: %p: %p\n", window_id, parent_id)); + + if (GDK_SURFACE_DESTROYED (window) || (parent && GDK_SURFACE_DESTROYED (parent))) + { + if (GDK_SURFACE_DESTROYED (window)) + GDK_NOTE (MISC, g_print ("... destroyed!\n")); + else + GDK_NOTE (MISC, g_print ("... owner destroyed!\n")); + + return; + } + + if (parent == NULL) + { + GdkSurfaceImplWin32 *trans_impl = GDK_SURFACE_IMPL_WIN32 (window_impl->transient_owner->impl); + if (trans_impl->transient_children != NULL) + { + item = g_slist_find (trans_impl->transient_children, window); + item->data = NULL; + trans_impl->transient_children = g_slist_delete_link (trans_impl->transient_children, item); + trans_impl->num_transients--; + + if (!trans_impl->num_transients) + { + trans_impl->transient_children = NULL; + } + } + g_object_unref (G_OBJECT (window_impl->transient_owner)); + g_object_unref (G_OBJECT (window)); + + window_impl->transient_owner = NULL; + } + else + { + parent_impl = GDK_SURFACE_IMPL_WIN32 (parent->impl); + + parent_impl->transient_children = g_slist_append (parent_impl->transient_children, window); + g_object_ref (G_OBJECT (window)); + parent_impl->num_transients++; + window_impl->transient_owner = parent; + g_object_ref (G_OBJECT (parent)); + } + + SetLastError (0); + old_ptr = GetWindowLongPtr (window_id, GWLP_HWNDPARENT); + w32_error = GetLastError (); + + /* Don't re-set GWLP_HWNDPARENT to the same value */ + if ((HWND) old_ptr == parent_id && w32_error == NO_ERROR) + return; + + /* Don't return if it failed, try SetWindowLongPtr() anyway */ + if (old_ptr == 0 && w32_error != NO_ERROR) + WIN32_API_FAILED ("GetWindowLongPtr"); + + /* This changes the *owner* of the window, despite the misleading + * name. (Owner and parent are unrelated concepts.) At least that's + * what people who seem to know what they talk about say on + * USENET. Search on Google. + */ + SetLastError (0); + old_ptr = SetWindowLongPtr (window_id, GWLP_HWNDPARENT, (LONG_PTR) parent_id); + w32_error = GetLastError (); + + if (old_ptr == 0 && w32_error != NO_ERROR) + WIN32_API_FAILED ("SetWindowLongPtr"); +} + +void +_gdk_push_modal_window (GdkSurface *window) +{ + modal_window_stack = g_slist_prepend (modal_window_stack, + window); +} + +void +_gdk_remove_modal_window (GdkSurface *window) +{ + GSList *tmp; + + g_return_if_fail (window != NULL); + + /* It's possible to be NULL here if someone sets the modal hint of the window + * to FALSE before a modal window stack has ever been created. */ + if (modal_window_stack == NULL) + return; + + /* Find the requested window in the stack and remove it. Yeah, I realize this + * means we're not a 'real stack', strictly speaking. Sue me. :) */ + tmp = g_slist_find (modal_window_stack, window); + if (tmp != NULL) + { + modal_window_stack = g_slist_delete_link (modal_window_stack, tmp); + } +} + +gboolean +_gdk_modal_blocked (GdkSurface *window) +{ + GSList *l; + gboolean found_any = FALSE; + + for (l = modal_window_stack; l != NULL; l = l->next) + { + GdkSurface *modal = l->data; + + if (modal == window) + return FALSE; + + if (GDK_SURFACE_IS_MAPPED (modal)) + found_any = TRUE; + } + + return found_any; +} + +GdkSurface * +_gdk_modal_current (void) +{ + GSList *l; + + for (l = modal_window_stack; l != NULL; l = l->next) + { + GdkSurface *modal = l->data; + + if (GDK_SURFACE_IS_MAPPED (modal)) + return modal; + } + + return NULL; +} + +static void +gdk_win32_surface_get_geometry (GdkSurface *window, + gint *x, + gint *y, + gint *width, + gint *height) +{ + GdkDisplay *display; + gboolean window_is_root; + + display = gdk_surface_get_display (window); + + if (!GDK_SURFACE_DESTROYED (window)) + { + RECT rect; + GdkSurfaceImplWin32 *impl = GDK_SURFACE_IMPL_WIN32 (window->impl); + + API_CALL (GetClientRect, (GDK_SURFACE_HWND (window), &rect)); + + POINT pt; + GdkSurface *parent = gdk_surface_get_parent (window); + + pt.x = rect.left; + pt.y = rect.top; + ClientToScreen (GDK_SURFACE_HWND (window), &pt); + if (parent) + ScreenToClient (GDK_SURFACE_HWND (parent), &pt); + rect.left = pt.x; + rect.top = pt.y; + + pt.x = rect.right; + pt.y = rect.bottom; + ClientToScreen (GDK_SURFACE_HWND (window), &pt); + if (parent) + ScreenToClient (GDK_SURFACE_HWND (parent), &pt); + rect.right = pt.x; + rect.bottom = pt.y; + + if (parent == NULL) + { + rect.left += _gdk_offset_x * impl->window_scale; + rect.top += _gdk_offset_y * impl->window_scale; + rect.right += _gdk_offset_x * impl->window_scale; + rect.bottom += _gdk_offset_y * impl->window_scale; + } + + if (x) + *x = rect.left / impl->window_scale; + if (y) + *y = rect.top / impl->window_scale; + if (width) + *width = (rect.right - rect.left) / impl->window_scale; + if (height) + *height = (rect.bottom - rect.top) / impl->window_scale; + + GDK_NOTE (MISC, g_print ("gdk_win32_surface_get_geometry: %p: %ldx%ld@%+ld%\n", + GDK_SURFACE_HWND (window), + (rect.right - rect.left) / impl->window_scale, + (rect.bottom - rect.top) / impl->window_scale, + rect.left, rect.top)); + } +} + +static void +gdk_win32_surface_get_root_coords (GdkSurface *window, + gint x, + gint y, + gint *root_x, + gint *root_y) +{ + gint tx; + gint ty; + POINT pt; + GdkSurfaceImplWin32 *impl = GDK_SURFACE_IMPL_WIN32 (window->impl); + + pt.x = x * impl->window_scale; + pt.y = y * impl->window_scale; + ClientToScreen (GDK_SURFACE_HWND (window), &pt); + tx = pt.x; + ty = pt.y; + + if (root_x) + *root_x = (tx + _gdk_offset_x) / impl->window_scale; + if (root_y) + *root_y = (ty + _gdk_offset_y) / impl->window_scale; + + GDK_NOTE (MISC, g_print ("gdk_win32_surface_get_root_coords: %p: %+d%+d %+d%+d\n", + GDK_SURFACE_HWND (window), + x * impl->window_scale, + y * impl->window_scale, + (tx + _gdk_offset_x) / impl->window_scale, + (ty + _gdk_offset_y) / impl->window_scale)); +} + +static void +gdk_win32_surface_restack_toplevel (GdkSurface *window, + GdkSurface *sibling, + gboolean above) +{ + // ### TODO +} + +static void +gdk_win32_surface_get_frame_extents (GdkSurface *window, + GdkRectangle *rect) +{ + HWND hwnd; + RECT r; + GdkSurfaceImplWin32 *impl; + + g_return_if_fail (GDK_IS_SURFACE (window)); + g_return_if_fail (rect != NULL); + + rect->x = 0; + rect->y = 0; + rect->width = 1; + rect->height = 1; + + if (GDK_SURFACE_DESTROYED (window)) + return; + + /* FIXME: window is documented to be a toplevel GdkSurface, so is it really + * necessary to walk its parent chain? + */ + while (window->parent && window->parent->parent) + window = window->parent; + + impl = GDK_SURFACE_IMPL_WIN32 (window->impl); + hwnd = GDK_SURFACE_HWND (window); + API_CALL (GetWindowRect, (hwnd, &r)); + + /* Initialize to real, unscaled size */ + rect->x = r.left + _gdk_offset_x * impl->window_scale; + rect->y = r.top + _gdk_offset_y * impl->window_scale; + rect->width = (r.right - r.left); + rect->height = (r.bottom - r.top); + + /* Extend width and height to ensure that they cover the real size when de-scaled, + * and replace everyting with scaled values + */ + rect->width = (rect->width + rect->x % impl->window_scale + impl->window_scale - 1) / impl->window_scale; + rect->height = (rect->height + rect->y % impl->window_scale + impl->window_scale - 1) / impl->window_scale; + rect->x = r.left / impl->window_scale + _gdk_offset_x; + rect->y = r.top / impl->window_scale + _gdk_offset_y; + + GDK_NOTE (MISC, g_print ("gdk_surface_get_frame_extents: %p: %ldx%ld@%+ld%+ld\n", + GDK_SURFACE_HWND (window), + rect->width, + rect->height, + rect->x, rect->y)); +} + +static gboolean +gdk_surface_win32_get_device_state (GdkSurface *window, + GdkDevice *device, + gdouble *x, + gdouble *y, + GdkModifierType *mask) +{ + GdkSurface *child; + + g_return_val_if_fail (window == NULL || GDK_IS_SURFACE (window), FALSE); + + GDK_DEVICE_GET_CLASS (device)->query_state (device, window, + &child, + NULL, NULL, + x, y, mask); + return (child != NULL); +} + +void +gdk_display_warp_device (GdkDisplay *display, + GdkDevice *device, + gint x, + gint y) +{ + g_return_if_fail (display == gdk_display_get_default ()); + g_return_if_fail (GDK_IS_DEVICE (device)); + g_return_if_fail (display == gdk_device_get_display (device)); + + GDK_DEVICE_GET_CLASS (device)->warp (device, x, y); +} + +static GdkEventMask +gdk_win32_surface_get_events (GdkSurface *window) +{ + GdkSurfaceImplWin32 *impl; + + if (GDK_SURFACE_DESTROYED (window)) + return 0; + + impl = GDK_SURFACE_IMPL_WIN32 (window->impl); + + return impl->native_event_mask; +} + +static void +gdk_win32_surface_set_events (GdkSurface *window, + GdkEventMask event_mask) +{ + GdkSurfaceImplWin32 *impl; + + impl = GDK_SURFACE_IMPL_WIN32 (window->impl); + + /* gdk_surface_new() always sets the GDK_STRUCTURE_MASK, so better + * set it here, too. Not that I know or remember why it is + * necessary, will have to test some day. + */ + impl->native_event_mask = GDK_STRUCTURE_MASK | event_mask; +} + +static void +do_shape_combine_region (GdkSurface *window, + HRGN hrgn, + gint x, gint y) +{ + RECT rect; + GdkSurfaceImplWin32 *impl = GDK_SURFACE_IMPL_WIN32 (window->impl); + + GetClientRect (GDK_SURFACE_HWND (window), &rect); + + _gdk_win32_adjust_client_rect (window, &rect); + + OffsetRgn (hrgn, -rect.left, -rect.top); + OffsetRgn (hrgn, x, y); + + /* If this is a top-level window, add the title bar to the region */ + if (GDK_SURFACE_TYPE (window) == GDK_SURFACE_TOPLEVEL) + { + HRGN tmp = CreateRectRgn (0, 0, rect.right - rect.left, -rect.top); + CombineRgn (hrgn, hrgn, tmp, RGN_OR); + DeleteObject (tmp); + } + + SetWindowRgn (GDK_SURFACE_HWND (window), hrgn, TRUE); +} + +static void +gdk_win32_surface_set_accept_focus (GdkSurface *window, + gboolean accept_focus) +{ + g_return_if_fail (GDK_IS_SURFACE (window)); + + accept_focus = accept_focus != FALSE; + + if (window->accept_focus != accept_focus) + window->accept_focus = accept_focus; +} + +static void +gdk_win32_surface_set_focus_on_map (GdkSurface *window, + gboolean focus_on_map) +{ + g_return_if_fail (GDK_IS_SURFACE (window)); + + focus_on_map = focus_on_map != FALSE; + + if (window->focus_on_map != focus_on_map) + window->focus_on_map = focus_on_map; +} + +static void +gdk_win32_surface_set_icon_list (GdkSurface *window, + GList *textures) +{ + GdkTexture *big_texture, *small_texture; + gint big_diff, small_diff; + gint big_w, big_h, small_w, small_h; + gint w, h; + gint dw, dh, diff; + HICON small_hicon, big_hicon; + GdkSurfaceImplWin32 *impl; + + g_return_if_fail (GDK_IS_SURFACE (window)); + + if (GDK_SURFACE_DESTROYED (window) || textures == NULL) + return; + + impl = GDK_SURFACE_IMPL_WIN32 (window->impl); + + /* ideal sizes for small and large icons */ + big_w = GetSystemMetrics (SM_CXICON); + big_h = GetSystemMetrics (SM_CYICON); + small_w = GetSystemMetrics (SM_CXSMICON); + small_h = GetSystemMetrics (SM_CYSMICON); + + /* find closest sized icons in the list */ + big_texture = NULL; + small_texture = NULL; + big_diff = 0; + small_diff = 0; + + for (GList *l = textures; l; l = l->next) + { + GdkTexture *texture = l->data; + w = gdk_texture_get_width (texture); + h = gdk_texture_get_height (texture); + + dw = ABS (w - big_w); + dh = ABS (h - big_h); + diff = dw*dw + dh*dh; + if (big_texture == NULL || diff < big_diff) + { + big_texture = texture; + big_diff = diff; + } + + dw = ABS (w - small_w); + dh = ABS (h - small_h); + diff = dw*dw + dh*dh; + if (small_texture == NULL || diff < small_diff) + { + small_texture = texture; + small_diff = diff; + } + + textures = textures->next; + } + + /* Create the icons */ + big_hicon = _gdk_win32_texture_to_hicon (big_texture); + g_object_unref (big_texture); + small_hicon = _gdk_win32_texture_to_hicon (small_texture); + g_object_unref (small_texture); + + /* Set the icons */ + SendMessageW (GDK_SURFACE_HWND (window), WM_SETICON, ICON_BIG, + (LPARAM)big_hicon); + SendMessageW (GDK_SURFACE_HWND (window), WM_SETICON, ICON_SMALL, + (LPARAM)small_hicon); + + /* Store the icons, destroying any previous icons */ + if (impl->hicon_big) + GDI_CALL (DestroyIcon, (impl->hicon_big)); + impl->hicon_big = big_hicon; + if (impl->hicon_small) + GDI_CALL (DestroyIcon, (impl->hicon_small)); + impl->hicon_small = small_hicon; +} + +static void +gdk_win32_surface_set_icon_name (GdkSurface *window, + const gchar *name) +{ + /* In case I manage to confuse this again (or somebody else does): + * Please note that "icon name" here really *does* mean the name or + * title of an window minimized as an icon on the desktop, or in the + * taskbar. It has nothing to do with the freedesktop.org icon + * naming stuff. + */ + + g_return_if_fail (GDK_IS_SURFACE (window)); + + if (GDK_SURFACE_DESTROYED (window)) + return; + +#if 0 + /* This is not the correct thing to do. We should keep both the + * "normal" window title, and the icon name. When the window is + * minimized, call SetWindowText() with the icon name, and when the + * window is restored, with the normal window title. Also, the name + * is in UTF-8, so we should do the normal conversion to either wide + * chars or system codepage, and use either the W or A version of + * SetWindowText(), depending on Windows version. + */ + API_CALL (SetWindowText, (GDK_SURFACE_HWND (window), name)); +#endif +} + +static GdkSurface * +gdk_win32_surface_get_group (GdkSurface *window) +{ + g_return_val_if_fail (GDK_IS_SURFACE (window), NULL); + + if (GDK_SURFACE_DESTROYED (window)) + return NULL; + + g_warning ("gdk_surface_get_group not yet implemented"); + + return NULL; +} + +static void +gdk_win32_surface_set_group (GdkSurface *window, + GdkSurface *leader) +{ + g_return_if_fail (GDK_IS_SURFACE (window)); + g_return_if_fail (leader == NULL || GDK_IS_SURFACE (leader)); + + if (GDK_SURFACE_DESTROYED (window) || GDK_SURFACE_DESTROYED (leader)) + return; + + g_warning ("gdk_surface_set_group not implemented"); +} + +static void +update_single_bit (LONG *style, + gboolean all, + int gdk_bit, + int style_bit) +{ + /* all controls the interpretation of gdk_bit -- if all is TRUE, + * gdk_bit indicates whether style_bit is off; if all is FALSE, gdk + * bit indicate whether style_bit is on + */ + if ((!all && gdk_bit) || (all && !gdk_bit)) + *style |= style_bit; + else + *style &= ~style_bit; +} + +/* + * Returns TRUE if window has no decorations. + * Usually it means CSD windows, because GTK + * calls gdk_surface_set_decorations (window, 0); + * This is used to decide whether a toplevel should + * be made layered, thus it + * only returns TRUE for toplevels (until GTK minimal + * system requirements are lifted to Windows 8 or newer, + * because only toplevels can be layered). + */ +gboolean +_gdk_win32_surface_lacks_wm_decorations (GdkSurface *window) +{ + GdkSurfaceImplWin32 *impl; + LONG style; + gboolean has_any_decorations; + + if (GDK_SURFACE_DESTROYED (window)) + return FALSE; + + /* only toplevels can be layered */ + if (!WINDOW_IS_TOPLEVEL (window)) + return FALSE; + + impl = GDK_SURFACE_IMPL_WIN32 (window->impl); + + /* This is because GTK calls gdk_surface_set_decorations (window, 0), + * even though GdkWMDecoration docs indicate that 0 does NOT mean + * "no decorations". + */ + if (impl->decorations && + *impl->decorations == 0) + return TRUE; + + if (GDK_SURFACE_HWND (window) == 0) + return FALSE; + + style = GetWindowLong (GDK_SURFACE_HWND (window), GWL_STYLE); + + if (style == 0) + { + DWORD w32_error = GetLastError (); + + GDK_NOTE (MISC, g_print ("Failed to get style of window %p (handle %p): %lu\n", + window, GDK_SURFACE_HWND (window), w32_error)); + return FALSE; + } + + /* Keep this in sync with _gdk_win32_surface_update_style_bits() */ + /* We don't check what get_effective_window_decorations() + * has to say, because it gives suggestions based on + * various hints, while we want *actual* decorations, + * or their absence. + */ + has_any_decorations = FALSE; + + if (style & (WS_BORDER | WS_THICKFRAME | WS_CAPTION | + WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZEBOX)) + has_any_decorations = TRUE; + else + GDK_NOTE (MISC, g_print ("Window %p (handle %p): has no decorations (style %lx)\n", + window, GDK_SURFACE_HWND (window), style)); + + return !has_any_decorations; +} + +void +_gdk_win32_surface_update_style_bits (GdkSurface *window) +{ + GdkSurfaceImplWin32 *impl = (GdkSurfaceImplWin32 *)window->impl; + GdkWMDecoration decorations; + LONG old_style, new_style, old_exstyle, new_exstyle; + gboolean all; + RECT rect, before, after; + gboolean was_topmost; + gboolean will_be_topmost; + HWND insert_after; + UINT flags; + + if (window->state & GDK_SURFACE_STATE_FULLSCREEN) + return; + + old_style = GetWindowLong (GDK_SURFACE_HWND (window), GWL_STYLE); + old_exstyle = GetWindowLong (GDK_SURFACE_HWND (window), GWL_EXSTYLE); + + GetClientRect (GDK_SURFACE_HWND (window), &before); + after = before; + AdjustWindowRectEx (&before, old_style, FALSE, old_exstyle); + + was_topmost = (old_exstyle & WS_EX_TOPMOST) ? TRUE : FALSE; + will_be_topmost = was_topmost; + + old_exstyle &= ~WS_EX_TOPMOST; + + new_style = old_style; + new_exstyle = old_exstyle; + + if (window->window_type == GDK_SURFACE_TEMP) + { + new_exstyle |= WS_EX_TOOLWINDOW; + will_be_topmost = TRUE; + } + else if (impl->type_hint == GDK_SURFACE_TYPE_HINT_UTILITY) + { + new_exstyle |= WS_EX_TOOLWINDOW; + } + else + { + new_exstyle &= ~WS_EX_TOOLWINDOW; + } + + /* We can get away with using layered windows + * only when no decorations are needed. It can mean + * CSD or borderless non-CSD windows (tooltips?). + * + * If this window cannot use layered windows, disable it always. + * This currently applies to windows using OpenGL, which + * does not work with layered windows. + */ + if (impl->suppress_layered == 0) + { + if (_gdk_win32_surface_lacks_wm_decorations (window)) + impl->layered = g_strcmp0 (g_getenv ("GDK_WIN32_LAYERED"), "0") != 0; + } + else + impl->layered = FALSE; + + if (impl->layered) + new_exstyle |= WS_EX_LAYERED; + else + new_exstyle &= ~WS_EX_LAYERED; + + if (get_effective_window_decorations (window, &decorations)) + { + all = (decorations & GDK_DECOR_ALL); + /* Keep this in sync with the test in _gdk_win32_surface_lacks_wm_decorations() */ + update_single_bit (&new_style, all, decorations & GDK_DECOR_BORDER, WS_BORDER); + update_single_bit (&new_style, all, decorations & GDK_DECOR_RESIZEH, WS_THICKFRAME); + update_single_bit (&new_style, all, decorations & GDK_DECOR_TITLE, WS_CAPTION); + update_single_bit (&new_style, all, decorations & GDK_DECOR_MENU, WS_SYSMENU); + update_single_bit (&new_style, all, decorations & GDK_DECOR_MINIMIZE, WS_MINIMIZEBOX); + update_single_bit (&new_style, all, decorations & GDK_DECOR_MAXIMIZE, WS_MAXIMIZEBOX); + } + + if (old_style == new_style && old_exstyle == new_exstyle ) + { + GDK_NOTE (MISC, g_print ("_gdk_win32_surface_update_style_bits: %p: no change\n", + GDK_SURFACE_HWND (window))); + return; + } + + if (old_style != new_style) + { + GDK_NOTE (MISC, g_print ("_gdk_win32_surface_update_style_bits: %p: STYLE: %s => %s\n", + GDK_SURFACE_HWND (window), + _gdk_win32_surface_style_to_string (old_style), + _gdk_win32_surface_style_to_string (new_style))); + + SetWindowLong (GDK_SURFACE_HWND (window), GWL_STYLE, new_style); + } + + if (old_exstyle != new_exstyle) + { + GDK_NOTE (MISC, g_print ("_gdk_win32_surface_update_style_bits: %p: EXSTYLE: %s => %s\n", + GDK_SURFACE_HWND (window), + _gdk_win32_surface_exstyle_to_string (old_exstyle), + _gdk_win32_surface_exstyle_to_string (new_exstyle))); + + SetWindowLong (GDK_SURFACE_HWND (window), GWL_EXSTYLE, new_exstyle); + } + + AdjustWindowRectEx (&after, new_style, FALSE, new_exstyle); + + GetWindowRect (GDK_SURFACE_HWND (window), &rect); + rect.left += after.left - before.left; + rect.top += after.top - before.top; + rect.right += after.right - before.right; + rect.bottom += after.bottom - before.bottom; + + flags = SWP_FRAMECHANGED | SWP_NOACTIVATE | SWP_NOREPOSITION; + + if (will_be_topmost && !was_topmost) + { + insert_after = HWND_TOPMOST; + } + else if (was_topmost && !will_be_topmost) + { + insert_after = HWND_NOTOPMOST; + } + else + { + flags |= SWP_NOZORDER; + insert_after = SWP_NOZORDER_SPECIFIED; + } + + SetWindowPos (GDK_SURFACE_HWND (window), insert_after, + rect.left, rect.top, + rect.right - rect.left, rect.bottom - rect.top, + flags); +} + +static void +update_single_system_menu_entry (HMENU hmenu, + gboolean all, + int gdk_bit, + int menu_entry) +{ + /* all controls the interpretation of gdk_bit -- if all is TRUE, + * gdk_bit indicates whether menu entry is disabled; if all is + * FALSE, gdk bit indicate whether menu entry is enabled + */ + if ((!all && gdk_bit) || (all && !gdk_bit)) + EnableMenuItem (hmenu, menu_entry, MF_BYCOMMAND | MF_ENABLED); + else + EnableMenuItem (hmenu, menu_entry, MF_BYCOMMAND | MF_GRAYED); +} + +static void +update_system_menu (GdkSurface *window) +{ + GdkWMFunction functions; + BOOL all; + + if (_gdk_surface_get_functions (window, &functions)) + { + HMENU hmenu = GetSystemMenu (GDK_SURFACE_HWND (window), FALSE); + + all = (functions & GDK_FUNC_ALL); + update_single_system_menu_entry (hmenu, all, functions & GDK_FUNC_RESIZE, SC_SIZE); + update_single_system_menu_entry (hmenu, all, functions & GDK_FUNC_MOVE, SC_MOVE); + update_single_system_menu_entry (hmenu, all, functions & GDK_FUNC_MINIMIZE, SC_MINIMIZE); + update_single_system_menu_entry (hmenu, all, functions & GDK_FUNC_MAXIMIZE, SC_MAXIMIZE); + update_single_system_menu_entry (hmenu, all, functions & GDK_FUNC_CLOSE, SC_CLOSE); + } +} + +static void +gdk_win32_surface_set_decorations (GdkSurface *window, + GdkWMDecoration decorations) +{ + GdkSurfaceImplWin32 *impl; + + g_return_if_fail (GDK_IS_SURFACE (window)); + + impl = GDK_SURFACE_IMPL_WIN32 (window->impl); + + GDK_NOTE (MISC, g_print ("gdk_surface_set_decorations: %p: %s %s%s%s%s%s%s\n", + GDK_SURFACE_HWND (window), + (decorations & GDK_DECOR_ALL ? "clearing" : "setting"), + (decorations & GDK_DECOR_BORDER ? "BORDER " : ""), + (decorations & GDK_DECOR_RESIZEH ? "RESIZEH " : ""), + (decorations & GDK_DECOR_TITLE ? "TITLE " : ""), + (decorations & GDK_DECOR_MENU ? "MENU " : ""), + (decorations & GDK_DECOR_MINIMIZE ? "MINIMIZE " : ""), + (decorations & GDK_DECOR_MAXIMIZE ? "MAXIMIZE " : ""))); + + if (!impl->decorations) + impl->decorations = g_malloc (sizeof (GdkWMDecoration)); + + *impl->decorations = decorations; + + _gdk_win32_surface_update_style_bits (window); +} + +static gboolean +gdk_win32_surface_get_decorations (GdkSurface *window, + GdkWMDecoration *decorations) +{ + GdkSurfaceImplWin32 *impl; + + g_return_val_if_fail (GDK_IS_SURFACE (window), FALSE); + + impl = GDK_SURFACE_IMPL_WIN32 (window->impl); + + if (impl->decorations == NULL) + return FALSE; + + *decorations = *impl->decorations; + + return TRUE; +} + +static GQuark +get_functions_quark () +{ + static GQuark quark = 0; + + if (!quark) + quark = g_quark_from_static_string ("gdk-window-functions"); + + return quark; +} + +static void +gdk_win32_surface_set_functions (GdkSurface *window, + GdkWMFunction functions) +{ + GdkWMFunction* functions_copy; + + g_return_if_fail (GDK_IS_SURFACE (window)); + + GDK_NOTE (MISC, g_print ("gdk_surface_set_functions: %p: %s %s%s%s%s%s\n", + GDK_SURFACE_HWND (window), + (functions & GDK_FUNC_ALL ? "clearing" : "setting"), + (functions & GDK_FUNC_RESIZE ? "RESIZE " : ""), + (functions & GDK_FUNC_MOVE ? "MOVE " : ""), + (functions & GDK_FUNC_MINIMIZE ? "MINIMIZE " : ""), + (functions & GDK_FUNC_MAXIMIZE ? "MAXIMIZE " : ""), + (functions & GDK_FUNC_CLOSE ? "CLOSE " : ""))); + + functions_copy = g_malloc (sizeof (GdkWMFunction)); + *functions_copy = functions; + g_object_set_qdata_full (G_OBJECT (window), get_functions_quark (), functions_copy, g_free); + + update_system_menu (window); +} + +gboolean +_gdk_surface_get_functions (GdkSurface *window, + GdkWMFunction *functions) +{ + GdkWMFunction* functions_set; + + functions_set = g_object_get_qdata (G_OBJECT (window), get_functions_quark ()); + if (functions_set) + *functions = *functions_set; + + return (functions_set != NULL); +} + +#if defined(MORE_AEROSNAP_DEBUGGING) +static void +log_region (gchar *prefix, AeroSnapEdgeRegion *region) +{ + GDK_NOTE (MISC, g_print ("Region %s:\n" + "edge %d x %d @ %d x %d\n" + "trig %d x %d @ %d x %d\n", + prefix, + region->edge.width, + region->edge.height, + region->edge.x, + region->edge.y, + region->trigger.width, + region->trigger.height, + region->trigger.x, + region->trigger.y)); +} +#endif + +static void +calculate_aerosnap_regions (GdkW32DragMoveResizeContext *context) +{ + GdkDisplay *display; + gint n_monitors, monitor_idx, other_monitor_idx; + GdkSurfaceImplWin32 *impl = GDK_SURFACE_IMPL_WIN32 (context->window->impl); +#if defined(MORE_AEROSNAP_DEBUGGING) + gint i; +#endif + + display = gdk_display_get_default (); + n_monitors = gdk_display_get_n_monitors (display); + +#define _M_UP 0 +#define _M_DOWN 1 +#define _M_LEFT 2 +#define _M_RIGHT 3 + + for (monitor_idx = 0; monitor_idx < n_monitors; monitor_idx++) + { + GdkRectangle wa; + GdkRectangle geometry; + AeroSnapEdgeRegion snap_region; + gboolean move_edge[4] = { TRUE, FALSE, TRUE, TRUE }; + gboolean resize_edge[2] = { TRUE, TRUE }; + gint diff; + gint thickness, trigger_thickness; + GdkMonitor *monitor; + + monitor = gdk_display_get_monitor (display, monitor_idx); + gdk_monitor_get_workarea (monitor, &wa); + gdk_monitor_get_geometry (monitor, &geometry); + + for (other_monitor_idx = 0; + other_monitor_idx < n_monitors && + (move_edge[_M_UP] || move_edge[_M_LEFT] || + move_edge[_M_RIGHT] || resize_edge[_M_DOWN]); + other_monitor_idx++) + { + GdkRectangle other_wa; + GdkMonitor *other_monitor; + + if (other_monitor_idx == monitor_idx) + continue; + + other_monitor = gdk_display_get_monitor (display, other_monitor_idx); + gdk_monitor_get_workarea (other_monitor, &other_wa); + + /* An edge triggers AeroSnap only if there are no + * monitors beyond that edge. + * Even if there's another monitor, but it does not cover + * the whole edge (it's smaller or is not aligned to + * the corner of current monitor), that edge is still + * removed from the trigger list. + */ + if (other_wa.x >= wa.x + wa.width) + move_edge[_M_RIGHT] = FALSE; + + if (other_wa.x + other_wa.width <= wa.x) + move_edge[_M_LEFT] = FALSE; + + if (other_wa.y + other_wa.height <= wa.y) + { + move_edge[_M_UP] = FALSE; + resize_edge[_M_UP] = FALSE; + } + + if (other_wa.y >= wa.y + wa.height) + { + /* no move_edge for the bottom edge, just resize_edge */ + resize_edge[_M_DOWN] = FALSE; + } + } + + thickness = AEROSNAP_REGION_THICKNESS * impl->window_scale; + trigger_thickness = AEROSNAP_REGION_TRIGGER_THICKNESS * impl->window_scale; + + snap_region.edge = wa; + snap_region.trigger = wa; + snap_region.edge.height = thickness; + snap_region.trigger.height = trigger_thickness; + + /* Extend both regions into toolbar space. + * When there's no toolbar, diff == 0. + */ + diff = wa.y - geometry.y; + snap_region.edge.height += diff; + snap_region.edge.y -= diff; + snap_region.trigger.height += diff; + snap_region.trigger.y -= diff; + + if (move_edge[_M_UP]) + g_array_append_val (context->maximize_regions, snap_region); + + if (resize_edge[_M_UP]) + g_array_append_val (context->fullup_regions, snap_region); + + snap_region.edge = wa; + snap_region.trigger = wa; + snap_region.edge.width = thickness; + snap_region.trigger.width = trigger_thickness; + + diff = wa.x - geometry.x; + snap_region.edge.width += diff; + snap_region.edge.x -= diff; + snap_region.trigger.width += diff; + snap_region.trigger.x -= diff; + + if (move_edge[_M_LEFT]) + g_array_append_val (context->halfleft_regions, snap_region); + + snap_region.edge = wa; + snap_region.trigger = wa; + snap_region.edge.x += wa.width - thickness; + snap_region.edge.width = thickness; + snap_region.trigger.x += wa.width - trigger_thickness; + snap_region.trigger.width = trigger_thickness; + + diff = (geometry.x + geometry.width) - (wa.x + wa.width); + snap_region.edge.width += diff; + snap_region.trigger.width += diff; + + if (move_edge[_M_RIGHT]) + g_array_append_val (context->halfright_regions, snap_region); + + snap_region.edge = wa; + snap_region.trigger = wa; + snap_region.edge.y += wa.height - thickness; + snap_region.edge.height = thickness; + snap_region.trigger.y += wa.height - trigger_thickness; + snap_region.trigger.height = trigger_thickness; + + diff = (geometry.y + geometry.height) - (wa.y + wa.height); + snap_region.edge.height += diff; + snap_region.trigger.height += diff; + + if (resize_edge[_M_DOWN]) + g_array_append_val (context->fullup_regions, snap_region); + } + +#undef _M_UP +#undef _M_DOWN +#undef _M_LEFT +#undef _M_RIGHT + +#if defined(MORE_AEROSNAP_DEBUGGING) + for (i = 0; i < context->maximize_regions->len; i++) + log_region ("maximize", &g_array_index (context->maximize_regions, AeroSnapEdgeRegion, i)); + + for (i = 0; i < context->halfleft_regions->len; i++) + log_region ("halfleft", &g_array_index (context->halfleft_regions, AeroSnapEdgeRegion, i)); + + for (i = 0; i < context->halfright_regions->len; i++) + log_region ("halfright", &g_array_index (context->halfright_regions, AeroSnapEdgeRegion, i)); + + for (i = 0; i < context->fullup_regions->len; i++) + log_region ("fullup", &g_array_index (context->fullup_regions, AeroSnapEdgeRegion, i)); +#endif +} + +static void +discard_snapinfo (GdkSurface *window) +{ + GdkSurfaceImplWin32 *impl; + + impl = GDK_SURFACE_IMPL_WIN32 (window->impl); + + impl->snap_state = GDK_WIN32_AEROSNAP_STATE_UNDETERMINED; + + if (impl->snap_stash == NULL) + return; + + g_clear_pointer (&impl->snap_stash, g_free); + g_clear_pointer (&impl->snap_stash_int, g_free); +} + +static void +unsnap (GdkSurface *window, + GdkMonitor *monitor) +{ + GdkSurfaceImplWin32 *impl; + GdkRectangle rect; + + impl = GDK_SURFACE_IMPL_WIN32 (window->impl); + + impl->snap_state = GDK_WIN32_AEROSNAP_STATE_UNDETERMINED; + + if (impl->snap_stash == NULL) + return; + + gdk_monitor_get_workarea (monitor, &rect); + + GDK_NOTE (MISC, g_print ("Monitor work area %d x %d @ %d : %d\n", rect.width, rect.height, rect.x, rect.y)); + + if (rect.width >= impl->snap_stash_int->width && + rect.height >= impl->snap_stash_int->height) + { + /* If the window fits into new work area without resizing it, + * place it into new work area without resizing it. + */ + gdouble left, right, up, down, hratio, vratio; + gdouble hscale, vscale; + gdouble new_left, new_up; + + left = impl->snap_stash->x; + right = 1.0 - (impl->snap_stash->x + impl->snap_stash->width); + up = impl->snap_stash->y; + down = 1.0 - (impl->snap_stash->y + impl->snap_stash->height); + hscale = 1.0; + + if (right > 0.001) + { + hratio = left / right; + hscale = hratio / (1.0 + hratio); + } + + new_left = (gdouble) (rect.width - impl->snap_stash_int->width) * hscale; + + vscale = 1.0; + + if (down > 0.001) + { + vratio = up / down; + vscale = vratio / (1.0 + vratio); + } + + new_up = (gdouble) (rect.height - impl->snap_stash_int->height) * vscale; + + rect.x = round (rect.x + new_left); + rect.y = round (rect.y + new_up); + rect.width = impl->snap_stash_int->width; + rect.height = impl->snap_stash_int->height; + } + else + { + /* Calculate actual unsnapped window size based on its + * old relative size. Same for position. + */ + rect.x += round (rect.width * impl->snap_stash->x); + rect.y += round (rect.height * impl->snap_stash->y); + rect.width = round (rect.width * impl->snap_stash->width); + rect.height = round (rect.height * impl->snap_stash->height); + } + + GDK_NOTE (MISC, g_print ("Unsnapped window size %d x %d @ %d : %d\n", rect.width, rect.height, rect.x, rect.y)); + + gdk_surface_move_resize (window, rect.x, rect.y, + rect.width, rect.height); + + g_clear_pointer (&impl->snap_stash, g_free); + g_clear_pointer (&impl->snap_stash_int, g_free); +} + +static void +stash_window (GdkSurface *window, + GdkSurfaceImplWin32 *impl) +{ + gint x, y; + gint width, wwidth; + gint height, wheight; + WINDOWPLACEMENT placement; + HMONITOR hmonitor; + MONITORINFO hmonitor_info; + + placement.length = sizeof(WINDOWPLACEMENT); + + /* Use W32 API to get unmaximized window size, which GDK doesn't remember */ + if (!GetWindowPlacement (GDK_SURFACE_HWND (window), &placement)) + return; + + /* MSDN is very vague, but in practice rcNormalPosition is the same as GetWindowRect(), + * only with adjustments for toolbars (which creates rather weird coodinate space issues). + * We need to get monitor info and apply workarea vs monitorarea diff to turn + * these into screen coordinates proper. + */ + hmonitor = MonitorFromWindow (GDK_SURFACE_HWND (window), MONITOR_DEFAULTTONEAREST); + hmonitor_info.cbSize = sizeof (hmonitor_info); + + if (!GetMonitorInfoA (hmonitor, &hmonitor_info)) + return; + + if (impl->snap_stash == NULL) + impl->snap_stash = g_new0 (GdkRectangleDouble, 1); + + if (impl->snap_stash_int == NULL) + impl->snap_stash_int = g_new0 (GdkRectangle, 1); + + GDK_NOTE (MISC, g_print ("monitor work area %ld x %ld @ %ld : %ld\n", + (hmonitor_info.rcWork.right - hmonitor_info.rcWork.left) / impl->window_scale, + (hmonitor_info.rcWork.bottom - hmonitor_info.rcWork.top) / impl->window_scale, + hmonitor_info.rcWork.left, + hmonitor_info.rcWork.top)); + GDK_NOTE (MISC, g_print ("monitor area %ld x %ld @ %ld : %ld\n", + (hmonitor_info.rcMonitor.right - hmonitor_info.rcMonitor.left) / impl->window_scale, + (hmonitor_info.rcMonitor.bottom - hmonitor_info.rcMonitor.top) / impl->window_scale, + hmonitor_info.rcMonitor.left, + hmonitor_info.rcMonitor.top)); + GDK_NOTE (MISC, g_print ("window work place %ld x %ld @ %ld : %ld\n", + (placement.rcNormalPosition.right - placement.rcNormalPosition.left) / impl->window_scale, + (placement.rcNormalPosition.bottom - placement.rcNormalPosition.top) / impl->window_scale, + placement.rcNormalPosition.left, + placement.rcNormalPosition.top)); + + width = (placement.rcNormalPosition.right - placement.rcNormalPosition.left) / impl->window_scale; + height = (placement.rcNormalPosition.bottom - placement.rcNormalPosition.top) / impl->window_scale; + x = (placement.rcNormalPosition.left - hmonitor_info.rcMonitor.left) / impl->window_scale; + y = (placement.rcNormalPosition.top - hmonitor_info.rcMonitor.top) / impl->window_scale; + + wwidth = (hmonitor_info.rcWork.right - hmonitor_info.rcWork.left) / impl->window_scale; + wheight = (hmonitor_info.rcWork.bottom - hmonitor_info.rcWork.top) / impl->window_scale; + + impl->snap_stash->x = (gdouble) (x) / (gdouble) (wwidth); + impl->snap_stash->y = (gdouble) (y) / (gdouble) (wheight); + impl->snap_stash->width = (gdouble) width / (gdouble) (wwidth); + impl->snap_stash->height = (gdouble) height / (gdouble) (wheight); + + impl->snap_stash_int->x = x; + impl->snap_stash_int->y = y; + impl->snap_stash_int->width = width; + impl->snap_stash_int->height = height; + + GDK_NOTE (MISC, g_print ("Stashed window %d x %d @ %d : %d as %f x %f @ %f : %f\n", + width, height, x, y, + impl->snap_stash->width, impl->snap_stash->height, impl->snap_stash->x, impl->snap_stash->y)); +} + +static void +snap_up (GdkSurface *window) +{ + SHORT maxysize; + gint x, y; + gint width, height; + GdkSurfaceImplWin32 *impl; + + impl = GDK_SURFACE_IMPL_WIN32 (window->impl); + + impl->snap_state = GDK_WIN32_AEROSNAP_STATE_FULLUP; + + stash_window (window, impl); + + maxysize = GetSystemMetrics (SM_CYVIRTUALSCREEN) / impl->window_scale; + gdk_surface_get_position (window, &x, &y); + width = gdk_surface_get_width (window); + + y = 0; + height = maxysize; + + x = x - impl->margins.left; + y = y - impl->margins.top; + width += impl->margins_x; + height += impl->margins_y; + + gdk_surface_move_resize (window, x, y, width, height); +} + +static void +snap_left (GdkSurface *window, + GdkMonitor *monitor, + GdkMonitor *snap_monitor) +{ + GdkRectangle rect; + GdkSurfaceImplWin32 *impl; + + impl = GDK_SURFACE_IMPL_WIN32 (window->impl); + + impl->snap_state = GDK_WIN32_AEROSNAP_STATE_HALFLEFT; + + gdk_monitor_get_workarea (snap_monitor, &rect); + + stash_window (window, impl); + + rect.width = rect.width / 2; + + rect.x = rect.x - impl->margins.left; + rect.y = rect.y - impl->margins.top; + rect.width = rect.width + impl->margins_x; + rect.height = rect.height + impl->margins_y; + + gdk_surface_move_resize (window, rect.x, rect.y, rect.width, rect.height); +} + +static void +snap_right (GdkSurface *window, + GdkMonitor *monitor, + GdkMonitor *snap_monitor) +{ + GdkRectangle rect; + GdkSurfaceImplWin32 *impl; + + impl = GDK_SURFACE_IMPL_WIN32 (window->impl); + + impl->snap_state = GDK_WIN32_AEROSNAP_STATE_HALFRIGHT; + + gdk_monitor_get_workarea (snap_monitor, &rect); + + stash_window (window, impl); + + rect.width = rect.width / 2; + rect.x += rect.width; + + rect.x = rect.x - impl->margins.left; + rect.y = rect.y - impl->margins.top; + rect.width = rect.width + impl->margins_x; + rect.height = rect.height + impl->margins_y; + + gdk_surface_move_resize (window, rect.x, rect.y, rect.width, rect.height); +} + +void +_gdk_win32_surface_handle_aerosnap (GdkSurface *window, + GdkWin32AeroSnapCombo combo) +{ + GdkSurfaceImplWin32 *impl; + GdkDisplay *display; + gint n_monitors; + GdkSurfaceState window_state = gdk_surface_get_state (window); + gboolean minimized = window_state & GDK_SURFACE_STATE_ICONIFIED; + gboolean maximized = window_state & GDK_SURFACE_STATE_MAXIMIZED; + gboolean halfsnapped; + GdkMonitor *monitor; + + impl = GDK_SURFACE_IMPL_WIN32 (window->impl); + display = gdk_surface_get_display (window); + n_monitors = gdk_display_get_n_monitors (display); + monitor = gdk_display_get_monitor_at_window (display, window); + + if (minimized && maximized) + minimized = FALSE; + + halfsnapped = (impl->snap_state == GDK_WIN32_AEROSNAP_STATE_HALFRIGHT || + impl->snap_state == GDK_WIN32_AEROSNAP_STATE_HALFLEFT || + impl->snap_state == GDK_WIN32_AEROSNAP_STATE_FULLUP); + + switch (combo) + { + case GDK_WIN32_AEROSNAP_COMBO_NOTHING: + /* Do nothing */ + break; + case GDK_WIN32_AEROSNAP_COMBO_UP: + if (!maximized) + { + unsnap (window, monitor); + gdk_surface_maximize (window); + } + break; + case GDK_WIN32_AEROSNAP_COMBO_DOWN: + case GDK_WIN32_AEROSNAP_COMBO_SHIFTDOWN: + if (maximized) + { + gdk_surface_unmaximize (window); + unsnap (window, monitor); + } + else if (halfsnapped) + unsnap (window, monitor); + else if (!minimized) + gdk_surface_iconify (window); + break; + case GDK_WIN32_AEROSNAP_COMBO_LEFT: + if (maximized) + gdk_surface_unmaximize (window); + + if (impl->snap_state == GDK_WIN32_AEROSNAP_STATE_UNDETERMINED || + impl->snap_state == GDK_WIN32_AEROSNAP_STATE_FULLUP) + { + unsnap (window, monitor); + snap_left (window, monitor, monitor); + } + else if (impl->snap_state == GDK_WIN32_AEROSNAP_STATE_HALFLEFT) + { + unsnap (window, monitor); + snap_right (window, + monitor, + gdk_monitor_is_primary (monitor) ? monitor : gdk_display_get_monitor (display, n_monitors - 1)); + } + else if (impl->snap_state == GDK_WIN32_AEROSNAP_STATE_HALFRIGHT) + { + unsnap (window, monitor); + } + break; + case GDK_WIN32_AEROSNAP_COMBO_RIGHT: + if (maximized) + gdk_surface_unmaximize (window); + + if (impl->snap_state == GDK_WIN32_AEROSNAP_STATE_UNDETERMINED || + impl->snap_state == GDK_WIN32_AEROSNAP_STATE_FULLUP) + { + unsnap (window, monitor); + snap_right (window, monitor, monitor); + } + else if (impl->snap_state == GDK_WIN32_AEROSNAP_STATE_HALFLEFT) + { + unsnap (window, monitor); + } + else if (impl->snap_state == GDK_WIN32_AEROSNAP_STATE_HALFRIGHT) + { + gint i; + + unsnap (window, monitor); + if (n_monitors == 1 || + monitor == gdk_display_get_monitor (display, n_monitors - 1)) + { + snap_left (window, monitor, monitor); + } + else + { + for (i = 0; i < n_monitors; i++) + { + if (monitor == gdk_display_get_monitor (display, i)) + break; + } + + snap_left (window, monitor, gdk_display_get_monitor (display, i + 1)); + } + } + break; + case GDK_WIN32_AEROSNAP_COMBO_SHIFTUP: + if (!maximized && + impl->snap_state == GDK_WIN32_AEROSNAP_STATE_UNDETERMINED) + { + snap_up (window); + } + break; + case GDK_WIN32_AEROSNAP_COMBO_SHIFTLEFT: + case GDK_WIN32_AEROSNAP_COMBO_SHIFTRIGHT: + /* No implementation needed at the moment */ + break; + } +} + +static void +apply_snap (GdkSurface *window, + GdkWin32AeroSnapState snap) +{ + GdkMonitor *monitor; + GdkDisplay *display; + + display = gdk_surface_get_display (window); + monitor = gdk_display_get_monitor_at_window (display, window); + + switch (snap) + { + case GDK_WIN32_AEROSNAP_STATE_UNDETERMINED: + break; + case GDK_WIN32_AEROSNAP_STATE_MAXIMIZE: + unsnap (window, monitor); + gdk_surface_maximize (window); + break; + case GDK_WIN32_AEROSNAP_STATE_HALFLEFT: + unsnap (window, monitor); + snap_left (window, monitor, monitor); + break; + case GDK_WIN32_AEROSNAP_STATE_HALFRIGHT: + unsnap (window, monitor); + snap_right (window, monitor, monitor); + break; + case GDK_WIN32_AEROSNAP_STATE_FULLUP: + snap_up (window); + break; + } +} + +/* Registers a dumb window class. This window + * has DefWindowProc() for a window procedure and + * does not do anything that GdkSurface-bound HWNDs do. + */ +static ATOM +RegisterGdkDumbClass () +{ + static ATOM klassDUMB = 0; + static WNDCLASSEXW wcl; + ATOM klass = 0; + + wcl.cbSize = sizeof (WNDCLASSEX); + wcl.style = 0; /* DON'T set CS_REDRAW. It causes total redraw + * on WM_SIZE and WM_MOVE. Flicker, Performance! + */ + wcl.lpfnWndProc = DefWindowProcW; + wcl.cbClsExtra = 0; + wcl.cbWndExtra = 0; + wcl.hInstance = _gdk_app_hmodule; + wcl.hIcon = 0; + wcl.hIconSm = 0; + wcl.lpszMenuName = NULL; + wcl.hbrBackground = NULL; + wcl.hCursor = LoadCursor (NULL, IDC_ARROW); + wcl.style |= CS_OWNDC; + wcl.lpszClassName = L"gdkWindowDumb"; + + if (klassDUMB == 0) + klassDUMB = RegisterClassExW (&wcl); + + klass = klassDUMB; + + if (klass == 0) + { + WIN32_API_FAILED ("RegisterClassExW"); + g_error ("That is a fatal error"); + } + + return klass; +} + +static gboolean +ensure_snap_indicator_exists (GdkW32DragMoveResizeContext *context) +{ + if (context->shape_indicator == NULL) + { + HWND handle; + ATOM klass; + klass = RegisterGdkDumbClass (); + + handle = CreateWindowExW (WS_EX_TRANSPARENT | WS_EX_LAYERED | WS_EX_NOACTIVATE, + MAKEINTRESOURCEW (klass), + L"", + WS_POPUP, + 0, + 0, + 0, 0, + NULL, + NULL, + _gdk_app_hmodule, + NULL); + + context->shape_indicator = handle; + } + + return context->shape_indicator != NULL; +} + +static gboolean +ensure_snap_indicator_surface (GdkW32DragMoveResizeContext *context, + gint width, + gint height, + guint scale) +{ + if (context->indicator_surface != NULL && + (context->indicator_surface_width < width || + context->indicator_surface_height < height)) + { + cairo_surface_destroy (context->indicator_surface); + context->indicator_surface = NULL; + } + + if (context->indicator_surface == NULL) + context->indicator_surface = cairo_win32_surface_create_with_dib (CAIRO_FORMAT_ARGB32, + width * scale, + height * scale); + + if (cairo_surface_status (context->indicator_surface) != CAIRO_STATUS_SUCCESS) + { + cairo_surface_destroy (context->indicator_surface); + context->indicator_surface = NULL; + + return FALSE; + } + + return TRUE; +} + +/* Indicator is drawn with some inward offset, so that it does + * not hug screen edges. + */ +static void +adjust_indicator_rectangle (GdkRectangle *rect, + gboolean inward) +{ + gdouble inverter; + const gint gap = AEROSNAP_INDICATOR_EDGE_GAP; +#if defined(MORE_AEROSNAP_DEBUGGING) + GdkRectangle cache = *rect; +#endif + + if (inward) + inverter = 1.0; + else + inverter = -1.0; + + rect->x += (gap * inverter); + rect->y += (gap * inverter); + rect->width -= (gap * 2 * inverter); + rect->height -= (gap * 2 * inverter); + +#if defined(MORE_AEROSNAP_DEBUGGING) + GDK_NOTE (MISC, g_print ("Adjusted %d x %d @ %d : %d -> %d x %d @ %d : %d\n", + cache.width, cache.height, cache.x, cache.y, + rect->width, rect->height, rect->x, rect->y)); +#endif +} + +static void +rounded_rectangle (cairo_t *cr, + gint x, + gint y, + gint width, + gint height, + gdouble radius, + gdouble line_width, + GdkRGBA *fill, + GdkRGBA *outline) +{ + gdouble degrees = M_PI / 180.0; + + if (fill == NULL && outline == NULL) + return; + + cairo_save (cr); + cairo_new_sub_path (cr); + cairo_arc (cr, x + width - radius, y + radius, radius, -90 * degrees, 0 * degrees); + cairo_arc (cr, x + width - radius, y + height - radius, radius, 0 * degrees, 90 * degrees); + cairo_arc (cr, (x + radius), y + height - radius, radius, 90 * degrees, 180 * degrees); + cairo_arc (cr, (x + radius), (y + radius), radius, 180 * degrees, 270 * degrees); + cairo_close_path (cr); + + if (fill) + { + cairo_set_source_rgba (cr, fill->red, fill->green, fill->blue, fill->alpha); + + if (outline) + cairo_fill_preserve (cr); + else + cairo_fill (cr); + } + + if (outline) + { + cairo_set_source_rgba (cr, outline->red, outline->green, outline->blue, outline->alpha); + cairo_set_line_width (cr, line_width); + cairo_stroke (cr); + } + + cairo_restore (cr); +} + +/* Translates linear animation scale into some kind of curve */ +static gdouble +curve (gdouble val) +{ + /* TODO: try different curves. For now it's just linear */ + return val; +} + +static gboolean +draw_indicator (GdkW32DragMoveResizeContext *context, + gint64 timestamp) +{ + cairo_t *cr; + GdkRGBA outline = {0, 0, 1.0, 1.0}; + GdkRGBA fill = {0, 0, 1.0, 0.8}; + GdkRectangle current_rect; + gint64 current_time = g_get_monotonic_time (); + gdouble animation_progress; + gboolean last_draw; + gdouble line_width; + gdouble corner_radius; + gint64 animation_duration; + GdkSurfaceImplWin32 *impl = GDK_SURFACE_IMPL_WIN32 (context->window->impl); + + line_width = AEROSNAP_INDICATOR_LINE_WIDTH * impl->window_scale; + corner_radius = AEROSNAP_INDICATOR_CORNER_RADIUS; + animation_duration = AEROSNAP_INDICATOR_ANIMATION_DURATION; + last_draw = FALSE; + + if (timestamp == 0 && + current_time - context->indicator_start_time > animation_duration) + { + timestamp = context->indicator_start_time + animation_duration; + last_draw = TRUE; + } + + if (timestamp != 0) + current_time = timestamp; + + animation_progress = (gdouble) (current_time - context->indicator_start_time) / animation_duration; + + if (animation_progress > 1.0) + animation_progress = 1.0; + + if (animation_progress < 0) + animation_progress = 0; + + animation_progress = curve (animation_progress); + + current_rect = context->indicator_start; + current_rect.x += (context->indicator_target.x - context->indicator_start.x) * animation_progress; + current_rect.y += (context->indicator_target.y - context->indicator_start.y) * animation_progress; + current_rect.width += (context->indicator_target.width - context->indicator_start.width) * animation_progress; + current_rect.height += (context->indicator_target.height - context->indicator_start.height) * animation_progress; + + if (context->op == GDK_WIN32_DRAGOP_RESIZE && last_draw) + { + switch (context->edge) + { + case GDK_SURFACE_EDGE_NORTH_WEST: + current_rect.x = context->indicator_target.x + (context->indicator_target.width - current_rect.width); + current_rect.y = context->indicator_target.y + (context->indicator_target.height - current_rect.height); + break; + case GDK_SURFACE_EDGE_NORTH: + current_rect.y = context->indicator_target.y + (context->indicator_target.height - current_rect.height); + break; + case GDK_SURFACE_EDGE_WEST: + current_rect.x = context->indicator_target.x + (context->indicator_target.width - current_rect.width); + break; + case GDK_SURFACE_EDGE_SOUTH_WEST: + current_rect.x = context->indicator_target.x + (context->indicator_target.width - current_rect.width); + current_rect.y = context->indicator_target.y; + break; + case GDK_SURFACE_EDGE_NORTH_EAST: + current_rect.x = context->indicator_target.x; + current_rect.y = context->indicator_target.y + (context->indicator_target.height - current_rect.height); + break; + case GDK_SURFACE_EDGE_SOUTH_EAST: + current_rect.x = context->indicator_target.x; + current_rect.y = context->indicator_target.y; + break; + case GDK_SURFACE_EDGE_SOUTH: + current_rect.y = context->indicator_target.y; + break; + case GDK_SURFACE_EDGE_EAST: + current_rect.x = context->indicator_target.x; + break; + } + } + + cr = cairo_create (context->indicator_surface); + rounded_rectangle (cr, + (current_rect.x - context->indicator_window_rect.x) * impl->window_scale, + (current_rect.y - context->indicator_window_rect.y) * impl->window_scale, + current_rect.width * impl->window_scale, + current_rect.height * impl->window_scale, + corner_radius, + line_width, + &fill, &outline); + cairo_destroy (cr); + +#if defined(MORE_AEROSNAP_DEBUGGING) + GDK_NOTE (MISC, g_print ("Indicator is %d x %d @ %d : %d; current time is %" G_GINT64_FORMAT "\n", + current_rect.width, current_rect.height, + current_rect.x - context->indicator_window_rect.x, + current_rect.y - context->indicator_window_rect.y, + current_time)); +#endif + + return last_draw; +} + +static gboolean +redraw_indicator (gpointer user_data) +{ + GdkW32DragMoveResizeContext *context = user_data; + POINT window_position; + SIZE window_size; + BLENDFUNCTION blender; + HDC hdc; + POINT source_point = { 0, 0 }; + gboolean last_draw; + gdouble indicator_opacity; + GdkSurfaceImplWin32 *impl; + gboolean do_source_remove = FALSE; + + indicator_opacity = AEROSNAP_INDICATOR_OPACITY; + + if (GDK_SURFACE_DESTROYED (context->window) || + !ensure_snap_indicator_exists (context)) + { + do_source_remove = TRUE; + } + + impl = GDK_SURFACE_IMPL_WIN32 (context->window->impl); + + if (!ensure_snap_indicator_surface (context, + context->indicator_window_rect.width, + context->indicator_window_rect.height, + impl->window_scale)) + { + do_source_remove = TRUE; + } + + if (do_source_remove) + { + context->timer = 0; + return G_SOURCE_REMOVE; + } + + last_draw = draw_indicator (context, context->draw_timestamp); + + window_position.x = (context->indicator_window_rect.x - _gdk_offset_x) * impl->window_scale; + window_position.y = (context->indicator_window_rect.y - _gdk_offset_y) * impl->window_scale; + window_size.cx = context->indicator_window_rect.width * impl->window_scale; + window_size.cy = context->indicator_window_rect.height * impl->window_scale; + + blender.BlendOp = AC_SRC_OVER; + blender.BlendFlags = 0; + blender.AlphaFormat = AC_SRC_ALPHA; + blender.SourceConstantAlpha = 255 * indicator_opacity; + + hdc = cairo_win32_surface_get_dc (context->indicator_surface); + + API_CALL (SetWindowPos, (context->shape_indicator, + GDK_SURFACE_HWND (context->window), + 0, 0, 0, 0, + SWP_NOMOVE | SWP_NOSIZE | SWP_NOREDRAW | SWP_SHOWWINDOW | SWP_NOACTIVATE)); + +#if defined(MORE_AEROSNAP_DEBUGGING) + GDK_NOTE (MISC, g_print ("Indicator window position is %ld x %ld @ %ld : %ld\n", + window_size.cx, window_size.cy, + window_position.x, window_position.y)); +#endif + + API_CALL (UpdateLayeredWindow, (context->shape_indicator, NULL, + &window_position, &window_size, + hdc, &source_point, + 0, &blender, ULW_ALPHA)); + + if (last_draw) + context->timer = 0; + + return last_draw ? G_SOURCE_REMOVE : G_SOURCE_CONTINUE; +} + +static GdkRectangle +unity_of_rects (GdkRectangle a, + GdkRectangle b) +{ + GdkRectangle u = b; + + if (a.x < u.x) + { + u.width += u.x - a.x; + u.x = a.x; + } + + if (a.y < u.y) + { + u.height += (u.y - a.y); + u.y = a.y; + } + + if (a.x + a.width > u.x + u.width) + u.width += (a.x + a.width) - (u.x + u.width); + + if (a.y + a.height > u.y + u.height) + u.height += (a.y + a.height) - (u.y + u.height); + +#if defined(MORE_AEROSNAP_DEBUGGING) + GDK_NOTE (MISC, g_print ("Unified 2 rects into %d x %d @ %d : %d\n", + u.width, u.height, u.x, u.y)); +#endif + + return u; +} + +static void +start_indicator_drawing (GdkW32DragMoveResizeContext *context, + GdkRectangle from, + GdkRectangle to, + guint scale) +{ + GdkRectangle to_adjusted, from_adjusted, from_or_to; + gint64 indicator_animation_tick = AEROSNAP_INDICATOR_ANIMATION_TICK; + + GDK_NOTE (MISC, g_print ("Start drawing snap indicator %d x %d @ %d : %d -> %d x %d @ %d : %d\n", + from.width * scale, from.height * scale, from.x, from.y, to.width * scale, to.height * scale, to.x, to.y)); + + if (GDK_SURFACE_DESTROYED (context->window)) + return; + + if (!ensure_snap_indicator_exists (context)) + return; + + from_or_to = unity_of_rects (from, to); + + if (!ensure_snap_indicator_surface (context, from_or_to.width, from_or_to.height, scale)) + return; + + to_adjusted = to; + adjust_indicator_rectangle (&to_adjusted, TRUE); + + from_adjusted = from; + adjust_indicator_rectangle (&from_adjusted, TRUE); + + context->draw_timestamp = 0; + context->indicator_start = from_adjusted; + context->indicator_target = to_adjusted; + context->indicator_window_rect = from_or_to; + context->indicator_start_time = g_get_monotonic_time (); + + if (context->timer) + { + g_source_remove (context->timer); + context->timer = 0; + } + + context->timer = g_timeout_add_full (G_PRIORITY_DEFAULT, + indicator_animation_tick, + redraw_indicator, + context, + NULL); +} + +static void +update_fullup_indicator (GdkSurface *window, + GdkW32DragMoveResizeContext *context) +{ + SHORT maxysize; + GdkRectangle from, to; + GdkRectangle to_adjusted, from_adjusted, from_or_to; + GdkSurfaceImplWin32 *impl; + + GDK_NOTE (MISC, g_print ("Update fullup indicator\n")); + + if (GDK_SURFACE_DESTROYED (context->window)) + return; + + if (context->shape_indicator == NULL) + return; + + impl = GDK_SURFACE_IMPL_WIN32 (window->impl); + maxysize = GetSystemMetrics (SM_CYVIRTUALSCREEN); + gdk_surface_get_position (window, &to.x, &to.y); + to.width = gdk_surface_get_width (window); + to.height = gdk_surface_get_height (window); + + to.y = 0; + to.height = maxysize; + from = context->indicator_target; + + if (context->timer == 0) + { + from_adjusted = from; + adjust_indicator_rectangle (&from_adjusted, FALSE); + + GDK_NOTE (MISC, g_print ("Restart fullup animation from %d x %d @ %d : %d -> %d x %d @ %d x %d\n", + context->indicator_target.width, context->indicator_target.height, + context->indicator_target.x, context->indicator_target.y, + to.width, to.height, to.x, to.y)); + start_indicator_drawing (context, from_adjusted, to, impl->window_scale); + + return; + } + + from_or_to = unity_of_rects (from, to); + + to_adjusted = to; + adjust_indicator_rectangle (&to_adjusted, TRUE); + + GDK_NOTE (MISC, g_print ("Retarget fullup animation %d x %d @ %d : %d -> %d x %d @ %d x %d\n", + context->indicator_target.width, context->indicator_target.height, + context->indicator_target.x, context->indicator_target.y, + to_adjusted.width, to_adjusted.height, to_adjusted.x, to_adjusted.y)); + + context->indicator_target = to_adjusted; + context->indicator_window_rect = from_or_to; + + ensure_snap_indicator_surface (context, from_or_to.width, from_or_to.height, impl->window_scale); +} + +static void +start_indicator (GdkSurface *window, + GdkW32DragMoveResizeContext *context, + gint x, + gint y, + GdkWin32AeroSnapState state) +{ + GdkMonitor *monitor; + GdkRectangle workarea; + SHORT maxysize; + GdkRectangle start_size, end_size; + GdkDisplay *display; + GdkSurfaceImplWin32 *impl = GDK_SURFACE_IMPL_WIN32 (window->impl); + + display = gdk_surface_get_display (window); + monitor = gdk_display_get_monitor_at_point (display, x, y); + gdk_monitor_get_workarea (monitor, &workarea); + + maxysize = GetSystemMetrics (SM_CYVIRTUALSCREEN) / impl->window_scale; + gdk_surface_get_position (window, &start_size.x, &start_size.y); + start_size.width = gdk_surface_get_width (window); + start_size.height = gdk_surface_get_height (window); + + end_size = start_size; + + switch (state) + { + case GDK_WIN32_AEROSNAP_STATE_UNDETERMINED: + return; + case GDK_WIN32_AEROSNAP_STATE_MAXIMIZE: + end_size.x = workarea.x; + end_size.y = workarea.y; + end_size.width = workarea.width; + end_size.height = workarea.height; + break; + case GDK_WIN32_AEROSNAP_STATE_HALFLEFT: + end_size.x = workarea.x; + end_size.y = workarea.y; + end_size.width = workarea.width / 2; + end_size.height = workarea.height; + break; + case GDK_WIN32_AEROSNAP_STATE_HALFRIGHT: + end_size.x = (workarea.x + workarea.width / 2); + end_size.y = workarea.y; + end_size.width = workarea.width / 2; + end_size.height = workarea.height; + break; + case GDK_WIN32_AEROSNAP_STATE_FULLUP: + end_size.y = 0; + end_size.height = maxysize; + break; + } + + start_indicator_drawing (context, start_size, end_size, impl->window_scale); +} + +static void +stop_indicator (GdkSurface *window, + GdkW32DragMoveResizeContext *context) +{ + GDK_NOTE (MISC, g_print ("Stop drawing snap indicator\n")); + + if (context->timer) + { + g_source_remove (context->timer); + context->timer = 0; + } + + API_CALL (SetWindowPos, (context->shape_indicator, + SWP_NOZORDER_SPECIFIED, + 0, 0, 0, 0, + SWP_NOZORDER | SWP_NOMOVE | + SWP_NOSIZE | SWP_NOREDRAW | SWP_HIDEWINDOW | SWP_NOACTIVATE)); +} + +static gint +point_in_aerosnap_region (gint x, + gint y, + AeroSnapEdgeRegion *region) +{ + gint edge, trigger; + + edge = (x >= region->edge.x && + y >= region->edge.y && + x <= region->edge.x + region->edge.width && + y <= region->edge.y + region->edge.height) ? 1 : 0; + trigger = (x >= region->trigger.x && + y >= region->trigger.y && + x <= region->trigger.x + region->trigger.width && + y <= region->trigger.y + region->trigger.height) ? 1 : 0; + return edge + trigger; +} + +static void +handle_aerosnap_move_resize (GdkSurface *window, + GdkW32DragMoveResizeContext *context, + gint x, + gint y) +{ + gint i; + AeroSnapEdgeRegion *reg; + gint maximize = 0; + gint halfleft = 0; + gint halfright = 0; + gint fullup = 0; + gboolean fullup_edge = FALSE; + GdkSurfaceImplWin32 *impl = GDK_SURFACE_IMPL_WIN32 (window->impl); + + if (context->op == GDK_WIN32_DRAGOP_RESIZE) + switch (context->edge) + { + case GDK_SURFACE_EDGE_NORTH_WEST: + case GDK_SURFACE_EDGE_NORTH_EAST: + case GDK_SURFACE_EDGE_WEST: + case GDK_SURFACE_EDGE_EAST: + case GDK_SURFACE_EDGE_SOUTH_WEST: + case GDK_SURFACE_EDGE_SOUTH_EAST: + break; + case GDK_SURFACE_EDGE_SOUTH: + case GDK_SURFACE_EDGE_NORTH: + fullup_edge = TRUE; + break; + } + + for (i = 0; i < context->maximize_regions->len && maximize == 0; i++) + { + reg = &g_array_index (context->maximize_regions, AeroSnapEdgeRegion, i); + maximize = point_in_aerosnap_region (x, y, reg); + } + + for (i = 0; i < context->halfleft_regions->len && halfleft == 0; i++) + { + reg = &g_array_index (context->halfleft_regions, AeroSnapEdgeRegion, i); + halfleft = point_in_aerosnap_region (x, y, reg); + } + + for (i = 0; i < context->halfright_regions->len && halfright == 0; i++) + { + reg = &g_array_index (context->halfright_regions, AeroSnapEdgeRegion, i); + halfright = point_in_aerosnap_region (x, y, reg); + } + + for (i = 0; i < context->fullup_regions->len && fullup == 0; i++) + { + reg = &g_array_index (context->fullup_regions, AeroSnapEdgeRegion, i); + fullup = point_in_aerosnap_region (x, y, reg); + } + +#if defined(MORE_AEROSNAP_DEBUGGING) + GDK_NOTE (MISC, g_print ("AeroSnap: point %d : %d - max: %d, left %d, right %d, up %d\n", + x, y, maximize, halfleft, halfright, fullup)); +#endif + + if (!context->revealed) + { + if (context->op == GDK_WIN32_DRAGOP_MOVE && maximize == 2) + { + context->revealed = TRUE; + context->current_snap = GDK_WIN32_AEROSNAP_STATE_MAXIMIZE; + start_indicator (window, context, x, y, context->current_snap); + } + else if (context->op == GDK_WIN32_DRAGOP_MOVE && halfleft == 2) + { + context->revealed = TRUE; + context->current_snap = GDK_WIN32_AEROSNAP_STATE_HALFLEFT; + start_indicator (window, context, x, y, context->current_snap); + } + else if (context->op == GDK_WIN32_DRAGOP_MOVE && halfright == 2) + { + context->revealed = TRUE; + context->current_snap = GDK_WIN32_AEROSNAP_STATE_HALFRIGHT; + start_indicator (window, context, x, y, context->current_snap); + } + else if (context->op == GDK_WIN32_DRAGOP_RESIZE && fullup == 2 && fullup_edge) + { + context->revealed = TRUE; + context->current_snap = GDK_WIN32_AEROSNAP_STATE_FULLUP; + start_indicator (window, context, x, y, context->current_snap); + } + + return; + } + + switch (context->current_snap) + { + case GDK_WIN32_AEROSNAP_STATE_UNDETERMINED: + if (context->op == GDK_WIN32_DRAGOP_RESIZE && fullup > 0) + { + context->current_snap = GDK_WIN32_AEROSNAP_STATE_FULLUP; + start_indicator (window, context, x, y, context->current_snap); + } + break; + case GDK_WIN32_AEROSNAP_STATE_MAXIMIZE: + if (context->op == GDK_WIN32_DRAGOP_MOVE && maximize > 0) + break; + if (context->op == GDK_WIN32_DRAGOP_MOVE && halfleft > 0) + { + context->current_snap = GDK_WIN32_AEROSNAP_STATE_HALFLEFT; + start_indicator (window, context, x, y, context->current_snap); + } + else if (context->op == GDK_WIN32_DRAGOP_MOVE && halfright > 0) + { + context->current_snap = GDK_WIN32_AEROSNAP_STATE_HALFRIGHT; + start_indicator (window, context, x, y, context->current_snap); + } + else + { + context->current_snap = GDK_WIN32_AEROSNAP_STATE_UNDETERMINED; + stop_indicator (window, context); + context->revealed = FALSE; + } + break; + case GDK_WIN32_AEROSNAP_STATE_HALFLEFT: + if (context->op == GDK_WIN32_DRAGOP_MOVE && halfleft > 0) + break; + if (context->op == GDK_WIN32_DRAGOP_MOVE && maximize > 0) + { + context->current_snap = GDK_WIN32_AEROSNAP_STATE_MAXIMIZE; + start_indicator (window, context, x, y, context->current_snap); + } + else if (context->op == GDK_WIN32_DRAGOP_MOVE && halfright > 0) + { + context->current_snap = GDK_WIN32_AEROSNAP_STATE_HALFRIGHT; + start_indicator (window, context, x, y, context->current_snap); + } + else + { + context->current_snap = GDK_WIN32_AEROSNAP_STATE_UNDETERMINED; + stop_indicator (window, context); + context->revealed = FALSE; + } + break; + case GDK_WIN32_AEROSNAP_STATE_HALFRIGHT: + if (context->op == GDK_WIN32_DRAGOP_MOVE && halfright > 0) + break; + if (context->op == GDK_WIN32_DRAGOP_MOVE && maximize > 0) + { + context->current_snap = GDK_WIN32_AEROSNAP_STATE_MAXIMIZE; + start_indicator (window, context, x, y, context->current_snap); + } + else if (context->op == GDK_WIN32_DRAGOP_MOVE && halfleft > 0) + { + context->current_snap = GDK_WIN32_AEROSNAP_STATE_HALFLEFT; + start_indicator (window, context, x, y, context->current_snap); + } + else + { + context->current_snap = GDK_WIN32_AEROSNAP_STATE_UNDETERMINED; + stop_indicator (window, context); + context->revealed = FALSE; + } + break; + case GDK_WIN32_AEROSNAP_STATE_FULLUP: + if (context->op == GDK_WIN32_DRAGOP_RESIZE && fullup > 0 && fullup_edge) + { + update_fullup_indicator (window, context); + break; + } + + context->current_snap = GDK_WIN32_AEROSNAP_STATE_UNDETERMINED; + stop_indicator (window, context); + break; + } +} + + +static const gchar * +get_cursor_name_from_op (GdkW32WindowDragOp op, + GdkSurfaceEdge edge) +{ + switch (op) + { + case GDK_WIN32_DRAGOP_MOVE: + return "move"; + case GDK_WIN32_DRAGOP_RESIZE: + switch (edge) + { + case GDK_SURFACE_EDGE_NORTH_WEST: + return "nw-resize"; + case GDK_SURFACE_EDGE_NORTH: + return "n-resize"; + case GDK_SURFACE_EDGE_NORTH_EAST: + return "ne-resize"; + case GDK_SURFACE_EDGE_WEST: + return "w-resize"; + case GDK_SURFACE_EDGE_EAST: + return "e-resize"; + case GDK_SURFACE_EDGE_SOUTH_WEST: + return "sw-resize"; + case GDK_SURFACE_EDGE_SOUTH: + return "s-resize"; + case GDK_SURFACE_EDGE_SOUTH_EAST: + return "se-resize"; + } + /* default: warn about unhandled enum values, + * fallthrough to GDK_WIN32_DRAGOP_NONE case + */ + case GDK_WIN32_DRAGOP_COUNT: + g_assert_not_reached (); + case GDK_WIN32_DRAGOP_NONE: + return "default"; + /* default: warn about unhandled enum values */ + } + + g_assert_not_reached (); + + return NULL; +} + +static gboolean +point_in_window (GdkSurface *window, + gdouble x, + gdouble y) +{ + return x >= 0 && x < window->width && + y >= 0 && y < window->height && + (window->shape == NULL || + cairo_region_contains_point (window->shape, x, y)) && + (window->input_shape == NULL || + cairo_region_contains_point (window->input_shape, x, y)); +} + +static GdkSurface * +child_window_at_coordinates (GdkSurface *window, + gint root_x, + gint root_y) +{ + gint x, y; + GList *l; + GList *children; + + children = gdk_surface_peek_children (window); + gdk_surface_get_root_origin (window, &x, &y); + x = root_x - x; + y = root_y - y; + + for (l = children; l; l = g_list_next (l)) + { + GdkSurface *child = GDK_SURFACE (l->data); + + if (point_in_window (child, x, y)) + return child; + } + + return window; +} + +static void +setup_drag_move_resize_context (GdkSurface *window, + GdkW32DragMoveResizeContext *context, + GdkW32WindowDragOp op, + GdkSurfaceEdge edge, + GdkDevice *device, + gint button, + gint root_x, + gint root_y, + guint32 timestamp) +{ + RECT rect; + const gchar *cursor_name; + GdkSurface *pointer_window; + GdkSurfaceImplWin32 *impl = GDK_SURFACE_IMPL_WIN32 (window->impl); + GdkDisplay *display = gdk_device_get_display (device); + gboolean maximized = gdk_surface_get_state (window) & GDK_SURFACE_STATE_MAXIMIZED; + + /* Before we drag, we need to undo any maximization or snapping. + * AeroSnap behaviour: + * If snapped halfleft/halfright: + * horizontal resize: + * resize + * don't unsnap + * keep stashed unsnapped size intact + * vertical resize: + * resize + * unsnap to new size (merge cached unsnapped state with current + * snapped state in such a way that the gripped edge + * does not move) + * diagonal resize: + * difficult to test (first move is usually either purely + * horizontal or purely vertical, in which + * case the above behaviour applies) + * If snapped up: + * horizontal resize: + * resize + * don't unsnap + * apply new width and x position to unsnapped cache, + * so that unsnapped window only regains its height + * and y position, but inherits x and width from + * the fullup snapped state + * vertical resize: + * unsnap to new size (merge cached unsnapped state with current + * snapped state in such a way that the gripped edge + * does not move) + * + * This implementation behaviour: + * If snapped halfleft/halfright/fullup: + * any resize: + * unsnap to current size, discard cached pre-snap state + * + * TODO: make this implementation behave as AeroSnap on resizes? + * There's also the case where + * a halfleft/halfright window isn't unsnapped when it's + * being moved horizontally, but it's more difficult to implement. + */ + if (op == GDK_WIN32_DRAGOP_RESIZE && + (impl->snap_state == GDK_WIN32_AEROSNAP_STATE_HALFRIGHT || + impl->snap_state == GDK_WIN32_AEROSNAP_STATE_HALFLEFT || + impl->snap_state == GDK_WIN32_AEROSNAP_STATE_FULLUP)) + { + discard_snapinfo (window); + } + else if (maximized || + (impl->snap_state == GDK_WIN32_AEROSNAP_STATE_HALFRIGHT || + impl->snap_state == GDK_WIN32_AEROSNAP_STATE_HALFLEFT || + impl->snap_state == GDK_WIN32_AEROSNAP_STATE_FULLUP)) + { + GdkMonitor *monitor; + gint wx, wy, wwidth, wheight; + gint swx, swy, swwidth, swheight; + gboolean pointer_outside_of_window; + gint offsetx, offsety; + gboolean left_half; + GdkDisplay *display; + + display = gdk_surface_get_display (window); + monitor = gdk_display_get_monitor_at_window (display, window); + gdk_surface_get_geometry (window, &wx, &wy, &wwidth, &wheight); + + swx = wx; + swy = wy; + swwidth = wwidth; + swheight = wheight; + + /* Subtract window shadow. We don't want pointer to go outside of + * the visible window during drag-move. For drag-resize it's OK. + * Don't take shadow into account if the window is maximized - + * maximized windows don't have shadows. + */ + if (op == GDK_WIN32_DRAGOP_MOVE && !maximized) + { + swx += impl->margins.left / impl->window_scale; + swy += impl->margins.top / impl->window_scale; + swwidth -= impl->margins_x; + swheight -= impl->margins_y; + } + + pointer_outside_of_window = root_x < swx || root_x > swx + swwidth || + root_y < swy || root_y > swy + swheight; + /* Calculate the offset of the pointer relative to the window */ + offsetx = root_x - swx; + offsety = root_y - swy; + + /* Figure out in which half of the window the pointer is. + * The code currently only concerns itself with horizontal + * dimension (left/right halves). + * There's no upper/lower half, because usually window + * is dragged by its upper half anyway. If that changes, adjust + * accordingly. + */ + left_half = (offsetx < swwidth / 2); + + /* Inverse the offset for it to be from the right edge */ + if (!left_half) + offsetx = swwidth - offsetx; + + GDK_NOTE (MISC, g_print ("Pointer at %d : %d, this is %d : %d relative to the window's %s\n", + root_x, root_y, offsetx, offsety, + left_half ? "left half" : "right half")); + + /* Move window in such a way that on unmaximization/unsnapping the pointer + * is still pointing at the appropriate half of the window, + * with the same offset from the left or right edge. If the new + * window size is too small, and adding that offset puts the pointer + * into the other half or even beyond, move the pointer to the middle. + */ + if (!pointer_outside_of_window && maximized) + { + WINDOWPLACEMENT placement; + gint unmax_width, unmax_height; + gint shadow_unmax_width, shadow_unmax_height; + + placement.length = sizeof (placement); + API_CALL (GetWindowPlacement, (GDK_SURFACE_HWND (window), &placement)); + + GDK_NOTE (MISC, g_print ("W32 WM unmaximized window placement is %ld x %ld @ %ld : %ld\n", + placement.rcNormalPosition.right - placement.rcNormalPosition.left, + placement.rcNormalPosition.bottom - placement.rcNormalPosition.top, + placement.rcNormalPosition.left + _gdk_offset_x * impl->window_scale, + placement.rcNormalPosition.top + _gdk_offset_y * impl->window_scale)); + + unmax_width = placement.rcNormalPosition.right - placement.rcNormalPosition.left; + unmax_height = placement.rcNormalPosition.bottom - placement.rcNormalPosition.top; + + shadow_unmax_width = unmax_width - impl->margins_x * impl->window_scale; + shadow_unmax_height = unmax_height - impl->margins_y * impl->window_scale; + + if (offsetx * impl->window_scale < (shadow_unmax_width / 2) && + offsety * impl->window_scale < (shadow_unmax_height / 2)) + { + placement.rcNormalPosition.top = (root_y - offsety + impl->margins.top - _gdk_offset_y) * impl->window_scale; + placement.rcNormalPosition.bottom = placement.rcNormalPosition.top + unmax_height; + + if (left_half) + { + placement.rcNormalPosition.left = (root_x - offsetx + impl->margins.left - _gdk_offset_x) * impl->window_scale; + placement.rcNormalPosition.right = placement.rcNormalPosition.left + unmax_width; + } + else + { + placement.rcNormalPosition.right = (root_x + offsetx + impl->margins.right - _gdk_offset_x) * impl->window_scale; + placement.rcNormalPosition.left = placement.rcNormalPosition.right - unmax_width; + } + } + else + { + placement.rcNormalPosition.left = (root_x * impl->window_scale) - + (unmax_width / 2) - + (_gdk_offset_x * impl->window_scale); + + if (offsety * impl->window_scale < shadow_unmax_height / 2) + placement.rcNormalPosition.top = (root_y - offsety + impl->margins.top - _gdk_offset_y) * impl->window_scale; + else + placement.rcNormalPosition.top = (root_y * impl->window_scale) - + (unmax_height / 2) - + (_gdk_offset_y * impl->window_scale); + + placement.rcNormalPosition.right = placement.rcNormalPosition.left + unmax_width; + placement.rcNormalPosition.bottom = placement.rcNormalPosition.top + unmax_height; + } + + GDK_NOTE (MISC, g_print ("Unmaximized window will be at %ld : %ld\n", + placement.rcNormalPosition.left + _gdk_offset_x * impl->window_scale, + placement.rcNormalPosition.top + _gdk_offset_y * impl->window_scale)); + + API_CALL (SetWindowPlacement, (GDK_SURFACE_HWND (window), &placement)); + } + else if (!pointer_outside_of_window && impl->snap_stash_int) + { + GdkRectangle new_pos; + GdkRectangle snew_pos; + + new_pos.width = impl->snap_stash_int->width; + new_pos.height = impl->snap_stash_int->height; + snew_pos = new_pos; + + if (op == GDK_WIN32_DRAGOP_MOVE) + { + snew_pos.width -= impl->margins_x; + snew_pos.height -= impl->margins_y; + } + + if (offsetx < snew_pos.width / 2 && offsety < snew_pos.height / 2) + { + new_pos.y = root_y - offsety + impl->margins.top / impl->window_scale; + + if (left_half) + new_pos.x = root_x - offsetx + impl->margins.left / impl->window_scale; + else + new_pos.x = root_x + offsetx + impl->margins.left / impl->window_scale - new_pos.width; + } + else + { + new_pos.x = root_x - new_pos.width / 2; + new_pos.y = root_y - new_pos.height / 2; + } + + GDK_NOTE (MISC, g_print ("Unsnapped window to %d : %d\n", + new_pos.x, new_pos.y)); + discard_snapinfo (window); + gdk_surface_move_resize (window, new_pos.x, new_pos.y, + new_pos.width, new_pos.height); + } + + + if (maximized) + gdk_surface_unmaximize (window); + else + unsnap (window, monitor); + + if (pointer_outside_of_window) + { + /* Pointer outside of the window, move pointer into window */ + GDK_NOTE (MISC, g_print ("Pointer at %d : %d is outside of %d x %d @ %d : %d, move it to %d : %d\n", + root_x, root_y, wwidth, wheight, wx, wy, wx + wwidth / 2, wy + wheight / 2)); + root_x = wx + wwidth / 2; + /* This is Gnome behaviour. Windows WM would put the pointer + * in the middle of the titlebar, but GDK doesn't know where + * the titlebar is, if any. + */ + root_y = wy + wheight / 2; + gdk_device_warp (device, root_x, root_y); + } + } + + _gdk_win32_get_window_rect (window, &rect); + + cursor_name = get_cursor_name_from_op (op, edge); + + context->cursor = gdk_cursor_new_from_name (cursor_name, NULL); + + pointer_window = child_window_at_coordinates (window, root_x, root_y); + + /* Note: This triggers a WM_CAPTURECHANGED, which will trigger + * gdk_win32_surface_end_move_resize_drag(), which will end + * our op before it even begins, but only if context->op is not NONE. + * This is why we first do the grab, *then* set the op. + */ + gdk_device_grab (device, pointer_window, + GDK_OWNERSHIP_NONE, FALSE, + GDK_ALL_EVENTS_MASK, + context->cursor, + timestamp); + + context->window = g_object_ref (window); + context->op = op; + context->edge = edge; + context->device = device; + context->button = button; + context->start_root_x = root_x; + context->start_root_y = root_y; + context->timestamp = timestamp; + context->start_rect = rect; + + context->shape_indicator = NULL; + context->revealed = FALSE; + context->halfleft_regions = g_array_new (FALSE, FALSE, sizeof (AeroSnapEdgeRegion)); + context->halfright_regions = g_array_new (FALSE, FALSE, sizeof (AeroSnapEdgeRegion)); + context->maximize_regions = g_array_new (FALSE, FALSE, sizeof (AeroSnapEdgeRegion)); + context->fullup_regions = g_array_new (FALSE, FALSE, sizeof (AeroSnapEdgeRegion)); + + calculate_aerosnap_regions (context); + + GDK_NOTE (EVENTS, + g_print ("begin drag moveresize: window %p, toplevel %p, " + "op %u, edge %d, device %p, " + "button %d, coord %d:%d, time %u\n", + pointer_window, gdk_surface_get_toplevel (window), + context->op, context->edge, context->device, + context->button, context->start_root_x, + context->start_root_y, context->timestamp)); +} + +void +gdk_win32_surface_end_move_resize_drag (GdkSurface *window) +{ + GdkSurfaceImplWin32 *impl = GDK_SURFACE_IMPL_WIN32 (window->impl); + GdkW32DragMoveResizeContext *context = &impl->drag_move_resize_context; + + context->op = GDK_WIN32_DRAGOP_NONE; + + gdk_device_ungrab (context->device, GDK_CURRENT_TIME); + + g_clear_object (&context->cursor); + + context->revealed = FALSE; + + if (context->timer) + { + g_source_remove (context->timer); + context->timer = 0; + } + + g_clear_object (&context->window); + + if (context->indicator_surface) + { + cairo_surface_destroy (context->indicator_surface); + context->indicator_surface = NULL; + } + + if (context->shape_indicator) + { + stop_indicator (window, context); + DestroyWindow (context->shape_indicator); + context->shape_indicator = NULL; + } + + g_clear_pointer (&context->halfleft_regions, g_array_unref); + g_clear_pointer (&context->halfright_regions, g_array_unref); + g_clear_pointer (&context->maximize_regions, g_array_unref); + g_clear_pointer (&context->fullup_regions, g_array_unref); + + GDK_NOTE (EVENTS, + g_print ("end drag moveresize: window %p, toplevel %p," + "op %u, edge %d, device %p, " + "button %d, coord %d:%d, time %u\n", + window, gdk_surface_get_toplevel (window), + context->op, context->edge, context->device, + context->button, context->start_root_x, + context->start_root_y, context->timestamp)); + + if (context->current_snap != GDK_WIN32_AEROSNAP_STATE_UNDETERMINED) + apply_snap (window, context->current_snap); + + context->current_snap = GDK_WIN32_AEROSNAP_STATE_UNDETERMINED; +} + +static void +gdk_win32_get_window_size_and_position_from_client_rect (GdkSurface *window, + RECT *window_rect, + SIZE *window_size, + POINT *window_position) +{ + GdkSurfaceImplWin32 *impl; + + impl = GDK_SURFACE_IMPL_WIN32 (window->impl); + + /* Turn client area into window area */ + _gdk_win32_adjust_client_rect (window, window_rect); + + /* Convert GDK screen coordinates to W32 desktop coordinates */ + window_rect->left -= _gdk_offset_x * impl->window_scale; + window_rect->right -= _gdk_offset_x * impl->window_scale; + window_rect->top -= _gdk_offset_y * impl->window_scale; + window_rect->bottom -= _gdk_offset_y * impl->window_scale; + + window_position->x = window_rect->left; + window_position->y = window_rect->top; + window_size->cx = window_rect->right - window_rect->left; + window_size->cy = window_rect->bottom - window_rect->top; +} + +static void +gdk_win32_update_layered_window_from_cache (GdkSurface *window, + RECT *client_rect) +{ + POINT window_position; + SIZE window_size; + BLENDFUNCTION blender; + HDC hdc; + SIZE *window_size_ptr; + POINT source_point = { 0, 0 }; + POINT *source_point_ptr; + GdkSurfaceImplWin32 *impl; + + impl = GDK_SURFACE_IMPL_WIN32 (window->impl); + + gdk_win32_get_window_size_and_position_from_client_rect (window, + client_rect, + &window_size, + &window_position); + + blender.BlendOp = AC_SRC_OVER; + blender.BlendFlags = 0; + blender.AlphaFormat = AC_SRC_ALPHA; + blender.SourceConstantAlpha = impl->layered_opacity * 255; + + /* Size didn't change, so move immediately, no need to wait for redraw */ + /* Strictly speaking, we don't need to supply hdc, source_point and + * window_size here. However, without these arguments + * the window moves but does not update its contents on Windows 7 when + * desktop composition is off. This forces us to provide hdc and + * source_point. window_size is here to avoid the function + * inexplicably failing with error 317. + */ + if (gdk_display_is_composited (gdk_surface_get_display (window))) + { + hdc = NULL; + window_size_ptr = NULL; + source_point_ptr = NULL; + } + else + { + hdc = cairo_win32_surface_get_dc (impl->cache_surface); + window_size_ptr = &window_size; + source_point_ptr = &source_point; + } + + API_CALL (UpdateLayeredWindow, (GDK_SURFACE_HWND (window), NULL, + &window_position, window_size_ptr, + hdc, source_point_ptr, + 0, &blender, ULW_ALPHA)); +} + +void +gdk_win32_surface_do_move_resize_drag (GdkSurface *window, + gint x, + gint y) +{ + RECT rect; + RECT new_rect; + gint diffy, diffx; + MINMAXINFO mmi; + GdkSurfaceImplWin32 *impl; + GdkW32DragMoveResizeContext *context; + gint width; + gint height; + + impl = GDK_SURFACE_IMPL_WIN32 (window->impl); + context = &impl->drag_move_resize_context; + + if (!_gdk_win32_get_window_rect (window, &rect)) + return; + + new_rect = context->start_rect; + diffx = (x - context->start_root_x) * impl->window_scale; + diffy = (y - context->start_root_y) * impl->window_scale; + + switch (context->op) + { + case GDK_WIN32_DRAGOP_RESIZE: + + switch (context->edge) + { + case GDK_SURFACE_EDGE_NORTH_WEST: + new_rect.left += diffx; + new_rect.top += diffy; + break; + + case GDK_SURFACE_EDGE_NORTH: + new_rect.top += diffy; + break; + + case GDK_SURFACE_EDGE_NORTH_EAST: + new_rect.right += diffx; + new_rect.top += diffy; + break; + + case GDK_SURFACE_EDGE_WEST: + new_rect.left += diffx; + break; + + case GDK_SURFACE_EDGE_EAST: + new_rect.right += diffx; + break; + + case GDK_SURFACE_EDGE_SOUTH_WEST: + new_rect.left += diffx; + new_rect.bottom += diffy; + break; + + case GDK_SURFACE_EDGE_SOUTH: + new_rect.bottom += diffy; + break; + + case GDK_SURFACE_EDGE_SOUTH_EAST: + default: + new_rect.right += diffx; + new_rect.bottom += diffy; + break; + } + + /* When handling WM_GETMINMAXINFO, mmi is already populated + * by W32 WM and we apply our stuff on top of that. + * Here it isn't, so we should at least clear it. + */ + memset (&mmi, 0, sizeof (mmi)); + + if (!_gdk_win32_surface_fill_min_max_info (window, &mmi)) + break; + + width = new_rect.right - new_rect.left; + height = new_rect.bottom - new_rect.top; + + if (width > mmi.ptMaxTrackSize.x) + { + switch (context->edge) + { + case GDK_SURFACE_EDGE_NORTH_WEST: + case GDK_SURFACE_EDGE_WEST: + case GDK_SURFACE_EDGE_SOUTH_WEST: + new_rect.left = new_rect.right - mmi.ptMaxTrackSize.x; + break; + + case GDK_SURFACE_EDGE_NORTH_EAST: + case GDK_SURFACE_EDGE_EAST: + case GDK_SURFACE_EDGE_SOUTH_EAST: + default: + new_rect.right = new_rect.left + mmi.ptMaxTrackSize.x; + break; + } + } + else if (width < mmi.ptMinTrackSize.x) + { + switch (context->edge) + { + case GDK_SURFACE_EDGE_NORTH_WEST: + case GDK_SURFACE_EDGE_WEST: + case GDK_SURFACE_EDGE_SOUTH_WEST: + new_rect.left = new_rect.right - mmi.ptMinTrackSize.x; + break; + + case GDK_SURFACE_EDGE_NORTH_EAST: + case GDK_SURFACE_EDGE_EAST: + case GDK_SURFACE_EDGE_SOUTH_EAST: + default: + new_rect.right = new_rect.left + mmi.ptMinTrackSize.x; + break; + } + } + + if (height > mmi.ptMaxTrackSize.y) + { + switch (context->edge) + { + case GDK_SURFACE_EDGE_NORTH_WEST: + case GDK_SURFACE_EDGE_NORTH: + case GDK_SURFACE_EDGE_NORTH_EAST: + new_rect.top = new_rect.bottom - mmi.ptMaxTrackSize.y; + + case GDK_SURFACE_EDGE_SOUTH_WEST: + case GDK_SURFACE_EDGE_SOUTH: + case GDK_SURFACE_EDGE_SOUTH_EAST: + default: + new_rect.bottom = new_rect.top + mmi.ptMaxTrackSize.y; + break; + } + } + else if (height < mmi.ptMinTrackSize.y) + { + switch (context->edge) + { + case GDK_SURFACE_EDGE_NORTH_WEST: + case GDK_SURFACE_EDGE_NORTH: + case GDK_SURFACE_EDGE_NORTH_EAST: + new_rect.top = new_rect.bottom - mmi.ptMinTrackSize.y; + + case GDK_SURFACE_EDGE_SOUTH_WEST: + case GDK_SURFACE_EDGE_SOUTH: + case GDK_SURFACE_EDGE_SOUTH_EAST: + default: + new_rect.bottom = new_rect.top + mmi.ptMinTrackSize.y; + break; + } + } + + break; + case GDK_WIN32_DRAGOP_MOVE: + new_rect.left += diffx; + new_rect.top += diffy; + new_rect.right += diffx; + new_rect.bottom += diffy; + break; + default: + break; + } + + if (context->op == GDK_WIN32_DRAGOP_RESIZE && + (rect.left != new_rect.left || + rect.right != new_rect.right || + rect.top != new_rect.top || + rect.bottom != new_rect.bottom)) + { + context->native_move_resize_pending = TRUE; + _gdk_win32_do_emit_configure_event (window, new_rect); + } + else if (context->op == GDK_WIN32_DRAGOP_MOVE && + (rect.left != new_rect.left || + rect.top != new_rect.top)) + { + context->native_move_resize_pending = FALSE; + + _gdk_win32_do_emit_configure_event (window, new_rect); + + if (impl->layered) + { + gdk_win32_update_layered_window_from_cache (window, &new_rect); + } + else + { + SIZE window_size; + POINT window_position; + + gdk_win32_get_window_size_and_position_from_client_rect (window, + &new_rect, + &window_size, + &window_position); + + API_CALL (SetWindowPos, (GDK_SURFACE_HWND (window), + SWP_NOZORDER_SPECIFIED, + window_position.x, window_position.y, + 0, 0, + SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOSIZE)); + } + } + + if (context->op == GDK_WIN32_DRAGOP_RESIZE || + context->op == GDK_WIN32_DRAGOP_MOVE) + handle_aerosnap_move_resize (window, context, x, y); +} + +static void +gdk_win32_surface_begin_resize_drag (GdkSurface *window, + GdkSurfaceEdge edge, + GdkDevice *device, + gint button, + gint root_x, + gint root_y, + guint32 timestamp) +{ + GdkSurfaceImplWin32 *impl; + + g_return_if_fail (GDK_IS_SURFACE (window)); + + if (GDK_SURFACE_DESTROYED (window) || + IsIconic (GDK_SURFACE_HWND (window))) + return; + + /* Tell Windows to start interactively resizing the window by pretending that + * the left pointer button was clicked in the suitable edge or corner. This + * will only work if the button is down when this function is called, and + * will only work with button 1 (left), since Windows only allows window + * dragging using the left mouse button. + */ + + if (button != 1) + return; + + impl = GDK_SURFACE_IMPL_WIN32 (window->impl); + + if (impl->drag_move_resize_context.op != GDK_WIN32_DRAGOP_NONE) + gdk_win32_surface_end_move_resize_drag (window); + + setup_drag_move_resize_context (window, &impl->drag_move_resize_context, + GDK_WIN32_DRAGOP_RESIZE, edge, device, + button, root_x, root_y, timestamp); +} + +static void +gdk_win32_surface_begin_move_drag (GdkSurface *window, + GdkDevice *device, + gint button, + gint root_x, + gint root_y, + guint32 timestamp) +{ + GdkSurfaceImplWin32 *impl; + + g_return_if_fail (GDK_IS_SURFACE (window)); + + if (GDK_SURFACE_DESTROYED (window) || + IsIconic (GDK_SURFACE_HWND (window))) + return; + + /* Tell Windows to start interactively moving the window by pretending that + * the left pointer button was clicked in the titlebar. This will only work + * if the button is down when this function is called, and will only work + * with button 1 (left), since Windows only allows window dragging using the + * left mouse button. + */ + if (button != 1) + return; + + impl = GDK_SURFACE_IMPL_WIN32 (window->impl); + + if (impl->drag_move_resize_context.op != GDK_WIN32_DRAGOP_NONE) + gdk_win32_surface_end_move_resize_drag (window); + + setup_drag_move_resize_context (window, &impl->drag_move_resize_context, + GDK_WIN32_DRAGOP_MOVE, GDK_SURFACE_EDGE_NORTH_WEST, + device, button, root_x, root_y, timestamp); +} + + +/* + * Setting window states + */ +static void +gdk_win32_surface_iconify (GdkSurface *window) +{ + HWND old_active_window; + + g_return_if_fail (GDK_IS_SURFACE (window)); + + if (GDK_SURFACE_DESTROYED (window)) + return; + + GDK_NOTE (MISC, g_print ("gdk_surface_iconify: %p: %s\n", + GDK_SURFACE_HWND (window), + _gdk_win32_surface_state_to_string (window->state))); + + if (GDK_SURFACE_IS_MAPPED (window)) + { + old_active_window = GetActiveWindow (); + GtkShowWindow (window, SW_MINIMIZE); + if (old_active_window != GDK_SURFACE_HWND (window)) + SetActiveWindow (old_active_window); + } + else + { + gdk_synthesize_window_state (window, + 0, + GDK_SURFACE_STATE_ICONIFIED); + } +} + +static void +gdk_win32_surface_deiconify (GdkSurface *window) +{ + g_return_if_fail (GDK_IS_SURFACE (window)); + + if (GDK_SURFACE_DESTROYED (window)) + return; + + GDK_NOTE (MISC, g_print ("gdk_surface_deiconify: %p: %s\n", + GDK_SURFACE_HWND (window), + _gdk_win32_surface_state_to_string (window->state))); + + if (GDK_SURFACE_IS_MAPPED (window)) + { + show_window_internal (window, GDK_SURFACE_IS_MAPPED (window), TRUE); + } + else + { + gdk_synthesize_window_state (window, + GDK_SURFACE_STATE_ICONIFIED, + 0); + } +} + +static void +gdk_win32_surface_stick (GdkSurface *window) +{ + g_return_if_fail (GDK_IS_SURFACE (window)); + + if (GDK_SURFACE_DESTROYED (window)) + return; + + /* FIXME: Do something? */ +} + +static void +gdk_win32_surface_unstick (GdkSurface *window) +{ + g_return_if_fail (GDK_IS_SURFACE (window)); + + if (GDK_SURFACE_DESTROYED (window)) + return; + + /* FIXME: Do something? */ +} + +static void +gdk_win32_surface_maximize (GdkSurface *window) +{ + + g_return_if_fail (GDK_IS_SURFACE (window)); + + if (GDK_SURFACE_DESTROYED (window)) + return; + + GDK_NOTE (MISC, g_print ("gdk_surface_maximize: %p: %s\n", + GDK_SURFACE_HWND (window), + _gdk_win32_surface_state_to_string (window->state))); + + if (GDK_SURFACE_IS_MAPPED (window)) + GtkShowWindow (window, SW_MAXIMIZE); + else + gdk_synthesize_window_state (window, + 0, + GDK_SURFACE_STATE_MAXIMIZED); +} + +static void +gdk_win32_surface_unmaximize (GdkSurface *window) +{ + g_return_if_fail (GDK_IS_SURFACE (window)); + + if (GDK_SURFACE_DESTROYED (window)) + return; + + GDK_NOTE (MISC, g_print ("gdk_surface_unmaximize: %p: %s\n", + GDK_SURFACE_HWND (window), + _gdk_win32_surface_state_to_string (window->state))); + + if (GDK_SURFACE_IS_MAPPED (window)) + GtkShowWindow (window, SW_RESTORE); + else + gdk_synthesize_window_state (window, + GDK_SURFACE_STATE_MAXIMIZED, + 0); +} + +static void +gdk_win32_surface_fullscreen (GdkSurface *window) +{ + gint x, y, width, height; + FullscreenInfo *fi; + HMONITOR monitor; + MONITORINFO mi; + + g_return_if_fail (GDK_IS_SURFACE (window)); + + fi = g_new (FullscreenInfo, 1); + + if (!GetWindowRect (GDK_SURFACE_HWND (window), &(fi->r))) + g_free (fi); + else + { + GdkSurfaceImplWin32 *impl = GDK_SURFACE_IMPL_WIN32 (window->impl); + + monitor = MonitorFromWindow (GDK_SURFACE_HWND (window), MONITOR_DEFAULTTONEAREST); + mi.cbSize = sizeof (mi); + if (monitor && GetMonitorInfo (monitor, &mi)) + { + x = mi.rcMonitor.left; + y = mi.rcMonitor.top; + width = mi.rcMonitor.right - x; + height = mi.rcMonitor.bottom - y; + } + else + { + x = y = 0; + width = GetSystemMetrics (SM_CXSCREEN); + height = GetSystemMetrics (SM_CYSCREEN); + } + + /* remember for restoring */ + fi->hint_flags = impl->hint_flags; + impl->hint_flags &= ~GDK_HINT_MAX_SIZE; + g_object_set_data (G_OBJECT (window), "fullscreen-info", fi); + fi->style = GetWindowLong (GDK_SURFACE_HWND (window), GWL_STYLE); + + /* Send state change before configure event */ + gdk_synthesize_window_state (window, 0, GDK_SURFACE_STATE_FULLSCREEN); + + SetWindowLong (GDK_SURFACE_HWND (window), GWL_STYLE, + (fi->style & ~WS_OVERLAPPEDWINDOW) | WS_POPUP); + + API_CALL (SetWindowPos, (GDK_SURFACE_HWND (window), HWND_TOP, + x, y, width, height, + SWP_NOCOPYBITS | SWP_SHOWWINDOW)); + } +} + +static void +gdk_win32_surface_unfullscreen (GdkSurface *window) +{ + FullscreenInfo *fi; + + g_return_if_fail (GDK_IS_SURFACE (window)); + + fi = g_object_get_data (G_OBJECT (window), "fullscreen-info"); + if (fi) + { + GdkSurfaceImplWin32 *impl = GDK_SURFACE_IMPL_WIN32 (window->impl); + + gdk_synthesize_window_state (window, GDK_SURFACE_STATE_FULLSCREEN, 0); + + impl->hint_flags = fi->hint_flags; + SetWindowLong (GDK_SURFACE_HWND (window), GWL_STYLE, fi->style); + API_CALL (SetWindowPos, (GDK_SURFACE_HWND (window), HWND_NOTOPMOST, + fi->r.left, fi->r.top, + fi->r.right - fi->r.left, fi->r.bottom - fi->r.top, + SWP_NOCOPYBITS | SWP_SHOWWINDOW)); + + g_object_set_data (G_OBJECT (window), "fullscreen-info", NULL); + g_free (fi); + _gdk_win32_surface_update_style_bits (window); + } +} + +static void +gdk_win32_surface_set_keep_above (GdkSurface *window, + gboolean setting) +{ + g_return_if_fail (GDK_IS_SURFACE (window)); + + if (GDK_SURFACE_DESTROYED (window)) + return; + + GDK_NOTE (MISC, g_print ("gdk_surface_set_keep_above: %p: %s\n", + GDK_SURFACE_HWND (window), + setting ? "YES" : "NO")); + + if (GDK_SURFACE_IS_MAPPED (window)) + { + API_CALL (SetWindowPos, (GDK_SURFACE_HWND (window), + setting ? HWND_TOPMOST : HWND_NOTOPMOST, + 0, 0, 0, 0, + SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE)); + } + + gdk_synthesize_window_state (window, + setting ? GDK_SURFACE_STATE_BELOW : GDK_SURFACE_STATE_ABOVE, + setting ? GDK_SURFACE_STATE_ABOVE : 0); +} + +static void +gdk_win32_surface_set_keep_below (GdkSurface *window, + gboolean setting) +{ + g_return_if_fail (GDK_IS_SURFACE (window)); + + if (GDK_SURFACE_DESTROYED (window)) + return; + + GDK_NOTE (MISC, g_print ("gdk_surface_set_keep_below: %p: %s\n", + GDK_SURFACE_HWND (window), + setting ? "YES" : "NO")); + + if (GDK_SURFACE_IS_MAPPED (window)) + { + API_CALL (SetWindowPos, (GDK_SURFACE_HWND (window), + setting ? HWND_BOTTOM : HWND_NOTOPMOST, + 0, 0, 0, 0, + SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE)); + } + + gdk_synthesize_window_state (window, + setting ? GDK_SURFACE_STATE_ABOVE : GDK_SURFACE_STATE_BELOW, + setting ? GDK_SURFACE_STATE_BELOW : 0); +} + +static void +gdk_win32_surface_focus (GdkSurface *window, + guint32 timestamp) +{ + g_return_if_fail (GDK_IS_SURFACE (window)); + + if (GDK_SURFACE_DESTROYED (window)) + return; + + GDK_NOTE (MISC, g_print ("gdk_surface_focus: %p: %s\n", + GDK_SURFACE_HWND (window), + _gdk_win32_surface_state_to_string (window->state))); + + if (window->state & GDK_SURFACE_STATE_MAXIMIZED) + GtkShowWindow (window, SW_SHOWMAXIMIZED); + else if (window->state & GDK_SURFACE_STATE_ICONIFIED) + GtkShowWindow (window, SW_RESTORE); + else if (!IsWindowVisible (GDK_SURFACE_HWND (window))) + GtkShowWindow (window, SW_SHOWNORMAL); + else + GtkShowWindow (window, SW_SHOW); + + SetFocus (GDK_SURFACE_HWND (window)); +} + +static void +gdk_win32_surface_set_modal_hint (GdkSurface *window, + gboolean modal) +{ + g_return_if_fail (GDK_IS_SURFACE (window)); + + if (GDK_SURFACE_DESTROYED (window)) + return; + + GDK_NOTE (MISC, g_print ("gdk_surface_set_modal_hint: %p: %s\n", + GDK_SURFACE_HWND (window), + modal ? "YES" : "NO")); + + if (modal == window->modal_hint) + return; + + window->modal_hint = modal; + +#if 0 + /* Not sure about this one.. -- Cody */ + if (GDK_SURFACE_IS_MAPPED (window)) + API_CALL (SetWindowPos, (GDK_SURFACE_HWND (window), + modal ? HWND_TOPMOST : HWND_NOTOPMOST, + 0, 0, 0, 0, + SWP_NOMOVE | SWP_NOSIZE)); +#else + + if (modal) + { + _gdk_push_modal_window (window); + gdk_surface_raise (window); + } + else + { + _gdk_remove_modal_window (window); + } + +#endif +} + +static void +gdk_win32_surface_set_skip_taskbar_hint (GdkSurface *window, + gboolean skips_taskbar) +{ + static GdkSurface *owner = NULL; + //GdkSurfaceAttr wa; + + g_return_if_fail (GDK_IS_SURFACE (window)); + + GDK_NOTE (MISC, g_print ("gdk_surface_set_skip_taskbar_hint: %p: %s, doing nothing\n", + GDK_SURFACE_HWND (window), + skips_taskbar ? "YES" : "NO")); + + // ### TODO: Need to figure out what to do here. + return; + + if (skips_taskbar) + { +#if 0 + if (owner == NULL) + { + wa.window_type = GDK_SURFACE_TEMP; + wa.wclass = GDK_INPUT_OUTPUT; + wa.width = wa.height = 1; + wa.event_mask = 0; + owner = gdk_surface_new_internal (NULL, &wa, 0, TRUE); + } +#endif + + SetWindowLongPtr (GDK_SURFACE_HWND (window), GWLP_HWNDPARENT, (LONG_PTR) GDK_SURFACE_HWND (owner)); + +#if 0 /* Should we also turn off the minimize and maximize buttons? */ + SetWindowLong (GDK_SURFACE_HWND (window), GWL_STYLE, + GetWindowLong (GDK_SURFACE_HWND (window), GWL_STYLE) & ~(WS_MINIMIZEBOX|WS_MAXIMIZEBOX|WS_SYSMENU)); + + SetWindowPos (GDK_SURFACE_HWND (window), SWP_NOZORDER_SPECIFIED, + 0, 0, 0, 0, + SWP_FRAMECHANGED | SWP_NOACTIVATE | SWP_NOMOVE | + SWP_NOREPOSITION | SWP_NOSIZE | SWP_NOZORDER); +#endif + } + else + { + SetWindowLongPtr (GDK_SURFACE_HWND (window), GWLP_HWNDPARENT, 0); + } +} + +static void +gdk_win32_surface_set_skip_pager_hint (GdkSurface *window, + gboolean skips_pager) +{ + g_return_if_fail (GDK_IS_SURFACE (window)); + + GDK_NOTE (MISC, g_print ("gdk_surface_set_skip_pager_hint: %p: %s, doing nothing\n", + GDK_SURFACE_HWND (window), + skips_pager ? "YES" : "NO")); +} + +static void +gdk_win32_surface_set_type_hint (GdkSurface *window, + GdkSurfaceTypeHint hint) +{ + g_return_if_fail (GDK_IS_SURFACE (window)); + + if (GDK_SURFACE_DESTROYED (window)) + return; + + GDK_NOTE (MISC, + G_STMT_START{ + static GEnumClass *class = NULL; + if (!class) + class = g_type_class_ref (GDK_TYPE_SURFACE_TYPE_HINT); + g_print ("gdk_surface_set_type_hint: %p: %s\n", + GDK_SURFACE_HWND (window), + g_enum_get_value (class, hint)->value_name); + }G_STMT_END); + + ((GdkSurfaceImplWin32 *)window->impl)->type_hint = hint; + + _gdk_win32_surface_update_style_bits (window); +} + +static GdkSurfaceTypeHint +gdk_win32_surface_get_type_hint (GdkSurface *window) +{ + g_return_val_if_fail (GDK_IS_SURFACE (window), GDK_SURFACE_TYPE_HINT_NORMAL); + + if (GDK_SURFACE_DESTROYED (window)) + return GDK_SURFACE_TYPE_HINT_NORMAL; + + return GDK_SURFACE_IMPL_WIN32 (window->impl)->type_hint; +} + +static HRGN +cairo_region_to_hrgn (const cairo_region_t *region, + gint x_origin, + gint y_origin, + guint scale) +{ + HRGN hrgn; + RGNDATA *rgndata; + RECT *rect; + cairo_rectangle_int_t r; + const int nrects = cairo_region_num_rectangles (region); + guint nbytes = + sizeof (RGNDATAHEADER) + (sizeof (RECT) * nrects); + int i; + + rgndata = g_malloc (nbytes); + rgndata->rdh.dwSize = sizeof (RGNDATAHEADER); + rgndata->rdh.iType = RDH_RECTANGLES; + rgndata->rdh.nCount = rgndata->rdh.nRgnSize = 0; + SetRect (&rgndata->rdh.rcBound, + G_MAXLONG, G_MAXLONG, G_MINLONG, G_MINLONG); + + for (i = 0; i < nrects; i++) + { + rect = ((RECT *) rgndata->Buffer) + rgndata->rdh.nCount++; + + cairo_region_get_rectangle (region, i, &r); + rect->left = (r.x + x_origin) * scale; + rect->right = (rect->left + r.width) * scale; + rect->top = (r.y + y_origin) * scale; + rect->bottom = (rect->top + r.height) * scale; + + if (rect->left < rgndata->rdh.rcBound.left) + rgndata->rdh.rcBound.left = rect->left; + if (rect->right > rgndata->rdh.rcBound.right) + rgndata->rdh.rcBound.right = rect->right; + if (rect->top < rgndata->rdh.rcBound.top) + rgndata->rdh.rcBound.top = rect->top; + if (rect->bottom > rgndata->rdh.rcBound.bottom) + rgndata->rdh.rcBound.bottom = rect->bottom; + } + if ((hrgn = ExtCreateRegion (NULL, nbytes, rgndata)) == NULL) + WIN32_API_FAILED ("ExtCreateRegion"); + + g_free (rgndata); + + return (hrgn); +} + +static void +gdk_win32_surface_shape_combine_region (GdkSurface *window, + const cairo_region_t *shape_region, + gint offset_x, + gint offset_y) +{ + GdkSurfaceImplWin32 *impl; + + if (GDK_SURFACE_DESTROYED (window)) + return; + + if (!shape_region) + { + GDK_NOTE (MISC, g_print ("gdk_win32_surface_shape_combine_region: %p: none\n", + GDK_SURFACE_HWND (window))); + SetWindowRgn (GDK_SURFACE_HWND (window), NULL, TRUE); + } + else + { + HRGN hrgn; + impl = GDK_SURFACE_IMPL_WIN32 (window->impl); + + hrgn = cairo_region_to_hrgn (shape_region, 0, 0, impl->window_scale); + + GDK_NOTE (MISC, g_print ("gdk_win32_surface_shape_combine_region: %p: %p\n", + GDK_SURFACE_HWND (window), + hrgn)); + + do_shape_combine_region (window, hrgn, offset_x, offset_y); + } +} + +GdkSurface * +gdk_win32_surface_lookup_for_display (GdkDisplay *display, + HWND anid) +{ + g_return_val_if_fail (display == gdk_display_get_default (), NULL); + + return (GdkSurface*) gdk_win32_handle_table_lookup (anid); +} + +static void +gdk_win32_surface_set_opacity (GdkSurface *window, + gdouble opacity) +{ + LONG exstyle; + typedef BOOL (WINAPI *PFN_SetLayeredWindowAttributes) (HWND, COLORREF, BYTE, DWORD); + PFN_SetLayeredWindowAttributes setLayeredWindowAttributes = NULL; + GdkSurfaceImplWin32 *impl; + + g_return_if_fail (GDK_IS_SURFACE (window)); + + if (!WINDOW_IS_TOPLEVEL (window) || GDK_SURFACE_DESTROYED (window)) + return; + + if (opacity < 0) + opacity = 0; + else if (opacity > 1) + opacity = 1; + + impl = GDK_SURFACE_IMPL_WIN32 (window->impl); + + if (impl->layered) + { + if (impl->layered_opacity != opacity) + { + RECT window_rect; + + impl->layered_opacity = opacity; + + gdk_win32_get_window_client_area_rect (window, impl->window_scale, &window_rect); + gdk_win32_update_layered_window_from_cache (window, &window_rect); + } + + return; + } + + exstyle = GetWindowLong (GDK_SURFACE_HWND (window), GWL_EXSTYLE); + + if (!(exstyle & WS_EX_LAYERED)) + SetWindowLong (GDK_SURFACE_HWND (window), + GWL_EXSTYLE, + exstyle | WS_EX_LAYERED); + + setLayeredWindowAttributes = + (PFN_SetLayeredWindowAttributes)GetProcAddress (GetModuleHandle ("user32.dll"), "SetLayeredWindowAttributes"); + + if (setLayeredWindowAttributes) + { + API_CALL (setLayeredWindowAttributes, (GDK_SURFACE_HWND (window), + 0, + opacity * 0xff, + LWA_ALPHA)); + } +} + +gboolean +gdk_win32_surface_is_win32 (GdkSurface *window) +{ + return GDK_SURFACE_IS_WIN32 (window); +} + +static gboolean +gdk_win32_surface_show_window_menu (GdkSurface *window, + GdkEvent *event) +{ + double event_x, event_y; + gint x, y; + GdkSurfaceImplWin32 *impl = GDK_SURFACE_IMPL_WIN32 (window->impl); + + switch (event->type) + { + case GDK_BUTTON_PRESS: + case GDK_BUTTON_RELEASE: + case GDK_TOUCH_BEGIN: + case GDK_TOUCH_END: + break; + default: + return FALSE; + } + + gdk_event_get_root_coords (event, &event_x, &event_y); + x = event_x - _gdk_offset_x; + y = event_y - _gdk_offset_y; + + SendMessage (GDK_SURFACE_HWND (window), + WM_SYSMENU, + 0, + MAKELPARAM (x * impl->window_scale, y * impl->window_scale)); + + return TRUE; +} + +/** + * _gdk_win32_acquire_dc + * @impl: a Win32 #GdkSurfaceImplWin32 implementation + * + * Gets a DC with the given drawable selected into it. + * + * Returns: The DC, on success. Otherwise + * %NULL. If this function succeeded + * _gdk_win32_impl_release_dc() must be called + * release the DC when you are done using it. + **/ +static HDC +_gdk_win32_impl_acquire_dc (GdkSurfaceImplWin32 *impl) +{ + if (GDK_IS_SURFACE_IMPL_WIN32 (impl) && + GDK_SURFACE_DESTROYED (impl->wrapper)) + return NULL; + + /* We don't call this function for layered windows, but + * in case we do... + */ + if (impl->layered) + return NULL; + + if (!impl->hdc) + { + impl->hdc = GetDC (impl->handle); + if (!impl->hdc) + WIN32_GDI_FAILED ("GetDC"); + } + + if (impl->hdc) + { + impl->hdc_count++; + return impl->hdc; + } + else + { + return NULL; + } +} + +/** + * _gdk_win32_impl_release_dc + * @impl: a Win32 #GdkSurfaceImplWin32 implementation + * + * Releases the reference count for the DC + * from _gdk_win32_impl_acquire_dc() + **/ +static void +_gdk_win32_impl_release_dc (GdkSurfaceImplWin32 *impl) +{ + if (impl->layered) + return; + + g_return_if_fail (impl->hdc_count > 0); + + impl->hdc_count--; + if (impl->hdc_count == 0) + { + if (impl->saved_dc_bitmap) + { + GDI_CALL (SelectObject, (impl->hdc, impl->saved_dc_bitmap)); + impl->saved_dc_bitmap = NULL; + } + + if (impl->hdc) + { + GDI_CALL (ReleaseDC, (impl->handle, impl->hdc)); + impl->hdc = NULL; + } + } +} + +HWND +gdk_win32_surface_get_impl_hwnd (GdkSurface *window) +{ + if (GDK_SURFACE_IS_WIN32 (window)) + return GDK_SURFACE_HWND (window); + return NULL; +} + +static void +gdk_win32_cairo_surface_destroy (void *data) +{ + GdkSurfaceImplWin32 *impl = data; + + _gdk_win32_impl_release_dc (impl); + impl->cairo_surface = NULL; +} + +static cairo_surface_t * +gdk_win32_ref_cairo_surface_layered (GdkSurface *window, + GdkSurfaceImplWin32 *impl) +{ + gint width, height; + RECT window_rect; + + gdk_win32_get_window_client_area_rect (window, impl->window_scale, &window_rect); + + /* Turn client area into window area */ + _gdk_win32_adjust_client_rect (window, &window_rect); + + width = window_rect.right - window_rect.left; + height = window_rect.bottom - window_rect.top; + + if (width > impl->dib_width || + height > impl->dib_height) + { + cairo_surface_t *new_cache; + cairo_t *cr; + + /* Create larger cache surface, copy old cache surface over it */ + new_cache = cairo_win32_surface_create_with_dib (CAIRO_FORMAT_ARGB32, + width, + height); + + if (impl->cache_surface) + { + cr = cairo_create (new_cache); + cairo_set_source_surface (cr, impl->cache_surface, 0, 0); + cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE); + cairo_paint (cr); + cairo_destroy (cr); + cairo_surface_flush (new_cache); + + cairo_surface_destroy (impl->cache_surface); + } + + impl->cache_surface = new_cache; + + cairo_surface_set_device_scale (impl->cache_surface, + impl->window_scale, + impl->window_scale); + + if (impl->cairo_surface) + cairo_surface_destroy (impl->cairo_surface); + + impl->cairo_surface = NULL; + } + + /* This is separate, because cairo_surface gets killed + * off frequently by outside code, whereas cache_surface + * is only killed by us, above. + */ + if (!impl->cairo_surface) + { + impl->cairo_surface = cairo_win32_surface_create_with_dib (CAIRO_FORMAT_ARGB32, + width, + height); + impl->dib_width = width; + impl->dib_height = height; + + cairo_surface_set_device_scale (impl->cairo_surface, + impl->window_scale, + impl->window_scale); + + cairo_surface_set_user_data (impl->cairo_surface, &gdk_win32_cairo_key, + impl, gdk_win32_cairo_surface_destroy); + } + else + { + cairo_surface_reference (impl->cairo_surface); + } + + return impl->cairo_surface; +} + +static cairo_surface_t * +gdk_win32_ref_cairo_surface (GdkSurface *window) +{ + GdkSurfaceImplWin32 *impl = GDK_SURFACE_IMPL_WIN32 (window->impl); + + if (GDK_IS_SURFACE_IMPL_WIN32 (impl) && + GDK_SURFACE_DESTROYED (impl->wrapper)) + return NULL; + + if (impl->layered) + return gdk_win32_ref_cairo_surface_layered (window, impl); + + if (!impl->cairo_surface) + { + HDC hdc = _gdk_win32_impl_acquire_dc (impl); + if (!hdc) + return NULL; + + impl->cairo_surface = cairo_win32_surface_create_with_format (hdc, CAIRO_FORMAT_ARGB32); + cairo_surface_set_device_scale (impl->cairo_surface, + impl->window_scale, + impl->window_scale); + + cairo_surface_set_user_data (impl->cairo_surface, &gdk_win32_cairo_key, + impl, gdk_win32_cairo_surface_destroy); + } + else + cairo_surface_reference (impl->cairo_surface); + + return impl->cairo_surface; +} + +BOOL WINAPI +GtkShowWindow (GdkSurface *window, + int cmd_show) +{ + cairo_t *cr; + cairo_surface_t *surface; + RECT window_rect; + HDC hdc; + POINT window_position; + SIZE window_size; + POINT source_point; + BLENDFUNCTION blender; + + HWND hwnd = GDK_SURFACE_HWND (window); + GdkSurfaceImplWin32 *impl = GDK_SURFACE_IMPL_WIN32 (window->impl); + + switch (cmd_show) + { + case SW_FORCEMINIMIZE: + case SW_HIDE: + case SW_MINIMIZE: + break; + case SW_MAXIMIZE: + case SW_RESTORE: + case SW_SHOW: + case SW_SHOWDEFAULT: + case SW_SHOWMINIMIZED: + case SW_SHOWMINNOACTIVE: + case SW_SHOWNA: + case SW_SHOWNOACTIVATE: + case SW_SHOWNORMAL: + if (IsWindowVisible (hwnd)) + break; + + if ((WS_EX_LAYERED & GetWindowLongPtr (hwnd, GWL_EXSTYLE)) != WS_EX_LAYERED) + break; + + /* Window was hidden, will be shown. Erase it, GDK will repaint soon, + * but not soon enough, so it's possible to see old content before + * the next redraw, unless we erase the window first. + */ + GetWindowRect (hwnd, &window_rect); + source_point.x = source_point.y = 0; + + window_position.x = window_rect.left; + window_position.y = window_rect.top; + window_size.cx = window_rect.right - window_rect.left; + window_size.cy = window_rect.bottom - window_rect.top; + + blender.BlendOp = AC_SRC_OVER; + blender.BlendFlags = 0; + blender.AlphaFormat = AC_SRC_ALPHA; + blender.SourceConstantAlpha = 255; + + /* Create a surface of appropriate size and clear it */ + surface = cairo_win32_surface_create_with_dib (CAIRO_FORMAT_ARGB32, + window_size.cx, + window_size.cy); + cairo_surface_set_device_scale (surface, impl->window_scale, impl->window_scale); + cr = cairo_create (surface); + cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE); + cairo_set_source_rgba (cr, 1.0, 1.0, 1.0, 0.0); + cairo_paint (cr); + cairo_destroy (cr); + cairo_surface_flush (surface); + hdc = cairo_win32_surface_get_dc (surface); + + /* No API_CALL() wrapper, don't check for errors */ + UpdateLayeredWindow (hwnd, NULL, + &window_position, &window_size, + hdc, &source_point, + 0, &blender, ULW_ALPHA); + + cairo_surface_destroy (surface); + + break; + } + + /* Ensure that maximized window size is corrected later on */ + if (cmd_show == SW_MAXIMIZE) + impl->maximizing = TRUE; + + return ShowWindow (hwnd, cmd_show); +} + +static void +gdk_win32_surface_set_shadow_width (GdkSurface *window, + gint left, + gint right, + gint top, + gint bottom) +{ + GdkSurfaceImplWin32 *impl = GDK_SURFACE_IMPL_WIN32 (window->impl); + + if (GDK_SURFACE_DESTROYED (window)) + return; + + GDK_NOTE (MISC, g_print ("gdk_win32_surface_set_shadow_width: window %p, " + "left %d, top %d, right %d, bottom %d\n", + window, left, top, right, bottom)); + + impl->zero_margins = left == 0 && right == 0 && top == 0 && bottom == 0; + + if (impl->zero_margins) + return; + + impl->margins.left = left; + impl->margins.right = right * impl->window_scale; + impl->margins.top = top; + impl->margins.bottom = bottom * impl->window_scale; + impl->margins_x = left + right; + impl->margins_y = top + bottom; +} + + +gint +_gdk_win32_surface_get_scale_factor (GdkSurface *window) +{ + GdkDisplay *display; + GdkSurfaceImplWin32 *impl; + + GdkWin32Display *win32_display; + UINT dpix, dpiy; + gboolean is_scale_acquired; + + if (GDK_SURFACE_DESTROYED (window)) + return 1; + + g_return_val_if_fail (window != NULL, 1); + + display = gdk_surface_get_display (window); + impl = GDK_SURFACE_IMPL_WIN32 (window->impl); + + win32_display = GDK_WIN32_DISPLAY (display); + + if (win32_display->dpi_aware_type != PROCESS_DPI_UNAWARE) + { + if (win32_display->has_fixed_scale) + impl->window_scale = win32_display->window_scale; + else + impl->window_scale = _gdk_win32_display_get_monitor_scale_factor (win32_display, + NULL, + GDK_SURFACE_HWND (window), + NULL); + + return impl->window_scale; + } + else + { + if (win32_display->has_fixed_scale) + { + static gsize hidpi_msg_displayed = 0; + + if (g_once_init_enter (&hidpi_msg_displayed)) + { + g_message ("Note: GDK_SCALE is ignored as HiDPI awareness is disabled."); + g_once_init_leave (&hidpi_msg_displayed, 1); + } + } + + /* Application is not DPI aware, don't bother */ + return 1; + } +} + +void +_gdk_win32_surface_get_unscaled_size (GdkSurface *window, + gint *unscaled_width, + gint *unscaled_height) +{ + GdkSurfaceImplWin32 *impl = GDK_SURFACE_IMPL_WIN32 (window->impl); + + if (unscaled_width) + *unscaled_width = impl->unscaled_width; + if (unscaled_height) + *unscaled_height = impl->unscaled_height; +} + +static void +gdk_win32_input_shape_combine_region (GdkSurface *window, + const cairo_region_t *shape_region, + gint offset_x, + gint offset_y) +{ + /* Partial input shape support is implemented by handling the + * NC_NCHITTEST message + */ +} + +static void +gdk_surface_impl_win32_class_init (GdkSurfaceImplWin32Class *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GdkSurfaceImplClass *impl_class = GDK_SURFACE_IMPL_CLASS (klass); + + parent_class = g_type_class_peek_parent (klass); + + object_class->finalize = gdk_surface_impl_win32_finalize; + + impl_class->ref_cairo_surface = gdk_win32_ref_cairo_surface; + + impl_class->show = gdk_win32_surface_show; + impl_class->hide = gdk_win32_surface_hide; + impl_class->withdraw = gdk_win32_surface_withdraw; + impl_class->set_events = gdk_win32_surface_set_events; + impl_class->get_events = gdk_win32_surface_get_events; + impl_class->raise = gdk_win32_surface_raise; + impl_class->lower = gdk_win32_surface_lower; + impl_class->restack_toplevel = gdk_win32_surface_restack_toplevel; + impl_class->move_resize = gdk_win32_surface_move_resize; + impl_class->get_geometry = gdk_win32_surface_get_geometry; + impl_class->get_device_state = gdk_surface_win32_get_device_state; + impl_class->get_root_coords = gdk_win32_surface_get_root_coords; + + impl_class->shape_combine_region = gdk_win32_surface_shape_combine_region; + impl_class->input_shape_combine_region = gdk_win32_input_shape_combine_region; + impl_class->destroy = gdk_win32_surface_destroy; + impl_class->begin_paint = gdk_win32_surface_begin_paint; + impl_class->end_paint = gdk_win32_surface_end_paint; + + //impl_class->beep = gdk_x11_surface_beep; + + + impl_class->show_window_menu = gdk_win32_surface_show_window_menu; + impl_class->focus = gdk_win32_surface_focus; + impl_class->set_type_hint = gdk_win32_surface_set_type_hint; + impl_class->get_type_hint = gdk_win32_surface_get_type_hint; + impl_class->set_modal_hint = gdk_win32_surface_set_modal_hint; + impl_class->set_skip_taskbar_hint = gdk_win32_surface_set_skip_taskbar_hint; + impl_class->set_skip_pager_hint = gdk_win32_surface_set_skip_pager_hint; + impl_class->set_urgency_hint = gdk_win32_surface_set_urgency_hint; + impl_class->set_geometry_hints = gdk_win32_surface_set_geometry_hints; + impl_class->set_title = gdk_win32_surface_set_title; + impl_class->set_role = gdk_win32_surface_set_role; + //impl_class->set_startup_id = gdk_x11_surface_set_startup_id; + impl_class->set_transient_for = gdk_win32_surface_set_transient_for; + impl_class->get_frame_extents = gdk_win32_surface_get_frame_extents; + impl_class->set_accept_focus = gdk_win32_surface_set_accept_focus; + impl_class->set_focus_on_map = gdk_win32_surface_set_focus_on_map; + impl_class->set_icon_list = gdk_win32_surface_set_icon_list; + impl_class->set_icon_name = gdk_win32_surface_set_icon_name; + impl_class->iconify = gdk_win32_surface_iconify; + impl_class->deiconify = gdk_win32_surface_deiconify; + impl_class->stick = gdk_win32_surface_stick; + impl_class->unstick = gdk_win32_surface_unstick; + impl_class->maximize = gdk_win32_surface_maximize; + impl_class->unmaximize = gdk_win32_surface_unmaximize; + impl_class->fullscreen = gdk_win32_surface_fullscreen; + impl_class->unfullscreen = gdk_win32_surface_unfullscreen; + impl_class->set_keep_above = gdk_win32_surface_set_keep_above; + impl_class->set_keep_below = gdk_win32_surface_set_keep_below; + impl_class->get_group = gdk_win32_surface_get_group; + impl_class->set_group = gdk_win32_surface_set_group; + impl_class->set_decorations = gdk_win32_surface_set_decorations; + impl_class->get_decorations = gdk_win32_surface_get_decorations; + impl_class->set_functions = gdk_win32_surface_set_functions; + + impl_class->set_shadow_width = gdk_win32_surface_set_shadow_width; + impl_class->begin_resize_drag = gdk_win32_surface_begin_resize_drag; + impl_class->begin_move_drag = gdk_win32_surface_begin_move_drag; + impl_class->set_opacity = gdk_win32_surface_set_opacity; + impl_class->destroy_notify = gdk_win32_surface_destroy_notify; + impl_class->register_dnd = _gdk_win32_surface_register_dnd; + impl_class->drag_begin = _gdk_win32_surface_drag_begin; + impl_class->create_gl_context = _gdk_win32_surface_create_gl_context; + impl_class->get_scale_factor = _gdk_win32_surface_get_scale_factor; + impl_class->get_unscaled_size = _gdk_win32_surface_get_unscaled_size; +} + +HGDIOBJ +gdk_win32_surface_get_handle (GdkSurface *window) +{ + if (!GDK_SURFACE_IS_WIN32 (window)) + { + g_warning (G_STRLOC " window is not a native Win32 window"); + return NULL; + } + + return GDK_SURFACE_HWND (window); +} diff --git a/gdk/win32/gdksurface-win32.h b/gdk/win32/gdksurface-win32.h new file mode 100644 index 0000000000..684690f65d --- /dev/null +++ b/gdk/win32/gdksurface-win32.h @@ -0,0 +1,376 @@ +/* GDK - The GIMP Drawing Kit + * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library. If not, see . + */ + +/* + * Modified by the GTK+ Team and others 1997-1999. See the AUTHORS + * file for a list of people on the GTK+ Team. See the ChangeLog + * files for a list of changes. These files are distributed with + * GTK+ at ftp://ftp.gtk.org/pub/gtk/. + */ + +#ifndef __GDK_SURFACE_WIN32_H__ +#define __GDK_SURFACE_WIN32_H__ + +#include "gdk/win32/gdkprivate-win32.h" +#include "gdk/gdksurfaceimpl.h" +#include "gdk/gdkcursor.h" + +#include + +G_BEGIN_DECLS + +/* Window implementation for Win32 + */ + +typedef struct _GdkSurfaceImplWin32 GdkSurfaceImplWin32; +typedef struct _GdkSurfaceImplWin32Class GdkSurfaceImplWin32Class; + +#define GDK_TYPE_SURFACE_IMPL_WIN32 (_gdk_surface_impl_win32_get_type ()) +#define GDK_SURFACE_IMPL_WIN32(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), GDK_TYPE_SURFACE_IMPL_WIN32, GdkSurfaceImplWin32)) +#define GDK_SURFACE_IMPL_WIN32_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GDK_TYPE_SURFACE_IMPL_WIN32, GdkSurfaceImplWin32Class)) +#define GDK_IS_SURFACE_IMPL_WIN32(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), GDK_TYPE_SURFACE_IMPL_WIN32)) +#define GDK_IS_SURFACE_IMPL_WIN32_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GDK_TYPE_SURFACE_IMPL_WIN32)) +#define GDK_SURFACE_IMPL_WIN32_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GDK_TYPE_SURFACE_IMPL_WIN32, GdkSurfaceImplWin32Class)) + +enum _GdkWin32AeroSnapCombo +{ + GDK_WIN32_AEROSNAP_COMBO_NOTHING = 0, + GDK_WIN32_AEROSNAP_COMBO_UP, + GDK_WIN32_AEROSNAP_COMBO_DOWN, + GDK_WIN32_AEROSNAP_COMBO_LEFT, + GDK_WIN32_AEROSNAP_COMBO_RIGHT, + /* Same order as non-shift variants. We use it to do things like: + * AEROSNAP_UP + 4 = AEROSNAP_SHIFTUP + */ + GDK_WIN32_AEROSNAP_COMBO_SHIFTUP, + GDK_WIN32_AEROSNAP_COMBO_SHIFTDOWN, + GDK_WIN32_AEROSNAP_COMBO_SHIFTLEFT, + GDK_WIN32_AEROSNAP_COMBO_SHIFTRIGHT +}; + +typedef enum _GdkWin32AeroSnapCombo GdkWin32AeroSnapCombo; + +enum _GdkWin32AeroSnapState +{ + GDK_WIN32_AEROSNAP_STATE_UNDETERMINED = 0, + GDK_WIN32_AEROSNAP_STATE_HALFLEFT, + GDK_WIN32_AEROSNAP_STATE_HALFRIGHT, + GDK_WIN32_AEROSNAP_STATE_FULLUP, + /* Maximize state is only used by edge-snap */ + GDK_WIN32_AEROSNAP_STATE_MAXIMIZE +}; + +typedef enum _GdkWin32AeroSnapState GdkWin32AeroSnapState; + +struct _GdkRectangleDouble +{ + gdouble x; + gdouble y; + gdouble width; + gdouble height; +}; + +typedef struct _GdkRectangleDouble GdkRectangleDouble; + +enum _GdkW32WindowDragOp +{ + GDK_WIN32_DRAGOP_NONE = 0, + GDK_WIN32_DRAGOP_RESIZE, + GDK_WIN32_DRAGOP_MOVE, + GDK_WIN32_DRAGOP_COUNT +}; + +typedef enum _GdkW32WindowDragOp GdkW32WindowDragOp; + +typedef enum _GdkWin32MonitorDpiType +{ + MDT_EFFECTIVE_DPI = 0, + MDT_ANGULAR_DPI = 1, + MDT_RAW_DPI = 2, + MDT_DEFAULT = MDT_EFFECTIVE_DPI +} GdkWin32MonitorDpiType; + +struct _GdkW32DragMoveResizeContext +{ + /* The window that is being moved/resized */ + GdkSurface *window; + + /* The kind of drag-operation going on. */ + GdkW32WindowDragOp op; + + /* The edge that was grabbed for resizing. Not used for moving. */ + GdkSurfaceEdge edge; + + /* The device used to initiate the op. + * We grab it at the beginning and ungrab it at the end. + */ + GdkDevice *device; + + /* The button pressed down to initiate the op. + * The op will be canceled only when *this* button + * is released. + */ + gint button; + + /* Initial cursor position when the operation began. + * Current cursor position is subtracted from it to find how far + * to move window border(s). + */ + gint start_root_x; + gint start_root_y; + + /* Initial window rectangle (position and size). + * The window is resized/moved relative to this (see start_root_*). + */ + RECT start_rect; + + /* Not used */ + guint32 timestamp; + + /* TRUE if during the next redraw we should call SetWindowPos() to push + * the window size and poistion to the native window. + */ + gboolean native_move_resize_pending; + + /* The cursor we should use while the operation is running. */ + GdkCursor *cursor; + + /* This window looks like an outline and is drawn under the window + * that is being dragged. It indicates the shape the dragged window + * will take if released at a particular point. + * Indicator window size always matches the target indicator shape, + * the the actual indicator drawn on it might not, depending on + * how much time elapsed since the animation started. + */ + HWND shape_indicator; + + /* Used to draw the indicator */ + cairo_surface_t *indicator_surface; + gint indicator_surface_width; + gint indicator_surface_height; + + /* Size/position of shape_indicator */ + GdkRectangle indicator_window_rect; + + /* Indicator will animate to occupy this rectangle */ + GdkRectangle indicator_target; + + /* Indicator will start animating from this rectangle */ + GdkRectangle indicator_start; + + /* Timestamp of the animation start */ + gint64 indicator_start_time; + + /* Timer that drives the animation */ + guint timer; + + /* A special timestamp, if we want to draw not how + * the animation should look *now*, but how it should + * look at arbitrary moment of time. + * Set to 0 to tell GDK to use current time. + */ + gint64 draw_timestamp; + + /* Indicates that a transformation was revealed: + * + * For drag-resize: If it's FALSE, + * then the pointer have not yet hit a trigger that triggers fullup. + * If TRUE, then the pointer did hit a trigger that triggers fullup + * at some point during this drag op. + * This is used to prevent drag-resize from triggering + * a transformation when first approaching a trigger of the work area - + * one must drag it all the way to the very trigger to trigger; afterwards + * a transformation will start triggering at some distance from the trigger + * for as long as the op is still running. This is how AeroSnap works. + * + * For drag-move: If it's FALSE, + * then the pointer have not yet hit a trigger, even if it is + * already within a edge region. + * If it's TRUE, then the pointer did hit a trigger within an + * edge region, and have not yet left an edge region + * (passing from one edge region into another doesn't count). + */ + gboolean revealed; + + /* Arrays of GdkRectangle pairs, describing the areas of the virtual + * desktop that trigger various AeroSnap window transofrmations + * Coordinates are GDK screen coordinates. + */ + GArray *halfleft_regions; + GArray *halfright_regions; + GArray *maximize_regions; + GArray *fullup_regions; + + /* Current pointer position will result in this kind of snapping, + * if the drag op is finished. + */ + GdkWin32AeroSnapState current_snap; +}; + +typedef struct _GdkW32DragMoveResizeContext GdkW32DragMoveResizeContext; + +struct _GdkSurfaceImplWin32 +{ + GdkSurfaceImpl parent_instance; + + GdkSurface *wrapper; + HANDLE handle; + + gint8 toplevel_window_type; + + HICON hicon_big; + HICON hicon_small; + + /* When VK_PACKET sends us a leading surrogate, it's stashed here. + * Later, when another VK_PACKET sends a tailing surrogate, we make up + * a full unicode character from them, or discard the leading surrogate, + * if the next key is not a tailing surrogate. + */ + wchar_t leading_surrogate_keydown; + wchar_t leading_surrogate_keyup; + + /* Window size hints */ + gint hint_flags; + GdkGeometry hints; + + GdkEventMask native_event_mask; + + GdkSurfaceTypeHint type_hint; + + GdkSurface *transient_owner; + GSList *transient_children; + gint num_transients; + gboolean changing_state; + + gint initial_x; + gint initial_y; + + /* left/right/top/bottom width of the shadow/resize-grip around the window */ + RECT margins; + + /* left+right and top+bottom from @margins */ + gint margins_x; + gint margins_y; + + /* Set to TRUE when GTK tells us that margins are 0 everywhere. + * We don't actually set margins to 0, we just set this bit. + */ + guint zero_margins : 1; + guint no_bg : 1; + guint inhibit_configure : 1; + + /* Set to TRUE if window is using true layered mode adjustments + * via UpdateLayeredWindow(). + * Layered windows that get SetLayeredWindowAttributes() called + * on them are not true layered windows. + */ + guint layered : 1; + + /* If TRUE, the @temp_styles is set to the styles that were temporarily + * added to this window. + */ + guint have_temp_styles : 1; + + /* If TRUE, the window is in the process of being maximized. + * This is set by WM_SYSCOMMAND and by gdk_win32_surface_maximize (), + * and is unset when WM_WINDOWPOSCHANGING is handled. + */ + guint maximizing : 1; + + /* GDK does not keep window contents around, it just draws new + * stuff over the window where changes occurred. + * cache_surface retains old window contents, because + * UpdateLayeredWindow() doesn't do partial redraws. + */ + cairo_surface_t *cache_surface; + cairo_surface_t *cairo_surface; + + /* Unlike window-backed surfaces, DIB-backed surface + * does not provide a way to query its size, + * so we have to remember it ourselves. + */ + gint dib_width; + gint dib_height; + + /* If the client wants uniformly-transparent window, + * we remember the opacity value here and apply it + * during UpdateLayredWindow() call, for layered windows. + */ + gdouble layered_opacity; + + HDC hdc; + int hdc_count; + HBITMAP saved_dc_bitmap; /* Original bitmap for dc */ + + GdkW32DragMoveResizeContext drag_move_resize_context; + + /* Remembers where the window was snapped. + * Some snap operations change their meaning if + * the window is already snapped. + */ + GdkWin32AeroSnapState snap_state; + + /* Remembers window position before it was snapped. + * This is used to unsnap it. + * Position and size are percentages of the workarea + * of the monitor on which the window was before it was snapped. + */ + GdkRectangleDouble *snap_stash; + + /* Also remember the same position, but in absolute form. */ + GdkRectangle *snap_stash_int; + + /* Decorations set by gdk_surface_set_decorations() or NULL if unset */ + GdkWMDecoration* decorations; + + /* No. of windows to force layered windows off */ + guint suppress_layered; + + /* Temporary styles that this window got for the purpose of + * handling WM_SYSMENU. + * They are removed at the first opportunity (usually WM_INITMENU). + */ + LONG_PTR temp_styles; + + /* scale of window on HiDPI */ + gint window_scale; + gint unscaled_width; + gint unscaled_height; +}; + +struct _GdkSurfaceImplWin32Class +{ + GdkSurfaceImplClass parent_class; +}; + +GType _gdk_surface_impl_win32_get_type (void); + +void _gdk_win32_surface_tmp_unset_bg (GdkSurface *window, + gboolean recurse); +void _gdk_win32_surface_tmp_reset_bg (GdkSurface *window, + gboolean recurse); + +void _gdk_win32_surface_tmp_unset_parent_bg (GdkSurface *window); +void _gdk_win32_surface_tmp_reset_parent_bg (GdkSurface *window); + +void _gdk_win32_surface_update_style_bits (GdkSurface *window); + +gint _gdk_win32_surface_get_scale_factor (GdkSurface *window); + +G_END_DECLS + +#endif /* __GDK_SURFACE_WIN32_H__ */ diff --git a/gdk/win32/gdkwin32.h b/gdk/win32/gdkwin32.h index e098366e0f..f4001dabb5 100644 --- a/gdk/win32/gdkwin32.h +++ b/gdk/win32/gdkwin32.h @@ -33,7 +33,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/gdk/win32/gdkwin32surface.h b/gdk/win32/gdkwin32surface.h new file mode 100644 index 0000000000..9dda179353 --- /dev/null +++ b/gdk/win32/gdkwin32surface.h @@ -0,0 +1,55 @@ +/* GDK - The GIMP Drawing Kit + * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * 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, see . + */ + +/* + * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS + * file for a list of people on the GTK+ Team. See the ChangeLog + * files for a list of changes. These files are distributed with + * GTK+ at ftp://ftp.gtk.org/pub/gtk/. + */ + +#ifndef __GDK_WIN32_SURFACE_H__ +#define __GDK_WIN32_SURFACE_H__ + +#if !defined (__GDKWIN32_H_INSIDE__) && !defined (GDK_COMPILATION) +#error "Only can be included directly." +#endif + +#include + +G_BEGIN_DECLS + +#define GDK_TYPE_WIN32_SURFACE (gdk_win32_surface_get_type ()) +#define GDK_WIN32_SURFACE(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), GDK_TYPE_WIN32_SURFACE, GdkWin32Surface)) +#define GDK_WIN32_SURFACE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GDK_TYPE_WIN32_SURFACE, GdkWin32SurfaceClass)) +#define GDK_IS_WIN32_SURFACE(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), GDK_TYPE_WIN32_SURFACE)) +#define GDK_IS_WIN32_SURFACE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GDK_TYPE_WIN32_SURFACE)) +#define GDK_WIN32_SURFACE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GDK_TYPE_WIN32_SURFACE, GdkWin32SurfaceClass)) + +#ifdef GDK_COMPILATION +typedef struct _GdkWin32Surface GdkWin32Surface; +#else +typedef GdkSurface GdkWin32Surface; +#endif +typedef struct _GdkWin32SurfaceClass GdkWin32SurfaceClass; + +GDK_AVAILABLE_IN_ALL +GType gdk_win32_surface_get_type (void); + +G_END_DECLS + +#endif /* __GDK_X11_SURFACE_H__ */ diff --git a/gdk/win32/gdkwin32window.h b/gdk/win32/gdkwin32window.h deleted file mode 100644 index 9dda179353..0000000000 --- a/gdk/win32/gdkwin32window.h +++ /dev/null @@ -1,55 +0,0 @@ -/* GDK - The GIMP Drawing Kit - * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * 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, see . - */ - -/* - * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS - * file for a list of people on the GTK+ Team. See the ChangeLog - * files for a list of changes. These files are distributed with - * GTK+ at ftp://ftp.gtk.org/pub/gtk/. - */ - -#ifndef __GDK_WIN32_SURFACE_H__ -#define __GDK_WIN32_SURFACE_H__ - -#if !defined (__GDKWIN32_H_INSIDE__) && !defined (GDK_COMPILATION) -#error "Only can be included directly." -#endif - -#include - -G_BEGIN_DECLS - -#define GDK_TYPE_WIN32_SURFACE (gdk_win32_surface_get_type ()) -#define GDK_WIN32_SURFACE(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), GDK_TYPE_WIN32_SURFACE, GdkWin32Surface)) -#define GDK_WIN32_SURFACE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GDK_TYPE_WIN32_SURFACE, GdkWin32SurfaceClass)) -#define GDK_IS_WIN32_SURFACE(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), GDK_TYPE_WIN32_SURFACE)) -#define GDK_IS_WIN32_SURFACE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GDK_TYPE_WIN32_SURFACE)) -#define GDK_WIN32_SURFACE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GDK_TYPE_WIN32_SURFACE, GdkWin32SurfaceClass)) - -#ifdef GDK_COMPILATION -typedef struct _GdkWin32Surface GdkWin32Surface; -#else -typedef GdkSurface GdkWin32Surface; -#endif -typedef struct _GdkWin32SurfaceClass GdkWin32SurfaceClass; - -GDK_AVAILABLE_IN_ALL -GType gdk_win32_surface_get_type (void); - -G_END_DECLS - -#endif /* __GDK_X11_SURFACE_H__ */ diff --git a/gdk/win32/gdkwindow-win32.c b/gdk/win32/gdkwindow-win32.c deleted file mode 100644 index 7d51af76fc..0000000000 --- a/gdk/win32/gdkwindow-win32.c +++ /dev/null @@ -1,5959 +0,0 @@ -/* GDK - The GIMP Drawing Kit - * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald - * Copyright (C) 1998-2004 Tor Lillqvist - * Copyright (C) 2001-2011 Hans Breuer - * Copyright (C) 2007-2009 Cody Russell - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * 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, see . - */ - -/* - * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS - * file for a list of people on the GTK+ Team. See the ChangeLog - * files for a list of changes. These files are distributed with - * GTK+ at ftp://ftp.gtk.org/pub/gtk/. - */ - -#include "config.h" -#include - -#include "gdk.h" -#include "gdkwindowimpl.h" -#include "gdkprivate-win32.h" -#include "gdkdeviceprivate.h" -#include "gdkdevicemanager-win32.h" -#include "gdkenumtypes.h" -#include "gdkwin32.h" -#include "gdkdisplayprivate.h" -#include "gdkmonitorprivate.h" -#include "gdkwin32window.h" -#include "gdkglcontext-win32.h" -#include "gdkdisplay-win32.h" - -#include -#include -#include -#include "fallback-c89.c" - -static void gdk_surface_impl_win32_init (GdkSurfaceImplWin32 *window); -static void gdk_surface_impl_win32_class_init (GdkSurfaceImplWin32Class *klass); -static void gdk_surface_impl_win32_finalize (GObject *object); - -static gpointer parent_class = NULL; -static GSList *modal_window_stack = NULL; - -static const cairo_user_data_key_t gdk_win32_cairo_key; -typedef struct _FullscreenInfo FullscreenInfo; - -struct _FullscreenInfo -{ - RECT r; - guint hint_flags; - LONG style; -}; - -struct _AeroSnapEdgeRegion -{ - /* The rectangle along the edge of the desktop - * that allows application of the snap transformation. - */ - GdkRectangle edge; - - /* A subregion of the "edge". When the pointer hits - * this region, the transformation is revealed. - * Usually it is 1-pixel thick and is located at the - * very edge of the screen. When there's a toolbar - * at that edge, the "trigger" and the "edge" regions - * are extended to cover that toolbar. - */ - GdkRectangle trigger; -}; - -typedef struct _AeroSnapEdgeRegion AeroSnapEdgeRegion; - -/* Use this for hWndInsertAfter (2nd argument to SetWindowPos()) if - * SWP_NOZORDER flag is used. Otherwise it's unobvious why a particular - * argument is used. Using NULL is misleading, because - * NULL is equivalent to HWND_TOP. - */ -#define SWP_NOZORDER_SPECIFIED HWND_TOP - -/* Size of the regions at the edges of the desktop where - * snapping can take place (in pixels) - */ -#define AEROSNAP_REGION_THICKNESS (20) -/* Size of the subregions that actually trigger the snapping prompt - * (in pixels). - */ -#define AEROSNAP_REGION_TRIGGER_THICKNESS (1) - -/* The gap between the snap indicator and the edge of the work area - * (in pixels). - */ -#define AEROSNAP_INDICATOR_EDGE_GAP (10) - -/* Width of the outline of the snap indicator - * (in pixels). - */ -#define AEROSNAP_INDICATOR_LINE_WIDTH (3.0) - -/* Corner radius of the snap indicator. - */ -#define AEROSNAP_INDICATOR_CORNER_RADIUS (3.0) - -/* The time it takes for snap indicator to expand/shrink - * from current window size to future position of the - * snapped window (in microseconds). - */ -#define AEROSNAP_INDICATOR_ANIMATION_DURATION (200 * 1000) - -/* Opacity if the snap indicator. */ -#define AEROSNAP_INDICATOR_OPACITY (0.5) - -/* The interval between snap indicator redraws (in milliseconds). - * 16 is ~ 1/60 of a second, for ~60 FPS. - */ -#define AEROSNAP_INDICATOR_ANIMATION_TICK (16) - -static gboolean _gdk_surface_get_functions (GdkSurface *window, - GdkWMFunction *functions); -static HDC _gdk_win32_impl_acquire_dc (GdkSurfaceImplWin32 *impl); -static void _gdk_win32_impl_release_dc (GdkSurfaceImplWin32 *impl); - -#define WINDOW_IS_TOPLEVEL(window) \ - (GDK_SURFACE_TYPE (window) != GDK_SURFACE_FOREIGN) - -struct _GdkWin32Surface { - GdkSurface parent; -}; - -struct _GdkWin32SurfaceClass { - GdkSurfaceClass parent_class; -}; - -G_DEFINE_TYPE (GdkWin32Surface, gdk_win32_surface, GDK_TYPE_SURFACE) - -static void -gdk_win32_surface_class_init (GdkWin32SurfaceClass *window_class) -{ -} - -static void -gdk_win32_surface_init (GdkWin32Surface *window) -{ -} - - -G_DEFINE_TYPE (GdkSurfaceImplWin32, gdk_surface_impl_win32, GDK_TYPE_SURFACE_IMPL) - -GType -_gdk_surface_impl_win32_get_type (void) -{ - static GType object_type = 0; - - if (!object_type) - { - const GTypeInfo object_info = - { - sizeof (GdkSurfaceImplWin32Class), - (GBaseInitFunc) NULL, - (GBaseFinalizeFunc) NULL, - (GClassInitFunc) gdk_surface_impl_win32_class_init, - NULL, /* class_finalize */ - NULL, /* class_data */ - sizeof (GdkSurfaceImplWin32), - 0, /* n_preallocs */ - (GInstanceInitFunc) gdk_surface_impl_win32_init, - }; - - object_type = g_type_register_static (GDK_TYPE_SURFACE_IMPL, - "GdkSurfaceImplWin32", - &object_info, 0); - } - - return object_type; -} - -static void -gdk_surface_impl_win32_init (GdkSurfaceImplWin32 *impl) -{ - GdkDisplay *display = gdk_display_get_default (); - - impl->toplevel_window_type = -1; - impl->hicon_big = NULL; - impl->hicon_small = NULL; - impl->hint_flags = 0; - impl->type_hint = GDK_SURFACE_TYPE_HINT_NORMAL; - impl->transient_owner = NULL; - impl->transient_children = NULL; - impl->num_transients = 0; - impl->changing_state = FALSE; - impl->window_scale = 1; -} - -static void -gdk_surface_impl_win32_finalize (GObject *object) -{ - GdkSurface *wrapper; - GdkSurfaceImplWin32 *window_impl; - - g_return_if_fail (GDK_IS_SURFACE_IMPL_WIN32 (object)); - - window_impl = GDK_SURFACE_IMPL_WIN32 (object); - - wrapper = window_impl->wrapper; - - if (!GDK_SURFACE_DESTROYED (wrapper)) - { - gdk_win32_handle_table_remove (window_impl->handle); - } - - g_clear_pointer (&window_impl->snap_stash, g_free); - g_clear_pointer (&window_impl->snap_stash_int, g_free); - - if (window_impl->hicon_big != NULL) - { - GDI_CALL (DestroyIcon, (window_impl->hicon_big)); - window_impl->hicon_big = NULL; - } - - if (window_impl->hicon_small != NULL) - { - GDI_CALL (DestroyIcon, (window_impl->hicon_small)); - window_impl->hicon_small = NULL; - } - - g_free (window_impl->decorations); - - if (window_impl->cache_surface) - { - cairo_surface_destroy (window_impl->cache_surface); - window_impl->cache_surface = NULL; - } - - if (window_impl->cairo_surface) - { - cairo_surface_destroy (window_impl->cairo_surface); - window_impl->cairo_surface = NULL; - } - - g_assert (window_impl->transient_owner == NULL); - g_assert (window_impl->transient_children == NULL); - - G_OBJECT_CLASS (parent_class)->finalize (object); -} - -static void -gdk_win32_get_window_client_area_rect (GdkSurface *window, - gint scale, - RECT *rect) -{ - gint x, y, width, height; - - gdk_surface_get_position (window, &x, &y); - width = gdk_surface_get_width (window); - height = gdk_surface_get_height (window); - rect->left = x * scale; - rect->top = y * scale; - rect->right = rect->left + width * scale; - rect->bottom = rect->top + height * scale; -} - -static void -gdk_win32_surface_get_queued_window_rect (GdkSurface *window, - RECT *return_window_rect) -{ - RECT window_rect; - GdkSurfaceImplWin32 *impl = GDK_SURFACE_IMPL_WIN32 (window->impl); - - gdk_win32_get_window_client_area_rect (window, impl->window_scale, &window_rect); - - /* Turn client area into window area */ - _gdk_win32_adjust_client_rect (window, &window_rect); - - /* Convert GDK screen coordinates to W32 desktop coordinates */ - window_rect.left -= _gdk_offset_x * impl->window_scale; - window_rect.right -= _gdk_offset_x * impl->window_scale; - window_rect.top -= _gdk_offset_y * impl->window_scale; - window_rect.bottom -= _gdk_offset_y * impl->window_scale; - - *return_window_rect = window_rect; -} - -static void -gdk_win32_surface_apply_queued_move_resize (GdkSurface *window, - RECT window_rect) -{ - if (!IsIconic (GDK_SURFACE_HWND (window))) - { - GdkSurfaceImplWin32 *impl = GDK_SURFACE_IMPL_WIN32 (window->impl); - GDK_NOTE (EVENTS, g_print ("Setting window position ... ")); - - API_CALL (SetWindowPos, (GDK_SURFACE_HWND (window), - SWP_NOZORDER_SPECIFIED, - window_rect.left, window_rect.top, - window_rect.right - window_rect.left, - window_rect.bottom - window_rect.top, - SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOREDRAW)); - - GDK_NOTE (EVENTS, g_print (" ... set window position\n")); - - return; - } - - /* Don't move iconic windows */ - /* TODO: use SetWindowPlacement() to change non-minimized window position */ -} - -static gboolean -gdk_win32_surface_begin_paint (GdkSurface *window) -{ - GdkSurfaceImplWin32 *impl; - RECT window_rect; - - if (window == NULL || GDK_SURFACE_DESTROYED (window)) - return TRUE; - - impl = GDK_SURFACE_IMPL_WIN32 (window->impl); - - /* Layered windows are moved *after* repaint. - * We supply our own surface, return FALSE to make GDK use it. - */ - if (impl->layered) - return FALSE; - - /* FIXME: Possibly remove the following lines when we transition to GL - * drawing fully. This will probably mean that we won't - * be able to use layered windows, as layered windows seem - * to support only up to OpenGL 1.1, which is not enough for our - * needs here. - */ - - /* Non-GL windows are moved *after* repaint. - * We don't supply our own surface, return TRUE to make GDK create - * one by itself. - *//* - if (!window->current_paint.use_gl) - return TRUE;*/ - - /* GL windows are moved *before* repaint (otherwise - * repainting doesn't work), but if there's no move queued up, - * return immediately. Doesn't matter what we return, GDK - * will create a surface anyway, as if we returned TRUE. - */ - if (!impl->drag_move_resize_context.native_move_resize_pending) - return TRUE; - - impl->drag_move_resize_context.native_move_resize_pending = FALSE; - - /* Get the position/size of the window that GDK wants, - * apply it. - */ - gdk_win32_surface_get_queued_window_rect (window, &window_rect); - gdk_win32_surface_apply_queued_move_resize (window, window_rect); - - return TRUE; -} - -static void -gdk_win32_surface_end_paint (GdkSurface *window) -{ - /* FIXME: Possibly make gdk_win32_surface_end_paint() a - * no-op stub, like what is done in Wayland, as - * the items here rely on layered window usage, - * when we transition to full GL drawing, as - * layered windows do not support enough GL - * for our needs here - */ - GdkSurfaceImplWin32 *impl; - RECT window_rect; - HDC hdc; - POINT window_position; - SIZE window_size; - POINT source_point; - BLENDFUNCTION blender; - cairo_t *cr; - - if (window == NULL || GDK_SURFACE_DESTROYED (window)) - return; - - impl = GDK_SURFACE_IMPL_WIN32 (window->impl); - - /* GL windows are moved *before* repaint */ - /*if (window->current_paint.use_gl) - return;*/ - - /* No move/resize is queued up, and we don't need to update - * the contents of a layered window, so return immediately. - */ - if (!impl->layered && - !impl->drag_move_resize_context.native_move_resize_pending) - return; - - impl->drag_move_resize_context.native_move_resize_pending = FALSE; - - /* Get the position/size of the window that GDK wants. */ - gdk_win32_surface_get_queued_window_rect (window, &window_rect); - - if (!impl->layered) - { - gdk_win32_surface_apply_queued_move_resize (window, window_rect); - - return; - } - - window_position.x = window_rect.left; - window_position.y = window_rect.top; - - window_size.cx = window_rect.right - window_rect.left; - window_size.cy = window_rect.bottom - window_rect.top; - - cairo_surface_flush (impl->cairo_surface); - - /* we always draw in the top-left corner of the surface */ - source_point.x = source_point.y = 0; - - blender.BlendOp = AC_SRC_OVER; - blender.BlendFlags = 0; - blender.AlphaFormat = AC_SRC_ALPHA; - blender.SourceConstantAlpha = impl->layered_opacity * 255; - - /* Update cache surface contents */ - cr = cairo_create (impl->cache_surface); - - cairo_set_source_surface (cr, window->current_paint.surface, 0, 0); - gdk_cairo_region (cr, window->current_paint.region); - cairo_clip (cr); - - cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE); - cairo_paint (cr); - - cairo_destroy (cr); - - cairo_surface_flush (impl->cache_surface); - hdc = cairo_win32_surface_get_dc (impl->cache_surface); - - /* Don't use UpdateLayeredWindow on minimized windows */ - if (IsIconic (GDK_SURFACE_HWND (window))) - { - gdk_win32_surface_apply_queued_move_resize (window, window_rect); - - return; - } - - /* Move, resize and redraw layered window in one call */ - API_CALL (UpdateLayeredWindow, (GDK_SURFACE_HWND (window), NULL, - &window_position, &window_size, - hdc, &source_point, - 0, &blender, ULW_ALPHA)); -} - -void -_gdk_win32_adjust_client_rect (GdkSurface *window, - RECT *rect) -{ - LONG style, exstyle; - - style = GetWindowLong (GDK_SURFACE_HWND (window), GWL_STYLE); - exstyle = GetWindowLong (GDK_SURFACE_HWND (window), GWL_EXSTYLE); - API_CALL (AdjustWindowRectEx, (rect, style, FALSE, exstyle)); -} - -gboolean -_gdk_win32_surface_enable_transparency (GdkSurface *window) -{ - GdkSurfaceImplWin32 *impl; - DWM_BLURBEHIND blur_behind; - HRGN empty_region; - HRESULT call_result; - HWND parent, thiswindow; - - if (window == NULL || GDK_SURFACE_HWND (window) == NULL) - return FALSE; - - impl = GDK_SURFACE_IMPL_WIN32 (window->impl); - - /* layered windows don't need blurbehind for transparency */ - if (impl->layered) - return TRUE; - - if (!gdk_display_is_composited (gdk_surface_get_display (window))) - return FALSE; - - thiswindow = GDK_SURFACE_HWND (window); - - /* Blurbehind only works on toplevel windows */ - parent = GetAncestor (thiswindow, GA_PARENT); - if (!(GetWindowLong (thiswindow, GWL_STYLE) & WS_POPUP) && - (parent == NULL || parent != GetDesktopWindow ())) - return FALSE; - - empty_region = CreateRectRgn (0, 0, -1, -1); - - if (empty_region == NULL) - return FALSE; - - memset (&blur_behind, 0, sizeof (blur_behind)); - blur_behind.dwFlags = DWM_BB_ENABLE | DWM_BB_BLURREGION; - blur_behind.hRgnBlur = empty_region; - blur_behind.fEnable = TRUE; - call_result = DwmEnableBlurBehindWindow (thiswindow, &blur_behind); - - if (!SUCCEEDED (call_result)) - g_warning ("%s: %s (%p) failed: %" G_GINT32_MODIFIER "x", - G_STRLOC, "DwmEnableBlurBehindWindow", thiswindow, (guint32) call_result); - - DeleteObject (empty_region); - - return SUCCEEDED (call_result); -} - -static const gchar * -get_default_title (void) -{ - const char *title; - title = g_get_application_name (); - if (!title) - title = g_get_prgname (); - - return title; -} - -/* RegisterGdkClass - * is a wrapper function for RegisterWindowClassEx. - * It creates at least one unique class for every - * GdkSurfaceType. If support for single window-specific icons - * is ever needed (e.g Dialog specific), every such window should - * get its own class - */ -static ATOM -RegisterGdkClass (GdkSurfaceType wtype, GdkSurfaceTypeHint wtype_hint) -{ - static ATOM klassTOPLEVEL = 0; - static ATOM klassCHILD = 0; - static ATOM klassTEMP = 0; - static ATOM klassTEMPSHADOW = 0; - static HICON hAppIcon = NULL; - static HICON hAppIconSm = NULL; - static WNDCLASSEXW wcl; - ATOM klass = 0; - - wcl.cbSize = sizeof (WNDCLASSEX); - wcl.style = 0; /* DON'T set CS_REDRAW. It causes total redraw - * on WM_SIZE and WM_MOVE. Flicker, Performance! - */ - wcl.lpfnWndProc = _gdk_win32_surface_procedure; - wcl.cbClsExtra = 0; - wcl.cbWndExtra = 0; - wcl.hInstance = _gdk_app_hmodule; - wcl.hIcon = 0; - wcl.hIconSm = 0; - - /* initialize once! */ - if (0 == hAppIcon && 0 == hAppIconSm) - { - gchar sLoc [MAX_PATH+1]; - - if (0 != GetModuleFileName (_gdk_app_hmodule, sLoc, MAX_PATH)) - { - ExtractIconEx (sLoc, 0, &hAppIcon, &hAppIconSm, 1); - - if (0 == hAppIcon && 0 == hAppIconSm) - { - if (0 != GetModuleFileName (_gdk_dll_hinstance, sLoc, MAX_PATH)) - { - ExtractIconEx (sLoc, 0, &hAppIcon, &hAppIconSm, 1); - } - } - } - - if (0 == hAppIcon && 0 == hAppIconSm) - { - hAppIcon = LoadImage (NULL, IDI_APPLICATION, IMAGE_ICON, - GetSystemMetrics (SM_CXICON), - GetSystemMetrics (SM_CYICON), 0); - hAppIconSm = LoadImage (NULL, IDI_APPLICATION, IMAGE_ICON, - GetSystemMetrics (SM_CXSMICON), - GetSystemMetrics (SM_CYSMICON), 0); - } - } - - if (0 == hAppIcon) - hAppIcon = hAppIconSm; - else if (0 == hAppIconSm) - hAppIconSm = hAppIcon; - - wcl.lpszMenuName = NULL; - - /* initialize once per class */ - /* - * HB: Setting the background brush leads to flicker, because we - * don't get asked how to clear the background. This is not what - * we want, at least not for input_only windows ... - */ -#define ONCE_PER_CLASS() \ - wcl.hIcon = CopyIcon (hAppIcon); \ - wcl.hIconSm = CopyIcon (hAppIconSm); \ - wcl.hbrBackground = NULL; \ - wcl.hCursor = LoadCursor (NULL, IDC_ARROW); - - switch (wtype) - { - case GDK_SURFACE_TOPLEVEL: - /* MSDN: CS_OWNDC is needed for OpenGL contexts */ - wcl.style |= CS_OWNDC; - if (0 == klassTOPLEVEL) - { - wcl.lpszClassName = L"gdkWindowToplevel"; - - ONCE_PER_CLASS (); - klassTOPLEVEL = RegisterClassExW (&wcl); - } - klass = klassTOPLEVEL; - break; - - case GDK_SURFACE_TEMP: - if ((wtype_hint == GDK_SURFACE_TYPE_HINT_MENU) || - (wtype_hint == GDK_SURFACE_TYPE_HINT_DROPDOWN_MENU) || - (wtype_hint == GDK_SURFACE_TYPE_HINT_POPUP_MENU)) - { - if (klassTEMPSHADOW == 0) - { - wcl.lpszClassName = L"gdkWindowTempShadow"; - wcl.style |= CS_SAVEBITS; - wcl.style |= 0x00020000; /* CS_DROPSHADOW */ - - ONCE_PER_CLASS (); - klassTEMPSHADOW = RegisterClassExW (&wcl); - } - - klass = klassTEMPSHADOW; - } - else - { - if (klassTEMP == 0) - { - wcl.lpszClassName = L"gdkWindowTemp"; - wcl.style |= CS_SAVEBITS; - ONCE_PER_CLASS (); - klassTEMP = RegisterClassExW (&wcl); - } - - klass = klassTEMP; - } - break; - - case GDK_SURFACE_CHILD: - default: - g_assert_not_reached (); - break; - } - - if (klass == 0) - { - WIN32_API_FAILED ("RegisterClassExW"); - g_error ("That is a fatal error"); - } - return klass; -} - -/* - * Create native windows. - * - * With the default Gdk the created windows are mostly toplevel windows. - * - * Placement of the window is derived from the passed in window, - * except for toplevel window where OS/Window Manager placement - * is used. - * - * From attributes the only things used is: colormap, title, - * wmclass and type_hint. [1]. We are checking redundant information - * and complain if that changes, which would break this implementation - * again. - * - * [1] http://mail.gnome.org/archives/gtk-devel-list/2010-August/msg00214.html - */ -void -_gdk_win32_display_create_window_impl (GdkDisplay *display, - GdkSurface *window, - GdkSurface *real_parent, - GdkEventMask event_mask, - GdkSurfaceAttr *attributes) -{ - HWND hwndNew; - HANDLE hparent; - ATOM klass = 0; - DWORD dwStyle = 0, dwExStyle; - RECT rect; - GdkSurfaceImplWin32 *impl; - GdkWin32Display *display_win32; - const gchar *title; - wchar_t *wtitle; - gint window_width, window_height; - gint offset_x = 0, offset_y = 0; - gint x, y, real_x = 0, real_y = 0; - - g_return_if_fail (display == _gdk_display); - - GDK_NOTE (MISC, - g_print ("_gdk_surface_impl_new: %s %s\n", (window->window_type == GDK_SURFACE_TOPLEVEL ? "TOPLEVEL" : - (window->window_type == GDK_SURFACE_TEMP ? "TEMP" : "???")), - (attributes->wclass == GDK_INPUT_OUTPUT ? "" : "input-only"))); - - hparent = (real_parent != NULL) ? GDK_SURFACE_HWND (real_parent) : NULL; - - impl = g_object_new (GDK_TYPE_SURFACE_IMPL_WIN32, NULL); - impl->wrapper = GDK_SURFACE (window); - window->impl = GDK_SURFACE_IMPL (impl); - - impl->layered = FALSE; - impl->layered_opacity = 1.0; - - display_win32 = GDK_WIN32_DISPLAY (display); - impl->window_scale = _gdk_win32_display_get_monitor_scale_factor (display_win32, NULL, NULL, NULL); - impl->unscaled_width = window->width * impl->window_scale; - impl->unscaled_height = window->height * impl->window_scale; - - if (!window->input_only) - { - dwExStyle = 0; - } - else - { - /* I very much doubt using WS_EX_TRANSPARENT actually - * corresponds to how X11 InputOnly windows work, but it appears - * to work well enough for the actual use cases in gtk. - */ - dwExStyle = WS_EX_TRANSPARENT; - GDK_NOTE (MISC, g_print ("... GDK_INPUT_ONLY\n")); - } - - switch (window->window_type) - { - case GDK_SURFACE_TOPLEVEL: - if (window->parent) - { - /* The common code warns for this case. */ - hparent = GetDesktopWindow (); - } - /* Children of foreign windows aren't toplevel windows */ - if (real_parent != NULL && GDK_SURFACE_TYPE (real_parent) == GDK_SURFACE_FOREIGN) - { - dwStyle = WS_CHILDWINDOW | WS_CLIPCHILDREN; - } - else - { - /* MSDN: We need WS_CLIPCHILDREN and WS_CLIPSIBLINGS for GL Context Creation */ - if (window->window_type == GDK_SURFACE_TOPLEVEL) - dwStyle = WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN | WS_CLIPSIBLINGS; - else - dwStyle = WS_OVERLAPPED | WS_MINIMIZEBOX | WS_SYSMENU | WS_CAPTION | WS_THICKFRAME | WS_CLIPCHILDREN; - - offset_x = _gdk_offset_x; - offset_y = _gdk_offset_y; - } - break; - - case GDK_SURFACE_TEMP: - /* A temp window is not necessarily a top level window */ - dwStyle = real_parent == NULL ? WS_POPUP : WS_CHILDWINDOW; - dwStyle |= WS_CLIPCHILDREN | WS_CLIPSIBLINGS; - dwExStyle |= WS_EX_TOOLWINDOW | WS_EX_TOPMOST; - offset_x = _gdk_offset_x; - offset_y = _gdk_offset_y; - break; - - - case GDK_SURFACE_CHILD: - default: - g_assert_not_reached (); - } - - rect.left = window->x * impl->window_scale; - rect.top = window->y * impl->window_scale; - rect.right = rect.left + window->width * impl->window_scale; - rect.bottom = rect.top + window->height * impl->window_scale; - - AdjustWindowRectEx (&rect, dwStyle, FALSE, dwExStyle); - - real_x = (window->x - offset_x) * impl->window_scale; - real_y = (window->y - offset_y) * impl->window_scale; - - if (window->window_type == GDK_SURFACE_TOPLEVEL) - { - /* We initially place it at default so that we can get the - default window positioning if we want */ - x = y = CW_USEDEFAULT; - } - else - { - /* TEMP, FOREIGN: Put these where requested */ - x = real_x; - y = real_y; - } - - window_width = rect.right - rect.left; - window_height = rect.bottom - rect.top; - - title = get_default_title (); - if (!title || !*title) - title = ""; - - impl->native_event_mask = GDK_STRUCTURE_MASK | event_mask; - - if (impl->type_hint == GDK_SURFACE_TYPE_HINT_UTILITY) - dwExStyle |= WS_EX_TOOLWINDOW; - - /* WS_EX_TRANSPARENT means "try draw this window last, and ignore input". - * It's the last part we're after. We don't want DND indicator to accept - * input, because that will make it a potential drop target, and if it's - * under the mouse cursor, this will kill any DND. - */ - if (impl->type_hint == GDK_SURFACE_TYPE_HINT_DND) - dwExStyle |= WS_EX_TRANSPARENT; - - klass = RegisterGdkClass (window->window_type, impl->type_hint); - - wtitle = g_utf8_to_utf16 (title, -1, NULL, NULL, NULL); - - hwndNew = CreateWindowExW (dwExStyle, - MAKEINTRESOURCEW (klass), - wtitle, - dwStyle, - x, - y, - window_width, window_height, - hparent, - NULL, - _gdk_app_hmodule, - window); - if (GDK_SURFACE_HWND (window) != hwndNew) - { - g_warning ("gdk_surface_new: gdk_event_translate::WM_CREATE (%p, %p) HWND mismatch.", - GDK_SURFACE_HWND (window), - hwndNew); - - /* HB: IHMO due to a race condition the handle was increased by - * one, which causes much trouble. Because I can't find the - * real bug, try to workaround it ... - * To reproduce: compile with MSVC 5, DEBUG=1 - */ -# if 0 - gdk_win32_handle_table_remove (GDK_SURFACE_HWND (window)); - GDK_SURFACE_HWND (window) = hwndNew; - gdk_win32_handle_table_insert (&GDK_SURFACE_HWND (window), window); -# else - /* the old behaviour, but with warning */ - impl->handle = hwndNew; -# endif - - } - - GetWindowRect (GDK_SURFACE_HWND (window), &rect); - impl->initial_x = rect.left; - impl->initial_y = rect.top; - - /* Now we know the initial position, move to actually specified position */ - if (real_x != x || real_y != y) - { - API_CALL (SetWindowPos, (GDK_SURFACE_HWND (window), - SWP_NOZORDER_SPECIFIED, - real_x, real_y, 0, 0, - SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOZORDER)); - } - - g_object_ref (window); - gdk_win32_handle_table_insert (&GDK_SURFACE_HWND (window), window); - - GDK_NOTE (MISC, g_print ("... \"%s\" %dx%d@%+d%+d %p = %p\n", - title, - window_width, window_height, - window->x - offset_x, - window->y - offset_y, - hparent, - GDK_SURFACE_HWND (window))); - - /* Add window handle to title */ - GDK_NOTE (MISC_OR_EVENTS, gdk_surface_set_title (window, title)); - - g_free (wtitle); - - if (impl->handle == NULL) - { - WIN32_API_FAILED ("CreateWindowExW"); - g_object_unref (window); - return; - } - -// if (!from_set_skip_taskbar_hint && window->window_type == GDK_SURFACE_TEMP) -// gdk_surface_set_skip_taskbar_hint (window, TRUE); - - _gdk_win32_surface_enable_transparency (window); -} - -GdkSurface * -gdk_win32_surface_foreign_new_for_display (GdkDisplay *display, - HWND anid) -{ - GdkSurface *window; - GdkSurfaceImplWin32 *impl; - - HANDLE parent; - RECT rect; - POINT point; - - if ((window = gdk_win32_surface_lookup_for_display (display, anid)) != NULL) - return g_object_ref (window); - - window = _gdk_display_create_window (display); - window->impl = g_object_new (GDK_TYPE_SURFACE_IMPL_WIN32, NULL); - window->impl_window = window; - impl = GDK_SURFACE_IMPL_WIN32 (window->impl); - impl->wrapper = window; - parent = GetParent (anid); - - /* Always treat foreigns as toplevels */ - window->parent = NULL; - - GetClientRect ((HWND) anid, &rect); - point.x = rect.left; - point.y = rect.right; - ClientToScreen ((HWND) anid, &point); - if (parent != GetDesktopWindow ()) - ScreenToClient (parent, &point); - window->x = point.x / impl->window_scale; - window->y = point.y / impl->window_scale; - impl->unscaled_width = rect.right - rect.left; - impl->unscaled_height = rect.bottom - rect.top; - window->width = (impl->unscaled_width + impl->window_scale - 1) / impl->window_scale; - window->height = (impl->unscaled_height + impl->window_scale - 1) / impl->window_scale; - window->window_type = GDK_SURFACE_FOREIGN; - window->destroyed = FALSE; - window->event_mask = GDK_ALL_EVENTS_MASK; /* XXX */ - if (IsWindowVisible ((HWND) anid)) - window->state &= (~GDK_SURFACE_STATE_WITHDRAWN); - else - window->state |= GDK_SURFACE_STATE_WITHDRAWN; - if (GetWindowLong ((HWND)anid, GWL_EXSTYLE) & WS_EX_TOPMOST) - window->state |= GDK_SURFACE_STATE_ABOVE; - else - window->state &= (~GDK_SURFACE_STATE_ABOVE); - window->state &= (~GDK_SURFACE_STATE_BELOW); - window->viewable = TRUE; - - GDK_SURFACE_HWND (window) = anid; - - g_object_ref (window); - gdk_win32_handle_table_insert (&GDK_SURFACE_HWND (window), window); - - GDK_NOTE (MISC, g_print ("gdk_win32_surface_foreign_new_for_display: %p: %s@%+d%+d\n", - (HWND) anid, - _gdk_win32_surface_description (window), - window->x, window->y)); - - return window; -} - -static void -gdk_win32_surface_destroy (GdkSurface *window, - gboolean recursing, - gboolean foreign_destroy) -{ - GdkSurfaceImplWin32 *window_impl = GDK_SURFACE_IMPL_WIN32 (window->impl); - GSList *tmp; - - g_return_if_fail (GDK_IS_SURFACE (window)); - - GDK_NOTE (MISC, g_print ("gdk_win32_surface_destroy: %p\n", - GDK_SURFACE_HWND (window))); - - /* Remove ourself from the modal stack */ - _gdk_remove_modal_window (window); - - /* Remove all our transient children */ - tmp = window_impl->transient_children; - while (tmp != NULL) - { - GdkSurface *child = tmp->data; - GdkSurfaceImplWin32 *child_impl = GDK_SURFACE_IMPL_WIN32 (GDK_SURFACE (child)->impl); - - child_impl->transient_owner = NULL; - tmp = tmp->next; - } - g_slist_free (window_impl->transient_children); - window_impl->transient_children = NULL; - - /* Remove ourself from our transient owner */ - if (window_impl->transient_owner != NULL) - { - gdk_surface_set_transient_for (window, NULL); - } - - if (!recursing && !foreign_destroy) - { - window->destroyed = TRUE; - DestroyWindow (GDK_SURFACE_HWND (window)); - } -} - -/* This function is called when the window really gone. - */ -static void -gdk_win32_surface_destroy_notify (GdkSurface *window) -{ - g_return_if_fail (GDK_IS_SURFACE (window)); - - GDK_NOTE (EVENTS, - g_print ("gdk_surface_destroy_notify: %p%s\n", - GDK_SURFACE_HWND (window), - (GDK_SURFACE_DESTROYED (window) ? " (destroyed)" : ""))); - - if (!GDK_SURFACE_DESTROYED (window)) - { - if (GDK_SURFACE_TYPE (window) != GDK_SURFACE_FOREIGN) - g_warning ("window %p unexpectedly destroyed", - GDK_SURFACE_HWND (window)); - - _gdk_surface_destroy (window, TRUE); - } - - gdk_win32_handle_table_remove (GDK_SURFACE_HWND (window)); - g_object_unref (window); -} - -static void -get_outer_rect (GdkSurface *window, - gint width, - gint height, - RECT *rect) -{ - GdkSurfaceImplWin32 *impl = GDK_SURFACE_IMPL_WIN32 (window->impl); - - rect->left = rect->top = 0; - rect->right = width * impl->window_scale; - rect->bottom = height * impl->window_scale; - - _gdk_win32_adjust_client_rect (window, rect); -} - -static void -adjust_for_gravity_hints (GdkSurface *window, - RECT *outer_rect, - gint *x, - gint *y) -{ - GdkSurfaceImplWin32 *impl = GDK_SURFACE_IMPL_WIN32 (window->impl); - - if (impl->hint_flags & GDK_HINT_WIN_GRAVITY) - { -#ifdef G_ENABLE_DEBUG - gint orig_x = *x, orig_y = *y; -#endif - - switch (impl->hints.win_gravity) - { - case GDK_GRAVITY_NORTH: - case GDK_GRAVITY_CENTER: - case GDK_GRAVITY_SOUTH: - *x -= (outer_rect->right - outer_rect->left / 2) / impl->window_scale; - *x += window->width / 2; - break; - - case GDK_GRAVITY_SOUTH_EAST: - case GDK_GRAVITY_EAST: - case GDK_GRAVITY_NORTH_EAST: - *x -= (outer_rect->right - outer_rect->left) / impl->window_scale; - *x += window->width; - break; - - case GDK_GRAVITY_STATIC: - *x += outer_rect->left / impl->window_scale; - break; - - default: - break; - } - - switch (impl->hints.win_gravity) - { - case GDK_GRAVITY_WEST: - case GDK_GRAVITY_CENTER: - case GDK_GRAVITY_EAST: - *y -= ((outer_rect->bottom - outer_rect->top) / 2) / impl->window_scale; - *y += window->height / 2; - break; - - case GDK_GRAVITY_SOUTH_WEST: - case GDK_GRAVITY_SOUTH: - case GDK_GRAVITY_SOUTH_EAST: - *y -= (outer_rect->bottom - outer_rect->top) / impl->window_scale; - *y += window->height; - break; - - case GDK_GRAVITY_STATIC: - *y += outer_rect->top * impl->window_scale; - break; - - default: - break; - } - GDK_NOTE (MISC, - (orig_x != *x || orig_y != *y) ? - g_print ("adjust_for_gravity_hints: x: %d->%d, y: %d->%d\n", - orig_x, *x, orig_y, *y) - : (void) 0); - } -} - -static void -show_window_internal (GdkSurface *window, - gboolean already_mapped, - gboolean deiconify) -{ - GdkSurfaceImplWin32 *window_impl; - gboolean focus_on_map = FALSE; - DWORD exstyle; - - if (window->destroyed) - return; - - GDK_NOTE (MISC, g_print ("show_window_internal: %p: %s%s\n", - GDK_SURFACE_HWND (window), - _gdk_win32_surface_state_to_string (window->state), - (deiconify ? " deiconify" : ""))); - - /* If asked to show (not deiconify) an withdrawn and iconified - * window, do that. - */ - if (!deiconify && - !already_mapped && - (window->state & GDK_SURFACE_STATE_ICONIFIED)) - { - GtkShowWindow (window, SW_SHOWMINNOACTIVE); - return; - } - - /* If asked to just show an iconified window, do nothing. */ - if (!deiconify && (window->state & GDK_SURFACE_STATE_ICONIFIED)) - return; - - /* If asked to deiconify an already noniconified window, do - * nothing. (Especially, don't cause the window to rise and - * activate. There are different calls for that.) - */ - if (deiconify && !(window->state & GDK_SURFACE_STATE_ICONIFIED)) - return; - - /* If asked to show (but not raise) a window that is already - * visible, do nothing. - */ - if (!deiconify && !already_mapped && IsWindowVisible (GDK_SURFACE_HWND (window))) - return; - - /* Other cases */ - - if (!already_mapped) - focus_on_map = window->focus_on_map; - - exstyle = GetWindowLong (GDK_SURFACE_HWND (window), GWL_EXSTYLE); - - /* Use SetWindowPos to show transparent windows so automatic redraws - * in other windows can be suppressed. - */ - if (exstyle & WS_EX_TRANSPARENT) - { - UINT flags = SWP_SHOWWINDOW | SWP_NOREDRAW | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER; - - if (GDK_SURFACE_TYPE (window) == GDK_SURFACE_TEMP || !focus_on_map) - flags |= SWP_NOACTIVATE; - - SetWindowPos (GDK_SURFACE_HWND (window), - SWP_NOZORDER_SPECIFIED, 0, 0, 0, 0, flags); - - return; - } - - /* For initial map of "normal" windows we want to emulate WM window - * positioning behaviour, which means: - * + Use user specified position if GDK_HINT_POS or GDK_HINT_USER_POS - * otherwise: - * + default to the initial CW_USEDEFAULT placement, - * no matter if the user moved the window before showing it. - * + Certain window types and hints have more elaborate positioning - * schemes. - */ - window_impl = GDK_SURFACE_IMPL_WIN32 (window->impl); - if (!already_mapped && - GDK_SURFACE_TYPE (window) == GDK_SURFACE_TOPLEVEL && - (window_impl->hint_flags & (GDK_HINT_POS | GDK_HINT_USER_POS)) == 0) - { - gboolean center = FALSE; - RECT window_rect, center_on_rect; - int x, y; - - x = window_impl->initial_x; - y = window_impl->initial_y; - - if (window_impl->type_hint == GDK_SURFACE_TYPE_HINT_SPLASHSCREEN) - { - HMONITOR monitor; - MONITORINFO mi; - - monitor = MonitorFromWindow (GDK_SURFACE_HWND (window), MONITOR_DEFAULTTONEAREST); - mi.cbSize = sizeof (mi); - if (monitor && GetMonitorInfo (monitor, &mi)) - center_on_rect = mi.rcMonitor; - else - { - center_on_rect.left = 0; - center_on_rect.top = 0; - center_on_rect.right = GetSystemMetrics (SM_CXSCREEN); - center_on_rect.bottom = GetSystemMetrics (SM_CYSCREEN); - } - center = TRUE; - } - else if (window_impl->transient_owner != NULL && - GDK_SURFACE_IS_MAPPED (window_impl->transient_owner)) - { - GdkSurface *owner = window_impl->transient_owner; - /* Center on transient parent */ - center_on_rect.left = (owner->x - _gdk_offset_x) * window_impl->window_scale; - center_on_rect.top = (owner->y - _gdk_offset_y) * window_impl->window_scale; - center_on_rect.right = center_on_rect.left + owner->width * window_impl->window_scale; - center_on_rect.bottom = center_on_rect.top + owner->height * window_impl->window_scale; - - _gdk_win32_adjust_client_rect (GDK_SURFACE (owner), ¢er_on_rect); - center = TRUE; - } - - if (center) - { - window_rect.left = 0; - window_rect.top = 0; - window_rect.right = window->width * window_impl->window_scale; - window_rect.bottom = window->height * window_impl->window_scale; - _gdk_win32_adjust_client_rect (window, &window_rect); - - x = center_on_rect.left + ((center_on_rect.right - center_on_rect.left) - (window_rect.right - window_rect.left)) / 2; - y = center_on_rect.top + ((center_on_rect.bottom - center_on_rect.top) - (window_rect.bottom - window_rect.top)) / 2; - } - - API_CALL (SetWindowPos, (GDK_SURFACE_HWND (window), - SWP_NOZORDER_SPECIFIED, - x, y, 0, 0, - SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOZORDER)); - } - - if (!already_mapped && - GDK_SURFACE_TYPE (window) == GDK_SURFACE_TOPLEVEL) - { - /* Ensure new windows are fully onscreen */ - RECT window_rect; - HMONITOR monitor; - MONITORINFO mi; - int x, y; - - GetWindowRect (GDK_SURFACE_HWND (window), &window_rect); - - monitor = MonitorFromWindow (GDK_SURFACE_HWND (window), MONITOR_DEFAULTTONEAREST); - mi.cbSize = sizeof (mi); - if (monitor && GetMonitorInfo (monitor, &mi)) - { - x = window_rect.left; - y = window_rect.top; - - if (window_rect.right > mi.rcWork.right) - { - window_rect.left -= (window_rect.right - mi.rcWork.right); - window_rect.right -= (window_rect.right - mi.rcWork.right); - } - - if (window_rect.bottom > mi.rcWork.bottom) - { - window_rect.top -= (window_rect.bottom - mi.rcWork.bottom); - window_rect.bottom -= (window_rect.bottom - mi.rcWork.bottom); - } - - if (window_rect.left < mi.rcWork.left) - { - window_rect.right += (mi.rcWork.left - window_rect.left); - window_rect.left += (mi.rcWork.left - window_rect.left); - } - - if (window_rect.top < mi.rcWork.top) - { - window_rect.bottom += (mi.rcWork.top - window_rect.top); - window_rect.top += (mi.rcWork.top - window_rect.top); - } - - if (x != window_rect.left || y != window_rect.top) - API_CALL (SetWindowPos, (GDK_SURFACE_HWND (window), - SWP_NOZORDER_SPECIFIED, - window_rect.left, window_rect.top, 0, 0, - SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOZORDER)); - } - } - - - if (window->state & GDK_SURFACE_STATE_FULLSCREEN) - { - gdk_surface_fullscreen (window); - } - else if (window->state & GDK_SURFACE_STATE_MAXIMIZED) - { - GtkShowWindow (window, SW_MAXIMIZE); - } - else if (window->state & GDK_SURFACE_STATE_ICONIFIED) - { - if (focus_on_map) - GtkShowWindow (window, SW_RESTORE); - else - GtkShowWindow (window, SW_SHOWNOACTIVATE); - } - else if (GDK_SURFACE_TYPE (window) == GDK_SURFACE_TEMP || !focus_on_map) - { - if (!IsWindowVisible (GDK_SURFACE_HWND (window))) - GtkShowWindow (window, SW_SHOWNOACTIVATE); - else - GtkShowWindow (window, SW_SHOWNA); - } - else if (!IsWindowVisible (GDK_SURFACE_HWND (window))) - { - GtkShowWindow (window, SW_SHOWNORMAL); - } - else - { - GtkShowWindow (window, SW_SHOW); - } - - /* Sync STATE_ABOVE to TOPMOST */ - if (GDK_SURFACE_TYPE (window) != GDK_SURFACE_TEMP && - (((window->state & GDK_SURFACE_STATE_ABOVE) && - !(exstyle & WS_EX_TOPMOST)) || - (!(window->state & GDK_SURFACE_STATE_ABOVE) && - (exstyle & WS_EX_TOPMOST)))) - { - API_CALL (SetWindowPos, (GDK_SURFACE_HWND (window), - (window->state & GDK_SURFACE_STATE_ABOVE)?HWND_TOPMOST:HWND_NOTOPMOST, - 0, 0, 0, 0, - SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE)); - } -} - -static void -gdk_win32_surface_show (GdkSurface *window, - gboolean already_mapped) -{ - show_window_internal (window, FALSE, FALSE); -} - -static void -gdk_win32_surface_hide (GdkSurface *window) -{ - if (window->destroyed) - return; - - GDK_NOTE (MISC, g_print ("gdk_win32_surface_hide: %p: %s\n", - GDK_SURFACE_HWND (window), - _gdk_win32_surface_state_to_string (window->state))); - - if (GDK_SURFACE_IS_MAPPED (window)) - gdk_synthesize_window_state (window, - 0, - GDK_SURFACE_STATE_WITHDRAWN); - - _gdk_surface_clear_update_area (window); - - if (GDK_SURFACE_TYPE (window) == GDK_SURFACE_TOPLEVEL) - ShowOwnedPopups (GDK_SURFACE_HWND (window), FALSE); - - /* Use SetWindowPos to hide transparent windows so automatic redraws - * in other windows can be suppressed. - */ - if (GetWindowLong (GDK_SURFACE_HWND (window), GWL_EXSTYLE) & WS_EX_TRANSPARENT) - { - SetWindowPos (GDK_SURFACE_HWND (window), SWP_NOZORDER_SPECIFIED, - 0, 0, 0, 0, - SWP_HIDEWINDOW | SWP_NOREDRAW | SWP_NOZORDER | SWP_NOMOVE | SWP_NOSIZE); - } - else - { - GtkShowWindow (window, SW_HIDE); - } -} - -static void -gdk_win32_surface_withdraw (GdkSurface *window) -{ - if (window->destroyed) - return; - - GDK_NOTE (MISC, g_print ("gdk_win32_surface_withdraw: %p: %s\n", - GDK_SURFACE_HWND (window), - _gdk_win32_surface_state_to_string (window->state))); - - gdk_surface_hide (window); /* ??? */ -} - -static void -gdk_win32_surface_move (GdkSurface *window, - gint x, gint y) -{ - g_return_if_fail (GDK_IS_SURFACE (window)); - - if (GDK_SURFACE_DESTROYED (window)) - return; - - GDK_NOTE (MISC, g_print ("gdk_win32_surface_move: %p: %+d%+d\n", - GDK_SURFACE_HWND (window), x, y)); - - if (window->state & GDK_SURFACE_STATE_FULLSCREEN) - return; - - /* Don't check GDK_SURFACE_TYPE (window) == GDK_SURFACE_CHILD. - * Foreign windows (another app's windows) might be children of our - * windows! Especially in the case of gtkplug/socket. - */ - if (GetAncestor (GDK_SURFACE_HWND (window), GA_PARENT) != GetDesktopWindow ()) - { - _gdk_surface_move_resize_child (window, x, y, window->width, window->height); - } - else - { - RECT outer_rect; - GdkSurfaceImplWin32 *impl = GDK_SURFACE_IMPL_WIN32 (window->impl); - - get_outer_rect (window, window->width, window->height, &outer_rect); - - adjust_for_gravity_hints (window, &outer_rect, &x, &y); - - GDK_NOTE (MISC, g_print ("... SetWindowPos(%p,NULL,%d,%d,0,0," - "NOACTIVATE|NOSIZE|NOZORDER)\n", - GDK_SURFACE_HWND (window), - (x - _gdk_offset_x) * impl->window_scale, - (y - _gdk_offset_y) * impl->window_scale)); - - API_CALL (SetWindowPos, (GDK_SURFACE_HWND (window), - SWP_NOZORDER_SPECIFIED, - (x - _gdk_offset_x) * impl->window_scale, - (y - _gdk_offset_y) * impl->window_scale, - 0, 0, - SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOZORDER)); - } -} - -static void -gdk_win32_surface_resize (GdkSurface *window, - gint width, gint height) -{ - g_return_if_fail (GDK_IS_SURFACE (window)); - - if (GDK_SURFACE_DESTROYED (window)) - return; - - if (width < 1) - width = 1; - if (height < 1) - height = 1; - - GDK_NOTE (MISC, g_print ("gdk_win32_surface_resize: %p: %dx%d\n", - GDK_SURFACE_HWND (window), width, height)); - - if (window->state & GDK_SURFACE_STATE_FULLSCREEN) - return; - - if (GetAncestor (GDK_SURFACE_HWND (window), GA_PARENT) != GetDesktopWindow ()) - { - _gdk_surface_move_resize_child (window, window->x, window->y, width, height); - } - else - { - RECT outer_rect; - GdkSurfaceImplWin32 *impl = GDK_SURFACE_IMPL_WIN32 (window->impl); - - get_outer_rect (window, width, height, &outer_rect); - - GDK_NOTE (MISC, g_print ("... SetWindowPos(%p,NULL,0,0,%ld,%ld," - "NOACTIVATE|NOMOVE|NOZORDER)\n", - GDK_SURFACE_HWND (window), - outer_rect.right - outer_rect.left, - outer_rect.bottom - outer_rect.top)); - - API_CALL (SetWindowPos, (GDK_SURFACE_HWND (window), - SWP_NOZORDER_SPECIFIED, - 0, 0, - outer_rect.right - outer_rect.left, - outer_rect.bottom - outer_rect.top, - SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER)); - window->resize_count += 1; - } -} - -static void -gdk_win32_surface_move_resize_internal (GdkSurface *window, - gint x, - gint y, - gint width, - gint height) -{ - g_return_if_fail (GDK_IS_SURFACE (window)); - - if (GDK_SURFACE_DESTROYED (window)) - return; - - if (width < 1) - width = 1; - if (height < 1) - height = 1; - - if (window->state & GDK_SURFACE_STATE_FULLSCREEN) - return; - - GDK_NOTE (MISC, g_print ("gdk_win32_surface_move_resize: %p: %dx%d@%+d%+d\n", - GDK_SURFACE_HWND (window), - width, height, x, y)); - - if (GetAncestor (GDK_SURFACE_HWND (window), GA_PARENT) != GetDesktopWindow ()) - { - _gdk_surface_move_resize_child (window, x, y, width, height); - } - else - { - RECT outer_rect; - GdkSurfaceImplWin32 *impl = GDK_SURFACE_IMPL_WIN32 (window->impl); - - get_outer_rect (window, width, height, &outer_rect); - - adjust_for_gravity_hints (window, &outer_rect, &x, &y); - - GDK_NOTE (MISC, g_print ("... SetWindowPos(%p,NULL,%d,%d,%ld,%ld," - "NOACTIVATE|NOZORDER)\n", - GDK_SURFACE_HWND (window), - (x - _gdk_offset_x) * impl->window_scale, - (y - _gdk_offset_y) * impl->window_scale, - outer_rect.right - outer_rect.left, - outer_rect.bottom - outer_rect.top)); - - API_CALL (SetWindowPos, (GDK_SURFACE_HWND (window), - SWP_NOZORDER_SPECIFIED, - (x - _gdk_offset_x) * impl->window_scale, - (y - _gdk_offset_y) * impl->window_scale, - outer_rect.right - outer_rect.left, - outer_rect.bottom - outer_rect.top, - SWP_NOACTIVATE | SWP_NOZORDER)); - } -} - -static void -gdk_win32_surface_move_resize (GdkSurface *window, - gboolean with_move, - gint x, - gint y, - gint width, - gint height) -{ - GdkSurfaceImplWin32 *window_impl; - - window_impl = GDK_SURFACE_IMPL_WIN32 (window->impl); - window_impl->inhibit_configure = TRUE; - - /* We ignore changes to the window being moved or resized by the - user, as we don't want to fight the user */ - if (GDK_SURFACE_HWND (window) == _modal_move_resize_window) - goto out; - - if (with_move && (width < 0 && height < 0)) - { - gdk_win32_surface_move (window, x, y); - } - else - { - if (with_move) - { - gdk_win32_surface_move_resize_internal (window, x, y, width, height); - } - else - { - gdk_win32_surface_resize (window, width, height); - } - } - - out: - window_impl->inhibit_configure = FALSE; - - if (WINDOW_IS_TOPLEVEL (window)) - _gdk_win32_emit_configure_event (window); -} - -static void -gdk_win32_surface_raise (GdkSurface *window) -{ - if (!GDK_SURFACE_DESTROYED (window)) - { - GDK_NOTE (MISC, g_print ("gdk_win32_surface_raise: %p\n", - GDK_SURFACE_HWND (window))); - - if (GDK_SURFACE_TYPE (window) == GDK_SURFACE_TEMP) - API_CALL (SetWindowPos, (GDK_SURFACE_HWND (window), HWND_TOPMOST, - 0, 0, 0, 0, - SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE)); - else if (window->accept_focus) - /* Do not wrap this in an API_CALL macro as SetForegroundWindow might - * fail when for example dragging a window belonging to a different - * application at the time of a gtk_window_present() call due to focus - * stealing prevention. */ - SetForegroundWindow (GDK_SURFACE_HWND (window)); - else - API_CALL (SetWindowPos, (GDK_SURFACE_HWND (window), HWND_TOP, - 0, 0, 0, 0, - SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE)); - } -} - -static void -gdk_win32_surface_lower (GdkSurface *window) -{ - if (!GDK_SURFACE_DESTROYED (window)) - { - GDK_NOTE (MISC, g_print ("gdk_win32_surface_lower: %p\n" - "... SetWindowPos(%p,HWND_BOTTOM,0,0,0,0," - "NOACTIVATE|NOMOVE|NOSIZE)\n", - GDK_SURFACE_HWND (window), - GDK_SURFACE_HWND (window))); - - API_CALL (SetWindowPos, (GDK_SURFACE_HWND (window), HWND_BOTTOM, - 0, 0, 0, 0, - SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE)); - } -} - -static void -gdk_win32_surface_set_urgency_hint (GdkSurface *window, - gboolean urgent) -{ - FLASHWINFO flashwinfo; - typedef BOOL (WINAPI *PFN_FlashWindowEx) (FLASHWINFO*); - PFN_FlashWindowEx flashWindowEx = NULL; - - g_return_if_fail (GDK_IS_SURFACE (window)); - - if (GDK_SURFACE_DESTROYED (window)) - return; - - flashWindowEx = (PFN_FlashWindowEx) GetProcAddress (GetModuleHandle ("user32.dll"), "FlashWindowEx"); - - if (flashWindowEx) - { - flashwinfo.cbSize = sizeof (flashwinfo); - flashwinfo.hwnd = GDK_SURFACE_HWND (window); - if (urgent) - flashwinfo.dwFlags = FLASHW_ALL | FLASHW_TIMER; - else - flashwinfo.dwFlags = FLASHW_STOP; - flashwinfo.uCount = 0; - flashwinfo.dwTimeout = 0; - - flashWindowEx (&flashwinfo); - } - else - { - FlashWindow (GDK_SURFACE_HWND (window), urgent); - } -} - -static gboolean -get_effective_window_decorations (GdkSurface *window, - GdkWMDecoration *decoration) -{ - GdkSurfaceImplWin32 *impl; - - impl = (GdkSurfaceImplWin32 *)window->impl; - - if (gdk_surface_get_decorations (window, decoration)) - return TRUE; - - if (window->window_type != GDK_SURFACE_TOPLEVEL) - { - return FALSE; - } - - if ((impl->hint_flags & GDK_HINT_MIN_SIZE) && - (impl->hint_flags & GDK_HINT_MAX_SIZE) && - impl->hints.min_width == impl->hints.max_width && - impl->hints.min_height == impl->hints.max_height) - { - *decoration = GDK_DECOR_ALL | GDK_DECOR_RESIZEH | GDK_DECOR_MAXIMIZE; - - if (impl->type_hint == GDK_SURFACE_TYPE_HINT_DIALOG || - impl->type_hint == GDK_SURFACE_TYPE_HINT_MENU || - impl->type_hint == GDK_SURFACE_TYPE_HINT_TOOLBAR) - { - *decoration |= GDK_DECOR_MINIMIZE; - } - else if (impl->type_hint == GDK_SURFACE_TYPE_HINT_SPLASHSCREEN) - { - *decoration |= GDK_DECOR_MENU | GDK_DECOR_MINIMIZE; - } - - return TRUE; - } - else if (impl->hint_flags & GDK_HINT_MAX_SIZE) - { - *decoration = GDK_DECOR_ALL | GDK_DECOR_MAXIMIZE; - if (impl->type_hint == GDK_SURFACE_TYPE_HINT_DIALOG || - impl->type_hint == GDK_SURFACE_TYPE_HINT_MENU || - impl->type_hint == GDK_SURFACE_TYPE_HINT_TOOLBAR) - { - *decoration |= GDK_DECOR_MINIMIZE; - } - - return TRUE; - } - else - { - switch (impl->type_hint) - { - case GDK_SURFACE_TYPE_HINT_DIALOG: - *decoration = (GDK_DECOR_ALL | GDK_DECOR_MINIMIZE | GDK_DECOR_MAXIMIZE); - return TRUE; - - case GDK_SURFACE_TYPE_HINT_MENU: - *decoration = (GDK_DECOR_ALL | GDK_DECOR_RESIZEH | GDK_DECOR_MINIMIZE | GDK_DECOR_MAXIMIZE); - return TRUE; - - case GDK_SURFACE_TYPE_HINT_TOOLBAR: - case GDK_SURFACE_TYPE_HINT_UTILITY: - gdk_surface_set_skip_taskbar_hint (window, TRUE); - gdk_surface_set_skip_pager_hint (window, TRUE); - *decoration = (GDK_DECOR_ALL | GDK_DECOR_MINIMIZE | GDK_DECOR_MAXIMIZE); - return TRUE; - - case GDK_SURFACE_TYPE_HINT_SPLASHSCREEN: - *decoration = (GDK_DECOR_ALL | GDK_DECOR_RESIZEH | GDK_DECOR_MENU | - GDK_DECOR_MINIMIZE | GDK_DECOR_MAXIMIZE); - return TRUE; - - case GDK_SURFACE_TYPE_HINT_DOCK: - return FALSE; - - case GDK_SURFACE_TYPE_HINT_DESKTOP: - return FALSE; - - default: - /* Fall thru */ - case GDK_SURFACE_TYPE_HINT_NORMAL: - *decoration = GDK_DECOR_ALL; - return TRUE; - } - } - - return FALSE; -} - -static void -gdk_win32_surface_set_geometry_hints (GdkSurface *window, - const GdkGeometry *geometry, - GdkSurfaceHints geom_mask) -{ - GdkSurfaceImplWin32 *impl; - FullscreenInfo *fi; - - g_return_if_fail (GDK_IS_SURFACE (window)); - - if (GDK_SURFACE_DESTROYED (window)) - return; - - GDK_NOTE (MISC, g_print ("gdk_surface_set_geometry_hints: %p\n", - GDK_SURFACE_HWND (window))); - - impl = GDK_SURFACE_IMPL_WIN32 (window->impl); - - fi = g_object_get_data (G_OBJECT (window), "fullscreen-info"); - if (fi) - fi->hint_flags = geom_mask; - else - impl->hint_flags = geom_mask; - impl->hints = *geometry; - - if (geom_mask & GDK_HINT_POS) - { - /* even the X11 mplementation doesn't care */ - } - - if (geom_mask & GDK_HINT_MIN_SIZE) - { - GDK_NOTE (MISC, g_print ("... MIN_SIZE: %dx%d\n", - geometry->min_width, geometry->min_height)); - } - - if (geom_mask & GDK_HINT_MAX_SIZE) - { - GDK_NOTE (MISC, g_print ("... MAX_SIZE: %dx%d\n", - geometry->max_width, geometry->max_height)); - } - - if (geom_mask & GDK_HINT_BASE_SIZE) - { - GDK_NOTE (MISC, g_print ("... BASE_SIZE: %dx%d\n", - geometry->base_width, geometry->base_height)); - } - - if (geom_mask & GDK_HINT_RESIZE_INC) - { - GDK_NOTE (MISC, g_print ("... RESIZE_INC: (%d,%d)\n", - geometry->width_inc, geometry->height_inc)); - } - - if (geom_mask & GDK_HINT_ASPECT) - { - GDK_NOTE (MISC, g_print ("... ASPECT: %g--%g\n", - geometry->min_aspect, geometry->max_aspect)); - } - - if (geom_mask & GDK_HINT_WIN_GRAVITY) - { - GDK_NOTE (MISC, g_print ("... GRAVITY: %d\n", geometry->win_gravity)); - } - - _gdk_win32_surface_update_style_bits (window); -} - -static void -gdk_win32_surface_set_title (GdkSurface *window, - const gchar *title) -{ - wchar_t *wtitle; - - g_return_if_fail (GDK_IS_SURFACE (window)); - g_return_if_fail (title != NULL); - - if (GDK_SURFACE_DESTROYED (window)) - return; - - /* Empty window titles not allowed, so set it to just a period. */ - if (!title[0]) - title = "."; - - GDK_NOTE (MISC, g_print ("gdk_surface_set_title: %p: %s\n", - GDK_SURFACE_HWND (window), title)); - - GDK_NOTE (MISC_OR_EVENTS, title = g_strdup_printf ("%p %s", GDK_SURFACE_HWND (window), title)); - - wtitle = g_utf8_to_utf16 (title, -1, NULL, NULL, NULL); - API_CALL (SetWindowTextW, (GDK_SURFACE_HWND (window), wtitle)); - g_free (wtitle); - - GDK_NOTE (MISC_OR_EVENTS, g_free ((char *) title)); -} - -static void -gdk_win32_surface_set_role (GdkSurface *window, - const gchar *role) -{ - g_return_if_fail (GDK_IS_SURFACE (window)); - - GDK_NOTE (MISC, g_print ("gdk_surface_set_role: %p: %s\n", - GDK_SURFACE_HWND (window), - (role ? role : "NULL"))); - /* XXX */ -} - -static void -gdk_win32_surface_set_transient_for (GdkSurface *window, - GdkSurface *parent) -{ - HWND window_id, parent_id; - LONG_PTR old_ptr; - DWORD w32_error; - GdkSurfaceImplWin32 *window_impl = GDK_SURFACE_IMPL_WIN32 (window->impl); - GdkSurfaceImplWin32 *parent_impl = NULL; - GSList *item; - - g_return_if_fail (GDK_IS_SURFACE (window)); - - window_id = GDK_SURFACE_HWND (window); - parent_id = parent != NULL ? GDK_SURFACE_HWND (parent) : NULL; - - GDK_NOTE (MISC, g_print ("gdk_surface_set_transient_for: %p: %p\n", window_id, parent_id)); - - if (GDK_SURFACE_DESTROYED (window) || (parent && GDK_SURFACE_DESTROYED (parent))) - { - if (GDK_SURFACE_DESTROYED (window)) - GDK_NOTE (MISC, g_print ("... destroyed!\n")); - else - GDK_NOTE (MISC, g_print ("... owner destroyed!\n")); - - return; - } - - if (parent == NULL) - { - GdkSurfaceImplWin32 *trans_impl = GDK_SURFACE_IMPL_WIN32 (window_impl->transient_owner->impl); - if (trans_impl->transient_children != NULL) - { - item = g_slist_find (trans_impl->transient_children, window); - item->data = NULL; - trans_impl->transient_children = g_slist_delete_link (trans_impl->transient_children, item); - trans_impl->num_transients--; - - if (!trans_impl->num_transients) - { - trans_impl->transient_children = NULL; - } - } - g_object_unref (G_OBJECT (window_impl->transient_owner)); - g_object_unref (G_OBJECT (window)); - - window_impl->transient_owner = NULL; - } - else - { - parent_impl = GDK_SURFACE_IMPL_WIN32 (parent->impl); - - parent_impl->transient_children = g_slist_append (parent_impl->transient_children, window); - g_object_ref (G_OBJECT (window)); - parent_impl->num_transients++; - window_impl->transient_owner = parent; - g_object_ref (G_OBJECT (parent)); - } - - SetLastError (0); - old_ptr = GetWindowLongPtr (window_id, GWLP_HWNDPARENT); - w32_error = GetLastError (); - - /* Don't re-set GWLP_HWNDPARENT to the same value */ - if ((HWND) old_ptr == parent_id && w32_error == NO_ERROR) - return; - - /* Don't return if it failed, try SetWindowLongPtr() anyway */ - if (old_ptr == 0 && w32_error != NO_ERROR) - WIN32_API_FAILED ("GetWindowLongPtr"); - - /* This changes the *owner* of the window, despite the misleading - * name. (Owner and parent are unrelated concepts.) At least that's - * what people who seem to know what they talk about say on - * USENET. Search on Google. - */ - SetLastError (0); - old_ptr = SetWindowLongPtr (window_id, GWLP_HWNDPARENT, (LONG_PTR) parent_id); - w32_error = GetLastError (); - - if (old_ptr == 0 && w32_error != NO_ERROR) - WIN32_API_FAILED ("SetWindowLongPtr"); -} - -void -_gdk_push_modal_window (GdkSurface *window) -{ - modal_window_stack = g_slist_prepend (modal_window_stack, - window); -} - -void -_gdk_remove_modal_window (GdkSurface *window) -{ - GSList *tmp; - - g_return_if_fail (window != NULL); - - /* It's possible to be NULL here if someone sets the modal hint of the window - * to FALSE before a modal window stack has ever been created. */ - if (modal_window_stack == NULL) - return; - - /* Find the requested window in the stack and remove it. Yeah, I realize this - * means we're not a 'real stack', strictly speaking. Sue me. :) */ - tmp = g_slist_find (modal_window_stack, window); - if (tmp != NULL) - { - modal_window_stack = g_slist_delete_link (modal_window_stack, tmp); - } -} - -gboolean -_gdk_modal_blocked (GdkSurface *window) -{ - GSList *l; - gboolean found_any = FALSE; - - for (l = modal_window_stack; l != NULL; l = l->next) - { - GdkSurface *modal = l->data; - - if (modal == window) - return FALSE; - - if (GDK_SURFACE_IS_MAPPED (modal)) - found_any = TRUE; - } - - return found_any; -} - -GdkSurface * -_gdk_modal_current (void) -{ - GSList *l; - - for (l = modal_window_stack; l != NULL; l = l->next) - { - GdkSurface *modal = l->data; - - if (GDK_SURFACE_IS_MAPPED (modal)) - return modal; - } - - return NULL; -} - -static void -gdk_win32_surface_get_geometry (GdkSurface *window, - gint *x, - gint *y, - gint *width, - gint *height) -{ - GdkDisplay *display; - gboolean window_is_root; - - display = gdk_surface_get_display (window); - - if (!GDK_SURFACE_DESTROYED (window)) - { - RECT rect; - GdkSurfaceImplWin32 *impl = GDK_SURFACE_IMPL_WIN32 (window->impl); - - API_CALL (GetClientRect, (GDK_SURFACE_HWND (window), &rect)); - - POINT pt; - GdkSurface *parent = gdk_surface_get_parent (window); - - pt.x = rect.left; - pt.y = rect.top; - ClientToScreen (GDK_SURFACE_HWND (window), &pt); - if (parent) - ScreenToClient (GDK_SURFACE_HWND (parent), &pt); - rect.left = pt.x; - rect.top = pt.y; - - pt.x = rect.right; - pt.y = rect.bottom; - ClientToScreen (GDK_SURFACE_HWND (window), &pt); - if (parent) - ScreenToClient (GDK_SURFACE_HWND (parent), &pt); - rect.right = pt.x; - rect.bottom = pt.y; - - if (parent == NULL) - { - rect.left += _gdk_offset_x * impl->window_scale; - rect.top += _gdk_offset_y * impl->window_scale; - rect.right += _gdk_offset_x * impl->window_scale; - rect.bottom += _gdk_offset_y * impl->window_scale; - } - - if (x) - *x = rect.left / impl->window_scale; - if (y) - *y = rect.top / impl->window_scale; - if (width) - *width = (rect.right - rect.left) / impl->window_scale; - if (height) - *height = (rect.bottom - rect.top) / impl->window_scale; - - GDK_NOTE (MISC, g_print ("gdk_win32_surface_get_geometry: %p: %ldx%ld@%+ld%\n", - GDK_SURFACE_HWND (window), - (rect.right - rect.left) / impl->window_scale, - (rect.bottom - rect.top) / impl->window_scale, - rect.left, rect.top)); - } -} - -static void -gdk_win32_surface_get_root_coords (GdkSurface *window, - gint x, - gint y, - gint *root_x, - gint *root_y) -{ - gint tx; - gint ty; - POINT pt; - GdkSurfaceImplWin32 *impl = GDK_SURFACE_IMPL_WIN32 (window->impl); - - pt.x = x * impl->window_scale; - pt.y = y * impl->window_scale; - ClientToScreen (GDK_SURFACE_HWND (window), &pt); - tx = pt.x; - ty = pt.y; - - if (root_x) - *root_x = (tx + _gdk_offset_x) / impl->window_scale; - if (root_y) - *root_y = (ty + _gdk_offset_y) / impl->window_scale; - - GDK_NOTE (MISC, g_print ("gdk_win32_surface_get_root_coords: %p: %+d%+d %+d%+d\n", - GDK_SURFACE_HWND (window), - x * impl->window_scale, - y * impl->window_scale, - (tx + _gdk_offset_x) / impl->window_scale, - (ty + _gdk_offset_y) / impl->window_scale)); -} - -static void -gdk_win32_surface_restack_toplevel (GdkSurface *window, - GdkSurface *sibling, - gboolean above) -{ - // ### TODO -} - -static void -gdk_win32_surface_get_frame_extents (GdkSurface *window, - GdkRectangle *rect) -{ - HWND hwnd; - RECT r; - GdkSurfaceImplWin32 *impl; - - g_return_if_fail (GDK_IS_SURFACE (window)); - g_return_if_fail (rect != NULL); - - rect->x = 0; - rect->y = 0; - rect->width = 1; - rect->height = 1; - - if (GDK_SURFACE_DESTROYED (window)) - return; - - /* FIXME: window is documented to be a toplevel GdkSurface, so is it really - * necessary to walk its parent chain? - */ - while (window->parent && window->parent->parent) - window = window->parent; - - impl = GDK_SURFACE_IMPL_WIN32 (window->impl); - hwnd = GDK_SURFACE_HWND (window); - API_CALL (GetWindowRect, (hwnd, &r)); - - /* Initialize to real, unscaled size */ - rect->x = r.left + _gdk_offset_x * impl->window_scale; - rect->y = r.top + _gdk_offset_y * impl->window_scale; - rect->width = (r.right - r.left); - rect->height = (r.bottom - r.top); - - /* Extend width and height to ensure that they cover the real size when de-scaled, - * and replace everyting with scaled values - */ - rect->width = (rect->width + rect->x % impl->window_scale + impl->window_scale - 1) / impl->window_scale; - rect->height = (rect->height + rect->y % impl->window_scale + impl->window_scale - 1) / impl->window_scale; - rect->x = r.left / impl->window_scale + _gdk_offset_x; - rect->y = r.top / impl->window_scale + _gdk_offset_y; - - GDK_NOTE (MISC, g_print ("gdk_surface_get_frame_extents: %p: %ldx%ld@%+ld%+ld\n", - GDK_SURFACE_HWND (window), - rect->width, - rect->height, - rect->x, rect->y)); -} - -static gboolean -gdk_surface_win32_get_device_state (GdkSurface *window, - GdkDevice *device, - gdouble *x, - gdouble *y, - GdkModifierType *mask) -{ - GdkSurface *child; - - g_return_val_if_fail (window == NULL || GDK_IS_SURFACE (window), FALSE); - - GDK_DEVICE_GET_CLASS (device)->query_state (device, window, - &child, - NULL, NULL, - x, y, mask); - return (child != NULL); -} - -void -gdk_display_warp_device (GdkDisplay *display, - GdkDevice *device, - gint x, - gint y) -{ - g_return_if_fail (display == gdk_display_get_default ()); - g_return_if_fail (GDK_IS_DEVICE (device)); - g_return_if_fail (display == gdk_device_get_display (device)); - - GDK_DEVICE_GET_CLASS (device)->warp (device, x, y); -} - -static GdkEventMask -gdk_win32_surface_get_events (GdkSurface *window) -{ - GdkSurfaceImplWin32 *impl; - - if (GDK_SURFACE_DESTROYED (window)) - return 0; - - impl = GDK_SURFACE_IMPL_WIN32 (window->impl); - - return impl->native_event_mask; -} - -static void -gdk_win32_surface_set_events (GdkSurface *window, - GdkEventMask event_mask) -{ - GdkSurfaceImplWin32 *impl; - - impl = GDK_SURFACE_IMPL_WIN32 (window->impl); - - /* gdk_surface_new() always sets the GDK_STRUCTURE_MASK, so better - * set it here, too. Not that I know or remember why it is - * necessary, will have to test some day. - */ - impl->native_event_mask = GDK_STRUCTURE_MASK | event_mask; -} - -static void -do_shape_combine_region (GdkSurface *window, - HRGN hrgn, - gint x, gint y) -{ - RECT rect; - GdkSurfaceImplWin32 *impl = GDK_SURFACE_IMPL_WIN32 (window->impl); - - GetClientRect (GDK_SURFACE_HWND (window), &rect); - - _gdk_win32_adjust_client_rect (window, &rect); - - OffsetRgn (hrgn, -rect.left, -rect.top); - OffsetRgn (hrgn, x, y); - - /* If this is a top-level window, add the title bar to the region */ - if (GDK_SURFACE_TYPE (window) == GDK_SURFACE_TOPLEVEL) - { - HRGN tmp = CreateRectRgn (0, 0, rect.right - rect.left, -rect.top); - CombineRgn (hrgn, hrgn, tmp, RGN_OR); - DeleteObject (tmp); - } - - SetWindowRgn (GDK_SURFACE_HWND (window), hrgn, TRUE); -} - -static void -gdk_win32_surface_set_accept_focus (GdkSurface *window, - gboolean accept_focus) -{ - g_return_if_fail (GDK_IS_SURFACE (window)); - - accept_focus = accept_focus != FALSE; - - if (window->accept_focus != accept_focus) - window->accept_focus = accept_focus; -} - -static void -gdk_win32_surface_set_focus_on_map (GdkSurface *window, - gboolean focus_on_map) -{ - g_return_if_fail (GDK_IS_SURFACE (window)); - - focus_on_map = focus_on_map != FALSE; - - if (window->focus_on_map != focus_on_map) - window->focus_on_map = focus_on_map; -} - -static void -gdk_win32_surface_set_icon_list (GdkSurface *window, - GList *textures) -{ - GdkTexture *big_texture, *small_texture; - gint big_diff, small_diff; - gint big_w, big_h, small_w, small_h; - gint w, h; - gint dw, dh, diff; - HICON small_hicon, big_hicon; - GdkSurfaceImplWin32 *impl; - - g_return_if_fail (GDK_IS_SURFACE (window)); - - if (GDK_SURFACE_DESTROYED (window) || textures == NULL) - return; - - impl = GDK_SURFACE_IMPL_WIN32 (window->impl); - - /* ideal sizes for small and large icons */ - big_w = GetSystemMetrics (SM_CXICON); - big_h = GetSystemMetrics (SM_CYICON); - small_w = GetSystemMetrics (SM_CXSMICON); - small_h = GetSystemMetrics (SM_CYSMICON); - - /* find closest sized icons in the list */ - big_texture = NULL; - small_texture = NULL; - big_diff = 0; - small_diff = 0; - - for (GList *l = textures; l; l = l->next) - { - GdkTexture *texture = l->data; - w = gdk_texture_get_width (texture); - h = gdk_texture_get_height (texture); - - dw = ABS (w - big_w); - dh = ABS (h - big_h); - diff = dw*dw + dh*dh; - if (big_texture == NULL || diff < big_diff) - { - big_texture = texture; - big_diff = diff; - } - - dw = ABS (w - small_w); - dh = ABS (h - small_h); - diff = dw*dw + dh*dh; - if (small_texture == NULL || diff < small_diff) - { - small_texture = texture; - small_diff = diff; - } - - textures = textures->next; - } - - /* Create the icons */ - big_hicon = _gdk_win32_texture_to_hicon (big_texture); - g_object_unref (big_texture); - small_hicon = _gdk_win32_texture_to_hicon (small_texture); - g_object_unref (small_texture); - - /* Set the icons */ - SendMessageW (GDK_SURFACE_HWND (window), WM_SETICON, ICON_BIG, - (LPARAM)big_hicon); - SendMessageW (GDK_SURFACE_HWND (window), WM_SETICON, ICON_SMALL, - (LPARAM)small_hicon); - - /* Store the icons, destroying any previous icons */ - if (impl->hicon_big) - GDI_CALL (DestroyIcon, (impl->hicon_big)); - impl->hicon_big = big_hicon; - if (impl->hicon_small) - GDI_CALL (DestroyIcon, (impl->hicon_small)); - impl->hicon_small = small_hicon; -} - -static void -gdk_win32_surface_set_icon_name (GdkSurface *window, - const gchar *name) -{ - /* In case I manage to confuse this again (or somebody else does): - * Please note that "icon name" here really *does* mean the name or - * title of an window minimized as an icon on the desktop, or in the - * taskbar. It has nothing to do with the freedesktop.org icon - * naming stuff. - */ - - g_return_if_fail (GDK_IS_SURFACE (window)); - - if (GDK_SURFACE_DESTROYED (window)) - return; - -#if 0 - /* This is not the correct thing to do. We should keep both the - * "normal" window title, and the icon name. When the window is - * minimized, call SetWindowText() with the icon name, and when the - * window is restored, with the normal window title. Also, the name - * is in UTF-8, so we should do the normal conversion to either wide - * chars or system codepage, and use either the W or A version of - * SetWindowText(), depending on Windows version. - */ - API_CALL (SetWindowText, (GDK_SURFACE_HWND (window), name)); -#endif -} - -static GdkSurface * -gdk_win32_surface_get_group (GdkSurface *window) -{ - g_return_val_if_fail (GDK_IS_SURFACE (window), NULL); - - if (GDK_SURFACE_DESTROYED (window)) - return NULL; - - g_warning ("gdk_surface_get_group not yet implemented"); - - return NULL; -} - -static void -gdk_win32_surface_set_group (GdkSurface *window, - GdkSurface *leader) -{ - g_return_if_fail (GDK_IS_SURFACE (window)); - g_return_if_fail (leader == NULL || GDK_IS_SURFACE (leader)); - - if (GDK_SURFACE_DESTROYED (window) || GDK_SURFACE_DESTROYED (leader)) - return; - - g_warning ("gdk_surface_set_group not implemented"); -} - -static void -update_single_bit (LONG *style, - gboolean all, - int gdk_bit, - int style_bit) -{ - /* all controls the interpretation of gdk_bit -- if all is TRUE, - * gdk_bit indicates whether style_bit is off; if all is FALSE, gdk - * bit indicate whether style_bit is on - */ - if ((!all && gdk_bit) || (all && !gdk_bit)) - *style |= style_bit; - else - *style &= ~style_bit; -} - -/* - * Returns TRUE if window has no decorations. - * Usually it means CSD windows, because GTK - * calls gdk_surface_set_decorations (window, 0); - * This is used to decide whether a toplevel should - * be made layered, thus it - * only returns TRUE for toplevels (until GTK minimal - * system requirements are lifted to Windows 8 or newer, - * because only toplevels can be layered). - */ -gboolean -_gdk_win32_surface_lacks_wm_decorations (GdkSurface *window) -{ - GdkSurfaceImplWin32 *impl; - LONG style; - gboolean has_any_decorations; - - if (GDK_SURFACE_DESTROYED (window)) - return FALSE; - - /* only toplevels can be layered */ - if (!WINDOW_IS_TOPLEVEL (window)) - return FALSE; - - impl = GDK_SURFACE_IMPL_WIN32 (window->impl); - - /* This is because GTK calls gdk_surface_set_decorations (window, 0), - * even though GdkWMDecoration docs indicate that 0 does NOT mean - * "no decorations". - */ - if (impl->decorations && - *impl->decorations == 0) - return TRUE; - - if (GDK_SURFACE_HWND (window) == 0) - return FALSE; - - style = GetWindowLong (GDK_SURFACE_HWND (window), GWL_STYLE); - - if (style == 0) - { - DWORD w32_error = GetLastError (); - - GDK_NOTE (MISC, g_print ("Failed to get style of window %p (handle %p): %lu\n", - window, GDK_SURFACE_HWND (window), w32_error)); - return FALSE; - } - - /* Keep this in sync with _gdk_win32_surface_update_style_bits() */ - /* We don't check what get_effective_window_decorations() - * has to say, because it gives suggestions based on - * various hints, while we want *actual* decorations, - * or their absence. - */ - has_any_decorations = FALSE; - - if (style & (WS_BORDER | WS_THICKFRAME | WS_CAPTION | - WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZEBOX)) - has_any_decorations = TRUE; - else - GDK_NOTE (MISC, g_print ("Window %p (handle %p): has no decorations (style %lx)\n", - window, GDK_SURFACE_HWND (window), style)); - - return !has_any_decorations; -} - -void -_gdk_win32_surface_update_style_bits (GdkSurface *window) -{ - GdkSurfaceImplWin32 *impl = (GdkSurfaceImplWin32 *)window->impl; - GdkWMDecoration decorations; - LONG old_style, new_style, old_exstyle, new_exstyle; - gboolean all; - RECT rect, before, after; - gboolean was_topmost; - gboolean will_be_topmost; - HWND insert_after; - UINT flags; - - if (window->state & GDK_SURFACE_STATE_FULLSCREEN) - return; - - old_style = GetWindowLong (GDK_SURFACE_HWND (window), GWL_STYLE); - old_exstyle = GetWindowLong (GDK_SURFACE_HWND (window), GWL_EXSTYLE); - - GetClientRect (GDK_SURFACE_HWND (window), &before); - after = before; - AdjustWindowRectEx (&before, old_style, FALSE, old_exstyle); - - was_topmost = (old_exstyle & WS_EX_TOPMOST) ? TRUE : FALSE; - will_be_topmost = was_topmost; - - old_exstyle &= ~WS_EX_TOPMOST; - - new_style = old_style; - new_exstyle = old_exstyle; - - if (window->window_type == GDK_SURFACE_TEMP) - { - new_exstyle |= WS_EX_TOOLWINDOW; - will_be_topmost = TRUE; - } - else if (impl->type_hint == GDK_SURFACE_TYPE_HINT_UTILITY) - { - new_exstyle |= WS_EX_TOOLWINDOW; - } - else - { - new_exstyle &= ~WS_EX_TOOLWINDOW; - } - - /* We can get away with using layered windows - * only when no decorations are needed. It can mean - * CSD or borderless non-CSD windows (tooltips?). - * - * If this window cannot use layered windows, disable it always. - * This currently applies to windows using OpenGL, which - * does not work with layered windows. - */ - if (impl->suppress_layered == 0) - { - if (_gdk_win32_surface_lacks_wm_decorations (window)) - impl->layered = g_strcmp0 (g_getenv ("GDK_WIN32_LAYERED"), "0") != 0; - } - else - impl->layered = FALSE; - - if (impl->layered) - new_exstyle |= WS_EX_LAYERED; - else - new_exstyle &= ~WS_EX_LAYERED; - - if (get_effective_window_decorations (window, &decorations)) - { - all = (decorations & GDK_DECOR_ALL); - /* Keep this in sync with the test in _gdk_win32_surface_lacks_wm_decorations() */ - update_single_bit (&new_style, all, decorations & GDK_DECOR_BORDER, WS_BORDER); - update_single_bit (&new_style, all, decorations & GDK_DECOR_RESIZEH, WS_THICKFRAME); - update_single_bit (&new_style, all, decorations & GDK_DECOR_TITLE, WS_CAPTION); - update_single_bit (&new_style, all, decorations & GDK_DECOR_MENU, WS_SYSMENU); - update_single_bit (&new_style, all, decorations & GDK_DECOR_MINIMIZE, WS_MINIMIZEBOX); - update_single_bit (&new_style, all, decorations & GDK_DECOR_MAXIMIZE, WS_MAXIMIZEBOX); - } - - if (old_style == new_style && old_exstyle == new_exstyle ) - { - GDK_NOTE (MISC, g_print ("_gdk_win32_surface_update_style_bits: %p: no change\n", - GDK_SURFACE_HWND (window))); - return; - } - - if (old_style != new_style) - { - GDK_NOTE (MISC, g_print ("_gdk_win32_surface_update_style_bits: %p: STYLE: %s => %s\n", - GDK_SURFACE_HWND (window), - _gdk_win32_surface_style_to_string (old_style), - _gdk_win32_surface_style_to_string (new_style))); - - SetWindowLong (GDK_SURFACE_HWND (window), GWL_STYLE, new_style); - } - - if (old_exstyle != new_exstyle) - { - GDK_NOTE (MISC, g_print ("_gdk_win32_surface_update_style_bits: %p: EXSTYLE: %s => %s\n", - GDK_SURFACE_HWND (window), - _gdk_win32_surface_exstyle_to_string (old_exstyle), - _gdk_win32_surface_exstyle_to_string (new_exstyle))); - - SetWindowLong (GDK_SURFACE_HWND (window), GWL_EXSTYLE, new_exstyle); - } - - AdjustWindowRectEx (&after, new_style, FALSE, new_exstyle); - - GetWindowRect (GDK_SURFACE_HWND (window), &rect); - rect.left += after.left - before.left; - rect.top += after.top - before.top; - rect.right += after.right - before.right; - rect.bottom += after.bottom - before.bottom; - - flags = SWP_FRAMECHANGED | SWP_NOACTIVATE | SWP_NOREPOSITION; - - if (will_be_topmost && !was_topmost) - { - insert_after = HWND_TOPMOST; - } - else if (was_topmost && !will_be_topmost) - { - insert_after = HWND_NOTOPMOST; - } - else - { - flags |= SWP_NOZORDER; - insert_after = SWP_NOZORDER_SPECIFIED; - } - - SetWindowPos (GDK_SURFACE_HWND (window), insert_after, - rect.left, rect.top, - rect.right - rect.left, rect.bottom - rect.top, - flags); -} - -static void -update_single_system_menu_entry (HMENU hmenu, - gboolean all, - int gdk_bit, - int menu_entry) -{ - /* all controls the interpretation of gdk_bit -- if all is TRUE, - * gdk_bit indicates whether menu entry is disabled; if all is - * FALSE, gdk bit indicate whether menu entry is enabled - */ - if ((!all && gdk_bit) || (all && !gdk_bit)) - EnableMenuItem (hmenu, menu_entry, MF_BYCOMMAND | MF_ENABLED); - else - EnableMenuItem (hmenu, menu_entry, MF_BYCOMMAND | MF_GRAYED); -} - -static void -update_system_menu (GdkSurface *window) -{ - GdkWMFunction functions; - BOOL all; - - if (_gdk_surface_get_functions (window, &functions)) - { - HMENU hmenu = GetSystemMenu (GDK_SURFACE_HWND (window), FALSE); - - all = (functions & GDK_FUNC_ALL); - update_single_system_menu_entry (hmenu, all, functions & GDK_FUNC_RESIZE, SC_SIZE); - update_single_system_menu_entry (hmenu, all, functions & GDK_FUNC_MOVE, SC_MOVE); - update_single_system_menu_entry (hmenu, all, functions & GDK_FUNC_MINIMIZE, SC_MINIMIZE); - update_single_system_menu_entry (hmenu, all, functions & GDK_FUNC_MAXIMIZE, SC_MAXIMIZE); - update_single_system_menu_entry (hmenu, all, functions & GDK_FUNC_CLOSE, SC_CLOSE); - } -} - -static void -gdk_win32_surface_set_decorations (GdkSurface *window, - GdkWMDecoration decorations) -{ - GdkSurfaceImplWin32 *impl; - - g_return_if_fail (GDK_IS_SURFACE (window)); - - impl = GDK_SURFACE_IMPL_WIN32 (window->impl); - - GDK_NOTE (MISC, g_print ("gdk_surface_set_decorations: %p: %s %s%s%s%s%s%s\n", - GDK_SURFACE_HWND (window), - (decorations & GDK_DECOR_ALL ? "clearing" : "setting"), - (decorations & GDK_DECOR_BORDER ? "BORDER " : ""), - (decorations & GDK_DECOR_RESIZEH ? "RESIZEH " : ""), - (decorations & GDK_DECOR_TITLE ? "TITLE " : ""), - (decorations & GDK_DECOR_MENU ? "MENU " : ""), - (decorations & GDK_DECOR_MINIMIZE ? "MINIMIZE " : ""), - (decorations & GDK_DECOR_MAXIMIZE ? "MAXIMIZE " : ""))); - - if (!impl->decorations) - impl->decorations = g_malloc (sizeof (GdkWMDecoration)); - - *impl->decorations = decorations; - - _gdk_win32_surface_update_style_bits (window); -} - -static gboolean -gdk_win32_surface_get_decorations (GdkSurface *window, - GdkWMDecoration *decorations) -{ - GdkSurfaceImplWin32 *impl; - - g_return_val_if_fail (GDK_IS_SURFACE (window), FALSE); - - impl = GDK_SURFACE_IMPL_WIN32 (window->impl); - - if (impl->decorations == NULL) - return FALSE; - - *decorations = *impl->decorations; - - return TRUE; -} - -static GQuark -get_functions_quark () -{ - static GQuark quark = 0; - - if (!quark) - quark = g_quark_from_static_string ("gdk-window-functions"); - - return quark; -} - -static void -gdk_win32_surface_set_functions (GdkSurface *window, - GdkWMFunction functions) -{ - GdkWMFunction* functions_copy; - - g_return_if_fail (GDK_IS_SURFACE (window)); - - GDK_NOTE (MISC, g_print ("gdk_surface_set_functions: %p: %s %s%s%s%s%s\n", - GDK_SURFACE_HWND (window), - (functions & GDK_FUNC_ALL ? "clearing" : "setting"), - (functions & GDK_FUNC_RESIZE ? "RESIZE " : ""), - (functions & GDK_FUNC_MOVE ? "MOVE " : ""), - (functions & GDK_FUNC_MINIMIZE ? "MINIMIZE " : ""), - (functions & GDK_FUNC_MAXIMIZE ? "MAXIMIZE " : ""), - (functions & GDK_FUNC_CLOSE ? "CLOSE " : ""))); - - functions_copy = g_malloc (sizeof (GdkWMFunction)); - *functions_copy = functions; - g_object_set_qdata_full (G_OBJECT (window), get_functions_quark (), functions_copy, g_free); - - update_system_menu (window); -} - -gboolean -_gdk_surface_get_functions (GdkSurface *window, - GdkWMFunction *functions) -{ - GdkWMFunction* functions_set; - - functions_set = g_object_get_qdata (G_OBJECT (window), get_functions_quark ()); - if (functions_set) - *functions = *functions_set; - - return (functions_set != NULL); -} - -#if defined(MORE_AEROSNAP_DEBUGGING) -static void -log_region (gchar *prefix, AeroSnapEdgeRegion *region) -{ - GDK_NOTE (MISC, g_print ("Region %s:\n" - "edge %d x %d @ %d x %d\n" - "trig %d x %d @ %d x %d\n", - prefix, - region->edge.width, - region->edge.height, - region->edge.x, - region->edge.y, - region->trigger.width, - region->trigger.height, - region->trigger.x, - region->trigger.y)); -} -#endif - -static void -calculate_aerosnap_regions (GdkW32DragMoveResizeContext *context) -{ - GdkDisplay *display; - gint n_monitors, monitor_idx, other_monitor_idx; - GdkSurfaceImplWin32 *impl = GDK_SURFACE_IMPL_WIN32 (context->window->impl); -#if defined(MORE_AEROSNAP_DEBUGGING) - gint i; -#endif - - display = gdk_display_get_default (); - n_monitors = gdk_display_get_n_monitors (display); - -#define _M_UP 0 -#define _M_DOWN 1 -#define _M_LEFT 2 -#define _M_RIGHT 3 - - for (monitor_idx = 0; monitor_idx < n_monitors; monitor_idx++) - { - GdkRectangle wa; - GdkRectangle geometry; - AeroSnapEdgeRegion snap_region; - gboolean move_edge[4] = { TRUE, FALSE, TRUE, TRUE }; - gboolean resize_edge[2] = { TRUE, TRUE }; - gint diff; - gint thickness, trigger_thickness; - GdkMonitor *monitor; - - monitor = gdk_display_get_monitor (display, monitor_idx); - gdk_monitor_get_workarea (monitor, &wa); - gdk_monitor_get_geometry (monitor, &geometry); - - for (other_monitor_idx = 0; - other_monitor_idx < n_monitors && - (move_edge[_M_UP] || move_edge[_M_LEFT] || - move_edge[_M_RIGHT] || resize_edge[_M_DOWN]); - other_monitor_idx++) - { - GdkRectangle other_wa; - GdkMonitor *other_monitor; - - if (other_monitor_idx == monitor_idx) - continue; - - other_monitor = gdk_display_get_monitor (display, other_monitor_idx); - gdk_monitor_get_workarea (other_monitor, &other_wa); - - /* An edge triggers AeroSnap only if there are no - * monitors beyond that edge. - * Even if there's another monitor, but it does not cover - * the whole edge (it's smaller or is not aligned to - * the corner of current monitor), that edge is still - * removed from the trigger list. - */ - if (other_wa.x >= wa.x + wa.width) - move_edge[_M_RIGHT] = FALSE; - - if (other_wa.x + other_wa.width <= wa.x) - move_edge[_M_LEFT] = FALSE; - - if (other_wa.y + other_wa.height <= wa.y) - { - move_edge[_M_UP] = FALSE; - resize_edge[_M_UP] = FALSE; - } - - if (other_wa.y >= wa.y + wa.height) - { - /* no move_edge for the bottom edge, just resize_edge */ - resize_edge[_M_DOWN] = FALSE; - } - } - - thickness = AEROSNAP_REGION_THICKNESS * impl->window_scale; - trigger_thickness = AEROSNAP_REGION_TRIGGER_THICKNESS * impl->window_scale; - - snap_region.edge = wa; - snap_region.trigger = wa; - snap_region.edge.height = thickness; - snap_region.trigger.height = trigger_thickness; - - /* Extend both regions into toolbar space. - * When there's no toolbar, diff == 0. - */ - diff = wa.y - geometry.y; - snap_region.edge.height += diff; - snap_region.edge.y -= diff; - snap_region.trigger.height += diff; - snap_region.trigger.y -= diff; - - if (move_edge[_M_UP]) - g_array_append_val (context->maximize_regions, snap_region); - - if (resize_edge[_M_UP]) - g_array_append_val (context->fullup_regions, snap_region); - - snap_region.edge = wa; - snap_region.trigger = wa; - snap_region.edge.width = thickness; - snap_region.trigger.width = trigger_thickness; - - diff = wa.x - geometry.x; - snap_region.edge.width += diff; - snap_region.edge.x -= diff; - snap_region.trigger.width += diff; - snap_region.trigger.x -= diff; - - if (move_edge[_M_LEFT]) - g_array_append_val (context->halfleft_regions, snap_region); - - snap_region.edge = wa; - snap_region.trigger = wa; - snap_region.edge.x += wa.width - thickness; - snap_region.edge.width = thickness; - snap_region.trigger.x += wa.width - trigger_thickness; - snap_region.trigger.width = trigger_thickness; - - diff = (geometry.x + geometry.width) - (wa.x + wa.width); - snap_region.edge.width += diff; - snap_region.trigger.width += diff; - - if (move_edge[_M_RIGHT]) - g_array_append_val (context->halfright_regions, snap_region); - - snap_region.edge = wa; - snap_region.trigger = wa; - snap_region.edge.y += wa.height - thickness; - snap_region.edge.height = thickness; - snap_region.trigger.y += wa.height - trigger_thickness; - snap_region.trigger.height = trigger_thickness; - - diff = (geometry.y + geometry.height) - (wa.y + wa.height); - snap_region.edge.height += diff; - snap_region.trigger.height += diff; - - if (resize_edge[_M_DOWN]) - g_array_append_val (context->fullup_regions, snap_region); - } - -#undef _M_UP -#undef _M_DOWN -#undef _M_LEFT -#undef _M_RIGHT - -#if defined(MORE_AEROSNAP_DEBUGGING) - for (i = 0; i < context->maximize_regions->len; i++) - log_region ("maximize", &g_array_index (context->maximize_regions, AeroSnapEdgeRegion, i)); - - for (i = 0; i < context->halfleft_regions->len; i++) - log_region ("halfleft", &g_array_index (context->halfleft_regions, AeroSnapEdgeRegion, i)); - - for (i = 0; i < context->halfright_regions->len; i++) - log_region ("halfright", &g_array_index (context->halfright_regions, AeroSnapEdgeRegion, i)); - - for (i = 0; i < context->fullup_regions->len; i++) - log_region ("fullup", &g_array_index (context->fullup_regions, AeroSnapEdgeRegion, i)); -#endif -} - -static void -discard_snapinfo (GdkSurface *window) -{ - GdkSurfaceImplWin32 *impl; - - impl = GDK_SURFACE_IMPL_WIN32 (window->impl); - - impl->snap_state = GDK_WIN32_AEROSNAP_STATE_UNDETERMINED; - - if (impl->snap_stash == NULL) - return; - - g_clear_pointer (&impl->snap_stash, g_free); - g_clear_pointer (&impl->snap_stash_int, g_free); -} - -static void -unsnap (GdkSurface *window, - GdkMonitor *monitor) -{ - GdkSurfaceImplWin32 *impl; - GdkRectangle rect; - - impl = GDK_SURFACE_IMPL_WIN32 (window->impl); - - impl->snap_state = GDK_WIN32_AEROSNAP_STATE_UNDETERMINED; - - if (impl->snap_stash == NULL) - return; - - gdk_monitor_get_workarea (monitor, &rect); - - GDK_NOTE (MISC, g_print ("Monitor work area %d x %d @ %d : %d\n", rect.width, rect.height, rect.x, rect.y)); - - if (rect.width >= impl->snap_stash_int->width && - rect.height >= impl->snap_stash_int->height) - { - /* If the window fits into new work area without resizing it, - * place it into new work area without resizing it. - */ - gdouble left, right, up, down, hratio, vratio; - gdouble hscale, vscale; - gdouble new_left, new_up; - - left = impl->snap_stash->x; - right = 1.0 - (impl->snap_stash->x + impl->snap_stash->width); - up = impl->snap_stash->y; - down = 1.0 - (impl->snap_stash->y + impl->snap_stash->height); - hscale = 1.0; - - if (right > 0.001) - { - hratio = left / right; - hscale = hratio / (1.0 + hratio); - } - - new_left = (gdouble) (rect.width - impl->snap_stash_int->width) * hscale; - - vscale = 1.0; - - if (down > 0.001) - { - vratio = up / down; - vscale = vratio / (1.0 + vratio); - } - - new_up = (gdouble) (rect.height - impl->snap_stash_int->height) * vscale; - - rect.x = round (rect.x + new_left); - rect.y = round (rect.y + new_up); - rect.width = impl->snap_stash_int->width; - rect.height = impl->snap_stash_int->height; - } - else - { - /* Calculate actual unsnapped window size based on its - * old relative size. Same for position. - */ - rect.x += round (rect.width * impl->snap_stash->x); - rect.y += round (rect.height * impl->snap_stash->y); - rect.width = round (rect.width * impl->snap_stash->width); - rect.height = round (rect.height * impl->snap_stash->height); - } - - GDK_NOTE (MISC, g_print ("Unsnapped window size %d x %d @ %d : %d\n", rect.width, rect.height, rect.x, rect.y)); - - gdk_surface_move_resize (window, rect.x, rect.y, - rect.width, rect.height); - - g_clear_pointer (&impl->snap_stash, g_free); - g_clear_pointer (&impl->snap_stash_int, g_free); -} - -static void -stash_window (GdkSurface *window, - GdkSurfaceImplWin32 *impl) -{ - gint x, y; - gint width, wwidth; - gint height, wheight; - WINDOWPLACEMENT placement; - HMONITOR hmonitor; - MONITORINFO hmonitor_info; - - placement.length = sizeof(WINDOWPLACEMENT); - - /* Use W32 API to get unmaximized window size, which GDK doesn't remember */ - if (!GetWindowPlacement (GDK_SURFACE_HWND (window), &placement)) - return; - - /* MSDN is very vague, but in practice rcNormalPosition is the same as GetWindowRect(), - * only with adjustments for toolbars (which creates rather weird coodinate space issues). - * We need to get monitor info and apply workarea vs monitorarea diff to turn - * these into screen coordinates proper. - */ - hmonitor = MonitorFromWindow (GDK_SURFACE_HWND (window), MONITOR_DEFAULTTONEAREST); - hmonitor_info.cbSize = sizeof (hmonitor_info); - - if (!GetMonitorInfoA (hmonitor, &hmonitor_info)) - return; - - if (impl->snap_stash == NULL) - impl->snap_stash = g_new0 (GdkRectangleDouble, 1); - - if (impl->snap_stash_int == NULL) - impl->snap_stash_int = g_new0 (GdkRectangle, 1); - - GDK_NOTE (MISC, g_print ("monitor work area %ld x %ld @ %ld : %ld\n", - (hmonitor_info.rcWork.right - hmonitor_info.rcWork.left) / impl->window_scale, - (hmonitor_info.rcWork.bottom - hmonitor_info.rcWork.top) / impl->window_scale, - hmonitor_info.rcWork.left, - hmonitor_info.rcWork.top)); - GDK_NOTE (MISC, g_print ("monitor area %ld x %ld @ %ld : %ld\n", - (hmonitor_info.rcMonitor.right - hmonitor_info.rcMonitor.left) / impl->window_scale, - (hmonitor_info.rcMonitor.bottom - hmonitor_info.rcMonitor.top) / impl->window_scale, - hmonitor_info.rcMonitor.left, - hmonitor_info.rcMonitor.top)); - GDK_NOTE (MISC, g_print ("window work place %ld x %ld @ %ld : %ld\n", - (placement.rcNormalPosition.right - placement.rcNormalPosition.left) / impl->window_scale, - (placement.rcNormalPosition.bottom - placement.rcNormalPosition.top) / impl->window_scale, - placement.rcNormalPosition.left, - placement.rcNormalPosition.top)); - - width = (placement.rcNormalPosition.right - placement.rcNormalPosition.left) / impl->window_scale; - height = (placement.rcNormalPosition.bottom - placement.rcNormalPosition.top) / impl->window_scale; - x = (placement.rcNormalPosition.left - hmonitor_info.rcMonitor.left) / impl->window_scale; - y = (placement.rcNormalPosition.top - hmonitor_info.rcMonitor.top) / impl->window_scale; - - wwidth = (hmonitor_info.rcWork.right - hmonitor_info.rcWork.left) / impl->window_scale; - wheight = (hmonitor_info.rcWork.bottom - hmonitor_info.rcWork.top) / impl->window_scale; - - impl->snap_stash->x = (gdouble) (x) / (gdouble) (wwidth); - impl->snap_stash->y = (gdouble) (y) / (gdouble) (wheight); - impl->snap_stash->width = (gdouble) width / (gdouble) (wwidth); - impl->snap_stash->height = (gdouble) height / (gdouble) (wheight); - - impl->snap_stash_int->x = x; - impl->snap_stash_int->y = y; - impl->snap_stash_int->width = width; - impl->snap_stash_int->height = height; - - GDK_NOTE (MISC, g_print ("Stashed window %d x %d @ %d : %d as %f x %f @ %f : %f\n", - width, height, x, y, - impl->snap_stash->width, impl->snap_stash->height, impl->snap_stash->x, impl->snap_stash->y)); -} - -static void -snap_up (GdkSurface *window) -{ - SHORT maxysize; - gint x, y; - gint width, height; - GdkSurfaceImplWin32 *impl; - - impl = GDK_SURFACE_IMPL_WIN32 (window->impl); - - impl->snap_state = GDK_WIN32_AEROSNAP_STATE_FULLUP; - - stash_window (window, impl); - - maxysize = GetSystemMetrics (SM_CYVIRTUALSCREEN) / impl->window_scale; - gdk_surface_get_position (window, &x, &y); - width = gdk_surface_get_width (window); - - y = 0; - height = maxysize; - - x = x - impl->margins.left; - y = y - impl->margins.top; - width += impl->margins_x; - height += impl->margins_y; - - gdk_surface_move_resize (window, x, y, width, height); -} - -static void -snap_left (GdkSurface *window, - GdkMonitor *monitor, - GdkMonitor *snap_monitor) -{ - GdkRectangle rect; - GdkSurfaceImplWin32 *impl; - - impl = GDK_SURFACE_IMPL_WIN32 (window->impl); - - impl->snap_state = GDK_WIN32_AEROSNAP_STATE_HALFLEFT; - - gdk_monitor_get_workarea (snap_monitor, &rect); - - stash_window (window, impl); - - rect.width = rect.width / 2; - - rect.x = rect.x - impl->margins.left; - rect.y = rect.y - impl->margins.top; - rect.width = rect.width + impl->margins_x; - rect.height = rect.height + impl->margins_y; - - gdk_surface_move_resize (window, rect.x, rect.y, rect.width, rect.height); -} - -static void -snap_right (GdkSurface *window, - GdkMonitor *monitor, - GdkMonitor *snap_monitor) -{ - GdkRectangle rect; - GdkSurfaceImplWin32 *impl; - - impl = GDK_SURFACE_IMPL_WIN32 (window->impl); - - impl->snap_state = GDK_WIN32_AEROSNAP_STATE_HALFRIGHT; - - gdk_monitor_get_workarea (snap_monitor, &rect); - - stash_window (window, impl); - - rect.width = rect.width / 2; - rect.x += rect.width; - - rect.x = rect.x - impl->margins.left; - rect.y = rect.y - impl->margins.top; - rect.width = rect.width + impl->margins_x; - rect.height = rect.height + impl->margins_y; - - gdk_surface_move_resize (window, rect.x, rect.y, rect.width, rect.height); -} - -void -_gdk_win32_surface_handle_aerosnap (GdkSurface *window, - GdkWin32AeroSnapCombo combo) -{ - GdkSurfaceImplWin32 *impl; - GdkDisplay *display; - gint n_monitors; - GdkSurfaceState window_state = gdk_surface_get_state (window); - gboolean minimized = window_state & GDK_SURFACE_STATE_ICONIFIED; - gboolean maximized = window_state & GDK_SURFACE_STATE_MAXIMIZED; - gboolean halfsnapped; - GdkMonitor *monitor; - - impl = GDK_SURFACE_IMPL_WIN32 (window->impl); - display = gdk_surface_get_display (window); - n_monitors = gdk_display_get_n_monitors (display); - monitor = gdk_display_get_monitor_at_window (display, window); - - if (minimized && maximized) - minimized = FALSE; - - halfsnapped = (impl->snap_state == GDK_WIN32_AEROSNAP_STATE_HALFRIGHT || - impl->snap_state == GDK_WIN32_AEROSNAP_STATE_HALFLEFT || - impl->snap_state == GDK_WIN32_AEROSNAP_STATE_FULLUP); - - switch (combo) - { - case GDK_WIN32_AEROSNAP_COMBO_NOTHING: - /* Do nothing */ - break; - case GDK_WIN32_AEROSNAP_COMBO_UP: - if (!maximized) - { - unsnap (window, monitor); - gdk_surface_maximize (window); - } - break; - case GDK_WIN32_AEROSNAP_COMBO_DOWN: - case GDK_WIN32_AEROSNAP_COMBO_SHIFTDOWN: - if (maximized) - { - gdk_surface_unmaximize (window); - unsnap (window, monitor); - } - else if (halfsnapped) - unsnap (window, monitor); - else if (!minimized) - gdk_surface_iconify (window); - break; - case GDK_WIN32_AEROSNAP_COMBO_LEFT: - if (maximized) - gdk_surface_unmaximize (window); - - if (impl->snap_state == GDK_WIN32_AEROSNAP_STATE_UNDETERMINED || - impl->snap_state == GDK_WIN32_AEROSNAP_STATE_FULLUP) - { - unsnap (window, monitor); - snap_left (window, monitor, monitor); - } - else if (impl->snap_state == GDK_WIN32_AEROSNAP_STATE_HALFLEFT) - { - unsnap (window, monitor); - snap_right (window, - monitor, - gdk_monitor_is_primary (monitor) ? monitor : gdk_display_get_monitor (display, n_monitors - 1)); - } - else if (impl->snap_state == GDK_WIN32_AEROSNAP_STATE_HALFRIGHT) - { - unsnap (window, monitor); - } - break; - case GDK_WIN32_AEROSNAP_COMBO_RIGHT: - if (maximized) - gdk_surface_unmaximize (window); - - if (impl->snap_state == GDK_WIN32_AEROSNAP_STATE_UNDETERMINED || - impl->snap_state == GDK_WIN32_AEROSNAP_STATE_FULLUP) - { - unsnap (window, monitor); - snap_right (window, monitor, monitor); - } - else if (impl->snap_state == GDK_WIN32_AEROSNAP_STATE_HALFLEFT) - { - unsnap (window, monitor); - } - else if (impl->snap_state == GDK_WIN32_AEROSNAP_STATE_HALFRIGHT) - { - gint i; - - unsnap (window, monitor); - if (n_monitors == 1 || - monitor == gdk_display_get_monitor (display, n_monitors - 1)) - { - snap_left (window, monitor, monitor); - } - else - { - for (i = 0; i < n_monitors; i++) - { - if (monitor == gdk_display_get_monitor (display, i)) - break; - } - - snap_left (window, monitor, gdk_display_get_monitor (display, i + 1)); - } - } - break; - case GDK_WIN32_AEROSNAP_COMBO_SHIFTUP: - if (!maximized && - impl->snap_state == GDK_WIN32_AEROSNAP_STATE_UNDETERMINED) - { - snap_up (window); - } - break; - case GDK_WIN32_AEROSNAP_COMBO_SHIFTLEFT: - case GDK_WIN32_AEROSNAP_COMBO_SHIFTRIGHT: - /* No implementation needed at the moment */ - break; - } -} - -static void -apply_snap (GdkSurface *window, - GdkWin32AeroSnapState snap) -{ - GdkMonitor *monitor; - GdkDisplay *display; - - display = gdk_surface_get_display (window); - monitor = gdk_display_get_monitor_at_window (display, window); - - switch (snap) - { - case GDK_WIN32_AEROSNAP_STATE_UNDETERMINED: - break; - case GDK_WIN32_AEROSNAP_STATE_MAXIMIZE: - unsnap (window, monitor); - gdk_surface_maximize (window); - break; - case GDK_WIN32_AEROSNAP_STATE_HALFLEFT: - unsnap (window, monitor); - snap_left (window, monitor, monitor); - break; - case GDK_WIN32_AEROSNAP_STATE_HALFRIGHT: - unsnap (window, monitor); - snap_right (window, monitor, monitor); - break; - case GDK_WIN32_AEROSNAP_STATE_FULLUP: - snap_up (window); - break; - } -} - -/* Registers a dumb window class. This window - * has DefWindowProc() for a window procedure and - * does not do anything that GdkSurface-bound HWNDs do. - */ -static ATOM -RegisterGdkDumbClass () -{ - static ATOM klassDUMB = 0; - static WNDCLASSEXW wcl; - ATOM klass = 0; - - wcl.cbSize = sizeof (WNDCLASSEX); - wcl.style = 0; /* DON'T set CS_REDRAW. It causes total redraw - * on WM_SIZE and WM_MOVE. Flicker, Performance! - */ - wcl.lpfnWndProc = DefWindowProcW; - wcl.cbClsExtra = 0; - wcl.cbWndExtra = 0; - wcl.hInstance = _gdk_app_hmodule; - wcl.hIcon = 0; - wcl.hIconSm = 0; - wcl.lpszMenuName = NULL; - wcl.hbrBackground = NULL; - wcl.hCursor = LoadCursor (NULL, IDC_ARROW); - wcl.style |= CS_OWNDC; - wcl.lpszClassName = L"gdkWindowDumb"; - - if (klassDUMB == 0) - klassDUMB = RegisterClassExW (&wcl); - - klass = klassDUMB; - - if (klass == 0) - { - WIN32_API_FAILED ("RegisterClassExW"); - g_error ("That is a fatal error"); - } - - return klass; -} - -static gboolean -ensure_snap_indicator_exists (GdkW32DragMoveResizeContext *context) -{ - if (context->shape_indicator == NULL) - { - HWND handle; - ATOM klass; - klass = RegisterGdkDumbClass (); - - handle = CreateWindowExW (WS_EX_TRANSPARENT | WS_EX_LAYERED | WS_EX_NOACTIVATE, - MAKEINTRESOURCEW (klass), - L"", - WS_POPUP, - 0, - 0, - 0, 0, - NULL, - NULL, - _gdk_app_hmodule, - NULL); - - context->shape_indicator = handle; - } - - return context->shape_indicator != NULL; -} - -static gboolean -ensure_snap_indicator_surface (GdkW32DragMoveResizeContext *context, - gint width, - gint height, - guint scale) -{ - if (context->indicator_surface != NULL && - (context->indicator_surface_width < width || - context->indicator_surface_height < height)) - { - cairo_surface_destroy (context->indicator_surface); - context->indicator_surface = NULL; - } - - if (context->indicator_surface == NULL) - context->indicator_surface = cairo_win32_surface_create_with_dib (CAIRO_FORMAT_ARGB32, - width * scale, - height * scale); - - if (cairo_surface_status (context->indicator_surface) != CAIRO_STATUS_SUCCESS) - { - cairo_surface_destroy (context->indicator_surface); - context->indicator_surface = NULL; - - return FALSE; - } - - return TRUE; -} - -/* Indicator is drawn with some inward offset, so that it does - * not hug screen edges. - */ -static void -adjust_indicator_rectangle (GdkRectangle *rect, - gboolean inward) -{ - gdouble inverter; - const gint gap = AEROSNAP_INDICATOR_EDGE_GAP; -#if defined(MORE_AEROSNAP_DEBUGGING) - GdkRectangle cache = *rect; -#endif - - if (inward) - inverter = 1.0; - else - inverter = -1.0; - - rect->x += (gap * inverter); - rect->y += (gap * inverter); - rect->width -= (gap * 2 * inverter); - rect->height -= (gap * 2 * inverter); - -#if defined(MORE_AEROSNAP_DEBUGGING) - GDK_NOTE (MISC, g_print ("Adjusted %d x %d @ %d : %d -> %d x %d @ %d : %d\n", - cache.width, cache.height, cache.x, cache.y, - rect->width, rect->height, rect->x, rect->y)); -#endif -} - -static void -rounded_rectangle (cairo_t *cr, - gint x, - gint y, - gint width, - gint height, - gdouble radius, - gdouble line_width, - GdkRGBA *fill, - GdkRGBA *outline) -{ - gdouble degrees = M_PI / 180.0; - - if (fill == NULL && outline == NULL) - return; - - cairo_save (cr); - cairo_new_sub_path (cr); - cairo_arc (cr, x + width - radius, y + radius, radius, -90 * degrees, 0 * degrees); - cairo_arc (cr, x + width - radius, y + height - radius, radius, 0 * degrees, 90 * degrees); - cairo_arc (cr, (x + radius), y + height - radius, radius, 90 * degrees, 180 * degrees); - cairo_arc (cr, (x + radius), (y + radius), radius, 180 * degrees, 270 * degrees); - cairo_close_path (cr); - - if (fill) - { - cairo_set_source_rgba (cr, fill->red, fill->green, fill->blue, fill->alpha); - - if (outline) - cairo_fill_preserve (cr); - else - cairo_fill (cr); - } - - if (outline) - { - cairo_set_source_rgba (cr, outline->red, outline->green, outline->blue, outline->alpha); - cairo_set_line_width (cr, line_width); - cairo_stroke (cr); - } - - cairo_restore (cr); -} - -/* Translates linear animation scale into some kind of curve */ -static gdouble -curve (gdouble val) -{ - /* TODO: try different curves. For now it's just linear */ - return val; -} - -static gboolean -draw_indicator (GdkW32DragMoveResizeContext *context, - gint64 timestamp) -{ - cairo_t *cr; - GdkRGBA outline = {0, 0, 1.0, 1.0}; - GdkRGBA fill = {0, 0, 1.0, 0.8}; - GdkRectangle current_rect; - gint64 current_time = g_get_monotonic_time (); - gdouble animation_progress; - gboolean last_draw; - gdouble line_width; - gdouble corner_radius; - gint64 animation_duration; - GdkSurfaceImplWin32 *impl = GDK_SURFACE_IMPL_WIN32 (context->window->impl); - - line_width = AEROSNAP_INDICATOR_LINE_WIDTH * impl->window_scale; - corner_radius = AEROSNAP_INDICATOR_CORNER_RADIUS; - animation_duration = AEROSNAP_INDICATOR_ANIMATION_DURATION; - last_draw = FALSE; - - if (timestamp == 0 && - current_time - context->indicator_start_time > animation_duration) - { - timestamp = context->indicator_start_time + animation_duration; - last_draw = TRUE; - } - - if (timestamp != 0) - current_time = timestamp; - - animation_progress = (gdouble) (current_time - context->indicator_start_time) / animation_duration; - - if (animation_progress > 1.0) - animation_progress = 1.0; - - if (animation_progress < 0) - animation_progress = 0; - - animation_progress = curve (animation_progress); - - current_rect = context->indicator_start; - current_rect.x += (context->indicator_target.x - context->indicator_start.x) * animation_progress; - current_rect.y += (context->indicator_target.y - context->indicator_start.y) * animation_progress; - current_rect.width += (context->indicator_target.width - context->indicator_start.width) * animation_progress; - current_rect.height += (context->indicator_target.height - context->indicator_start.height) * animation_progress; - - if (context->op == GDK_WIN32_DRAGOP_RESIZE && last_draw) - { - switch (context->edge) - { - case GDK_SURFACE_EDGE_NORTH_WEST: - current_rect.x = context->indicator_target.x + (context->indicator_target.width - current_rect.width); - current_rect.y = context->indicator_target.y + (context->indicator_target.height - current_rect.height); - break; - case GDK_SURFACE_EDGE_NORTH: - current_rect.y = context->indicator_target.y + (context->indicator_target.height - current_rect.height); - break; - case GDK_SURFACE_EDGE_WEST: - current_rect.x = context->indicator_target.x + (context->indicator_target.width - current_rect.width); - break; - case GDK_SURFACE_EDGE_SOUTH_WEST: - current_rect.x = context->indicator_target.x + (context->indicator_target.width - current_rect.width); - current_rect.y = context->indicator_target.y; - break; - case GDK_SURFACE_EDGE_NORTH_EAST: - current_rect.x = context->indicator_target.x; - current_rect.y = context->indicator_target.y + (context->indicator_target.height - current_rect.height); - break; - case GDK_SURFACE_EDGE_SOUTH_EAST: - current_rect.x = context->indicator_target.x; - current_rect.y = context->indicator_target.y; - break; - case GDK_SURFACE_EDGE_SOUTH: - current_rect.y = context->indicator_target.y; - break; - case GDK_SURFACE_EDGE_EAST: - current_rect.x = context->indicator_target.x; - break; - } - } - - cr = cairo_create (context->indicator_surface); - rounded_rectangle (cr, - (current_rect.x - context->indicator_window_rect.x) * impl->window_scale, - (current_rect.y - context->indicator_window_rect.y) * impl->window_scale, - current_rect.width * impl->window_scale, - current_rect.height * impl->window_scale, - corner_radius, - line_width, - &fill, &outline); - cairo_destroy (cr); - -#if defined(MORE_AEROSNAP_DEBUGGING) - GDK_NOTE (MISC, g_print ("Indicator is %d x %d @ %d : %d; current time is %" G_GINT64_FORMAT "\n", - current_rect.width, current_rect.height, - current_rect.x - context->indicator_window_rect.x, - current_rect.y - context->indicator_window_rect.y, - current_time)); -#endif - - return last_draw; -} - -static gboolean -redraw_indicator (gpointer user_data) -{ - GdkW32DragMoveResizeContext *context = user_data; - POINT window_position; - SIZE window_size; - BLENDFUNCTION blender; - HDC hdc; - POINT source_point = { 0, 0 }; - gboolean last_draw; - gdouble indicator_opacity; - GdkSurfaceImplWin32 *impl; - gboolean do_source_remove = FALSE; - - indicator_opacity = AEROSNAP_INDICATOR_OPACITY; - - if (GDK_SURFACE_DESTROYED (context->window) || - !ensure_snap_indicator_exists (context)) - { - do_source_remove = TRUE; - } - - impl = GDK_SURFACE_IMPL_WIN32 (context->window->impl); - - if (!ensure_snap_indicator_surface (context, - context->indicator_window_rect.width, - context->indicator_window_rect.height, - impl->window_scale)) - { - do_source_remove = TRUE; - } - - if (do_source_remove) - { - context->timer = 0; - return G_SOURCE_REMOVE; - } - - last_draw = draw_indicator (context, context->draw_timestamp); - - window_position.x = (context->indicator_window_rect.x - _gdk_offset_x) * impl->window_scale; - window_position.y = (context->indicator_window_rect.y - _gdk_offset_y) * impl->window_scale; - window_size.cx = context->indicator_window_rect.width * impl->window_scale; - window_size.cy = context->indicator_window_rect.height * impl->window_scale; - - blender.BlendOp = AC_SRC_OVER; - blender.BlendFlags = 0; - blender.AlphaFormat = AC_SRC_ALPHA; - blender.SourceConstantAlpha = 255 * indicator_opacity; - - hdc = cairo_win32_surface_get_dc (context->indicator_surface); - - API_CALL (SetWindowPos, (context->shape_indicator, - GDK_SURFACE_HWND (context->window), - 0, 0, 0, 0, - SWP_NOMOVE | SWP_NOSIZE | SWP_NOREDRAW | SWP_SHOWWINDOW | SWP_NOACTIVATE)); - -#if defined(MORE_AEROSNAP_DEBUGGING) - GDK_NOTE (MISC, g_print ("Indicator window position is %ld x %ld @ %ld : %ld\n", - window_size.cx, window_size.cy, - window_position.x, window_position.y)); -#endif - - API_CALL (UpdateLayeredWindow, (context->shape_indicator, NULL, - &window_position, &window_size, - hdc, &source_point, - 0, &blender, ULW_ALPHA)); - - if (last_draw) - context->timer = 0; - - return last_draw ? G_SOURCE_REMOVE : G_SOURCE_CONTINUE; -} - -static GdkRectangle -unity_of_rects (GdkRectangle a, - GdkRectangle b) -{ - GdkRectangle u = b; - - if (a.x < u.x) - { - u.width += u.x - a.x; - u.x = a.x; - } - - if (a.y < u.y) - { - u.height += (u.y - a.y); - u.y = a.y; - } - - if (a.x + a.width > u.x + u.width) - u.width += (a.x + a.width) - (u.x + u.width); - - if (a.y + a.height > u.y + u.height) - u.height += (a.y + a.height) - (u.y + u.height); - -#if defined(MORE_AEROSNAP_DEBUGGING) - GDK_NOTE (MISC, g_print ("Unified 2 rects into %d x %d @ %d : %d\n", - u.width, u.height, u.x, u.y)); -#endif - - return u; -} - -static void -start_indicator_drawing (GdkW32DragMoveResizeContext *context, - GdkRectangle from, - GdkRectangle to, - guint scale) -{ - GdkRectangle to_adjusted, from_adjusted, from_or_to; - gint64 indicator_animation_tick = AEROSNAP_INDICATOR_ANIMATION_TICK; - - GDK_NOTE (MISC, g_print ("Start drawing snap indicator %d x %d @ %d : %d -> %d x %d @ %d : %d\n", - from.width * scale, from.height * scale, from.x, from.y, to.width * scale, to.height * scale, to.x, to.y)); - - if (GDK_SURFACE_DESTROYED (context->window)) - return; - - if (!ensure_snap_indicator_exists (context)) - return; - - from_or_to = unity_of_rects (from, to); - - if (!ensure_snap_indicator_surface (context, from_or_to.width, from_or_to.height, scale)) - return; - - to_adjusted = to; - adjust_indicator_rectangle (&to_adjusted, TRUE); - - from_adjusted = from; - adjust_indicator_rectangle (&from_adjusted, TRUE); - - context->draw_timestamp = 0; - context->indicator_start = from_adjusted; - context->indicator_target = to_adjusted; - context->indicator_window_rect = from_or_to; - context->indicator_start_time = g_get_monotonic_time (); - - if (context->timer) - { - g_source_remove (context->timer); - context->timer = 0; - } - - context->timer = g_timeout_add_full (G_PRIORITY_DEFAULT, - indicator_animation_tick, - redraw_indicator, - context, - NULL); -} - -static void -update_fullup_indicator (GdkSurface *window, - GdkW32DragMoveResizeContext *context) -{ - SHORT maxysize; - GdkRectangle from, to; - GdkRectangle to_adjusted, from_adjusted, from_or_to; - GdkSurfaceImplWin32 *impl; - - GDK_NOTE (MISC, g_print ("Update fullup indicator\n")); - - if (GDK_SURFACE_DESTROYED (context->window)) - return; - - if (context->shape_indicator == NULL) - return; - - impl = GDK_SURFACE_IMPL_WIN32 (window->impl); - maxysize = GetSystemMetrics (SM_CYVIRTUALSCREEN); - gdk_surface_get_position (window, &to.x, &to.y); - to.width = gdk_surface_get_width (window); - to.height = gdk_surface_get_height (window); - - to.y = 0; - to.height = maxysize; - from = context->indicator_target; - - if (context->timer == 0) - { - from_adjusted = from; - adjust_indicator_rectangle (&from_adjusted, FALSE); - - GDK_NOTE (MISC, g_print ("Restart fullup animation from %d x %d @ %d : %d -> %d x %d @ %d x %d\n", - context->indicator_target.width, context->indicator_target.height, - context->indicator_target.x, context->indicator_target.y, - to.width, to.height, to.x, to.y)); - start_indicator_drawing (context, from_adjusted, to, impl->window_scale); - - return; - } - - from_or_to = unity_of_rects (from, to); - - to_adjusted = to; - adjust_indicator_rectangle (&to_adjusted, TRUE); - - GDK_NOTE (MISC, g_print ("Retarget fullup animation %d x %d @ %d : %d -> %d x %d @ %d x %d\n", - context->indicator_target.width, context->indicator_target.height, - context->indicator_target.x, context->indicator_target.y, - to_adjusted.width, to_adjusted.height, to_adjusted.x, to_adjusted.y)); - - context->indicator_target = to_adjusted; - context->indicator_window_rect = from_or_to; - - ensure_snap_indicator_surface (context, from_or_to.width, from_or_to.height, impl->window_scale); -} - -static void -start_indicator (GdkSurface *window, - GdkW32DragMoveResizeContext *context, - gint x, - gint y, - GdkWin32AeroSnapState state) -{ - GdkMonitor *monitor; - GdkRectangle workarea; - SHORT maxysize; - GdkRectangle start_size, end_size; - GdkDisplay *display; - GdkSurfaceImplWin32 *impl = GDK_SURFACE_IMPL_WIN32 (window->impl); - - display = gdk_surface_get_display (window); - monitor = gdk_display_get_monitor_at_point (display, x, y); - gdk_monitor_get_workarea (monitor, &workarea); - - maxysize = GetSystemMetrics (SM_CYVIRTUALSCREEN) / impl->window_scale; - gdk_surface_get_position (window, &start_size.x, &start_size.y); - start_size.width = gdk_surface_get_width (window); - start_size.height = gdk_surface_get_height (window); - - end_size = start_size; - - switch (state) - { - case GDK_WIN32_AEROSNAP_STATE_UNDETERMINED: - return; - case GDK_WIN32_AEROSNAP_STATE_MAXIMIZE: - end_size.x = workarea.x; - end_size.y = workarea.y; - end_size.width = workarea.width; - end_size.height = workarea.height; - break; - case GDK_WIN32_AEROSNAP_STATE_HALFLEFT: - end_size.x = workarea.x; - end_size.y = workarea.y; - end_size.width = workarea.width / 2; - end_size.height = workarea.height; - break; - case GDK_WIN32_AEROSNAP_STATE_HALFRIGHT: - end_size.x = (workarea.x + workarea.width / 2); - end_size.y = workarea.y; - end_size.width = workarea.width / 2; - end_size.height = workarea.height; - break; - case GDK_WIN32_AEROSNAP_STATE_FULLUP: - end_size.y = 0; - end_size.height = maxysize; - break; - } - - start_indicator_drawing (context, start_size, end_size, impl->window_scale); -} - -static void -stop_indicator (GdkSurface *window, - GdkW32DragMoveResizeContext *context) -{ - GDK_NOTE (MISC, g_print ("Stop drawing snap indicator\n")); - - if (context->timer) - { - g_source_remove (context->timer); - context->timer = 0; - } - - API_CALL (SetWindowPos, (context->shape_indicator, - SWP_NOZORDER_SPECIFIED, - 0, 0, 0, 0, - SWP_NOZORDER | SWP_NOMOVE | - SWP_NOSIZE | SWP_NOREDRAW | SWP_HIDEWINDOW | SWP_NOACTIVATE)); -} - -static gint -point_in_aerosnap_region (gint x, - gint y, - AeroSnapEdgeRegion *region) -{ - gint edge, trigger; - - edge = (x >= region->edge.x && - y >= region->edge.y && - x <= region->edge.x + region->edge.width && - y <= region->edge.y + region->edge.height) ? 1 : 0; - trigger = (x >= region->trigger.x && - y >= region->trigger.y && - x <= region->trigger.x + region->trigger.width && - y <= region->trigger.y + region->trigger.height) ? 1 : 0; - return edge + trigger; -} - -static void -handle_aerosnap_move_resize (GdkSurface *window, - GdkW32DragMoveResizeContext *context, - gint x, - gint y) -{ - gint i; - AeroSnapEdgeRegion *reg; - gint maximize = 0; - gint halfleft = 0; - gint halfright = 0; - gint fullup = 0; - gboolean fullup_edge = FALSE; - GdkSurfaceImplWin32 *impl = GDK_SURFACE_IMPL_WIN32 (window->impl); - - if (context->op == GDK_WIN32_DRAGOP_RESIZE) - switch (context->edge) - { - case GDK_SURFACE_EDGE_NORTH_WEST: - case GDK_SURFACE_EDGE_NORTH_EAST: - case GDK_SURFACE_EDGE_WEST: - case GDK_SURFACE_EDGE_EAST: - case GDK_SURFACE_EDGE_SOUTH_WEST: - case GDK_SURFACE_EDGE_SOUTH_EAST: - break; - case GDK_SURFACE_EDGE_SOUTH: - case GDK_SURFACE_EDGE_NORTH: - fullup_edge = TRUE; - break; - } - - for (i = 0; i < context->maximize_regions->len && maximize == 0; i++) - { - reg = &g_array_index (context->maximize_regions, AeroSnapEdgeRegion, i); - maximize = point_in_aerosnap_region (x, y, reg); - } - - for (i = 0; i < context->halfleft_regions->len && halfleft == 0; i++) - { - reg = &g_array_index (context->halfleft_regions, AeroSnapEdgeRegion, i); - halfleft = point_in_aerosnap_region (x, y, reg); - } - - for (i = 0; i < context->halfright_regions->len && halfright == 0; i++) - { - reg = &g_array_index (context->halfright_regions, AeroSnapEdgeRegion, i); - halfright = point_in_aerosnap_region (x, y, reg); - } - - for (i = 0; i < context->fullup_regions->len && fullup == 0; i++) - { - reg = &g_array_index (context->fullup_regions, AeroSnapEdgeRegion, i); - fullup = point_in_aerosnap_region (x, y, reg); - } - -#if defined(MORE_AEROSNAP_DEBUGGING) - GDK_NOTE (MISC, g_print ("AeroSnap: point %d : %d - max: %d, left %d, right %d, up %d\n", - x, y, maximize, halfleft, halfright, fullup)); -#endif - - if (!context->revealed) - { - if (context->op == GDK_WIN32_DRAGOP_MOVE && maximize == 2) - { - context->revealed = TRUE; - context->current_snap = GDK_WIN32_AEROSNAP_STATE_MAXIMIZE; - start_indicator (window, context, x, y, context->current_snap); - } - else if (context->op == GDK_WIN32_DRAGOP_MOVE && halfleft == 2) - { - context->revealed = TRUE; - context->current_snap = GDK_WIN32_AEROSNAP_STATE_HALFLEFT; - start_indicator (window, context, x, y, context->current_snap); - } - else if (context->op == GDK_WIN32_DRAGOP_MOVE && halfright == 2) - { - context->revealed = TRUE; - context->current_snap = GDK_WIN32_AEROSNAP_STATE_HALFRIGHT; - start_indicator (window, context, x, y, context->current_snap); - } - else if (context->op == GDK_WIN32_DRAGOP_RESIZE && fullup == 2 && fullup_edge) - { - context->revealed = TRUE; - context->current_snap = GDK_WIN32_AEROSNAP_STATE_FULLUP; - start_indicator (window, context, x, y, context->current_snap); - } - - return; - } - - switch (context->current_snap) - { - case GDK_WIN32_AEROSNAP_STATE_UNDETERMINED: - if (context->op == GDK_WIN32_DRAGOP_RESIZE && fullup > 0) - { - context->current_snap = GDK_WIN32_AEROSNAP_STATE_FULLUP; - start_indicator (window, context, x, y, context->current_snap); - } - break; - case GDK_WIN32_AEROSNAP_STATE_MAXIMIZE: - if (context->op == GDK_WIN32_DRAGOP_MOVE && maximize > 0) - break; - if (context->op == GDK_WIN32_DRAGOP_MOVE && halfleft > 0) - { - context->current_snap = GDK_WIN32_AEROSNAP_STATE_HALFLEFT; - start_indicator (window, context, x, y, context->current_snap); - } - else if (context->op == GDK_WIN32_DRAGOP_MOVE && halfright > 0) - { - context->current_snap = GDK_WIN32_AEROSNAP_STATE_HALFRIGHT; - start_indicator (window, context, x, y, context->current_snap); - } - else - { - context->current_snap = GDK_WIN32_AEROSNAP_STATE_UNDETERMINED; - stop_indicator (window, context); - context->revealed = FALSE; - } - break; - case GDK_WIN32_AEROSNAP_STATE_HALFLEFT: - if (context->op == GDK_WIN32_DRAGOP_MOVE && halfleft > 0) - break; - if (context->op == GDK_WIN32_DRAGOP_MOVE && maximize > 0) - { - context->current_snap = GDK_WIN32_AEROSNAP_STATE_MAXIMIZE; - start_indicator (window, context, x, y, context->current_snap); - } - else if (context->op == GDK_WIN32_DRAGOP_MOVE && halfright > 0) - { - context->current_snap = GDK_WIN32_AEROSNAP_STATE_HALFRIGHT; - start_indicator (window, context, x, y, context->current_snap); - } - else - { - context->current_snap = GDK_WIN32_AEROSNAP_STATE_UNDETERMINED; - stop_indicator (window, context); - context->revealed = FALSE; - } - break; - case GDK_WIN32_AEROSNAP_STATE_HALFRIGHT: - if (context->op == GDK_WIN32_DRAGOP_MOVE && halfright > 0) - break; - if (context->op == GDK_WIN32_DRAGOP_MOVE && maximize > 0) - { - context->current_snap = GDK_WIN32_AEROSNAP_STATE_MAXIMIZE; - start_indicator (window, context, x, y, context->current_snap); - } - else if (context->op == GDK_WIN32_DRAGOP_MOVE && halfleft > 0) - { - context->current_snap = GDK_WIN32_AEROSNAP_STATE_HALFLEFT; - start_indicator (window, context, x, y, context->current_snap); - } - else - { - context->current_snap = GDK_WIN32_AEROSNAP_STATE_UNDETERMINED; - stop_indicator (window, context); - context->revealed = FALSE; - } - break; - case GDK_WIN32_AEROSNAP_STATE_FULLUP: - if (context->op == GDK_WIN32_DRAGOP_RESIZE && fullup > 0 && fullup_edge) - { - update_fullup_indicator (window, context); - break; - } - - context->current_snap = GDK_WIN32_AEROSNAP_STATE_UNDETERMINED; - stop_indicator (window, context); - break; - } -} - - -static const gchar * -get_cursor_name_from_op (GdkW32WindowDragOp op, - GdkSurfaceEdge edge) -{ - switch (op) - { - case GDK_WIN32_DRAGOP_MOVE: - return "move"; - case GDK_WIN32_DRAGOP_RESIZE: - switch (edge) - { - case GDK_SURFACE_EDGE_NORTH_WEST: - return "nw-resize"; - case GDK_SURFACE_EDGE_NORTH: - return "n-resize"; - case GDK_SURFACE_EDGE_NORTH_EAST: - return "ne-resize"; - case GDK_SURFACE_EDGE_WEST: - return "w-resize"; - case GDK_SURFACE_EDGE_EAST: - return "e-resize"; - case GDK_SURFACE_EDGE_SOUTH_WEST: - return "sw-resize"; - case GDK_SURFACE_EDGE_SOUTH: - return "s-resize"; - case GDK_SURFACE_EDGE_SOUTH_EAST: - return "se-resize"; - } - /* default: warn about unhandled enum values, - * fallthrough to GDK_WIN32_DRAGOP_NONE case - */ - case GDK_WIN32_DRAGOP_COUNT: - g_assert_not_reached (); - case GDK_WIN32_DRAGOP_NONE: - return "default"; - /* default: warn about unhandled enum values */ - } - - g_assert_not_reached (); - - return NULL; -} - -static gboolean -point_in_window (GdkSurface *window, - gdouble x, - gdouble y) -{ - return x >= 0 && x < window->width && - y >= 0 && y < window->height && - (window->shape == NULL || - cairo_region_contains_point (window->shape, x, y)) && - (window->input_shape == NULL || - cairo_region_contains_point (window->input_shape, x, y)); -} - -static GdkSurface * -child_window_at_coordinates (GdkSurface *window, - gint root_x, - gint root_y) -{ - gint x, y; - GList *l; - GList *children; - - children = gdk_surface_peek_children (window); - gdk_surface_get_root_origin (window, &x, &y); - x = root_x - x; - y = root_y - y; - - for (l = children; l; l = g_list_next (l)) - { - GdkSurface *child = GDK_SURFACE (l->data); - - if (point_in_window (child, x, y)) - return child; - } - - return window; -} - -static void -setup_drag_move_resize_context (GdkSurface *window, - GdkW32DragMoveResizeContext *context, - GdkW32WindowDragOp op, - GdkSurfaceEdge edge, - GdkDevice *device, - gint button, - gint root_x, - gint root_y, - guint32 timestamp) -{ - RECT rect; - const gchar *cursor_name; - GdkSurface *pointer_window; - GdkSurfaceImplWin32 *impl = GDK_SURFACE_IMPL_WIN32 (window->impl); - GdkDisplay *display = gdk_device_get_display (device); - gboolean maximized = gdk_surface_get_state (window) & GDK_SURFACE_STATE_MAXIMIZED; - - /* Before we drag, we need to undo any maximization or snapping. - * AeroSnap behaviour: - * If snapped halfleft/halfright: - * horizontal resize: - * resize - * don't unsnap - * keep stashed unsnapped size intact - * vertical resize: - * resize - * unsnap to new size (merge cached unsnapped state with current - * snapped state in such a way that the gripped edge - * does not move) - * diagonal resize: - * difficult to test (first move is usually either purely - * horizontal or purely vertical, in which - * case the above behaviour applies) - * If snapped up: - * horizontal resize: - * resize - * don't unsnap - * apply new width and x position to unsnapped cache, - * so that unsnapped window only regains its height - * and y position, but inherits x and width from - * the fullup snapped state - * vertical resize: - * unsnap to new size (merge cached unsnapped state with current - * snapped state in such a way that the gripped edge - * does not move) - * - * This implementation behaviour: - * If snapped halfleft/halfright/fullup: - * any resize: - * unsnap to current size, discard cached pre-snap state - * - * TODO: make this implementation behave as AeroSnap on resizes? - * There's also the case where - * a halfleft/halfright window isn't unsnapped when it's - * being moved horizontally, but it's more difficult to implement. - */ - if (op == GDK_WIN32_DRAGOP_RESIZE && - (impl->snap_state == GDK_WIN32_AEROSNAP_STATE_HALFRIGHT || - impl->snap_state == GDK_WIN32_AEROSNAP_STATE_HALFLEFT || - impl->snap_state == GDK_WIN32_AEROSNAP_STATE_FULLUP)) - { - discard_snapinfo (window); - } - else if (maximized || - (impl->snap_state == GDK_WIN32_AEROSNAP_STATE_HALFRIGHT || - impl->snap_state == GDK_WIN32_AEROSNAP_STATE_HALFLEFT || - impl->snap_state == GDK_WIN32_AEROSNAP_STATE_FULLUP)) - { - GdkMonitor *monitor; - gint wx, wy, wwidth, wheight; - gint swx, swy, swwidth, swheight; - gboolean pointer_outside_of_window; - gint offsetx, offsety; - gboolean left_half; - GdkDisplay *display; - - display = gdk_surface_get_display (window); - monitor = gdk_display_get_monitor_at_window (display, window); - gdk_surface_get_geometry (window, &wx, &wy, &wwidth, &wheight); - - swx = wx; - swy = wy; - swwidth = wwidth; - swheight = wheight; - - /* Subtract window shadow. We don't want pointer to go outside of - * the visible window during drag-move. For drag-resize it's OK. - * Don't take shadow into account if the window is maximized - - * maximized windows don't have shadows. - */ - if (op == GDK_WIN32_DRAGOP_MOVE && !maximized) - { - swx += impl->margins.left / impl->window_scale; - swy += impl->margins.top / impl->window_scale; - swwidth -= impl->margins_x; - swheight -= impl->margins_y; - } - - pointer_outside_of_window = root_x < swx || root_x > swx + swwidth || - root_y < swy || root_y > swy + swheight; - /* Calculate the offset of the pointer relative to the window */ - offsetx = root_x - swx; - offsety = root_y - swy; - - /* Figure out in which half of the window the pointer is. - * The code currently only concerns itself with horizontal - * dimension (left/right halves). - * There's no upper/lower half, because usually window - * is dragged by its upper half anyway. If that changes, adjust - * accordingly. - */ - left_half = (offsetx < swwidth / 2); - - /* Inverse the offset for it to be from the right edge */ - if (!left_half) - offsetx = swwidth - offsetx; - - GDK_NOTE (MISC, g_print ("Pointer at %d : %d, this is %d : %d relative to the window's %s\n", - root_x, root_y, offsetx, offsety, - left_half ? "left half" : "right half")); - - /* Move window in such a way that on unmaximization/unsnapping the pointer - * is still pointing at the appropriate half of the window, - * with the same offset from the left or right edge. If the new - * window size is too small, and adding that offset puts the pointer - * into the other half or even beyond, move the pointer to the middle. - */ - if (!pointer_outside_of_window && maximized) - { - WINDOWPLACEMENT placement; - gint unmax_width, unmax_height; - gint shadow_unmax_width, shadow_unmax_height; - - placement.length = sizeof (placement); - API_CALL (GetWindowPlacement, (GDK_SURFACE_HWND (window), &placement)); - - GDK_NOTE (MISC, g_print ("W32 WM unmaximized window placement is %ld x %ld @ %ld : %ld\n", - placement.rcNormalPosition.right - placement.rcNormalPosition.left, - placement.rcNormalPosition.bottom - placement.rcNormalPosition.top, - placement.rcNormalPosition.left + _gdk_offset_x * impl->window_scale, - placement.rcNormalPosition.top + _gdk_offset_y * impl->window_scale)); - - unmax_width = placement.rcNormalPosition.right - placement.rcNormalPosition.left; - unmax_height = placement.rcNormalPosition.bottom - placement.rcNormalPosition.top; - - shadow_unmax_width = unmax_width - impl->margins_x * impl->window_scale; - shadow_unmax_height = unmax_height - impl->margins_y * impl->window_scale; - - if (offsetx * impl->window_scale < (shadow_unmax_width / 2) && - offsety * impl->window_scale < (shadow_unmax_height / 2)) - { - placement.rcNormalPosition.top = (root_y - offsety + impl->margins.top - _gdk_offset_y) * impl->window_scale; - placement.rcNormalPosition.bottom = placement.rcNormalPosition.top + unmax_height; - - if (left_half) - { - placement.rcNormalPosition.left = (root_x - offsetx + impl->margins.left - _gdk_offset_x) * impl->window_scale; - placement.rcNormalPosition.right = placement.rcNormalPosition.left + unmax_width; - } - else - { - placement.rcNormalPosition.right = (root_x + offsetx + impl->margins.right - _gdk_offset_x) * impl->window_scale; - placement.rcNormalPosition.left = placement.rcNormalPosition.right - unmax_width; - } - } - else - { - placement.rcNormalPosition.left = (root_x * impl->window_scale) - - (unmax_width / 2) - - (_gdk_offset_x * impl->window_scale); - - if (offsety * impl->window_scale < shadow_unmax_height / 2) - placement.rcNormalPosition.top = (root_y - offsety + impl->margins.top - _gdk_offset_y) * impl->window_scale; - else - placement.rcNormalPosition.top = (root_y * impl->window_scale) - - (unmax_height / 2) - - (_gdk_offset_y * impl->window_scale); - - placement.rcNormalPosition.right = placement.rcNormalPosition.left + unmax_width; - placement.rcNormalPosition.bottom = placement.rcNormalPosition.top + unmax_height; - } - - GDK_NOTE (MISC, g_print ("Unmaximized window will be at %ld : %ld\n", - placement.rcNormalPosition.left + _gdk_offset_x * impl->window_scale, - placement.rcNormalPosition.top + _gdk_offset_y * impl->window_scale)); - - API_CALL (SetWindowPlacement, (GDK_SURFACE_HWND (window), &placement)); - } - else if (!pointer_outside_of_window && impl->snap_stash_int) - { - GdkRectangle new_pos; - GdkRectangle snew_pos; - - new_pos.width = impl->snap_stash_int->width; - new_pos.height = impl->snap_stash_int->height; - snew_pos = new_pos; - - if (op == GDK_WIN32_DRAGOP_MOVE) - { - snew_pos.width -= impl->margins_x; - snew_pos.height -= impl->margins_y; - } - - if (offsetx < snew_pos.width / 2 && offsety < snew_pos.height / 2) - { - new_pos.y = root_y - offsety + impl->margins.top / impl->window_scale; - - if (left_half) - new_pos.x = root_x - offsetx + impl->margins.left / impl->window_scale; - else - new_pos.x = root_x + offsetx + impl->margins.left / impl->window_scale - new_pos.width; - } - else - { - new_pos.x = root_x - new_pos.width / 2; - new_pos.y = root_y - new_pos.height / 2; - } - - GDK_NOTE (MISC, g_print ("Unsnapped window to %d : %d\n", - new_pos.x, new_pos.y)); - discard_snapinfo (window); - gdk_surface_move_resize (window, new_pos.x, new_pos.y, - new_pos.width, new_pos.height); - } - - - if (maximized) - gdk_surface_unmaximize (window); - else - unsnap (window, monitor); - - if (pointer_outside_of_window) - { - /* Pointer outside of the window, move pointer into window */ - GDK_NOTE (MISC, g_print ("Pointer at %d : %d is outside of %d x %d @ %d : %d, move it to %d : %d\n", - root_x, root_y, wwidth, wheight, wx, wy, wx + wwidth / 2, wy + wheight / 2)); - root_x = wx + wwidth / 2; - /* This is Gnome behaviour. Windows WM would put the pointer - * in the middle of the titlebar, but GDK doesn't know where - * the titlebar is, if any. - */ - root_y = wy + wheight / 2; - gdk_device_warp (device, root_x, root_y); - } - } - - _gdk_win32_get_window_rect (window, &rect); - - cursor_name = get_cursor_name_from_op (op, edge); - - context->cursor = gdk_cursor_new_from_name (cursor_name, NULL); - - pointer_window = child_window_at_coordinates (window, root_x, root_y); - - /* Note: This triggers a WM_CAPTURECHANGED, which will trigger - * gdk_win32_surface_end_move_resize_drag(), which will end - * our op before it even begins, but only if context->op is not NONE. - * This is why we first do the grab, *then* set the op. - */ - gdk_device_grab (device, pointer_window, - GDK_OWNERSHIP_NONE, FALSE, - GDK_ALL_EVENTS_MASK, - context->cursor, - timestamp); - - context->window = g_object_ref (window); - context->op = op; - context->edge = edge; - context->device = device; - context->button = button; - context->start_root_x = root_x; - context->start_root_y = root_y; - context->timestamp = timestamp; - context->start_rect = rect; - - context->shape_indicator = NULL; - context->revealed = FALSE; - context->halfleft_regions = g_array_new (FALSE, FALSE, sizeof (AeroSnapEdgeRegion)); - context->halfright_regions = g_array_new (FALSE, FALSE, sizeof (AeroSnapEdgeRegion)); - context->maximize_regions = g_array_new (FALSE, FALSE, sizeof (AeroSnapEdgeRegion)); - context->fullup_regions = g_array_new (FALSE, FALSE, sizeof (AeroSnapEdgeRegion)); - - calculate_aerosnap_regions (context); - - GDK_NOTE (EVENTS, - g_print ("begin drag moveresize: window %p, toplevel %p, " - "op %u, edge %d, device %p, " - "button %d, coord %d:%d, time %u\n", - pointer_window, gdk_surface_get_toplevel (window), - context->op, context->edge, context->device, - context->button, context->start_root_x, - context->start_root_y, context->timestamp)); -} - -void -gdk_win32_surface_end_move_resize_drag (GdkSurface *window) -{ - GdkSurfaceImplWin32 *impl = GDK_SURFACE_IMPL_WIN32 (window->impl); - GdkW32DragMoveResizeContext *context = &impl->drag_move_resize_context; - - context->op = GDK_WIN32_DRAGOP_NONE; - - gdk_device_ungrab (context->device, GDK_CURRENT_TIME); - - g_clear_object (&context->cursor); - - context->revealed = FALSE; - - if (context->timer) - { - g_source_remove (context->timer); - context->timer = 0; - } - - g_clear_object (&context->window); - - if (context->indicator_surface) - { - cairo_surface_destroy (context->indicator_surface); - context->indicator_surface = NULL; - } - - if (context->shape_indicator) - { - stop_indicator (window, context); - DestroyWindow (context->shape_indicator); - context->shape_indicator = NULL; - } - - g_clear_pointer (&context->halfleft_regions, g_array_unref); - g_clear_pointer (&context->halfright_regions, g_array_unref); - g_clear_pointer (&context->maximize_regions, g_array_unref); - g_clear_pointer (&context->fullup_regions, g_array_unref); - - GDK_NOTE (EVENTS, - g_print ("end drag moveresize: window %p, toplevel %p," - "op %u, edge %d, device %p, " - "button %d, coord %d:%d, time %u\n", - window, gdk_surface_get_toplevel (window), - context->op, context->edge, context->device, - context->button, context->start_root_x, - context->start_root_y, context->timestamp)); - - if (context->current_snap != GDK_WIN32_AEROSNAP_STATE_UNDETERMINED) - apply_snap (window, context->current_snap); - - context->current_snap = GDK_WIN32_AEROSNAP_STATE_UNDETERMINED; -} - -static void -gdk_win32_get_window_size_and_position_from_client_rect (GdkSurface *window, - RECT *window_rect, - SIZE *window_size, - POINT *window_position) -{ - GdkSurfaceImplWin32 *impl; - - impl = GDK_SURFACE_IMPL_WIN32 (window->impl); - - /* Turn client area into window area */ - _gdk_win32_adjust_client_rect (window, window_rect); - - /* Convert GDK screen coordinates to W32 desktop coordinates */ - window_rect->left -= _gdk_offset_x * impl->window_scale; - window_rect->right -= _gdk_offset_x * impl->window_scale; - window_rect->top -= _gdk_offset_y * impl->window_scale; - window_rect->bottom -= _gdk_offset_y * impl->window_scale; - - window_position->x = window_rect->left; - window_position->y = window_rect->top; - window_size->cx = window_rect->right - window_rect->left; - window_size->cy = window_rect->bottom - window_rect->top; -} - -static void -gdk_win32_update_layered_window_from_cache (GdkSurface *window, - RECT *client_rect) -{ - POINT window_position; - SIZE window_size; - BLENDFUNCTION blender; - HDC hdc; - SIZE *window_size_ptr; - POINT source_point = { 0, 0 }; - POINT *source_point_ptr; - GdkSurfaceImplWin32 *impl; - - impl = GDK_SURFACE_IMPL_WIN32 (window->impl); - - gdk_win32_get_window_size_and_position_from_client_rect (window, - client_rect, - &window_size, - &window_position); - - blender.BlendOp = AC_SRC_OVER; - blender.BlendFlags = 0; - blender.AlphaFormat = AC_SRC_ALPHA; - blender.SourceConstantAlpha = impl->layered_opacity * 255; - - /* Size didn't change, so move immediately, no need to wait for redraw */ - /* Strictly speaking, we don't need to supply hdc, source_point and - * window_size here. However, without these arguments - * the window moves but does not update its contents on Windows 7 when - * desktop composition is off. This forces us to provide hdc and - * source_point. window_size is here to avoid the function - * inexplicably failing with error 317. - */ - if (gdk_display_is_composited (gdk_surface_get_display (window))) - { - hdc = NULL; - window_size_ptr = NULL; - source_point_ptr = NULL; - } - else - { - hdc = cairo_win32_surface_get_dc (impl->cache_surface); - window_size_ptr = &window_size; - source_point_ptr = &source_point; - } - - API_CALL (UpdateLayeredWindow, (GDK_SURFACE_HWND (window), NULL, - &window_position, window_size_ptr, - hdc, source_point_ptr, - 0, &blender, ULW_ALPHA)); -} - -void -gdk_win32_surface_do_move_resize_drag (GdkSurface *window, - gint x, - gint y) -{ - RECT rect; - RECT new_rect; - gint diffy, diffx; - MINMAXINFO mmi; - GdkSurfaceImplWin32 *impl; - GdkW32DragMoveResizeContext *context; - gint width; - gint height; - - impl = GDK_SURFACE_IMPL_WIN32 (window->impl); - context = &impl->drag_move_resize_context; - - if (!_gdk_win32_get_window_rect (window, &rect)) - return; - - new_rect = context->start_rect; - diffx = (x - context->start_root_x) * impl->window_scale; - diffy = (y - context->start_root_y) * impl->window_scale; - - switch (context->op) - { - case GDK_WIN32_DRAGOP_RESIZE: - - switch (context->edge) - { - case GDK_SURFACE_EDGE_NORTH_WEST: - new_rect.left += diffx; - new_rect.top += diffy; - break; - - case GDK_SURFACE_EDGE_NORTH: - new_rect.top += diffy; - break; - - case GDK_SURFACE_EDGE_NORTH_EAST: - new_rect.right += diffx; - new_rect.top += diffy; - break; - - case GDK_SURFACE_EDGE_WEST: - new_rect.left += diffx; - break; - - case GDK_SURFACE_EDGE_EAST: - new_rect.right += diffx; - break; - - case GDK_SURFACE_EDGE_SOUTH_WEST: - new_rect.left += diffx; - new_rect.bottom += diffy; - break; - - case GDK_SURFACE_EDGE_SOUTH: - new_rect.bottom += diffy; - break; - - case GDK_SURFACE_EDGE_SOUTH_EAST: - default: - new_rect.right += diffx; - new_rect.bottom += diffy; - break; - } - - /* When handling WM_GETMINMAXINFO, mmi is already populated - * by W32 WM and we apply our stuff on top of that. - * Here it isn't, so we should at least clear it. - */ - memset (&mmi, 0, sizeof (mmi)); - - if (!_gdk_win32_surface_fill_min_max_info (window, &mmi)) - break; - - width = new_rect.right - new_rect.left; - height = new_rect.bottom - new_rect.top; - - if (width > mmi.ptMaxTrackSize.x) - { - switch (context->edge) - { - case GDK_SURFACE_EDGE_NORTH_WEST: - case GDK_SURFACE_EDGE_WEST: - case GDK_SURFACE_EDGE_SOUTH_WEST: - new_rect.left = new_rect.right - mmi.ptMaxTrackSize.x; - break; - - case GDK_SURFACE_EDGE_NORTH_EAST: - case GDK_SURFACE_EDGE_EAST: - case GDK_SURFACE_EDGE_SOUTH_EAST: - default: - new_rect.right = new_rect.left + mmi.ptMaxTrackSize.x; - break; - } - } - else if (width < mmi.ptMinTrackSize.x) - { - switch (context->edge) - { - case GDK_SURFACE_EDGE_NORTH_WEST: - case GDK_SURFACE_EDGE_WEST: - case GDK_SURFACE_EDGE_SOUTH_WEST: - new_rect.left = new_rect.right - mmi.ptMinTrackSize.x; - break; - - case GDK_SURFACE_EDGE_NORTH_EAST: - case GDK_SURFACE_EDGE_EAST: - case GDK_SURFACE_EDGE_SOUTH_EAST: - default: - new_rect.right = new_rect.left + mmi.ptMinTrackSize.x; - break; - } - } - - if (height > mmi.ptMaxTrackSize.y) - { - switch (context->edge) - { - case GDK_SURFACE_EDGE_NORTH_WEST: - case GDK_SURFACE_EDGE_NORTH: - case GDK_SURFACE_EDGE_NORTH_EAST: - new_rect.top = new_rect.bottom - mmi.ptMaxTrackSize.y; - - case GDK_SURFACE_EDGE_SOUTH_WEST: - case GDK_SURFACE_EDGE_SOUTH: - case GDK_SURFACE_EDGE_SOUTH_EAST: - default: - new_rect.bottom = new_rect.top + mmi.ptMaxTrackSize.y; - break; - } - } - else if (height < mmi.ptMinTrackSize.y) - { - switch (context->edge) - { - case GDK_SURFACE_EDGE_NORTH_WEST: - case GDK_SURFACE_EDGE_NORTH: - case GDK_SURFACE_EDGE_NORTH_EAST: - new_rect.top = new_rect.bottom - mmi.ptMinTrackSize.y; - - case GDK_SURFACE_EDGE_SOUTH_WEST: - case GDK_SURFACE_EDGE_SOUTH: - case GDK_SURFACE_EDGE_SOUTH_EAST: - default: - new_rect.bottom = new_rect.top + mmi.ptMinTrackSize.y; - break; - } - } - - break; - case GDK_WIN32_DRAGOP_MOVE: - new_rect.left += diffx; - new_rect.top += diffy; - new_rect.right += diffx; - new_rect.bottom += diffy; - break; - default: - break; - } - - if (context->op == GDK_WIN32_DRAGOP_RESIZE && - (rect.left != new_rect.left || - rect.right != new_rect.right || - rect.top != new_rect.top || - rect.bottom != new_rect.bottom)) - { - context->native_move_resize_pending = TRUE; - _gdk_win32_do_emit_configure_event (window, new_rect); - } - else if (context->op == GDK_WIN32_DRAGOP_MOVE && - (rect.left != new_rect.left || - rect.top != new_rect.top)) - { - context->native_move_resize_pending = FALSE; - - _gdk_win32_do_emit_configure_event (window, new_rect); - - if (impl->layered) - { - gdk_win32_update_layered_window_from_cache (window, &new_rect); - } - else - { - SIZE window_size; - POINT window_position; - - gdk_win32_get_window_size_and_position_from_client_rect (window, - &new_rect, - &window_size, - &window_position); - - API_CALL (SetWindowPos, (GDK_SURFACE_HWND (window), - SWP_NOZORDER_SPECIFIED, - window_position.x, window_position.y, - 0, 0, - SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOSIZE)); - } - } - - if (context->op == GDK_WIN32_DRAGOP_RESIZE || - context->op == GDK_WIN32_DRAGOP_MOVE) - handle_aerosnap_move_resize (window, context, x, y); -} - -static void -gdk_win32_surface_begin_resize_drag (GdkSurface *window, - GdkSurfaceEdge edge, - GdkDevice *device, - gint button, - gint root_x, - gint root_y, - guint32 timestamp) -{ - GdkSurfaceImplWin32 *impl; - - g_return_if_fail (GDK_IS_SURFACE (window)); - - if (GDK_SURFACE_DESTROYED (window) || - IsIconic (GDK_SURFACE_HWND (window))) - return; - - /* Tell Windows to start interactively resizing the window by pretending that - * the left pointer button was clicked in the suitable edge or corner. This - * will only work if the button is down when this function is called, and - * will only work with button 1 (left), since Windows only allows window - * dragging using the left mouse button. - */ - - if (button != 1) - return; - - impl = GDK_SURFACE_IMPL_WIN32 (window->impl); - - if (impl->drag_move_resize_context.op != GDK_WIN32_DRAGOP_NONE) - gdk_win32_surface_end_move_resize_drag (window); - - setup_drag_move_resize_context (window, &impl->drag_move_resize_context, - GDK_WIN32_DRAGOP_RESIZE, edge, device, - button, root_x, root_y, timestamp); -} - -static void -gdk_win32_surface_begin_move_drag (GdkSurface *window, - GdkDevice *device, - gint button, - gint root_x, - gint root_y, - guint32 timestamp) -{ - GdkSurfaceImplWin32 *impl; - - g_return_if_fail (GDK_IS_SURFACE (window)); - - if (GDK_SURFACE_DESTROYED (window) || - IsIconic (GDK_SURFACE_HWND (window))) - return; - - /* Tell Windows to start interactively moving the window by pretending that - * the left pointer button was clicked in the titlebar. This will only work - * if the button is down when this function is called, and will only work - * with button 1 (left), since Windows only allows window dragging using the - * left mouse button. - */ - if (button != 1) - return; - - impl = GDK_SURFACE_IMPL_WIN32 (window->impl); - - if (impl->drag_move_resize_context.op != GDK_WIN32_DRAGOP_NONE) - gdk_win32_surface_end_move_resize_drag (window); - - setup_drag_move_resize_context (window, &impl->drag_move_resize_context, - GDK_WIN32_DRAGOP_MOVE, GDK_SURFACE_EDGE_NORTH_WEST, - device, button, root_x, root_y, timestamp); -} - - -/* - * Setting window states - */ -static void -gdk_win32_surface_iconify (GdkSurface *window) -{ - HWND old_active_window; - - g_return_if_fail (GDK_IS_SURFACE (window)); - - if (GDK_SURFACE_DESTROYED (window)) - return; - - GDK_NOTE (MISC, g_print ("gdk_surface_iconify: %p: %s\n", - GDK_SURFACE_HWND (window), - _gdk_win32_surface_state_to_string (window->state))); - - if (GDK_SURFACE_IS_MAPPED (window)) - { - old_active_window = GetActiveWindow (); - GtkShowWindow (window, SW_MINIMIZE); - if (old_active_window != GDK_SURFACE_HWND (window)) - SetActiveWindow (old_active_window); - } - else - { - gdk_synthesize_window_state (window, - 0, - GDK_SURFACE_STATE_ICONIFIED); - } -} - -static void -gdk_win32_surface_deiconify (GdkSurface *window) -{ - g_return_if_fail (GDK_IS_SURFACE (window)); - - if (GDK_SURFACE_DESTROYED (window)) - return; - - GDK_NOTE (MISC, g_print ("gdk_surface_deiconify: %p: %s\n", - GDK_SURFACE_HWND (window), - _gdk_win32_surface_state_to_string (window->state))); - - if (GDK_SURFACE_IS_MAPPED (window)) - { - show_window_internal (window, GDK_SURFACE_IS_MAPPED (window), TRUE); - } - else - { - gdk_synthesize_window_state (window, - GDK_SURFACE_STATE_ICONIFIED, - 0); - } -} - -static void -gdk_win32_surface_stick (GdkSurface *window) -{ - g_return_if_fail (GDK_IS_SURFACE (window)); - - if (GDK_SURFACE_DESTROYED (window)) - return; - - /* FIXME: Do something? */ -} - -static void -gdk_win32_surface_unstick (GdkSurface *window) -{ - g_return_if_fail (GDK_IS_SURFACE (window)); - - if (GDK_SURFACE_DESTROYED (window)) - return; - - /* FIXME: Do something? */ -} - -static void -gdk_win32_surface_maximize (GdkSurface *window) -{ - - g_return_if_fail (GDK_IS_SURFACE (window)); - - if (GDK_SURFACE_DESTROYED (window)) - return; - - GDK_NOTE (MISC, g_print ("gdk_surface_maximize: %p: %s\n", - GDK_SURFACE_HWND (window), - _gdk_win32_surface_state_to_string (window->state))); - - if (GDK_SURFACE_IS_MAPPED (window)) - GtkShowWindow (window, SW_MAXIMIZE); - else - gdk_synthesize_window_state (window, - 0, - GDK_SURFACE_STATE_MAXIMIZED); -} - -static void -gdk_win32_surface_unmaximize (GdkSurface *window) -{ - g_return_if_fail (GDK_IS_SURFACE (window)); - - if (GDK_SURFACE_DESTROYED (window)) - return; - - GDK_NOTE (MISC, g_print ("gdk_surface_unmaximize: %p: %s\n", - GDK_SURFACE_HWND (window), - _gdk_win32_surface_state_to_string (window->state))); - - if (GDK_SURFACE_IS_MAPPED (window)) - GtkShowWindow (window, SW_RESTORE); - else - gdk_synthesize_window_state (window, - GDK_SURFACE_STATE_MAXIMIZED, - 0); -} - -static void -gdk_win32_surface_fullscreen (GdkSurface *window) -{ - gint x, y, width, height; - FullscreenInfo *fi; - HMONITOR monitor; - MONITORINFO mi; - - g_return_if_fail (GDK_IS_SURFACE (window)); - - fi = g_new (FullscreenInfo, 1); - - if (!GetWindowRect (GDK_SURFACE_HWND (window), &(fi->r))) - g_free (fi); - else - { - GdkSurfaceImplWin32 *impl = GDK_SURFACE_IMPL_WIN32 (window->impl); - - monitor = MonitorFromWindow (GDK_SURFACE_HWND (window), MONITOR_DEFAULTTONEAREST); - mi.cbSize = sizeof (mi); - if (monitor && GetMonitorInfo (monitor, &mi)) - { - x = mi.rcMonitor.left; - y = mi.rcMonitor.top; - width = mi.rcMonitor.right - x; - height = mi.rcMonitor.bottom - y; - } - else - { - x = y = 0; - width = GetSystemMetrics (SM_CXSCREEN); - height = GetSystemMetrics (SM_CYSCREEN); - } - - /* remember for restoring */ - fi->hint_flags = impl->hint_flags; - impl->hint_flags &= ~GDK_HINT_MAX_SIZE; - g_object_set_data (G_OBJECT (window), "fullscreen-info", fi); - fi->style = GetWindowLong (GDK_SURFACE_HWND (window), GWL_STYLE); - - /* Send state change before configure event */ - gdk_synthesize_window_state (window, 0, GDK_SURFACE_STATE_FULLSCREEN); - - SetWindowLong (GDK_SURFACE_HWND (window), GWL_STYLE, - (fi->style & ~WS_OVERLAPPEDWINDOW) | WS_POPUP); - - API_CALL (SetWindowPos, (GDK_SURFACE_HWND (window), HWND_TOP, - x, y, width, height, - SWP_NOCOPYBITS | SWP_SHOWWINDOW)); - } -} - -static void -gdk_win32_surface_unfullscreen (GdkSurface *window) -{ - FullscreenInfo *fi; - - g_return_if_fail (GDK_IS_SURFACE (window)); - - fi = g_object_get_data (G_OBJECT (window), "fullscreen-info"); - if (fi) - { - GdkSurfaceImplWin32 *impl = GDK_SURFACE_IMPL_WIN32 (window->impl); - - gdk_synthesize_window_state (window, GDK_SURFACE_STATE_FULLSCREEN, 0); - - impl->hint_flags = fi->hint_flags; - SetWindowLong (GDK_SURFACE_HWND (window), GWL_STYLE, fi->style); - API_CALL (SetWindowPos, (GDK_SURFACE_HWND (window), HWND_NOTOPMOST, - fi->r.left, fi->r.top, - fi->r.right - fi->r.left, fi->r.bottom - fi->r.top, - SWP_NOCOPYBITS | SWP_SHOWWINDOW)); - - g_object_set_data (G_OBJECT (window), "fullscreen-info", NULL); - g_free (fi); - _gdk_win32_surface_update_style_bits (window); - } -} - -static void -gdk_win32_surface_set_keep_above (GdkSurface *window, - gboolean setting) -{ - g_return_if_fail (GDK_IS_SURFACE (window)); - - if (GDK_SURFACE_DESTROYED (window)) - return; - - GDK_NOTE (MISC, g_print ("gdk_surface_set_keep_above: %p: %s\n", - GDK_SURFACE_HWND (window), - setting ? "YES" : "NO")); - - if (GDK_SURFACE_IS_MAPPED (window)) - { - API_CALL (SetWindowPos, (GDK_SURFACE_HWND (window), - setting ? HWND_TOPMOST : HWND_NOTOPMOST, - 0, 0, 0, 0, - SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE)); - } - - gdk_synthesize_window_state (window, - setting ? GDK_SURFACE_STATE_BELOW : GDK_SURFACE_STATE_ABOVE, - setting ? GDK_SURFACE_STATE_ABOVE : 0); -} - -static void -gdk_win32_surface_set_keep_below (GdkSurface *window, - gboolean setting) -{ - g_return_if_fail (GDK_IS_SURFACE (window)); - - if (GDK_SURFACE_DESTROYED (window)) - return; - - GDK_NOTE (MISC, g_print ("gdk_surface_set_keep_below: %p: %s\n", - GDK_SURFACE_HWND (window), - setting ? "YES" : "NO")); - - if (GDK_SURFACE_IS_MAPPED (window)) - { - API_CALL (SetWindowPos, (GDK_SURFACE_HWND (window), - setting ? HWND_BOTTOM : HWND_NOTOPMOST, - 0, 0, 0, 0, - SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE)); - } - - gdk_synthesize_window_state (window, - setting ? GDK_SURFACE_STATE_ABOVE : GDK_SURFACE_STATE_BELOW, - setting ? GDK_SURFACE_STATE_BELOW : 0); -} - -static void -gdk_win32_surface_focus (GdkSurface *window, - guint32 timestamp) -{ - g_return_if_fail (GDK_IS_SURFACE (window)); - - if (GDK_SURFACE_DESTROYED (window)) - return; - - GDK_NOTE (MISC, g_print ("gdk_surface_focus: %p: %s\n", - GDK_SURFACE_HWND (window), - _gdk_win32_surface_state_to_string (window->state))); - - if (window->state & GDK_SURFACE_STATE_MAXIMIZED) - GtkShowWindow (window, SW_SHOWMAXIMIZED); - else if (window->state & GDK_SURFACE_STATE_ICONIFIED) - GtkShowWindow (window, SW_RESTORE); - else if (!IsWindowVisible (GDK_SURFACE_HWND (window))) - GtkShowWindow (window, SW_SHOWNORMAL); - else - GtkShowWindow (window, SW_SHOW); - - SetFocus (GDK_SURFACE_HWND (window)); -} - -static void -gdk_win32_surface_set_modal_hint (GdkSurface *window, - gboolean modal) -{ - g_return_if_fail (GDK_IS_SURFACE (window)); - - if (GDK_SURFACE_DESTROYED (window)) - return; - - GDK_NOTE (MISC, g_print ("gdk_surface_set_modal_hint: %p: %s\n", - GDK_SURFACE_HWND (window), - modal ? "YES" : "NO")); - - if (modal == window->modal_hint) - return; - - window->modal_hint = modal; - -#if 0 - /* Not sure about this one.. -- Cody */ - if (GDK_SURFACE_IS_MAPPED (window)) - API_CALL (SetWindowPos, (GDK_SURFACE_HWND (window), - modal ? HWND_TOPMOST : HWND_NOTOPMOST, - 0, 0, 0, 0, - SWP_NOMOVE | SWP_NOSIZE)); -#else - - if (modal) - { - _gdk_push_modal_window (window); - gdk_surface_raise (window); - } - else - { - _gdk_remove_modal_window (window); - } - -#endif -} - -static void -gdk_win32_surface_set_skip_taskbar_hint (GdkSurface *window, - gboolean skips_taskbar) -{ - static GdkSurface *owner = NULL; - //GdkSurfaceAttr wa; - - g_return_if_fail (GDK_IS_SURFACE (window)); - - GDK_NOTE (MISC, g_print ("gdk_surface_set_skip_taskbar_hint: %p: %s, doing nothing\n", - GDK_SURFACE_HWND (window), - skips_taskbar ? "YES" : "NO")); - - // ### TODO: Need to figure out what to do here. - return; - - if (skips_taskbar) - { -#if 0 - if (owner == NULL) - { - wa.window_type = GDK_SURFACE_TEMP; - wa.wclass = GDK_INPUT_OUTPUT; - wa.width = wa.height = 1; - wa.event_mask = 0; - owner = gdk_surface_new_internal (NULL, &wa, 0, TRUE); - } -#endif - - SetWindowLongPtr (GDK_SURFACE_HWND (window), GWLP_HWNDPARENT, (LONG_PTR) GDK_SURFACE_HWND (owner)); - -#if 0 /* Should we also turn off the minimize and maximize buttons? */ - SetWindowLong (GDK_SURFACE_HWND (window), GWL_STYLE, - GetWindowLong (GDK_SURFACE_HWND (window), GWL_STYLE) & ~(WS_MINIMIZEBOX|WS_MAXIMIZEBOX|WS_SYSMENU)); - - SetWindowPos (GDK_SURFACE_HWND (window), SWP_NOZORDER_SPECIFIED, - 0, 0, 0, 0, - SWP_FRAMECHANGED | SWP_NOACTIVATE | SWP_NOMOVE | - SWP_NOREPOSITION | SWP_NOSIZE | SWP_NOZORDER); -#endif - } - else - { - SetWindowLongPtr (GDK_SURFACE_HWND (window), GWLP_HWNDPARENT, 0); - } -} - -static void -gdk_win32_surface_set_skip_pager_hint (GdkSurface *window, - gboolean skips_pager) -{ - g_return_if_fail (GDK_IS_SURFACE (window)); - - GDK_NOTE (MISC, g_print ("gdk_surface_set_skip_pager_hint: %p: %s, doing nothing\n", - GDK_SURFACE_HWND (window), - skips_pager ? "YES" : "NO")); -} - -static void -gdk_win32_surface_set_type_hint (GdkSurface *window, - GdkSurfaceTypeHint hint) -{ - g_return_if_fail (GDK_IS_SURFACE (window)); - - if (GDK_SURFACE_DESTROYED (window)) - return; - - GDK_NOTE (MISC, - G_STMT_START{ - static GEnumClass *class = NULL; - if (!class) - class = g_type_class_ref (GDK_TYPE_SURFACE_TYPE_HINT); - g_print ("gdk_surface_set_type_hint: %p: %s\n", - GDK_SURFACE_HWND (window), - g_enum_get_value (class, hint)->value_name); - }G_STMT_END); - - ((GdkSurfaceImplWin32 *)window->impl)->type_hint = hint; - - _gdk_win32_surface_update_style_bits (window); -} - -static GdkSurfaceTypeHint -gdk_win32_surface_get_type_hint (GdkSurface *window) -{ - g_return_val_if_fail (GDK_IS_SURFACE (window), GDK_SURFACE_TYPE_HINT_NORMAL); - - if (GDK_SURFACE_DESTROYED (window)) - return GDK_SURFACE_TYPE_HINT_NORMAL; - - return GDK_SURFACE_IMPL_WIN32 (window->impl)->type_hint; -} - -static HRGN -cairo_region_to_hrgn (const cairo_region_t *region, - gint x_origin, - gint y_origin, - guint scale) -{ - HRGN hrgn; - RGNDATA *rgndata; - RECT *rect; - cairo_rectangle_int_t r; - const int nrects = cairo_region_num_rectangles (region); - guint nbytes = - sizeof (RGNDATAHEADER) + (sizeof (RECT) * nrects); - int i; - - rgndata = g_malloc (nbytes); - rgndata->rdh.dwSize = sizeof (RGNDATAHEADER); - rgndata->rdh.iType = RDH_RECTANGLES; - rgndata->rdh.nCount = rgndata->rdh.nRgnSize = 0; - SetRect (&rgndata->rdh.rcBound, - G_MAXLONG, G_MAXLONG, G_MINLONG, G_MINLONG); - - for (i = 0; i < nrects; i++) - { - rect = ((RECT *) rgndata->Buffer) + rgndata->rdh.nCount++; - - cairo_region_get_rectangle (region, i, &r); - rect->left = (r.x + x_origin) * scale; - rect->right = (rect->left + r.width) * scale; - rect->top = (r.y + y_origin) * scale; - rect->bottom = (rect->top + r.height) * scale; - - if (rect->left < rgndata->rdh.rcBound.left) - rgndata->rdh.rcBound.left = rect->left; - if (rect->right > rgndata->rdh.rcBound.right) - rgndata->rdh.rcBound.right = rect->right; - if (rect->top < rgndata->rdh.rcBound.top) - rgndata->rdh.rcBound.top = rect->top; - if (rect->bottom > rgndata->rdh.rcBound.bottom) - rgndata->rdh.rcBound.bottom = rect->bottom; - } - if ((hrgn = ExtCreateRegion (NULL, nbytes, rgndata)) == NULL) - WIN32_API_FAILED ("ExtCreateRegion"); - - g_free (rgndata); - - return (hrgn); -} - -static void -gdk_win32_surface_shape_combine_region (GdkSurface *window, - const cairo_region_t *shape_region, - gint offset_x, - gint offset_y) -{ - GdkSurfaceImplWin32 *impl; - - if (GDK_SURFACE_DESTROYED (window)) - return; - - if (!shape_region) - { - GDK_NOTE (MISC, g_print ("gdk_win32_surface_shape_combine_region: %p: none\n", - GDK_SURFACE_HWND (window))); - SetWindowRgn (GDK_SURFACE_HWND (window), NULL, TRUE); - } - else - { - HRGN hrgn; - impl = GDK_SURFACE_IMPL_WIN32 (window->impl); - - hrgn = cairo_region_to_hrgn (shape_region, 0, 0, impl->window_scale); - - GDK_NOTE (MISC, g_print ("gdk_win32_surface_shape_combine_region: %p: %p\n", - GDK_SURFACE_HWND (window), - hrgn)); - - do_shape_combine_region (window, hrgn, offset_x, offset_y); - } -} - -GdkSurface * -gdk_win32_surface_lookup_for_display (GdkDisplay *display, - HWND anid) -{ - g_return_val_if_fail (display == gdk_display_get_default (), NULL); - - return (GdkSurface*) gdk_win32_handle_table_lookup (anid); -} - -static void -gdk_win32_surface_set_opacity (GdkSurface *window, - gdouble opacity) -{ - LONG exstyle; - typedef BOOL (WINAPI *PFN_SetLayeredWindowAttributes) (HWND, COLORREF, BYTE, DWORD); - PFN_SetLayeredWindowAttributes setLayeredWindowAttributes = NULL; - GdkSurfaceImplWin32 *impl; - - g_return_if_fail (GDK_IS_SURFACE (window)); - - if (!WINDOW_IS_TOPLEVEL (window) || GDK_SURFACE_DESTROYED (window)) - return; - - if (opacity < 0) - opacity = 0; - else if (opacity > 1) - opacity = 1; - - impl = GDK_SURFACE_IMPL_WIN32 (window->impl); - - if (impl->layered) - { - if (impl->layered_opacity != opacity) - { - RECT window_rect; - - impl->layered_opacity = opacity; - - gdk_win32_get_window_client_area_rect (window, impl->window_scale, &window_rect); - gdk_win32_update_layered_window_from_cache (window, &window_rect); - } - - return; - } - - exstyle = GetWindowLong (GDK_SURFACE_HWND (window), GWL_EXSTYLE); - - if (!(exstyle & WS_EX_LAYERED)) - SetWindowLong (GDK_SURFACE_HWND (window), - GWL_EXSTYLE, - exstyle | WS_EX_LAYERED); - - setLayeredWindowAttributes = - (PFN_SetLayeredWindowAttributes)GetProcAddress (GetModuleHandle ("user32.dll"), "SetLayeredWindowAttributes"); - - if (setLayeredWindowAttributes) - { - API_CALL (setLayeredWindowAttributes, (GDK_SURFACE_HWND (window), - 0, - opacity * 0xff, - LWA_ALPHA)); - } -} - -gboolean -gdk_win32_surface_is_win32 (GdkSurface *window) -{ - return GDK_SURFACE_IS_WIN32 (window); -} - -static gboolean -gdk_win32_surface_show_window_menu (GdkSurface *window, - GdkEvent *event) -{ - double event_x, event_y; - gint x, y; - GdkSurfaceImplWin32 *impl = GDK_SURFACE_IMPL_WIN32 (window->impl); - - switch (event->type) - { - case GDK_BUTTON_PRESS: - case GDK_BUTTON_RELEASE: - case GDK_TOUCH_BEGIN: - case GDK_TOUCH_END: - break; - default: - return FALSE; - } - - gdk_event_get_root_coords (event, &event_x, &event_y); - x = event_x - _gdk_offset_x; - y = event_y - _gdk_offset_y; - - SendMessage (GDK_SURFACE_HWND (window), - WM_SYSMENU, - 0, - MAKELPARAM (x * impl->window_scale, y * impl->window_scale)); - - return TRUE; -} - -/** - * _gdk_win32_acquire_dc - * @impl: a Win32 #GdkSurfaceImplWin32 implementation - * - * Gets a DC with the given drawable selected into it. - * - * Returns: The DC, on success. Otherwise - * %NULL. If this function succeeded - * _gdk_win32_impl_release_dc() must be called - * release the DC when you are done using it. - **/ -static HDC -_gdk_win32_impl_acquire_dc (GdkSurfaceImplWin32 *impl) -{ - if (GDK_IS_SURFACE_IMPL_WIN32 (impl) && - GDK_SURFACE_DESTROYED (impl->wrapper)) - return NULL; - - /* We don't call this function for layered windows, but - * in case we do... - */ - if (impl->layered) - return NULL; - - if (!impl->hdc) - { - impl->hdc = GetDC (impl->handle); - if (!impl->hdc) - WIN32_GDI_FAILED ("GetDC"); - } - - if (impl->hdc) - { - impl->hdc_count++; - return impl->hdc; - } - else - { - return NULL; - } -} - -/** - * _gdk_win32_impl_release_dc - * @impl: a Win32 #GdkSurfaceImplWin32 implementation - * - * Releases the reference count for the DC - * from _gdk_win32_impl_acquire_dc() - **/ -static void -_gdk_win32_impl_release_dc (GdkSurfaceImplWin32 *impl) -{ - if (impl->layered) - return; - - g_return_if_fail (impl->hdc_count > 0); - - impl->hdc_count--; - if (impl->hdc_count == 0) - { - if (impl->saved_dc_bitmap) - { - GDI_CALL (SelectObject, (impl->hdc, impl->saved_dc_bitmap)); - impl->saved_dc_bitmap = NULL; - } - - if (impl->hdc) - { - GDI_CALL (ReleaseDC, (impl->handle, impl->hdc)); - impl->hdc = NULL; - } - } -} - -HWND -gdk_win32_surface_get_impl_hwnd (GdkSurface *window) -{ - if (GDK_SURFACE_IS_WIN32 (window)) - return GDK_SURFACE_HWND (window); - return NULL; -} - -static void -gdk_win32_cairo_surface_destroy (void *data) -{ - GdkSurfaceImplWin32 *impl = data; - - _gdk_win32_impl_release_dc (impl); - impl->cairo_surface = NULL; -} - -static cairo_surface_t * -gdk_win32_ref_cairo_surface_layered (GdkSurface *window, - GdkSurfaceImplWin32 *impl) -{ - gint width, height; - RECT window_rect; - - gdk_win32_get_window_client_area_rect (window, impl->window_scale, &window_rect); - - /* Turn client area into window area */ - _gdk_win32_adjust_client_rect (window, &window_rect); - - width = window_rect.right - window_rect.left; - height = window_rect.bottom - window_rect.top; - - if (width > impl->dib_width || - height > impl->dib_height) - { - cairo_surface_t *new_cache; - cairo_t *cr; - - /* Create larger cache surface, copy old cache surface over it */ - new_cache = cairo_win32_surface_create_with_dib (CAIRO_FORMAT_ARGB32, - width, - height); - - if (impl->cache_surface) - { - cr = cairo_create (new_cache); - cairo_set_source_surface (cr, impl->cache_surface, 0, 0); - cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE); - cairo_paint (cr); - cairo_destroy (cr); - cairo_surface_flush (new_cache); - - cairo_surface_destroy (impl->cache_surface); - } - - impl->cache_surface = new_cache; - - cairo_surface_set_device_scale (impl->cache_surface, - impl->window_scale, - impl->window_scale); - - if (impl->cairo_surface) - cairo_surface_destroy (impl->cairo_surface); - - impl->cairo_surface = NULL; - } - - /* This is separate, because cairo_surface gets killed - * off frequently by outside code, whereas cache_surface - * is only killed by us, above. - */ - if (!impl->cairo_surface) - { - impl->cairo_surface = cairo_win32_surface_create_with_dib (CAIRO_FORMAT_ARGB32, - width, - height); - impl->dib_width = width; - impl->dib_height = height; - - cairo_surface_set_device_scale (impl->cairo_surface, - impl->window_scale, - impl->window_scale); - - cairo_surface_set_user_data (impl->cairo_surface, &gdk_win32_cairo_key, - impl, gdk_win32_cairo_surface_destroy); - } - else - { - cairo_surface_reference (impl->cairo_surface); - } - - return impl->cairo_surface; -} - -static cairo_surface_t * -gdk_win32_ref_cairo_surface (GdkSurface *window) -{ - GdkSurfaceImplWin32 *impl = GDK_SURFACE_IMPL_WIN32 (window->impl); - - if (GDK_IS_SURFACE_IMPL_WIN32 (impl) && - GDK_SURFACE_DESTROYED (impl->wrapper)) - return NULL; - - if (impl->layered) - return gdk_win32_ref_cairo_surface_layered (window, impl); - - if (!impl->cairo_surface) - { - HDC hdc = _gdk_win32_impl_acquire_dc (impl); - if (!hdc) - return NULL; - - impl->cairo_surface = cairo_win32_surface_create_with_format (hdc, CAIRO_FORMAT_ARGB32); - cairo_surface_set_device_scale (impl->cairo_surface, - impl->window_scale, - impl->window_scale); - - cairo_surface_set_user_data (impl->cairo_surface, &gdk_win32_cairo_key, - impl, gdk_win32_cairo_surface_destroy); - } - else - cairo_surface_reference (impl->cairo_surface); - - return impl->cairo_surface; -} - -BOOL WINAPI -GtkShowWindow (GdkSurface *window, - int cmd_show) -{ - cairo_t *cr; - cairo_surface_t *surface; - RECT window_rect; - HDC hdc; - POINT window_position; - SIZE window_size; - POINT source_point; - BLENDFUNCTION blender; - - HWND hwnd = GDK_SURFACE_HWND (window); - GdkSurfaceImplWin32 *impl = GDK_SURFACE_IMPL_WIN32 (window->impl); - - switch (cmd_show) - { - case SW_FORCEMINIMIZE: - case SW_HIDE: - case SW_MINIMIZE: - break; - case SW_MAXIMIZE: - case SW_RESTORE: - case SW_SHOW: - case SW_SHOWDEFAULT: - case SW_SHOWMINIMIZED: - case SW_SHOWMINNOACTIVE: - case SW_SHOWNA: - case SW_SHOWNOACTIVATE: - case SW_SHOWNORMAL: - if (IsWindowVisible (hwnd)) - break; - - if ((WS_EX_LAYERED & GetWindowLongPtr (hwnd, GWL_EXSTYLE)) != WS_EX_LAYERED) - break; - - /* Window was hidden, will be shown. Erase it, GDK will repaint soon, - * but not soon enough, so it's possible to see old content before - * the next redraw, unless we erase the window first. - */ - GetWindowRect (hwnd, &window_rect); - source_point.x = source_point.y = 0; - - window_position.x = window_rect.left; - window_position.y = window_rect.top; - window_size.cx = window_rect.right - window_rect.left; - window_size.cy = window_rect.bottom - window_rect.top; - - blender.BlendOp = AC_SRC_OVER; - blender.BlendFlags = 0; - blender.AlphaFormat = AC_SRC_ALPHA; - blender.SourceConstantAlpha = 255; - - /* Create a surface of appropriate size and clear it */ - surface = cairo_win32_surface_create_with_dib (CAIRO_FORMAT_ARGB32, - window_size.cx, - window_size.cy); - cairo_surface_set_device_scale (surface, impl->window_scale, impl->window_scale); - cr = cairo_create (surface); - cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE); - cairo_set_source_rgba (cr, 1.0, 1.0, 1.0, 0.0); - cairo_paint (cr); - cairo_destroy (cr); - cairo_surface_flush (surface); - hdc = cairo_win32_surface_get_dc (surface); - - /* No API_CALL() wrapper, don't check for errors */ - UpdateLayeredWindow (hwnd, NULL, - &window_position, &window_size, - hdc, &source_point, - 0, &blender, ULW_ALPHA); - - cairo_surface_destroy (surface); - - break; - } - - /* Ensure that maximized window size is corrected later on */ - if (cmd_show == SW_MAXIMIZE) - impl->maximizing = TRUE; - - return ShowWindow (hwnd, cmd_show); -} - -static void -gdk_win32_surface_set_shadow_width (GdkSurface *window, - gint left, - gint right, - gint top, - gint bottom) -{ - GdkSurfaceImplWin32 *impl = GDK_SURFACE_IMPL_WIN32 (window->impl); - - if (GDK_SURFACE_DESTROYED (window)) - return; - - GDK_NOTE (MISC, g_print ("gdk_win32_surface_set_shadow_width: window %p, " - "left %d, top %d, right %d, bottom %d\n", - window, left, top, right, bottom)); - - impl->zero_margins = left == 0 && right == 0 && top == 0 && bottom == 0; - - if (impl->zero_margins) - return; - - impl->margins.left = left; - impl->margins.right = right * impl->window_scale; - impl->margins.top = top; - impl->margins.bottom = bottom * impl->window_scale; - impl->margins_x = left + right; - impl->margins_y = top + bottom; -} - - -gint -_gdk_win32_surface_get_scale_factor (GdkSurface *window) -{ - GdkDisplay *display; - GdkSurfaceImplWin32 *impl; - - GdkWin32Display *win32_display; - UINT dpix, dpiy; - gboolean is_scale_acquired; - - if (GDK_SURFACE_DESTROYED (window)) - return 1; - - g_return_val_if_fail (window != NULL, 1); - - display = gdk_surface_get_display (window); - impl = GDK_SURFACE_IMPL_WIN32 (window->impl); - - win32_display = GDK_WIN32_DISPLAY (display); - - if (win32_display->dpi_aware_type != PROCESS_DPI_UNAWARE) - { - if (win32_display->has_fixed_scale) - impl->window_scale = win32_display->window_scale; - else - impl->window_scale = _gdk_win32_display_get_monitor_scale_factor (win32_display, - NULL, - GDK_SURFACE_HWND (window), - NULL); - - return impl->window_scale; - } - else - { - if (win32_display->has_fixed_scale) - { - static gsize hidpi_msg_displayed = 0; - - if (g_once_init_enter (&hidpi_msg_displayed)) - { - g_message ("Note: GDK_SCALE is ignored as HiDPI awareness is disabled."); - g_once_init_leave (&hidpi_msg_displayed, 1); - } - } - - /* Application is not DPI aware, don't bother */ - return 1; - } -} - -void -_gdk_win32_surface_get_unscaled_size (GdkSurface *window, - gint *unscaled_width, - gint *unscaled_height) -{ - GdkSurfaceImplWin32 *impl = GDK_SURFACE_IMPL_WIN32 (window->impl); - - if (unscaled_width) - *unscaled_width = impl->unscaled_width; - if (unscaled_height) - *unscaled_height = impl->unscaled_height; -} - -static void -gdk_win32_input_shape_combine_region (GdkSurface *window, - const cairo_region_t *shape_region, - gint offset_x, - gint offset_y) -{ - /* Partial input shape support is implemented by handling the - * NC_NCHITTEST message - */ -} - -static void -gdk_surface_impl_win32_class_init (GdkSurfaceImplWin32Class *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - GdkSurfaceImplClass *impl_class = GDK_SURFACE_IMPL_CLASS (klass); - - parent_class = g_type_class_peek_parent (klass); - - object_class->finalize = gdk_surface_impl_win32_finalize; - - impl_class->ref_cairo_surface = gdk_win32_ref_cairo_surface; - - impl_class->show = gdk_win32_surface_show; - impl_class->hide = gdk_win32_surface_hide; - impl_class->withdraw = gdk_win32_surface_withdraw; - impl_class->set_events = gdk_win32_surface_set_events; - impl_class->get_events = gdk_win32_surface_get_events; - impl_class->raise = gdk_win32_surface_raise; - impl_class->lower = gdk_win32_surface_lower; - impl_class->restack_toplevel = gdk_win32_surface_restack_toplevel; - impl_class->move_resize = gdk_win32_surface_move_resize; - impl_class->get_geometry = gdk_win32_surface_get_geometry; - impl_class->get_device_state = gdk_surface_win32_get_device_state; - impl_class->get_root_coords = gdk_win32_surface_get_root_coords; - - impl_class->shape_combine_region = gdk_win32_surface_shape_combine_region; - impl_class->input_shape_combine_region = gdk_win32_input_shape_combine_region; - impl_class->destroy = gdk_win32_surface_destroy; - impl_class->begin_paint = gdk_win32_surface_begin_paint; - impl_class->end_paint = gdk_win32_surface_end_paint; - - //impl_class->beep = gdk_x11_surface_beep; - - - impl_class->show_window_menu = gdk_win32_surface_show_window_menu; - impl_class->focus = gdk_win32_surface_focus; - impl_class->set_type_hint = gdk_win32_surface_set_type_hint; - impl_class->get_type_hint = gdk_win32_surface_get_type_hint; - impl_class->set_modal_hint = gdk_win32_surface_set_modal_hint; - impl_class->set_skip_taskbar_hint = gdk_win32_surface_set_skip_taskbar_hint; - impl_class->set_skip_pager_hint = gdk_win32_surface_set_skip_pager_hint; - impl_class->set_urgency_hint = gdk_win32_surface_set_urgency_hint; - impl_class->set_geometry_hints = gdk_win32_surface_set_geometry_hints; - impl_class->set_title = gdk_win32_surface_set_title; - impl_class->set_role = gdk_win32_surface_set_role; - //impl_class->set_startup_id = gdk_x11_surface_set_startup_id; - impl_class->set_transient_for = gdk_win32_surface_set_transient_for; - impl_class->get_frame_extents = gdk_win32_surface_get_frame_extents; - impl_class->set_accept_focus = gdk_win32_surface_set_accept_focus; - impl_class->set_focus_on_map = gdk_win32_surface_set_focus_on_map; - impl_class->set_icon_list = gdk_win32_surface_set_icon_list; - impl_class->set_icon_name = gdk_win32_surface_set_icon_name; - impl_class->iconify = gdk_win32_surface_iconify; - impl_class->deiconify = gdk_win32_surface_deiconify; - impl_class->stick = gdk_win32_surface_stick; - impl_class->unstick = gdk_win32_surface_unstick; - impl_class->maximize = gdk_win32_surface_maximize; - impl_class->unmaximize = gdk_win32_surface_unmaximize; - impl_class->fullscreen = gdk_win32_surface_fullscreen; - impl_class->unfullscreen = gdk_win32_surface_unfullscreen; - impl_class->set_keep_above = gdk_win32_surface_set_keep_above; - impl_class->set_keep_below = gdk_win32_surface_set_keep_below; - impl_class->get_group = gdk_win32_surface_get_group; - impl_class->set_group = gdk_win32_surface_set_group; - impl_class->set_decorations = gdk_win32_surface_set_decorations; - impl_class->get_decorations = gdk_win32_surface_get_decorations; - impl_class->set_functions = gdk_win32_surface_set_functions; - - impl_class->set_shadow_width = gdk_win32_surface_set_shadow_width; - impl_class->begin_resize_drag = gdk_win32_surface_begin_resize_drag; - impl_class->begin_move_drag = gdk_win32_surface_begin_move_drag; - impl_class->set_opacity = gdk_win32_surface_set_opacity; - impl_class->destroy_notify = gdk_win32_surface_destroy_notify; - impl_class->register_dnd = _gdk_win32_surface_register_dnd; - impl_class->drag_begin = _gdk_win32_surface_drag_begin; - impl_class->create_gl_context = _gdk_win32_surface_create_gl_context; - impl_class->get_scale_factor = _gdk_win32_surface_get_scale_factor; - impl_class->get_unscaled_size = _gdk_win32_surface_get_unscaled_size; -} - -HGDIOBJ -gdk_win32_surface_get_handle (GdkSurface *window) -{ - if (!GDK_SURFACE_IS_WIN32 (window)) - { - g_warning (G_STRLOC " window is not a native Win32 window"); - return NULL; - } - - return GDK_SURFACE_HWND (window); -} diff --git a/gdk/win32/gdkwindow-win32.h b/gdk/win32/gdkwindow-win32.h deleted file mode 100644 index 4856287611..0000000000 --- a/gdk/win32/gdkwindow-win32.h +++ /dev/null @@ -1,376 +0,0 @@ -/* GDK - The GIMP Drawing Kit - * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library. If not, see . - */ - -/* - * Modified by the GTK+ Team and others 1997-1999. See the AUTHORS - * file for a list of people on the GTK+ Team. See the ChangeLog - * files for a list of changes. These files are distributed with - * GTK+ at ftp://ftp.gtk.org/pub/gtk/. - */ - -#ifndef __GDK_SURFACE_WIN32_H__ -#define __GDK_SURFACE_WIN32_H__ - -#include "gdk/win32/gdkprivate-win32.h" -#include "gdk/gdkwindowimpl.h" -#include "gdk/gdkcursor.h" - -#include - -G_BEGIN_DECLS - -/* Window implementation for Win32 - */ - -typedef struct _GdkSurfaceImplWin32 GdkSurfaceImplWin32; -typedef struct _GdkSurfaceImplWin32Class GdkSurfaceImplWin32Class; - -#define GDK_TYPE_SURFACE_IMPL_WIN32 (_gdk_surface_impl_win32_get_type ()) -#define GDK_SURFACE_IMPL_WIN32(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), GDK_TYPE_SURFACE_IMPL_WIN32, GdkSurfaceImplWin32)) -#define GDK_SURFACE_IMPL_WIN32_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GDK_TYPE_SURFACE_IMPL_WIN32, GdkSurfaceImplWin32Class)) -#define GDK_IS_SURFACE_IMPL_WIN32(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), GDK_TYPE_SURFACE_IMPL_WIN32)) -#define GDK_IS_SURFACE_IMPL_WIN32_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GDK_TYPE_SURFACE_IMPL_WIN32)) -#define GDK_SURFACE_IMPL_WIN32_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GDK_TYPE_SURFACE_IMPL_WIN32, GdkSurfaceImplWin32Class)) - -enum _GdkWin32AeroSnapCombo -{ - GDK_WIN32_AEROSNAP_COMBO_NOTHING = 0, - GDK_WIN32_AEROSNAP_COMBO_UP, - GDK_WIN32_AEROSNAP_COMBO_DOWN, - GDK_WIN32_AEROSNAP_COMBO_LEFT, - GDK_WIN32_AEROSNAP_COMBO_RIGHT, - /* Same order as non-shift variants. We use it to do things like: - * AEROSNAP_UP + 4 = AEROSNAP_SHIFTUP - */ - GDK_WIN32_AEROSNAP_COMBO_SHIFTUP, - GDK_WIN32_AEROSNAP_COMBO_SHIFTDOWN, - GDK_WIN32_AEROSNAP_COMBO_SHIFTLEFT, - GDK_WIN32_AEROSNAP_COMBO_SHIFTRIGHT -}; - -typedef enum _GdkWin32AeroSnapCombo GdkWin32AeroSnapCombo; - -enum _GdkWin32AeroSnapState -{ - GDK_WIN32_AEROSNAP_STATE_UNDETERMINED = 0, - GDK_WIN32_AEROSNAP_STATE_HALFLEFT, - GDK_WIN32_AEROSNAP_STATE_HALFRIGHT, - GDK_WIN32_AEROSNAP_STATE_FULLUP, - /* Maximize state is only used by edge-snap */ - GDK_WIN32_AEROSNAP_STATE_MAXIMIZE -}; - -typedef enum _GdkWin32AeroSnapState GdkWin32AeroSnapState; - -struct _GdkRectangleDouble -{ - gdouble x; - gdouble y; - gdouble width; - gdouble height; -}; - -typedef struct _GdkRectangleDouble GdkRectangleDouble; - -enum _GdkW32WindowDragOp -{ - GDK_WIN32_DRAGOP_NONE = 0, - GDK_WIN32_DRAGOP_RESIZE, - GDK_WIN32_DRAGOP_MOVE, - GDK_WIN32_DRAGOP_COUNT -}; - -typedef enum _GdkW32WindowDragOp GdkW32WindowDragOp; - -typedef enum _GdkWin32MonitorDpiType -{ - MDT_EFFECTIVE_DPI = 0, - MDT_ANGULAR_DPI = 1, - MDT_RAW_DPI = 2, - MDT_DEFAULT = MDT_EFFECTIVE_DPI -} GdkWin32MonitorDpiType; - -struct _GdkW32DragMoveResizeContext -{ - /* The window that is being moved/resized */ - GdkSurface *window; - - /* The kind of drag-operation going on. */ - GdkW32WindowDragOp op; - - /* The edge that was grabbed for resizing. Not used for moving. */ - GdkSurfaceEdge edge; - - /* The device used to initiate the op. - * We grab it at the beginning and ungrab it at the end. - */ - GdkDevice *device; - - /* The button pressed down to initiate the op. - * The op will be canceled only when *this* button - * is released. - */ - gint button; - - /* Initial cursor position when the operation began. - * Current cursor position is subtracted from it to find how far - * to move window border(s). - */ - gint start_root_x; - gint start_root_y; - - /* Initial window rectangle (position and size). - * The window is resized/moved relative to this (see start_root_*). - */ - RECT start_rect; - - /* Not used */ - guint32 timestamp; - - /* TRUE if during the next redraw we should call SetWindowPos() to push - * the window size and poistion to the native window. - */ - gboolean native_move_resize_pending; - - /* The cursor we should use while the operation is running. */ - GdkCursor *cursor; - - /* This window looks like an outline and is drawn under the window - * that is being dragged. It indicates the shape the dragged window - * will take if released at a particular point. - * Indicator window size always matches the target indicator shape, - * the the actual indicator drawn on it might not, depending on - * how much time elapsed since the animation started. - */ - HWND shape_indicator; - - /* Used to draw the indicator */ - cairo_surface_t *indicator_surface; - gint indicator_surface_width; - gint indicator_surface_height; - - /* Size/position of shape_indicator */ - GdkRectangle indicator_window_rect; - - /* Indicator will animate to occupy this rectangle */ - GdkRectangle indicator_target; - - /* Indicator will start animating from this rectangle */ - GdkRectangle indicator_start; - - /* Timestamp of the animation start */ - gint64 indicator_start_time; - - /* Timer that drives the animation */ - guint timer; - - /* A special timestamp, if we want to draw not how - * the animation should look *now*, but how it should - * look at arbitrary moment of time. - * Set to 0 to tell GDK to use current time. - */ - gint64 draw_timestamp; - - /* Indicates that a transformation was revealed: - * - * For drag-resize: If it's FALSE, - * then the pointer have not yet hit a trigger that triggers fullup. - * If TRUE, then the pointer did hit a trigger that triggers fullup - * at some point during this drag op. - * This is used to prevent drag-resize from triggering - * a transformation when first approaching a trigger of the work area - - * one must drag it all the way to the very trigger to trigger; afterwards - * a transformation will start triggering at some distance from the trigger - * for as long as the op is still running. This is how AeroSnap works. - * - * For drag-move: If it's FALSE, - * then the pointer have not yet hit a trigger, even if it is - * already within a edge region. - * If it's TRUE, then the pointer did hit a trigger within an - * edge region, and have not yet left an edge region - * (passing from one edge region into another doesn't count). - */ - gboolean revealed; - - /* Arrays of GdkRectangle pairs, describing the areas of the virtual - * desktop that trigger various AeroSnap window transofrmations - * Coordinates are GDK screen coordinates. - */ - GArray *halfleft_regions; - GArray *halfright_regions; - GArray *maximize_regions; - GArray *fullup_regions; - - /* Current pointer position will result in this kind of snapping, - * if the drag op is finished. - */ - GdkWin32AeroSnapState current_snap; -}; - -typedef struct _GdkW32DragMoveResizeContext GdkW32DragMoveResizeContext; - -struct _GdkSurfaceImplWin32 -{ - GdkSurfaceImpl parent_instance; - - GdkSurface *wrapper; - HANDLE handle; - - gint8 toplevel_window_type; - - HICON hicon_big; - HICON hicon_small; - - /* When VK_PACKET sends us a leading surrogate, it's stashed here. - * Later, when another VK_PACKET sends a tailing surrogate, we make up - * a full unicode character from them, or discard the leading surrogate, - * if the next key is not a tailing surrogate. - */ - wchar_t leading_surrogate_keydown; - wchar_t leading_surrogate_keyup; - - /* Window size hints */ - gint hint_flags; - GdkGeometry hints; - - GdkEventMask native_event_mask; - - GdkSurfaceTypeHint type_hint; - - GdkSurface *transient_owner; - GSList *transient_children; - gint num_transients; - gboolean changing_state; - - gint initial_x; - gint initial_y; - - /* left/right/top/bottom width of the shadow/resize-grip around the window */ - RECT margins; - - /* left+right and top+bottom from @margins */ - gint margins_x; - gint margins_y; - - /* Set to TRUE when GTK tells us that margins are 0 everywhere. - * We don't actually set margins to 0, we just set this bit. - */ - guint zero_margins : 1; - guint no_bg : 1; - guint inhibit_configure : 1; - - /* Set to TRUE if window is using true layered mode adjustments - * via UpdateLayeredWindow(). - * Layered windows that get SetLayeredWindowAttributes() called - * on them are not true layered windows. - */ - guint layered : 1; - - /* If TRUE, the @temp_styles is set to the styles that were temporarily - * added to this window. - */ - guint have_temp_styles : 1; - - /* If TRUE, the window is in the process of being maximized. - * This is set by WM_SYSCOMMAND and by gdk_win32_surface_maximize (), - * and is unset when WM_WINDOWPOSCHANGING is handled. - */ - guint maximizing : 1; - - /* GDK does not keep window contents around, it just draws new - * stuff over the window where changes occurred. - * cache_surface retains old window contents, because - * UpdateLayeredWindow() doesn't do partial redraws. - */ - cairo_surface_t *cache_surface; - cairo_surface_t *cairo_surface; - - /* Unlike window-backed surfaces, DIB-backed surface - * does not provide a way to query its size, - * so we have to remember it ourselves. - */ - gint dib_width; - gint dib_height; - - /* If the client wants uniformly-transparent window, - * we remember the opacity value here and apply it - * during UpdateLayredWindow() call, for layered windows. - */ - gdouble layered_opacity; - - HDC hdc; - int hdc_count; - HBITMAP saved_dc_bitmap; /* Original bitmap for dc */ - - GdkW32DragMoveResizeContext drag_move_resize_context; - - /* Remembers where the window was snapped. - * Some snap operations change their meaning if - * the window is already snapped. - */ - GdkWin32AeroSnapState snap_state; - - /* Remembers window position before it was snapped. - * This is used to unsnap it. - * Position and size are percentages of the workarea - * of the monitor on which the window was before it was snapped. - */ - GdkRectangleDouble *snap_stash; - - /* Also remember the same position, but in absolute form. */ - GdkRectangle *snap_stash_int; - - /* Decorations set by gdk_surface_set_decorations() or NULL if unset */ - GdkWMDecoration* decorations; - - /* No. of windows to force layered windows off */ - guint suppress_layered; - - /* Temporary styles that this window got for the purpose of - * handling WM_SYSMENU. - * They are removed at the first opportunity (usually WM_INITMENU). - */ - LONG_PTR temp_styles; - - /* scale of window on HiDPI */ - gint window_scale; - gint unscaled_width; - gint unscaled_height; -}; - -struct _GdkSurfaceImplWin32Class -{ - GdkSurfaceImplClass parent_class; -}; - -GType _gdk_surface_impl_win32_get_type (void); - -void _gdk_win32_surface_tmp_unset_bg (GdkSurface *window, - gboolean recurse); -void _gdk_win32_surface_tmp_reset_bg (GdkSurface *window, - gboolean recurse); - -void _gdk_win32_surface_tmp_unset_parent_bg (GdkSurface *window); -void _gdk_win32_surface_tmp_reset_parent_bg (GdkSurface *window); - -void _gdk_win32_surface_update_style_bits (GdkSurface *window); - -gint _gdk_win32_surface_get_scale_factor (GdkSurface *window); - -G_END_DECLS - -#endif /* __GDK_SURFACE_WIN32_H__ */ diff --git a/gdk/win32/meson.build b/gdk/win32/meson.build index c99f60981b..d609ca8d03 100644 --- a/gdk/win32/meson.build +++ b/gdk/win32/meson.build @@ -21,7 +21,7 @@ gdk_win32_sources = files([ 'gdkwin32cursor.h', 'gdkwin32display.h', 'gdkwin32id.c', - 'gdkwindow-win32.c', + 'gdksurface-win32.c', ]) gdk_win32_public_headers = files([ @@ -34,7 +34,7 @@ gdk_win32_public_headers = files([ 'gdkwin32misc.h', 'gdkwin32monitor.h', 'gdkwin32screen.h', - 'gdkwin32window.h', + 'gdkwin32surface.h', ]) install_headers(gdk_win32_public_headers, subdir: 'gtk-4.0/gdk/win32/') diff --git a/gdk/x11/gdkdevice-core-x11.c b/gdk/x11/gdkdevice-core-x11.c index 6bbdda6693..055fa593ac 100644 --- a/gdk/x11/gdkdevice-core-x11.c +++ b/gdk/x11/gdkdevice-core-x11.c @@ -22,7 +22,7 @@ #include "gdkdeviceprivate.h" #include "gdkinternals.h" -#include "gdkwindow.h" +#include "gdksurface.h" #include "gdkprivate-x11.h" #include "gdkdisplay-x11.h" #include "gdkasync.h" diff --git a/gdk/x11/gdkdisplay-x11.h b/gdk/x11/gdkdisplay-x11.h index ed08da2ff1..c4cc9e61b7 100644 --- a/gdk/x11/gdkdisplay-x11.h +++ b/gdk/x11/gdkdisplay-x11.h @@ -24,7 +24,7 @@ #include "gdkdisplayprivate.h" #include "gdkkeys.h" -#include "gdkwindow.h" +#include "gdksurface.h" #include "gdkinternals.h" #include "gdkx11devicemanager.h" #include "gdkx11display.h" diff --git a/gdk/x11/gdkeventsource.c b/gdk/x11/gdkeventsource.c index 2a26669c1a..168e33cfb8 100644 --- a/gdk/x11/gdkeventsource.c +++ b/gdk/x11/gdkeventsource.c @@ -20,7 +20,7 @@ #include "gdkeventsource.h" #include "gdkinternals.h" -#include "gdkwindow-x11.h" +#include "gdksurface-x11.h" #include "gdkprivate-x11.h" #include "gdkdisplay-x11.h" #include "xsettings-client.h" diff --git a/gdk/x11/gdkeventtranslator.c b/gdk/x11/gdkeventtranslator.c index 96c2ed9957..579c59ae23 100644 --- a/gdk/x11/gdkeventtranslator.c +++ b/gdk/x11/gdkeventtranslator.c @@ -18,7 +18,7 @@ #include "config.h" #include "gdkeventtranslator.h" -#include "gdkwindow-x11.h" +#include "gdksurface-x11.h" typedef GdkEventTranslatorIface GdkEventTranslatorInterface; G_DEFINE_INTERFACE (GdkEventTranslator, _gdk_x11_event_translator, G_TYPE_OBJECT); diff --git a/gdk/x11/gdkgeometry-x11.c b/gdk/x11/gdkgeometry-x11.c index 208704c360..d75acffbd6 100644 --- a/gdk/x11/gdkgeometry-x11.c +++ b/gdk/x11/gdkgeometry-x11.c @@ -22,7 +22,7 @@ #include "gdkprivate-x11.h" #include "gdkscreen-x11.h" #include "gdkdisplay-x11.h" -#include "gdkwindow-x11.h" +#include "gdksurface-x11.h" typedef struct _GdkSurfaceQueueItem GdkSurfaceQueueItem; diff --git a/gdk/x11/gdkglcontext-x11.c b/gdk/x11/gdkglcontext-x11.c index 682df01b39..0405d0588a 100644 --- a/gdk/x11/gdkglcontext-x11.c +++ b/gdk/x11/gdkglcontext-x11.c @@ -28,7 +28,7 @@ #include "gdkx11display.h" #include "gdkx11glcontext.h" #include "gdkx11screen.h" -#include "gdkx11window.h" +#include "gdkx11surface.h" #include "gdkvisual-x11.h" #include "gdkx11property.h" #include diff --git a/gdk/x11/gdkglcontext-x11.h b/gdk/x11/gdkglcontext-x11.h index 61dbadef1f..89d1131fcc 100644 --- a/gdk/x11/gdkglcontext-x11.h +++ b/gdk/x11/gdkglcontext-x11.h @@ -30,7 +30,7 @@ #include "gdkglcontextprivate.h" #include "gdkdisplayprivate.h" #include "gdkvisual-x11.h" -#include "gdkwindow.h" +#include "gdksurface.h" #include "gdkinternals.h" G_BEGIN_DECLS diff --git a/gdk/x11/gdkprivate-x11.h b/gdk/x11/gdkprivate-x11.h index 90d6ecaafe..e784b1f1b7 100644 --- a/gdk/x11/gdkprivate-x11.h +++ b/gdk/x11/gdkprivate-x11.h @@ -32,7 +32,7 @@ #include "gdkcursor.h" #include "gdkinternals.h" #include "gdkx.h" -#include "gdkwindow-x11.h" +#include "gdksurface-x11.h" #include "gdkscreen-x11.h" #include diff --git a/gdk/x11/gdkselectioninputstream-x11.c b/gdk/x11/gdkselectioninputstream-x11.c index c3ae895344..55f83b4975 100644 --- a/gdk/x11/gdkselectioninputstream-x11.c +++ b/gdk/x11/gdkselectioninputstream-x11.c @@ -27,7 +27,7 @@ #include "gdkintl.h" #include "gdkx11display.h" #include "gdkx11property.h" -#include "gdkx11window.h" +#include "gdkx11surface.h" typedef struct GdkX11SelectionInputStreamPrivate GdkX11SelectionInputStreamPrivate; diff --git a/gdk/x11/gdkselectionoutputstream-x11.c b/gdk/x11/gdkselectionoutputstream-x11.c index 106c6b780c..5f35cdf6be 100644 --- a/gdk/x11/gdkselectionoutputstream-x11.c +++ b/gdk/x11/gdkselectionoutputstream-x11.c @@ -29,7 +29,7 @@ #include "gdktextlistconverter-x11.h" #include "gdkx11display.h" #include "gdkx11property.h" -#include "gdkx11window.h" +#include "gdkx11surface.h" typedef struct _GdkX11PendingSelectionNotify GdkX11PendingSelectionNotify; typedef struct _GdkX11SelectionOutputStreamPrivate GdkX11SelectionOutputStreamPrivate; diff --git a/gdk/x11/gdksurface-x11.c b/gdk/x11/gdksurface-x11.c new file mode 100644 index 0000000000..21f1d14bf1 --- /dev/null +++ b/gdk/x11/gdksurface-x11.c @@ -0,0 +1,4948 @@ +/* GDK - The GIMP Drawing Kit + * Copyright (C) 1995-2007 Peter Mattis, Spencer Kimball, + * Josh MacDonald, Ryan Lortie + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * 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, see . + */ + +/* + * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS + * file for a list of people on the GTK+ Team. See the ChangeLog + * files for a list of changes. These files are distributed with + * GTK+ at ftp://ftp.gtk.org/pub/gtk/. + */ + +#include "config.h" + +#include "gdksurface-x11.h" + +#include "gdksurface.h" +#include "gdksurfaceimpl.h" +#include "gdkvisual-x11.h" +#include "gdkinternals.h" +#include "gdkdeviceprivate.h" +#include "gdkframeclockprivate.h" +#include "gdkasync.h" +#include "gdkeventsource.h" +#include "gdkdisplay-x11.h" +#include "gdkglcontext-x11.h" +#include "gdkprivate-x11.h" +#include "gdktextureprivate.h" +#include "gdk-private.h" + +#include +#include +#include +#include +#include + +#include + +#include "MwmUtil.h" + +#include +#include +#include + +#include + +#ifdef HAVE_XKB +#include +#endif + +#ifdef HAVE_XCOMPOSITE +#include +#endif + +#ifdef HAVE_XFIXES +#include +#endif + +#ifdef HAVE_XDAMAGE +#include +#endif + +const int _gdk_x11_event_mask_table[21] = +{ + ExposureMask, + PointerMotionMask, + PointerMotionHintMask, + ButtonMotionMask, + Button1MotionMask, + Button2MotionMask, + Button3MotionMask, + ButtonPressMask, + ButtonReleaseMask, + KeyPressMask, + KeyReleaseMask, + EnterWindowMask, + LeaveWindowMask, + FocusChangeMask, + StructureNotifyMask, + PropertyChangeMask, + VisibilityChangeMask, + 0, /* PROXIMITY_IN */ + 0, /* PROXIMTY_OUT */ + SubstructureNotifyMask, + ButtonPressMask /* SCROLL; on X mouse wheel events is treated as mouse button 4/5 */ +}; + +const gint _gdk_x11_event_mask_table_size = G_N_ELEMENTS (_gdk_x11_event_mask_table); + +/* Forward declarations */ +static void gdk_x11_surface_apply_fullscreen_mode (GdkSurface *window); +static gboolean gdk_surface_icon_name_set (GdkSurface *window); +static void set_wm_name (GdkDisplay *display, + Window xwindow, + const gchar *name); +static void move_to_current_desktop (GdkSurface *window); + +static void gdk_surface_impl_x11_finalize (GObject *object); + +#define WINDOW_IS_TOPLEVEL_OR_FOREIGN(window) \ + (GDK_SURFACE_TYPE (window) == GDK_SURFACE_TOPLEVEL || \ + GDK_SURFACE_TYPE (window) == GDK_SURFACE_TEMP || \ + GDK_SURFACE_TYPE (window) == GDK_SURFACE_FOREIGN) + +#define WINDOW_IS_TOPLEVEL(window) \ + (GDK_SURFACE_TYPE (window) == GDK_SURFACE_TOPLEVEL || \ + GDK_SURFACE_TYPE (window) == GDK_SURFACE_TEMP) + +/* Return whether time1 is considered later than time2 as far as xserver + * time is concerned. Accounts for wraparound. + */ +#define XSERVER_TIME_IS_LATER(time1, time2) \ + ( (( time1 > time2 ) && ( time1 - time2 < ((guint32)-1)/2 )) || \ + (( time1 < time2 ) && ( time2 - time1 > ((guint32)-1)/2 )) \ + ) + +struct _GdkX11Surface { + GdkSurface parent; +}; + +struct _GdkX11SurfaceClass { + GdkSurfaceClass parent_class; +}; + +G_DEFINE_TYPE (GdkX11Surface, gdk_x11_surface, GDK_TYPE_SURFACE) + +static void +gdk_x11_surface_class_init (GdkX11SurfaceClass *x11_surface_class) +{ +} + +static void +gdk_x11_surface_init (GdkX11Surface *x11_surface) +{ +} + + +G_DEFINE_TYPE (GdkSurfaceImplX11, gdk_surface_impl_x11, GDK_TYPE_SURFACE_IMPL) + +static void +gdk_surface_impl_x11_init (GdkSurfaceImplX11 *impl) +{ + impl->window_scale = 1; + impl->frame_sync_enabled = TRUE; +} + +GdkToplevelX11 * +_gdk_x11_surface_get_toplevel (GdkSurface *window) +{ + GdkSurfaceImplX11 *impl; + + g_return_val_if_fail (GDK_IS_SURFACE (window), NULL); + + if (!WINDOW_IS_TOPLEVEL (window)) + return NULL; + + impl = GDK_SURFACE_IMPL_X11 (window->impl); + + if (!impl->toplevel) + { + impl->toplevel = g_new0 (GdkToplevelX11, 1); + impl->toplevel->have_focused = FALSE; + } + + return impl->toplevel; +} + +/** + * _gdk_x11_surface_update_size: + * @impl: a #GdkSurfaceImplX11. + * + * Updates the state of the window (in particular the drawable's + * cairo surface) when its size has changed. + **/ +void +_gdk_x11_surface_update_size (GdkSurfaceImplX11 *impl) +{ + if (impl->cairo_surface) + { + cairo_xlib_surface_set_size (impl->cairo_surface, + impl->unscaled_width, impl->unscaled_height); + } +} + +static void +gdk_x11_surface_get_unscaled_size (GdkSurface *window, + int *unscaled_width, + int *unscaled_height) +{ + GdkSurfaceImplX11 *impl = GDK_SURFACE_IMPL_X11 (window->impl); + + if (unscaled_width) + *unscaled_width = impl->unscaled_width; + + if (unscaled_height) + *unscaled_height = impl->unscaled_height; +} + +static gboolean +gdk_x11_surface_supports_edge_constraints (GdkSurface *window) +{ + return gdk_x11_screen_supports_net_wm_hint (GDK_SURFACE_SCREEN (window), + g_intern_static_string ("_GTK_EDGE_CONSTRAINTS")); +} + +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 (GdkSurface *window) +{ + GdkSurface *toplevel_window = gdk_surface_get_toplevel (window); + GdkSurfaceImplX11 *impl; + + if (!toplevel_window || !WINDOW_IS_TOPLEVEL (toplevel_window)) + return; + + impl = GDK_SURFACE_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_SURFACE_XDISPLAY (impl->wrapper), + impl->toplevel->extended_update_counter, + impl->toplevel->current_counter_value); + } +} + +static void +on_surface_changed (void *data) +{ + GdkSurface *window = data; + GdkSurfaceImplX11 *impl = GDK_SURFACE_IMPL_X11 (window->impl); + + if (impl->tracking_damage) + 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 (GdkSurface *window) +{ + GdkSurfaceImplX11 *impl = GDK_SURFACE_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); + impl->tracking_damage = 1; + } +} + +static void +unhook_surface_changed (GdkSurface *window) +{ + GdkSurfaceImplX11 *impl = GDK_SURFACE_IMPL_X11 (window->impl); + + if (impl->cairo_surface) + { + impl->tracking_damage = 0; + cairo_surface_set_mime_data (impl->cairo_surface, + "x-gdk/change-notify", + NULL, 0, + NULL, NULL); + } +} + +static void +gdk_x11_surface_predict_presentation_time (GdkSurface *window) +{ + GdkSurfaceImplX11 *impl = GDK_SURFACE_IMPL_X11 (window->impl); + GdkFrameClock *clock; + GdkFrameTimings *timings; + gint64 presentation_time; + gint64 refresh_interval; + + if (!WINDOW_IS_TOPLEVEL (window)) + return; + + clock = gdk_surface_get_frame_clock (window); + + timings = gdk_frame_clock_get_current_timings (clock); + + gdk_frame_clock_get_refresh_info (clock, + timings->frame_time, + &refresh_interval, &presentation_time); + + if (presentation_time != 0) + { + if (timings->slept_before) + { + presentation_time += refresh_interval; + } + else + { + if (presentation_time < timings->frame_time + refresh_interval / 2) + presentation_time += refresh_interval; + } + } + else + { + if (timings->slept_before) + presentation_time = timings->frame_time + refresh_interval + refresh_interval / 2; + else + presentation_time = timings->frame_time + refresh_interval; + } + + if (presentation_time < impl->toplevel->throttled_presentation_time) + presentation_time = impl->toplevel->throttled_presentation_time; + + timings->predicted_presentation_time = presentation_time; +} + +static void +gdk_x11_surface_begin_frame (GdkSurface *window, + gboolean force_frame) +{ + GdkSurfaceImplX11 *impl; + + g_return_if_fail (GDK_IS_SURFACE (window)); + + impl = GDK_SURFACE_IMPL_X11 (window->impl); + + if (!WINDOW_IS_TOPLEVEL (window) || + impl->toplevel->extended_update_counter == None) + return; + + impl->toplevel->in_frame = TRUE; + + if (impl->toplevel->configure_counter_value != 0 && + impl->toplevel->configure_counter_value_is_extended) + { + impl->toplevel->current_counter_value = impl->toplevel->configure_counter_value; + if ((impl->toplevel->current_counter_value % 2) == 1) + impl->toplevel->current_counter_value += 1; + + impl->toplevel->configure_counter_value = 0; + + window_pre_damage (window); + } + else if (force_frame) + { + /* When mapping the window, we really want to freeze the + rendering of the window by the compositor until we've + actually painted something into the window's buffer. */ + window_pre_damage (window); + } + else + { + hook_surface_changed (window); + } +} + +static void +gdk_x11_surface_end_frame (GdkSurface *window) +{ + GdkFrameClock *clock; + GdkFrameTimings *timings; + GdkSurfaceImplX11 *impl; + + g_return_if_fail (GDK_IS_SURFACE (window)); + + impl = GDK_SURFACE_IMPL_X11 (window->impl); + + if (!WINDOW_IS_TOPLEVEL (window) || + impl->toplevel->extended_update_counter == None || + !impl->toplevel->in_frame) + return; + + clock = gdk_surface_get_frame_clock (window); + timings = gdk_frame_clock_get_current_timings (clock); + + impl->toplevel->in_frame = FALSE; + + if (impl->toplevel->current_counter_value % 2 == 1) + { + if (GDK_DISPLAY_DEBUG_CHECK (gdk_surface_get_display (window), FRAMES)) + { + XImage *image = XGetImage (GDK_SURFACE_XDISPLAY (window), + GDK_SURFACE_XID (window), + 0, 0, 1, 1, + (1 << 24) - 1, + ZPixmap); + XDestroyImage (image); + } + + /* An increment of 3 means that the frame was not drawn as fast as possible, + * but rather at a particular time. This can trigger different handling from + * the compositor. + */ + if (timings->slept_before) + impl->toplevel->current_counter_value += 3; + else + impl->toplevel->current_counter_value += 1; + + set_sync_counter(GDK_SURFACE_XDISPLAY (impl->wrapper), + impl->toplevel->extended_update_counter, + impl->toplevel->current_counter_value); + + if (impl->frame_sync_enabled && + gdk_x11_screen_supports_net_wm_hint (GDK_SURFACE_SCREEN (window), + g_intern_static_string ("_NET_WM_FRAME_DRAWN"))) + { + impl->toplevel->frame_pending = TRUE; + _gdk_frame_clock_freeze (gdk_surface_get_frame_clock (window)); + timings->cookie = impl->toplevel->current_counter_value; + } + } + + unhook_surface_changed (window); + + if (impl->toplevel->configure_counter_value != 0 && + !impl->toplevel->configure_counter_value_is_extended) + { + set_sync_counter (GDK_SURFACE_XDISPLAY (window), + impl->toplevel->update_counter, + impl->toplevel->configure_counter_value); + + impl->toplevel->configure_counter_value = 0; + } + + if (!impl->toplevel->frame_pending) + timings->complete = TRUE; +} + +/***************************************************** + * X11 specific implementations of generic functions * + *****************************************************/ + +static cairo_surface_t * +gdk_x11_create_cairo_surface (GdkSurfaceImplX11 *impl, + int width, + int height) +{ + Visual *visual; + + visual = gdk_x11_display_get_window_visual (GDK_X11_DISPLAY (gdk_surface_get_display (impl->wrapper))); + return cairo_xlib_surface_create (GDK_SURFACE_XDISPLAY (impl->wrapper), + GDK_SURFACE_IMPL_X11 (impl)->xid, + visual, + width, height); +} + +static cairo_surface_t * +gdk_x11_ref_cairo_surface (GdkSurface *window) +{ + GdkSurfaceImplX11 *impl = GDK_SURFACE_IMPL_X11 (window->impl); + + if (GDK_SURFACE_DESTROYED (window)) + return NULL; + + if (!impl->cairo_surface) + { + impl->cairo_surface = gdk_x11_create_cairo_surface (impl, + gdk_surface_get_width (window) * impl->window_scale, + gdk_surface_get_height (window) * impl->window_scale); + cairo_surface_set_device_scale (impl->cairo_surface, impl->window_scale, impl->window_scale); + + if (WINDOW_IS_TOPLEVEL (window) && impl->toplevel->in_frame) + hook_surface_changed (window); + } + + cairo_surface_reference (impl->cairo_surface); + + return impl->cairo_surface; +} + +static void +gdk_surface_impl_x11_finalize (GObject *object) +{ + GdkSurface *wrapper; + GdkSurfaceImplX11 *impl; + + g_return_if_fail (GDK_IS_SURFACE_IMPL_X11 (object)); + + impl = GDK_SURFACE_IMPL_X11 (object); + + wrapper = impl->wrapper; + + if (WINDOW_IS_TOPLEVEL (wrapper) && impl->toplevel->in_frame) + unhook_surface_changed (wrapper); + + _gdk_x11_surface_grab_check_destroy (wrapper); + + if (!GDK_SURFACE_DESTROYED (wrapper)) + { + GdkDisplay *display = GDK_SURFACE_DISPLAY (wrapper); + + _gdk_x11_display_remove_window (display, impl->xid); + if (impl->toplevel && impl->toplevel->focus_window) + _gdk_x11_display_remove_window (display, impl->toplevel->focus_window); + } + + g_free (impl->toplevel); + + if (impl->cursor) + g_object_unref (impl->cursor); + + G_OBJECT_CLASS (gdk_surface_impl_x11_parent_class)->finalize (object); +} + +typedef struct { + GdkDisplay *display; + Pixmap pixmap; +} FreePixmapData; + +static void +free_pixmap (gpointer datap) +{ + FreePixmapData *data = datap; + + if (!gdk_display_is_closed (data->display)) + { + XFreePixmap (GDK_DISPLAY_XDISPLAY (data->display), + data->pixmap); + } + + g_object_unref (data->display); + g_slice_free (FreePixmapData, data); +} + +static void +attach_free_pixmap_handler (cairo_surface_t *surface, + GdkDisplay *display, + Pixmap pixmap) +{ + static const cairo_user_data_key_t key; + FreePixmapData *data; + + data = g_slice_new (FreePixmapData); + data->display = g_object_ref (display); + data->pixmap = pixmap; + + cairo_surface_set_user_data (surface, &key, data, free_pixmap); +} + +/* Cairo does not guarantee we get an xlib surface if we call + * cairo_surface_create_similar(). In some cases however, we must use a + * pixmap or bitmap in the X11 API. + * These functions ensure an Xlib surface. + */ +cairo_surface_t * +_gdk_x11_display_create_bitmap_surface (GdkDisplay *display, + int width, + int height) +{ + cairo_surface_t *surface; + Pixmap pixmap; + + pixmap = XCreatePixmap (GDK_DISPLAY_XDISPLAY (display), + GDK_SCREEN_XROOTWIN (GDK_X11_DISPLAY (display)->screen), + width, height, 1); + surface = cairo_xlib_surface_create_for_bitmap (GDK_DISPLAY_XDISPLAY (display), + pixmap, + GDK_X11_SCREEN (GDK_X11_DISPLAY (display)->screen)->xscreen, + width, height); + attach_free_pixmap_handler (surface, display, pixmap); + + return surface; +} + +/* Create a surface backed with a pixmap without alpha on the same screen as window */ +static cairo_surface_t * +gdk_x11_surface_create_pixmap_surface (GdkSurface *window, + int width, + int height) +{ + GdkDisplay *display; + Display *dpy; + cairo_surface_t *surface; + Pixmap pixmap; + + display = gdk_surface_get_display (window); + dpy = GDK_DISPLAY_XDISPLAY (display); + + pixmap = XCreatePixmap (dpy, + GDK_SURFACE_XID (window), + width, height, + DefaultDepth (dpy, DefaultScreen (dpy))); + surface = cairo_xlib_surface_create (dpy, + pixmap, + DefaultVisual (dpy, DefaultScreen (dpy)), + width, height); + attach_free_pixmap_handler (surface, display, pixmap); + + return surface; +} + +static void +set_wm_protocols (GdkSurface *window) +{ + GdkDisplay *display = gdk_surface_get_display (window); + Atom protocols[4]; + int n = 0; + + protocols[n++] = gdk_x11_get_xatom_by_name_for_display (display, "WM_DELETE_WINDOW"); + protocols[n++] = gdk_x11_get_xatom_by_name_for_display (display, "WM_TAKE_FOCUS"); + protocols[n++] = gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_PING"); + +#ifdef HAVE_XSYNC + if (GDK_X11_DISPLAY (display)->use_sync) + protocols[n++] = gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_SYNC_REQUEST"); +#endif + + XSetWMProtocols (GDK_DISPLAY_XDISPLAY (display), GDK_SURFACE_XID (window), protocols, n); +} + +static const gchar * +get_default_title (void) +{ + const char *title; + + title = g_get_application_name (); + if (!title) + title = g_get_prgname (); + if (!title) + title = ""; + + return title; +} + +static void +check_leader_window_title (GdkDisplay *display) +{ + GdkX11Display *display_x11 = GDK_X11_DISPLAY (display); + + if (display_x11->leader_window && !display_x11->leader_window_title_set) + { + set_wm_name (display, + display_x11->leader_window, + get_default_title ()); + + display_x11->leader_window_title_set = TRUE; + } +} + +static Window +create_focus_window (GdkDisplay *display, + XID parent) +{ + GdkX11Display *display_x11; + GdkEventMask event_mask; + Display *xdisplay; + Window focus_window; + XSetWindowAttributes attrs; + + xdisplay = GDK_DISPLAY_XDISPLAY (display); + display_x11 = GDK_X11_DISPLAY (display); + + focus_window = XCreateWindow (xdisplay, parent, + -1, -1, 1, 1, 0, + 0, /* depth */ + InputOnly, + CopyFromParent, + 0, &attrs); + + event_mask = (GDK_KEY_PRESS_MASK | + GDK_KEY_RELEASE_MASK | + GDK_FOCUS_CHANGE_MASK); + + gdk_x11_event_source_select_events ((GdkEventSource *) display_x11->event_source, + focus_window, + event_mask, 0); + + XMapWindow (xdisplay, focus_window); + + return focus_window; +} + +static void +ensure_sync_counter (GdkSurface *window) +{ +#ifdef HAVE_XSYNC + if (!GDK_SURFACE_DESTROYED (window)) + { + GdkDisplay *display = GDK_SURFACE_DISPLAY (window); + GdkToplevelX11 *toplevel = _gdk_x11_surface_get_toplevel (window); + + 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_SURFACE_XID (window), + atom, XA_CARDINAL, + 32, PropModeReplace, + (guchar *)counters, 2); + + toplevel->current_counter_value = 0; + } + } +#endif +} + +static void +setup_toplevel_window (GdkSurface *window, + GdkX11Screen *x11_screen) +{ + GdkToplevelX11 *toplevel = _gdk_x11_surface_get_toplevel (window); + GdkSurfaceImplX11 *impl = GDK_SURFACE_IMPL_X11 (window->impl); + GdkDisplay *display = gdk_surface_get_display (window); + Display *xdisplay = GDK_SURFACE_XDISPLAY (window); + XID xid = GDK_SURFACE_XID (window); + XSizeHints size_hints; + long pid; + Window leader_window; + + set_wm_protocols (window); + + if (!window->input_only) + { + /* The focus window is off the visible area, and serves to receive key + * press events so they don't get sent to child windows. + */ + toplevel->focus_window = create_focus_window (display, xid); + _gdk_x11_display_add_window (x11_screen->display, + &toplevel->focus_window, + window); + } + + check_leader_window_title (x11_screen->display); + + /* FIXME: Is there any point in doing this? Do any WM's pay + * attention to PSize, and even if they do, is this the + * correct value??? + */ + size_hints.flags = PSize; + size_hints.width = window->width * impl->window_scale; + size_hints.height = window->height * impl->window_scale; + + XSetWMNormalHints (xdisplay, xid, &size_hints); + + /* This will set WM_CLIENT_MACHINE and WM_LOCALE_NAME */ + XSetWMProperties (xdisplay, xid, NULL, NULL, NULL, 0, NULL, NULL, NULL); + + pid = getpid (); + XChangeProperty (xdisplay, xid, + gdk_x11_get_xatom_by_name_for_display (x11_screen->display, "_NET_WM_PID"), + XA_CARDINAL, 32, + PropModeReplace, + (guchar *)&pid, 1); + + leader_window = GDK_X11_DISPLAY (x11_screen->display)->leader_window; + if (!leader_window) + leader_window = xid; + XChangeProperty (xdisplay, xid, + gdk_x11_get_xatom_by_name_for_display (x11_screen->display, "WM_CLIENT_LEADER"), + XA_WINDOW, 32, PropModeReplace, + (guchar *) &leader_window, 1); + + if (toplevel->focus_window != None) + XChangeProperty (xdisplay, xid, + gdk_x11_get_xatom_by_name_for_display (x11_screen->display, "_NET_WM_USER_TIME_WINDOW"), + XA_WINDOW, 32, PropModeReplace, + (guchar *) &toplevel->focus_window, 1); + + if (!window->focus_on_map) + gdk_x11_surface_set_user_time (window, 0); + else if (GDK_X11_DISPLAY (x11_screen->display)->user_time != 0) + gdk_x11_surface_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_surface_begin_frame (window, TRUE); +} + +static void +on_frame_clock_before_paint (GdkFrameClock *clock, + GdkSurface *window) +{ + gdk_x11_surface_predict_presentation_time (window); + gdk_x11_surface_begin_frame (window, FALSE); +} + +static void +on_frame_clock_after_paint (GdkFrameClock *clock, + GdkSurface *window) +{ + gdk_x11_surface_end_frame (window); + +} + +static void +connect_frame_clock (GdkSurface *window) +{ + GdkSurfaceImplX11 *impl; + + impl = GDK_SURFACE_IMPL_X11 (window->impl); + if (WINDOW_IS_TOPLEVEL (window) && !impl->frame_clock_connected) + { + GdkFrameClock *frame_clock = gdk_surface_get_frame_clock (window); + + g_signal_connect (frame_clock, "before-paint", + G_CALLBACK (on_frame_clock_before_paint), window); + g_signal_connect (frame_clock, "after-paint", + G_CALLBACK (on_frame_clock_after_paint), window); + + impl->frame_clock_connected = TRUE; + } +} + +void +_gdk_x11_display_create_window_impl (GdkDisplay *display, + GdkSurface *window, + GdkSurface *real_parent, + GdkEventMask event_mask, + GdkSurfaceAttr *attributes) +{ + GdkSurfaceImplX11 *impl; + GdkX11Screen *x11_screen; + GdkX11Display *display_x11; + + Window xparent; + Visual *xvisual; + Display *xdisplay; + + XSetWindowAttributes xattributes; + long xattributes_mask; + XClassHint *class_hint; + + unsigned int class; + int depth; + + int abs_x; + int abs_y; + + display_x11 = GDK_X11_DISPLAY (display); + x11_screen = GDK_X11_SCREEN (display_x11->screen); + if (real_parent) + xparent = GDK_SURFACE_XID (real_parent); + else + xparent = GDK_SCREEN_XROOTWIN (x11_screen); + + impl = g_object_new (GDK_TYPE_SURFACE_IMPL_X11, NULL); + window->impl = GDK_SURFACE_IMPL (impl); + impl->wrapper = GDK_SURFACE (window); + impl->window_scale = x11_screen->window_scale; + + xdisplay = x11_screen->xdisplay; + + xattributes_mask = 0; + + xvisual = gdk_x11_display_get_window_visual (display_x11); + + impl->override_redirect = FALSE; + + /* Sanity checks */ + switch (window->window_type) + { + case GDK_SURFACE_TOPLEVEL: + case GDK_SURFACE_TEMP: + if (window->parent) + { + /* The common code warns for this case */ + xparent = GDK_SCREEN_XROOTWIN (x11_screen); + } + break; + + case GDK_SURFACE_CHILD: + default: + g_assert_not_reached (); + break; + } + + if (!window->input_only) + { + class = InputOutput; + + xattributes.background_pixel = BlackPixel (xdisplay, x11_screen->screen_num); + + xattributes.border_pixel = BlackPixel (xdisplay, x11_screen->screen_num); + xattributes_mask |= CWBorderPixel | CWBackPixel; + + xattributes.bit_gravity = NorthWestGravity; + xattributes_mask |= CWBitGravity; + + xattributes.colormap = gdk_x11_display_get_window_colormap (display_x11); + xattributes_mask |= CWColormap; + + if (window->window_type == GDK_SURFACE_TEMP) + { + xattributes.save_under = True; + xattributes.override_redirect = True; + xattributes.cursor = None; + xattributes_mask |= CWSaveUnder | CWOverrideRedirect; + + impl->override_redirect = TRUE; + } + + depth = gdk_x11_display_get_window_depth (display_x11); + } + else + { + class = InputOnly; + + if (window->window_type == GDK_SURFACE_TEMP) + { + xattributes.override_redirect = True; + xattributes_mask |= CWOverrideRedirect; + + impl->override_redirect = TRUE; + } + + depth = 0; + } + + if (window->width * impl->window_scale > 32767 || + window->height * impl->window_scale > 32767) + { + g_warning ("Native Windows wider or taller than 32767 pixels are not supported"); + + if (window->width * impl->window_scale > 32767) + window->width = 32767 / impl->window_scale; + if (window->height * impl->window_scale > 32767) + window->height = 32767 / impl->window_scale; + } + + impl->unscaled_width = window->width * impl->window_scale; + impl->unscaled_height = window->height * impl->window_scale; + + if (window->parent) + { + abs_x = window->parent->abs_x; + abs_y = window->parent->abs_y; + } + else + { + abs_x = 0; + abs_y = 0; + } + + impl->xid = XCreateWindow (xdisplay, xparent, + (window->x + abs_x) * impl->window_scale, + (window->y + abs_y) * impl->window_scale, + window->width * impl->window_scale, window->height * impl->window_scale, + 0, depth, class, xvisual, + xattributes_mask, &xattributes); + + g_object_ref (window); + _gdk_x11_display_add_window (x11_screen->display, &impl->xid, window); + + switch (GDK_SURFACE_TYPE (window)) + { + case GDK_SURFACE_TOPLEVEL: + case GDK_SURFACE_TEMP: + gdk_surface_set_title (window, get_default_title ()); + + class_hint = XAllocClassHint (); + class_hint->res_name = (char *) g_get_prgname (); + class_hint->res_class = (char *) display_x11->program_class; + XSetClassHint (xdisplay, impl->xid, class_hint); + XFree (class_hint); + + setup_toplevel_window (window, x11_screen); + break; + + case GDK_SURFACE_CHILD: + default: + break; + } + + gdk_x11_event_source_select_events ((GdkEventSource *) display_x11->event_source, + GDK_SURFACE_XID (window), event_mask, + StructureNotifyMask | PropertyChangeMask); + + connect_frame_clock (window); + + gdk_surface_freeze_toplevel_updates (window); +} + +static GdkEventMask +x_event_mask_to_gdk_event_mask (long mask) +{ + GdkEventMask event_mask = 0; + int i; + + for (i = 0; i < _gdk_x11_event_mask_table_size; i++) + { + if (mask & _gdk_x11_event_mask_table[i]) + event_mask |= 1 << (i + 1); + } + + return event_mask; +} + +/** + * gdk_x11_surface_foreign_new_for_display: + * @display: (type GdkX11Display): the #GdkDisplay where the window handle comes from. + * @window: an Xlib Window + * + * Wraps a native window in a #GdkSurface. The function will try to + * look up the window using gdk_x11_surface_lookup_for_display() first. + * If it does not find it there, it will create a new window. + * + * This may fail if the window has been destroyed. If the window + * was already known to GDK, a new reference to the existing + * #GdkSurface is returned. + * + * Returns: (transfer full): a #GdkSurface wrapper for the native + * window, or %NULL if the window has been destroyed. The wrapper + * will be newly created, if one doesn’t exist already. + */ +GdkSurface * +gdk_x11_surface_foreign_new_for_display (GdkDisplay *display, + Window window) +{ + GdkX11Screen *screen; + GdkSurface *win; + GdkSurfaceImplX11 *impl; + GdkX11Display *display_x11; + XWindowAttributes attrs; + Window root, parent; + Window *children = NULL; + guint nchildren; + gboolean result; + + g_return_val_if_fail (GDK_IS_DISPLAY (display), NULL); + + display_x11 = GDK_X11_DISPLAY (display); + + if ((win = gdk_x11_surface_lookup_for_display (display, window)) != NULL) + return g_object_ref (win); + + gdk_x11_display_error_trap_push (display); + result = XGetWindowAttributes (display_x11->xdisplay, window, &attrs); + if (gdk_x11_display_error_trap_pop (display) || !result) + return NULL; + + /* FIXME: This is pretty expensive. + * Maybe the caller should supply the parent + */ + gdk_x11_display_error_trap_push (display); + result = XQueryTree (display_x11->xdisplay, window, &root, &parent, &children, &nchildren); + if (gdk_x11_display_error_trap_pop (display) || !result) + return NULL; + + if (children) + XFree (children); + + screen = _gdk_x11_display_screen_for_xrootwin (display, root); + if (screen == NULL) + return NULL; + + win = _gdk_display_create_window (display); + win->impl = g_object_new (GDK_TYPE_SURFACE_IMPL_X11, NULL); + win->impl_window = win; + + impl = GDK_SURFACE_IMPL_X11 (win->impl); + impl->wrapper = win; + impl->window_scale = GDK_X11_SCREEN (screen)->window_scale; + + /* Always treat foreigns as toplevels */ + win->parent = NULL; + + impl->xid = window; + + win->x = attrs.x / impl->window_scale; + win->y = attrs.y / impl->window_scale; + impl->unscaled_width = attrs.width; + impl->unscaled_height = attrs.height; + win->width = attrs.width / impl->window_scale; + win->height = attrs.height / impl->window_scale; + win->window_type = GDK_SURFACE_FOREIGN; + win->destroyed = FALSE; + + win->event_mask = x_event_mask_to_gdk_event_mask (attrs.your_event_mask); + + if (attrs.map_state == IsUnmapped) + win->state = GDK_SURFACE_STATE_WITHDRAWN; + else + win->state = 0; + win->viewable = TRUE; + + g_object_ref (win); + _gdk_x11_display_add_window (display, &GDK_SURFACE_XID (win), win); + + /* Update the clip region, etc */ + _gdk_surface_update_size (win); + + return win; +} + +static void +gdk_toplevel_x11_free_contents (GdkDisplay *display, + GdkToplevelX11 *toplevel) +{ + if (toplevel->icon_pixmap) + { + cairo_surface_destroy (toplevel->icon_pixmap); + toplevel->icon_pixmap = NULL; + } + if (toplevel->icon_mask) + { + cairo_surface_destroy (toplevel->icon_mask); + toplevel->icon_mask = NULL; + } + if (toplevel->group_leader) + { + g_object_unref (toplevel->group_leader); + toplevel->group_leader = NULL; + } +#ifdef HAVE_XSYNC + if (toplevel->update_counter != None) + { + XSyncDestroyCounter (GDK_DISPLAY_XDISPLAY (display), + toplevel->update_counter); + XSyncDestroyCounter (GDK_DISPLAY_XDISPLAY (display), + toplevel->extended_update_counter); + toplevel->update_counter = None; + toplevel->extended_update_counter = None; + + toplevel->current_counter_value = 0; + } +#endif +} + +static void +gdk_x11_surface_destroy (GdkSurface *window, + gboolean recursing, + gboolean foreign_destroy) +{ + GdkSurfaceImplX11 *impl = GDK_SURFACE_IMPL_X11 (window->impl); + GdkToplevelX11 *toplevel; + + g_return_if_fail (GDK_IS_SURFACE (window)); + + toplevel = _gdk_x11_surface_get_toplevel (window); + if (toplevel) + gdk_toplevel_x11_free_contents (GDK_SURFACE_DISPLAY (window), toplevel); + + unhook_surface_changed (window); + + if (impl->cairo_surface) + { + cairo_surface_finish (impl->cairo_surface); + cairo_surface_destroy (impl->cairo_surface); + impl->cairo_surface = NULL; + } + + if (!recursing && !foreign_destroy) + XDestroyWindow (GDK_SURFACE_XDISPLAY (window), GDK_SURFACE_XID (window)); +} + +/* This function is called when the XWindow is really gone. + */ +static void +gdk_x11_surface_destroy_notify (GdkSurface *window) +{ + GdkSurfaceImplX11 *window_impl; + + window_impl = GDK_SURFACE_IMPL_X11 ((window)->impl); + + if (!GDK_SURFACE_DESTROYED (window)) + { + if (GDK_SURFACE_TYPE(window) != GDK_SURFACE_FOREIGN) + g_warning ("GdkSurface %#lx unexpectedly destroyed", GDK_SURFACE_XID (window)); + + _gdk_surface_destroy (window, TRUE); + } + + _gdk_x11_display_remove_window (GDK_SURFACE_DISPLAY (window), GDK_SURFACE_XID (window)); + if (window_impl->toplevel && window_impl->toplevel->focus_window) + _gdk_x11_display_remove_window (GDK_SURFACE_DISPLAY (window), window_impl->toplevel->focus_window); + + _gdk_x11_surface_grab_check_destroy (window); + + g_object_unref (window); +} + +static void +update_wm_hints (GdkSurface *window, + gboolean force) +{ + GdkToplevelX11 *toplevel = _gdk_x11_surface_get_toplevel (window); + GdkDisplay *display = GDK_SURFACE_DISPLAY (window); + XWMHints wm_hints; + + if (!force && + !toplevel->is_leader && + window->state & GDK_SURFACE_STATE_WITHDRAWN) + return; + + wm_hints.flags = StateHint | InputHint; + wm_hints.input = window->accept_focus ? True : False; + wm_hints.initial_state = NormalState; + + if (window->state & GDK_SURFACE_STATE_ICONIFIED) + { + wm_hints.flags |= StateHint; + wm_hints.initial_state = IconicState; + } + + if (toplevel->icon_pixmap) + { + wm_hints.flags |= IconPixmapHint; + wm_hints.icon_pixmap = cairo_xlib_surface_get_drawable (toplevel->icon_pixmap); + } + + if (toplevel->icon_mask) + { + wm_hints.flags |= IconMaskHint; + wm_hints.icon_mask = cairo_xlib_surface_get_drawable (toplevel->icon_mask); + } + + wm_hints.flags |= WindowGroupHint; + if (toplevel->group_leader && !GDK_SURFACE_DESTROYED (toplevel->group_leader)) + { + wm_hints.flags |= WindowGroupHint; + wm_hints.window_group = GDK_SURFACE_XID (toplevel->group_leader); + } + else + wm_hints.window_group = GDK_X11_DISPLAY (display)->leader_window; + + if (toplevel->urgency_hint) + wm_hints.flags |= XUrgencyHint; + + XSetWMHints (GDK_SURFACE_XDISPLAY (window), + GDK_SURFACE_XID (window), + &wm_hints); +} + +static void +set_initial_hints (GdkSurface *window) +{ + GdkDisplay *display = GDK_SURFACE_DISPLAY (window); + Display *xdisplay = GDK_DISPLAY_XDISPLAY (display); + Window xwindow = GDK_SURFACE_XID (window); + GdkToplevelX11 *toplevel; + Atom atoms[9]; + gint i; + + toplevel = _gdk_x11_surface_get_toplevel (window); + + if (!toplevel) + return; + + update_wm_hints (window, TRUE); + + /* We set the spec hints regardless of whether the spec is supported, + * since it can't hurt and it's kind of expensive to check whether + * it's supported. + */ + + i = 0; + + if (window->state & GDK_SURFACE_STATE_MAXIMIZED) + { + atoms[i] = gdk_x11_get_xatom_by_name_for_display (display, + "_NET_WM_STATE_MAXIMIZED_VERT"); + ++i; + atoms[i] = gdk_x11_get_xatom_by_name_for_display (display, + "_NET_WM_STATE_MAXIMIZED_HORZ"); + ++i; + toplevel->have_maxhorz = toplevel->have_maxvert = TRUE; + } + + if (window->state & GDK_SURFACE_STATE_ABOVE) + { + atoms[i] = gdk_x11_get_xatom_by_name_for_display (display, + "_NET_WM_STATE_ABOVE"); + ++i; + } + + if (window->state & GDK_SURFACE_STATE_BELOW) + { + atoms[i] = gdk_x11_get_xatom_by_name_for_display (display, + "_NET_WM_STATE_BELOW"); + ++i; + } + + if (window->state & GDK_SURFACE_STATE_STICKY) + { + atoms[i] = gdk_x11_get_xatom_by_name_for_display (display, + "_NET_WM_STATE_STICKY"); + ++i; + toplevel->have_sticky = TRUE; + } + + if (window->state & GDK_SURFACE_STATE_FULLSCREEN) + { + atoms[i] = gdk_x11_get_xatom_by_name_for_display (display, + "_NET_WM_STATE_FULLSCREEN"); + ++i; + toplevel->have_fullscreen = TRUE; + } + + if (window->modal_hint) + { + atoms[i] = gdk_x11_get_xatom_by_name_for_display (display, + "_NET_WM_STATE_MODAL"); + ++i; + } + + if (toplevel->skip_taskbar_hint) + { + atoms[i] = gdk_x11_get_xatom_by_name_for_display (display, + "_NET_WM_STATE_SKIP_TASKBAR"); + ++i; + } + + if (toplevel->skip_pager_hint) + { + atoms[i] = gdk_x11_get_xatom_by_name_for_display (display, + "_NET_WM_STATE_SKIP_PAGER"); + ++i; + } + + if (window->state & GDK_SURFACE_STATE_ICONIFIED) + { + atoms[i] = gdk_x11_get_xatom_by_name_for_display (display, + "_NET_WM_STATE_HIDDEN"); + ++i; + toplevel->have_hidden = TRUE; + } + + if (i > 0) + { + XChangeProperty (xdisplay, + xwindow, + gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_STATE"), + XA_ATOM, 32, PropModeReplace, + (guchar*) atoms, i); + } + else + { + XDeleteProperty (xdisplay, + xwindow, + gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_STATE")); + } + + if (window->state & GDK_SURFACE_STATE_STICKY) + { + atoms[0] = 0xFFFFFFFF; + XChangeProperty (xdisplay, + xwindow, + gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_DESKTOP"), + XA_CARDINAL, 32, PropModeReplace, + (guchar*) atoms, 1); + toplevel->on_all_desktops = TRUE; + } + else + { + XDeleteProperty (xdisplay, + xwindow, + gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_DESKTOP")); + } + + toplevel->map_serial = NextRequest (xdisplay); +} + +static void +gdk_surface_x11_show (GdkSurface *window, gboolean already_mapped) +{ + GdkDisplay *display; + GdkX11Display *display_x11; + GdkToplevelX11 *toplevel; + Display *xdisplay = GDK_SURFACE_XDISPLAY (window); + Window xwindow = GDK_SURFACE_XID (window); + + if (!already_mapped) + set_initial_hints (window); + + if (WINDOW_IS_TOPLEVEL (window)) + { + display = gdk_surface_get_display (window); + display_x11 = GDK_X11_DISPLAY (display); + toplevel = _gdk_x11_surface_get_toplevel (window); + + if (toplevel->user_time != 0 && + display_x11->user_time != 0 && + XSERVER_TIME_IS_LATER (display_x11->user_time, toplevel->user_time)) + gdk_x11_surface_set_user_time (window, display_x11->user_time); + } + + XMapWindow (xdisplay, xwindow); + + /* 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_surface_apply_fullscreen_mode (window); +} + +static void +gdk_surface_x11_hide (GdkSurface *window) +{ + /* We'll get the unmap notify eventually, and handle it then, + * but checking here makes things more consistent if we are + * just doing stuff ourself. + */ + _gdk_x11_surface_grab_check_unmap (window, + NextRequest (GDK_SURFACE_XDISPLAY (window))); + + /* You can't simply unmap toplevel windows. */ + switch (window->window_type) + { + case GDK_SURFACE_TOPLEVEL: + case GDK_SURFACE_TEMP: /* ? */ + gdk_surface_withdraw (window); + return; + + case GDK_SURFACE_FOREIGN: + case GDK_SURFACE_CHILD: + default: + break; + } + + _gdk_surface_clear_update_area (window); + + XUnmapWindow (GDK_SURFACE_XDISPLAY (window), + GDK_SURFACE_XID (window)); +} + +static void +gdk_surface_x11_withdraw (GdkSurface *window) +{ + if (!window->destroyed) + { + if (GDK_SURFACE_IS_MAPPED (window)) + gdk_synthesize_window_state (window, + 0, + GDK_SURFACE_STATE_WITHDRAWN); + + g_assert (!GDK_SURFACE_IS_MAPPED (window)); + + XWithdrawWindow (GDK_SURFACE_XDISPLAY (window), + GDK_SURFACE_XID (window), 0); + } +} + +static inline void +window_x11_move (GdkSurface *window, + gint x, + gint y) +{ + GdkSurfaceImplX11 *impl = GDK_SURFACE_IMPL_X11 (window->impl); + + XMoveWindow (GDK_SURFACE_XDISPLAY (window), + GDK_SURFACE_XID (window), + x * impl->window_scale, y * impl->window_scale); + + if (impl->override_redirect) + { + window->x = x; + window->y = y; + } +} + +static inline void +window_x11_resize (GdkSurface *window, + gint width, + gint height) +{ + GdkSurfaceImplX11 *impl = GDK_SURFACE_IMPL_X11 (window->impl); + + if (width < 1) + width = 1; + + if (height < 1) + height = 1; + + window_pre_damage (window); + + XResizeWindow (GDK_SURFACE_XDISPLAY (window), + GDK_SURFACE_XID (window), + width * impl->window_scale, height * impl->window_scale); + + if (impl->override_redirect) + { + impl->unscaled_width = width * impl->window_scale; + impl->unscaled_height = height * impl->window_scale; + window->width = width; + window->height = height; + _gdk_x11_surface_update_size (GDK_SURFACE_IMPL_X11 (window->impl)); + } + else + { + if (width * impl->window_scale != impl->unscaled_width || height * impl->window_scale != impl->unscaled_height) + window->resize_count += 1; + } +} + +static inline void +window_x11_move_resize (GdkSurface *window, + gint x, + gint y, + gint width, + gint height) +{ + GdkSurfaceImplX11 *impl = GDK_SURFACE_IMPL_X11 (window->impl); + + if (width < 1) + width = 1; + + if (height < 1) + height = 1; + + window_pre_damage (window); + + XMoveResizeWindow (GDK_SURFACE_XDISPLAY (window), + GDK_SURFACE_XID (window), + x * impl->window_scale, y * impl->window_scale, + width * impl->window_scale, height * impl->window_scale); + + if (impl->override_redirect) + { + window->x = x; + window->y = y; + + impl->unscaled_width = width * impl->window_scale; + impl->unscaled_height = height * impl->window_scale; + window->width = width; + window->height = height; + + _gdk_x11_surface_update_size (GDK_SURFACE_IMPL_X11 (window->impl)); + } + else + { + if (width * impl->window_scale != impl->unscaled_width || height * impl->window_scale != impl->unscaled_height) + window->resize_count += 1; + } +} + +static void +gdk_surface_x11_move_resize (GdkSurface *window, + gboolean with_move, + gint x, + gint y, + gint width, + gint height) +{ + if (with_move && (width < 0 && height < 0)) + window_x11_move (window, x, y); + else + { + if (with_move) + window_x11_move_resize (window, x, y, width, height); + else + window_x11_resize (window, width, height); + } +} + +void +_gdk_x11_surface_set_window_scale (GdkSurface *window, + int scale) +{ + GdkSurfaceImplX11 *impl; + GdkToplevelX11 *toplevel; + GdkSurfaceHints geom_mask; + + impl = GDK_SURFACE_IMPL_X11 (window->impl); + + impl->window_scale = scale; + if (impl->cairo_surface) + cairo_surface_set_device_scale (impl->cairo_surface, impl->window_scale, impl->window_scale); + _gdk_surface_update_size (window); + + toplevel = _gdk_x11_surface_get_toplevel (window); + if (toplevel && window->window_type != GDK_SURFACE_FOREIGN) + { + /* These are affected by window scale: */ + geom_mask = toplevel->last_geometry_hints_mask & + (GDK_HINT_MIN_SIZE | GDK_HINT_MAX_SIZE | GDK_HINT_BASE_SIZE | GDK_HINT_RESIZE_INC); + if (geom_mask) + gdk_surface_set_geometry_hints (window, + &toplevel->last_geometry_hints, + geom_mask); + } + + if (window->window_type == GDK_SURFACE_FOREIGN) + XMoveWindow (GDK_SURFACE_XDISPLAY (window), + GDK_SURFACE_XID (window), + window->x * impl->window_scale, + window->y * impl->window_scale); + else + { + if (impl->override_redirect) + { + impl->unscaled_width = window->width * impl->window_scale; + impl->unscaled_height = window->height * impl->window_scale; + } + + XResizeWindow (GDK_SURFACE_XDISPLAY (window), + GDK_SURFACE_XID (window), + window->width * impl->window_scale, + window->height * impl->window_scale); + } + + gdk_surface_invalidate_rect (window, NULL, TRUE); +} + +static void +gdk_surface_x11_raise (GdkSurface *window) +{ + XRaiseWindow (GDK_SURFACE_XDISPLAY (window), GDK_SURFACE_XID (window)); +} + +static void +gdk_surface_x11_restack_toplevel (GdkSurface *window, + GdkSurface *sibling, + gboolean above) +{ + XWindowChanges changes; + + changes.sibling = GDK_SURFACE_XID (sibling); + changes.stack_mode = above ? Above : Below; + XReconfigureWMWindow (GDK_SURFACE_XDISPLAY (window), + GDK_SURFACE_XID (window), + gdk_x11_screen_get_screen_number (GDK_SURFACE_SCREEN (window)), + CWStackMode | CWSibling, &changes); +} + +static void +gdk_surface_x11_lower (GdkSurface *window) +{ + XLowerWindow (GDK_SURFACE_XDISPLAY (window), GDK_SURFACE_XID (window)); +} + +/** + * gdk_x11_surface_move_to_current_desktop: + * @window: (type GdkX11Surface): a #GdkSurface + * + * Moves the window to the correct workspace when running under a + * window manager that supports multiple workspaces, as described + * in the [Extended Window Manager Hints](http://www.freedesktop.org/Standards/wm-spec) specification. + * Will not do anything if the window is already on all workspaces. + */ +void +gdk_x11_surface_move_to_current_desktop (GdkSurface *window) +{ + GdkToplevelX11 *toplevel; + + g_return_if_fail (GDK_IS_SURFACE (window)); + g_return_if_fail (GDK_SURFACE_TYPE (window) != GDK_SURFACE_CHILD); + + toplevel = _gdk_x11_surface_get_toplevel (window); + + if (toplevel->on_all_desktops) + return; + + move_to_current_desktop (window); +} + +static void +move_to_current_desktop (GdkSurface *window) +{ + guint32 desktop; + + desktop = gdk_x11_screen_get_current_desktop (GDK_SURFACE_SCREEN (window)); + gdk_x11_surface_move_to_desktop (window, desktop); +} + +static guint32 +get_netwm_cardinal_property (GdkSurface *window, + const gchar *name) +{ + GdkX11Screen *x11_screen = GDK_SURFACE_SCREEN (window); + GdkAtom atom; + guint32 prop = 0; + Atom type; + gint format; + gulong nitems; + gulong bytes_after; + guchar *data; + + atom = g_intern_static_string (name); + + if (!gdk_x11_screen_supports_net_wm_hint (x11_screen, atom)) + return 0; + + XGetWindowProperty (x11_screen->xdisplay, + GDK_SURFACE_XID (window), + gdk_x11_get_xatom_by_name_for_display (GDK_SURFACE_DISPLAY (window), name), + 0, G_MAXLONG, + False, XA_CARDINAL, &type, &format, &nitems, + &bytes_after, &data); + if (type == XA_CARDINAL) + { + prop = *(gulong *)data; + XFree (data); + } + + return prop; +} + +/** + * gdk_x11_surface_get_desktop: + * @window: (type GdkX11Surface): a #GdkSurface + * + * Gets the number of the workspace @window is on. + * + * Returns: the current workspace of @window + */ +guint32 +gdk_x11_surface_get_desktop (GdkSurface *window) +{ + g_return_val_if_fail (GDK_IS_SURFACE (window), 0); + + return get_netwm_cardinal_property (window, "_NET_WM_DESKTOP"); +} + +/** + * gdk_x11_surface_move_to_desktop: + * @window: (type GdkX11Surface): a #GdkSurface + * @desktop: the number of the workspace to move the window to + * + * Moves the window to the given workspace when running unde a + * window manager that supports multiple workspaces, as described + * in the [Extended Window Manager Hints](http://www.freedesktop.org/Standards/wm-spec) specification. + */ +void +gdk_x11_surface_move_to_desktop (GdkSurface *window, + guint32 desktop) +{ + GdkAtom atom; + XClientMessageEvent xclient; + + g_return_if_fail (GDK_IS_SURFACE (window)); + + atom = g_intern_static_string ("_NET_WM_DESKTOP"); + if (!gdk_x11_screen_supports_net_wm_hint (GDK_SURFACE_SCREEN (window), atom)) + return; + + memset (&xclient, 0, sizeof (xclient)); + xclient.type = ClientMessage; + xclient.serial = 0; + xclient.send_event = True; + xclient.window = GDK_SURFACE_XID (window); + xclient.message_type = gdk_x11_atom_to_xatom_for_display (GDK_SURFACE_DISPLAY (window), atom); + xclient.format = 32; + + xclient.data.l[0] = desktop; + xclient.data.l[1] = 1; /* source indication */ + xclient.data.l[2] = 0; + xclient.data.l[3] = 0; + xclient.data.l[4] = 0; + + XSendEvent (GDK_SURFACE_XDISPLAY (window), + GDK_SURFACE_XROOTWIN (window), + False, + SubstructureRedirectMask | SubstructureNotifyMask, + (XEvent *)&xclient); +} + +static void +gdk_x11_surface_focus (GdkSurface *window, + guint32 timestamp) +{ + GdkDisplay *display; + + g_return_if_fail (GDK_IS_SURFACE (window)); + + if (GDK_SURFACE_DESTROYED (window) || + !WINDOW_IS_TOPLEVEL_OR_FOREIGN (window)) + return; + + display = GDK_SURFACE_DISPLAY (window); + + if (gdk_x11_screen_supports_net_wm_hint (GDK_SURFACE_SCREEN (window), + g_intern_static_string ("_NET_ACTIVE_WINDOW"))) + { + XClientMessageEvent xclient; + + memset (&xclient, 0, sizeof (xclient)); + xclient.type = ClientMessage; + xclient.window = GDK_SURFACE_XID (window); + xclient.message_type = gdk_x11_get_xatom_by_name_for_display (display, + "_NET_ACTIVE_WINDOW"); + xclient.format = 32; + xclient.data.l[0] = 1; /* requestor type; we're an app */ + xclient.data.l[1] = timestamp; + xclient.data.l[2] = None; /* currently active window */ + xclient.data.l[3] = 0; + xclient.data.l[4] = 0; + + XSendEvent (GDK_DISPLAY_XDISPLAY (display), GDK_SURFACE_XROOTWIN (window), False, + SubstructureRedirectMask | SubstructureNotifyMask, + (XEvent *)&xclient); + } + else + { + XRaiseWindow (GDK_DISPLAY_XDISPLAY (display), GDK_SURFACE_XID (window)); + + /* There is no way of knowing reliably whether we are viewable; + * so trap errors asynchronously around the XSetInputFocus call + */ + gdk_x11_display_error_trap_push (display); + XSetInputFocus (GDK_DISPLAY_XDISPLAY (display), + GDK_SURFACE_XID (window), + RevertToParent, + timestamp); + gdk_x11_display_error_trap_pop_ignored (display); + } +} + +static void +gdk_x11_surface_set_type_hint (GdkSurface *window, + GdkSurfaceTypeHint hint) +{ + GdkDisplay *display; + Atom atom; + + if (GDK_SURFACE_DESTROYED (window) || + !WINDOW_IS_TOPLEVEL_OR_FOREIGN (window)) + return; + + display = gdk_surface_get_display (window); + + switch (hint) + { + case GDK_SURFACE_TYPE_HINT_DIALOG: + atom = gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_WINDOW_TYPE_DIALOG"); + break; + case GDK_SURFACE_TYPE_HINT_MENU: + atom = gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_WINDOW_TYPE_MENU"); + break; + case GDK_SURFACE_TYPE_HINT_TOOLBAR: + atom = gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_WINDOW_TYPE_TOOLBAR"); + break; + case GDK_SURFACE_TYPE_HINT_UTILITY: + atom = gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_WINDOW_TYPE_UTILITY"); + break; + case GDK_SURFACE_TYPE_HINT_SPLASHSCREEN: + atom = gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_WINDOW_TYPE_SPLASH"); + break; + case GDK_SURFACE_TYPE_HINT_DOCK: + atom = gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_WINDOW_TYPE_DOCK"); + break; + case GDK_SURFACE_TYPE_HINT_DESKTOP: + atom = gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_WINDOW_TYPE_DESKTOP"); + break; + case GDK_SURFACE_TYPE_HINT_DROPDOWN_MENU: + atom = gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_WINDOW_TYPE_DROPDOWN_MENU"); + break; + case GDK_SURFACE_TYPE_HINT_POPUP_MENU: + atom = gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_WINDOW_TYPE_POPUP_MENU"); + break; + case GDK_SURFACE_TYPE_HINT_TOOLTIP: + atom = gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_WINDOW_TYPE_TOOLTIP"); + break; + case GDK_SURFACE_TYPE_HINT_NOTIFICATION: + atom = gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_WINDOW_TYPE_NOTIFICATION"); + break; + case GDK_SURFACE_TYPE_HINT_COMBO: + atom = gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_WINDOW_TYPE_COMBO"); + break; + case GDK_SURFACE_TYPE_HINT_DND: + atom = gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_WINDOW_TYPE_DND"); + break; + default: + g_warning ("Unknown hint %d passed to gdk_surface_set_type_hint", hint); + /* Fall thru */ + case GDK_SURFACE_TYPE_HINT_NORMAL: + atom = gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_WINDOW_TYPE_NORMAL"); + break; + } + + XChangeProperty (GDK_DISPLAY_XDISPLAY (display), GDK_SURFACE_XID (window), + gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_WINDOW_TYPE"), + XA_ATOM, 32, PropModeReplace, + (guchar *)&atom, 1); +} + +static GdkSurfaceTypeHint +gdk_x11_surface_get_type_hint (GdkSurface *window) +{ + GdkDisplay *display; + GdkSurfaceTypeHint type; + Atom type_return; + gint format_return; + gulong nitems_return; + gulong bytes_after_return; + guchar *data = NULL; + + g_return_val_if_fail (GDK_IS_SURFACE (window), GDK_SURFACE_TYPE_HINT_NORMAL); + + if (GDK_SURFACE_DESTROYED (window) || + !WINDOW_IS_TOPLEVEL_OR_FOREIGN (window)) + return GDK_SURFACE_TYPE_HINT_NORMAL; + + type = GDK_SURFACE_TYPE_HINT_NORMAL; + + display = gdk_surface_get_display (window); + + if (XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display), GDK_SURFACE_XID (window), + gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_WINDOW_TYPE"), + 0, G_MAXLONG, False, XA_ATOM, &type_return, + &format_return, &nitems_return, &bytes_after_return, + &data) == Success) + { + if ((type_return == XA_ATOM) && (format_return == 32) && + (data) && (nitems_return == 1)) + { + Atom atom = *(Atom*)data; + + if (atom == gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_WINDOW_TYPE_DIALOG")) + type = GDK_SURFACE_TYPE_HINT_DIALOG; + else if (atom == gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_WINDOW_TYPE_MENU")) + type = GDK_SURFACE_TYPE_HINT_MENU; + else if (atom == gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_WINDOW_TYPE_TOOLBAR")) + type = GDK_SURFACE_TYPE_HINT_TOOLBAR; + else if (atom == gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_WINDOW_TYPE_UTILITY")) + type = GDK_SURFACE_TYPE_HINT_UTILITY; + else if (atom == gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_WINDOW_TYPE_SPLASH")) + type = GDK_SURFACE_TYPE_HINT_SPLASHSCREEN; + else if (atom == gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_WINDOW_TYPE_DOCK")) + type = GDK_SURFACE_TYPE_HINT_DOCK; + else if (atom == gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_WINDOW_TYPE_DESKTOP")) + type = GDK_SURFACE_TYPE_HINT_DESKTOP; + else if (atom == gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_WINDOW_TYPE_DROPDOWN_MENU")) + type = GDK_SURFACE_TYPE_HINT_DROPDOWN_MENU; + else if (atom == gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_WINDOW_TYPE_POPUP_MENU")) + type = GDK_SURFACE_TYPE_HINT_POPUP_MENU; + else if (atom == gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_WINDOW_TYPE_TOOLTIP")) + type = GDK_SURFACE_TYPE_HINT_TOOLTIP; + else if (atom == gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_WINDOW_TYPE_NOTIFICATION")) + type = GDK_SURFACE_TYPE_HINT_NOTIFICATION; + else if (atom == gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_WINDOW_TYPE_COMBO")) + type = GDK_SURFACE_TYPE_HINT_COMBO; + else if (atom == gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_WINDOW_TYPE_DND")) + type = GDK_SURFACE_TYPE_HINT_DND; + } + + if (type_return != None && data != NULL) + XFree (data); + } + + return type; +} + +static void +gdk_wmspec_change_state (gboolean add, + GdkSurface *window, + GdkAtom state1, + GdkAtom state2) +{ + GdkDisplay *display = GDK_SURFACE_DISPLAY (window); + XClientMessageEvent xclient; + +#define _NET_WM_STATE_REMOVE 0 /* remove/unset property */ +#define _NET_WM_STATE_ADD 1 /* add/set property */ +#define _NET_WM_STATE_TOGGLE 2 /* toggle property */ + + memset (&xclient, 0, sizeof (xclient)); + xclient.type = ClientMessage; + xclient.window = GDK_SURFACE_XID (window); + xclient.message_type = gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_STATE"); + xclient.format = 32; + xclient.data.l[0] = add ? _NET_WM_STATE_ADD : _NET_WM_STATE_REMOVE; + xclient.data.l[1] = gdk_x11_atom_to_xatom_for_display (display, state1); + xclient.data.l[2] = gdk_x11_atom_to_xatom_for_display (display, state2); + xclient.data.l[3] = 1; /* source indication */ + xclient.data.l[4] = 0; + + XSendEvent (GDK_SURFACE_XDISPLAY (window), GDK_SURFACE_XROOTWIN (window), False, + SubstructureRedirectMask | SubstructureNotifyMask, + (XEvent *)&xclient); +} + +static void +gdk_x11_surface_set_modal_hint (GdkSurface *window, + gboolean modal) +{ + if (GDK_SURFACE_DESTROYED (window) || + !WINDOW_IS_TOPLEVEL_OR_FOREIGN (window)) + return; + + window->modal_hint = modal; + + if (GDK_SURFACE_IS_MAPPED (window)) + gdk_wmspec_change_state (modal, window, + g_intern_static_string ("_NET_WM_STATE_MODAL"), + NULL); +} + +static void +gdk_x11_surface_set_skip_taskbar_hint (GdkSurface *window, + gboolean skips_taskbar) +{ + GdkToplevelX11 *toplevel; + + g_return_if_fail (GDK_SURFACE_TYPE (window) != GDK_SURFACE_CHILD); + + if (GDK_SURFACE_DESTROYED (window) || + !WINDOW_IS_TOPLEVEL_OR_FOREIGN (window)) + return; + + toplevel = _gdk_x11_surface_get_toplevel (window); + toplevel->skip_taskbar_hint = skips_taskbar; + + if (GDK_SURFACE_IS_MAPPED (window)) + gdk_wmspec_change_state (skips_taskbar, window, + g_intern_static_string ("_NET_WM_STATE_SKIP_TASKBAR"), + NULL); +} + +static void +gdk_x11_surface_set_skip_pager_hint (GdkSurface *window, + gboolean skips_pager) +{ + GdkToplevelX11 *toplevel; + + g_return_if_fail (GDK_SURFACE_TYPE (window) != GDK_SURFACE_CHILD); + + if (GDK_SURFACE_DESTROYED (window) || + !WINDOW_IS_TOPLEVEL_OR_FOREIGN (window)) + return; + + toplevel = _gdk_x11_surface_get_toplevel (window); + toplevel->skip_pager_hint = skips_pager; + + if (GDK_SURFACE_IS_MAPPED (window)) + gdk_wmspec_change_state (skips_pager, window, + g_intern_static_string ("_NET_WM_STATE_SKIP_PAGER"), + NULL); +} + +static void +gdk_x11_surface_set_urgency_hint (GdkSurface *window, + gboolean urgent) +{ + GdkToplevelX11 *toplevel; + + g_return_if_fail (GDK_SURFACE_TYPE (window) != GDK_SURFACE_CHILD); + + if (GDK_SURFACE_DESTROYED (window) || + !WINDOW_IS_TOPLEVEL_OR_FOREIGN (window)) + return; + + toplevel = _gdk_x11_surface_get_toplevel (window); + toplevel->urgency_hint = urgent; + + update_wm_hints (window, FALSE); +} + +static void +gdk_x11_surface_set_geometry_hints (GdkSurface *window, + const GdkGeometry *geometry, + GdkSurfaceHints geom_mask) +{ + GdkSurfaceImplX11 *impl = GDK_SURFACE_IMPL_X11 (window->impl); + XSizeHints size_hints; + GdkToplevelX11 *toplevel; + + if (GDK_SURFACE_DESTROYED (window) || + !WINDOW_IS_TOPLEVEL_OR_FOREIGN (window)) + return; + + toplevel = _gdk_x11_surface_get_toplevel (window); + if (toplevel) + { + if (geometry) + toplevel->last_geometry_hints = *geometry; + toplevel->last_geometry_hints_mask = geom_mask; + } + + size_hints.flags = 0; + + if (geom_mask & GDK_HINT_POS) + { + size_hints.flags |= PPosition; + /* We need to initialize the following obsolete fields because KWM + * apparently uses these fields if they are non-zero. + * #@#!#!$!. + */ + size_hints.x = 0; + size_hints.y = 0; + } + + if (geom_mask & GDK_HINT_USER_POS) + { + size_hints.flags |= USPosition; + } + + if (geom_mask & GDK_HINT_USER_SIZE) + { + size_hints.flags |= USSize; + } + + if (geom_mask & GDK_HINT_MIN_SIZE) + { + size_hints.flags |= PMinSize; + size_hints.min_width = geometry->min_width * impl->window_scale; + size_hints.min_height = geometry->min_height * impl->window_scale; + } + + if (geom_mask & GDK_HINT_MAX_SIZE) + { + size_hints.flags |= PMaxSize; + size_hints.max_width = MAX (geometry->max_width, 1) * impl->window_scale; + size_hints.max_height = MAX (geometry->max_height, 1) * impl->window_scale; + } + + if (geom_mask & GDK_HINT_BASE_SIZE) + { + size_hints.flags |= PBaseSize; + size_hints.base_width = geometry->base_width * impl->window_scale; + size_hints.base_height = geometry->base_height * impl->window_scale; + } + + if (geom_mask & GDK_HINT_RESIZE_INC) + { + size_hints.flags |= PResizeInc; + size_hints.width_inc = geometry->width_inc * impl->window_scale; + size_hints.height_inc = geometry->height_inc * impl->window_scale; + } + else if (impl->window_scale > 1) + { + size_hints.flags |= PResizeInc; + size_hints.width_inc = impl->window_scale; + size_hints.height_inc = impl->window_scale; + } + + if (geom_mask & GDK_HINT_ASPECT) + { + size_hints.flags |= PAspect; + if (geometry->min_aspect <= 1) + { + size_hints.min_aspect.x = 65536 * geometry->min_aspect; + size_hints.min_aspect.y = 65536; + } + else + { + size_hints.min_aspect.x = 65536; + size_hints.min_aspect.y = 65536 / geometry->min_aspect;; + } + if (geometry->max_aspect <= 1) + { + size_hints.max_aspect.x = 65536 * geometry->max_aspect; + size_hints.max_aspect.y = 65536; + } + else + { + size_hints.max_aspect.x = 65536; + size_hints.max_aspect.y = 65536 / geometry->max_aspect;; + } + } + + if (geom_mask & GDK_HINT_WIN_GRAVITY) + { + size_hints.flags |= PWinGravity; + size_hints.win_gravity = geometry->win_gravity; + } + + /* FIXME: Would it be better to delete this property if + * geom_mask == 0? It would save space on the server + */ + XSetWMNormalHints (GDK_SURFACE_XDISPLAY (window), + GDK_SURFACE_XID (window), + &size_hints); +} + +static void +gdk_surface_get_geometry_hints (GdkSurface *window, + GdkGeometry *geometry, + GdkSurfaceHints *geom_mask) +{ + GdkSurfaceImplX11 *impl; + XSizeHints *size_hints; + glong junk_supplied_mask = 0; + + g_return_if_fail (GDK_IS_SURFACE (window)); + g_return_if_fail (geometry != NULL); + g_return_if_fail (geom_mask != NULL); + + *geom_mask = 0; + + if (GDK_SURFACE_DESTROYED (window) || + !WINDOW_IS_TOPLEVEL_OR_FOREIGN (window)) + return; + + impl = GDK_SURFACE_IMPL_X11 (window->impl); + + size_hints = XAllocSizeHints (); + if (!size_hints) + return; + + if (!XGetWMNormalHints (GDK_SURFACE_XDISPLAY (window), + GDK_SURFACE_XID (window), + size_hints, + &junk_supplied_mask)) + size_hints->flags = 0; + + if (size_hints->flags & PMinSize) + { + *geom_mask |= GDK_HINT_MIN_SIZE; + geometry->min_width = size_hints->min_width / impl->window_scale; + geometry->min_height = size_hints->min_height / impl->window_scale; + } + + if (size_hints->flags & PMaxSize) + { + *geom_mask |= GDK_HINT_MAX_SIZE; + geometry->max_width = MAX (size_hints->max_width, 1) / impl->window_scale; + geometry->max_height = MAX (size_hints->max_height, 1) / impl->window_scale; + } + + if (size_hints->flags & PResizeInc) + { + *geom_mask |= GDK_HINT_RESIZE_INC; + geometry->width_inc = size_hints->width_inc / impl->window_scale; + geometry->height_inc = size_hints->height_inc / impl->window_scale; + } + + if (size_hints->flags & PAspect) + { + *geom_mask |= GDK_HINT_ASPECT; + + geometry->min_aspect = (gdouble) size_hints->min_aspect.x / (gdouble) size_hints->min_aspect.y; + geometry->max_aspect = (gdouble) size_hints->max_aspect.x / (gdouble) size_hints->max_aspect.y; + } + + if (size_hints->flags & PWinGravity) + { + *geom_mask |= GDK_HINT_WIN_GRAVITY; + geometry->win_gravity = size_hints->win_gravity; + } + + XFree (size_hints); +} + +static gboolean +utf8_is_latin1 (const gchar *str) +{ + const char *p = str; + + while (*p) + { + gunichar ch = g_utf8_get_char (p); + + if (ch > 0xff) + return FALSE; + + p = g_utf8_next_char (p); + } + + return TRUE; +} + +/* Set the property to @utf8_str as STRING if the @utf8_str is fully + * convertable to STRING, otherwise, set it as compound text + */ +static void +set_text_property (GdkDisplay *display, + Window xwindow, + Atom property, + const gchar *utf8_str) +{ + gchar *prop_text = NULL; + Atom prop_type; + gint prop_length; + gint prop_format; + gboolean is_compound_text; + + if (utf8_is_latin1 (utf8_str)) + { + prop_type = XA_STRING; + prop_text = _gdk_x11_display_utf8_to_string_target (display, utf8_str); + prop_length = prop_text ? strlen (prop_text) : 0; + prop_format = 8; + is_compound_text = FALSE; + } + else + { + GdkAtom gdk_type; + + gdk_x11_display_utf8_to_compound_text (display, + utf8_str, &gdk_type, &prop_format, + (guchar **)&prop_text, &prop_length); + prop_type = gdk_x11_atom_to_xatom_for_display (display, gdk_type); + is_compound_text = TRUE; + } + + if (prop_text) + { + XChangeProperty (GDK_DISPLAY_XDISPLAY (display), + xwindow, + property, + prop_type, prop_format, + PropModeReplace, (guchar *)prop_text, + prop_length); + + if (is_compound_text) + gdk_x11_free_compound_text ((guchar *)prop_text); + else + g_free (prop_text); + } +} + +/* Set WM_NAME and _NET_WM_NAME + */ +static void +set_wm_name (GdkDisplay *display, + Window xwindow, + const gchar *name) +{ + XChangeProperty (GDK_DISPLAY_XDISPLAY (display), xwindow, + gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_NAME"), + gdk_x11_get_xatom_by_name_for_display (display, "UTF8_STRING"), 8, + PropModeReplace, (guchar *)name, strlen (name)); + + set_text_property (display, xwindow, + gdk_x11_get_xatom_by_name_for_display (display, "WM_NAME"), + name); +} + +static void +gdk_x11_surface_set_title (GdkSurface *window, + const gchar *title) +{ + GdkDisplay *display; + Display *xdisplay; + Window xwindow; + + g_return_if_fail (title != NULL); + + if (GDK_SURFACE_DESTROYED (window) || + !WINDOW_IS_TOPLEVEL_OR_FOREIGN (window)) + return; + + display = gdk_surface_get_display (window); + xdisplay = GDK_DISPLAY_XDISPLAY (display); + xwindow = GDK_SURFACE_XID (window); + + set_wm_name (display, xwindow, title); + + if (!gdk_surface_icon_name_set (window)) + { + XChangeProperty (xdisplay, xwindow, + gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_ICON_NAME"), + gdk_x11_get_xatom_by_name_for_display (display, "UTF8_STRING"), 8, + PropModeReplace, (guchar *)title, strlen (title)); + + set_text_property (display, xwindow, + gdk_x11_get_xatom_by_name_for_display (display, "WM_ICON_NAME"), + title); + } +} + +static void +gdk_x11_surface_set_role (GdkSurface *window, + const gchar *role) +{ + GdkDisplay *display; + + display = gdk_surface_get_display (window); + + if (GDK_SURFACE_DESTROYED (window) || + !WINDOW_IS_TOPLEVEL_OR_FOREIGN (window)) + return; + + if (role) + XChangeProperty (GDK_DISPLAY_XDISPLAY (display), GDK_SURFACE_XID (window), + gdk_x11_get_xatom_by_name_for_display (display, "WM_WINDOW_ROLE"), + XA_STRING, 8, PropModeReplace, (guchar *)role, strlen (role)); + else + XDeleteProperty (GDK_DISPLAY_XDISPLAY (display), GDK_SURFACE_XID (window), + gdk_x11_get_xatom_by_name_for_display (display, "WM_WINDOW_ROLE")); +} + +static void +gdk_x11_surface_set_startup_id (GdkSurface *window, + const gchar *startup_id) +{ + GdkDisplay *display; + + g_return_if_fail (GDK_IS_SURFACE (window)); + + display = gdk_surface_get_display (window); + + if (GDK_SURFACE_DESTROYED (window) || + !WINDOW_IS_TOPLEVEL_OR_FOREIGN (window)) + return; + + if (startup_id) + XChangeProperty (GDK_DISPLAY_XDISPLAY (display), GDK_SURFACE_XID (window), + gdk_x11_get_xatom_by_name_for_display (display, "_NET_STARTUP_ID"), + gdk_x11_get_xatom_by_name_for_display (display, "UTF8_STRING"), 8, + PropModeReplace, (unsigned char *)startup_id, strlen (startup_id)); + else + XDeleteProperty (GDK_DISPLAY_XDISPLAY (display), GDK_SURFACE_XID (window), + gdk_x11_get_xatom_by_name_for_display (display, "_NET_STARTUP_ID")); +} + +static void +gdk_x11_surface_set_transient_for (GdkSurface *window, + GdkSurface *parent) +{ + if (GDK_SURFACE_DESTROYED (window) || + !WINDOW_IS_TOPLEVEL_OR_FOREIGN (window)) + return; + + /* XSetTransientForHint() doesn't allow unsetting, so do it manually */ + if (parent && !GDK_SURFACE_DESTROYED (parent)) + XSetTransientForHint (GDK_SURFACE_XDISPLAY (window), + GDK_SURFACE_XID (window), + GDK_SURFACE_XID (parent)); + else + XDeleteProperty (GDK_SURFACE_XDISPLAY (window), + GDK_SURFACE_XID (window), + gdk_x11_get_xatom_by_name_for_display (GDK_SURFACE_DISPLAY (window), "WM_TRANSIENT_FOR")); +} + +GdkCursor * +_gdk_x11_surface_get_cursor (GdkSurface *window) +{ + GdkSurfaceImplX11 *impl; + + g_return_val_if_fail (GDK_IS_SURFACE (window), NULL); + + impl = GDK_SURFACE_IMPL_X11 (window->impl); + + return impl->cursor; +} + +static void +gdk_surface_x11_get_geometry (GdkSurface *window, + gint *x, + gint *y, + gint *width, + gint *height) +{ + GdkSurfaceImplX11 *impl; + Window root; + gint tx; + gint ty; + guint twidth; + guint theight; + guint tborder_width; + guint tdepth; + + if (!GDK_SURFACE_DESTROYED (window)) + { + impl = GDK_SURFACE_IMPL_X11 (window->impl); + + XGetGeometry (GDK_SURFACE_XDISPLAY (window), + GDK_SURFACE_XID (window), + &root, &tx, &ty, &twidth, &theight, &tborder_width, &tdepth); + + if (x) + *x = tx / impl->window_scale; + if (y) + *y = ty / impl->window_scale; + if (width) + *width = twidth / impl->window_scale; + if (height) + *height = theight / impl->window_scale; + } +} + +static void +gdk_surface_x11_get_root_coords (GdkSurface *window, + gint x, + gint y, + gint *root_x, + gint *root_y) +{ + GdkSurfaceImplX11 *impl = GDK_SURFACE_IMPL_X11 (window->impl); + Window child; + gint tx; + gint ty; + + XTranslateCoordinates (GDK_SURFACE_XDISPLAY (window), + GDK_SURFACE_XID (window), + GDK_SURFACE_XROOTWIN (window), + x * impl->window_scale, y * impl->window_scale, &tx, &ty, + &child); + + if (root_x) + *root_x = tx / impl->window_scale; + if (root_y) + *root_y = ty / impl->window_scale; +} + +static void +gdk_x11_surface_get_frame_extents (GdkSurface *window, + GdkRectangle *rect) +{ + GdkDisplay *display; + GdkSurfaceImplX11 *impl; + Window xwindow; + Window xparent; + Window root; + Window child; + Window *children; + guchar *data; + Window *vroots; + Atom type_return; + guint nchildren; + guint nvroots; + gulong nitems_return; + gulong bytes_after_return; + gint format_return; + gint i; + guint ww, wh, wb, wd; + gint wx, wy; + gboolean got_frame_extents = FALSE; + + g_return_if_fail (rect != NULL); + + rect->x = 0; + rect->y = 0; + rect->width = 1; + rect->height = 1; + + while (window->parent && (window->parent)->parent) + window = window->parent; + + impl = GDK_SURFACE_IMPL_X11 (window->impl); + + /* Refine our fallback answer a bit using local information */ + rect->x = window->x * impl->window_scale; + rect->y = window->y * impl->window_scale; + rect->width = window->width * impl->window_scale; + rect->height = window->height * impl->window_scale; + + if (GDK_SURFACE_DESTROYED (window) || impl->override_redirect) + return; + + nvroots = 0; + vroots = NULL; + + display = gdk_surface_get_display (window); + + gdk_x11_display_error_trap_push (display); + + xwindow = GDK_SURFACE_XID (window); + + /* first try: use _NET_FRAME_EXTENTS */ + if (gdk_x11_screen_supports_net_wm_hint (GDK_SURFACE_SCREEN (window), + g_intern_static_string ("_NET_FRAME_EXTENTS")) && + XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display), xwindow, + gdk_x11_get_xatom_by_name_for_display (display, + "_NET_FRAME_EXTENTS"), + 0, G_MAXLONG, False, XA_CARDINAL, &type_return, + &format_return, &nitems_return, &bytes_after_return, + &data) + == Success) + { + if ((type_return == XA_CARDINAL) && (format_return == 32) && + (nitems_return == 4) && (data)) + { + gulong *ldata = (gulong *) data; + got_frame_extents = TRUE; + + /* try to get the real client window geometry */ + if (XGetGeometry (GDK_DISPLAY_XDISPLAY (display), xwindow, + &root, &wx, &wy, &ww, &wh, &wb, &wd) && + XTranslateCoordinates (GDK_DISPLAY_XDISPLAY (display), + xwindow, root, 0, 0, &wx, &wy, &child)) + { + rect->x = wx; + rect->y = wy; + rect->width = ww; + rect->height = wh; + } + + /* _NET_FRAME_EXTENTS format is left, right, top, bottom */ + rect->x -= ldata[0]; + rect->y -= ldata[2]; + rect->width += ldata[0] + ldata[1]; + rect->height += ldata[2] + ldata[3]; + } + + if (data) + XFree (data); + } + + if (got_frame_extents) + goto out; + + /* no frame extents property available, which means we either have a WM that + is not EWMH compliant or is broken - try fallback and walk up the window + tree to get our window's parent which hopefully is the window frame */ + + /* use NETWM_VIRTUAL_ROOTS if available */ + root = GDK_SURFACE_XROOTWIN (window); + + if (gdk_x11_screen_supports_net_wm_hint (GDK_SURFACE_SCREEN (window), + g_intern_static_string ("_NET_VIRTUAL_ROOTS")) && + XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display), root, + gdk_x11_get_xatom_by_name_for_display (display, + "_NET_VIRTUAL_ROOTS"), + 0, G_MAXLONG, False, XA_WINDOW, &type_return, + &format_return, &nitems_return, &bytes_after_return, + &data) + == Success) + { + if ((type_return == XA_WINDOW) && (format_return == 32) && (data)) + { + nvroots = nitems_return; + vroots = (Window *)data; + } + } + + xparent = GDK_SURFACE_XID (window); + + do + { + xwindow = xparent; + + if (!XQueryTree (GDK_DISPLAY_XDISPLAY (display), xwindow, + &root, &xparent, + &children, &nchildren)) + goto out; + + if (children) + XFree (children); + + /* check virtual roots */ + for (i = 0; i < nvroots; i++) + { + if (xparent == vroots[i]) + { + root = xparent; + break; + } + } + } + while (xparent != root); + + if (XGetGeometry (GDK_DISPLAY_XDISPLAY (display), xwindow, + &root, &wx, &wy, &ww, &wh, &wb, &wd)) + { + rect->x = wx; + rect->y = wy; + rect->width = ww; + rect->height = wh; + } + + out: + if (vroots) + XFree (vroots); + + /* Here we extend the size to include the extra pixels if we round x/y down + as well as round the size up when we divide by scale so that the returned + size is guaranteed to cover the real pixels, but it may overshoot a bit + in case the window is not positioned/sized according to the scale */ + rect->width = (rect->width + rect->x % impl->window_scale + impl->window_scale - 1) / impl->window_scale; + rect->height = (rect->height + rect->y % impl->window_scale + impl->window_scale - 1) / impl->window_scale; + rect->x = rect->x / impl->window_scale; + rect->y = rect->y / impl->window_scale; + gdk_x11_display_error_trap_pop_ignored (display); +} + +static gboolean +gdk_surface_x11_get_device_state (GdkSurface *window, + GdkDevice *device, + gdouble *x, + gdouble *y, + GdkModifierType *mask) +{ + GdkSurface *child; + + if (GDK_SURFACE_DESTROYED (window)) + return FALSE; + + /*HIDPI: handle coords here?*/ + GDK_DEVICE_GET_CLASS (device)->query_state (device, window, + &child, + NULL, NULL, + x, y, mask); + return child != NULL; +} + +static GdkEventMask +gdk_surface_x11_get_events (GdkSurface *window) +{ + XWindowAttributes attrs; + GdkEventMask event_mask; + GdkEventMask filtered; + + if (GDK_SURFACE_DESTROYED (window)) + return 0; + else + { + XGetWindowAttributes (GDK_SURFACE_XDISPLAY (window), + GDK_SURFACE_XID (window), + &attrs); + event_mask = x_event_mask_to_gdk_event_mask (attrs.your_event_mask); + /* if property change was filtered out before, keep it filtered out */ + filtered = GDK_STRUCTURE_MASK | GDK_PROPERTY_CHANGE_MASK; + window->event_mask = event_mask & ((window->event_mask & filtered) | ~filtered); + + return event_mask; + } +} +static void +gdk_surface_x11_set_events (GdkSurface *window, + GdkEventMask event_mask) +{ + long xevent_mask = 0; + + if (!GDK_SURFACE_DESTROYED (window)) + { + GdkX11Display *display_x11; + + if (GDK_SURFACE_XID (window) != GDK_SURFACE_XROOTWIN (window)) + xevent_mask = StructureNotifyMask | PropertyChangeMask; + + display_x11 = GDK_X11_DISPLAY (gdk_surface_get_display (window)); + gdk_x11_event_source_select_events ((GdkEventSource *) display_x11->event_source, + GDK_SURFACE_XID (window), event_mask, + xevent_mask); + } +} + +static inline void +do_shape_combine_region (GdkSurface *window, + const cairo_region_t *shape_region, + gint offset_x, + gint offset_y, + gint shape) +{ + GdkSurfaceImplX11 *impl = GDK_SURFACE_IMPL_X11 (window->impl); + + if (GDK_SURFACE_DESTROYED (window)) + return; + + if (shape_region == NULL) + { + /* Use NULL mask to unset the shape */ + if (shape == ShapeBounding + ? gdk_display_supports_shapes (GDK_SURFACE_DISPLAY (window)) + : gdk_display_supports_input_shapes (GDK_SURFACE_DISPLAY (window))) + { + XShapeCombineMask (GDK_SURFACE_XDISPLAY (window), + GDK_SURFACE_XID (window), + shape, + 0, 0, + None, + ShapeSet); + } + return; + } + + if (shape == ShapeBounding + ? gdk_display_supports_shapes (GDK_SURFACE_DISPLAY (window)) + : gdk_display_supports_input_shapes (GDK_SURFACE_DISPLAY (window))) + { + gint n_rects = 0; + XRectangle *xrects = NULL; + + _gdk_x11_region_get_xrectangles (shape_region, + 0, 0, impl->window_scale, + &xrects, &n_rects); + + XShapeCombineRectangles (GDK_SURFACE_XDISPLAY (window), + GDK_SURFACE_XID (window), + shape, + offset_x * impl->window_scale, + offset_y * impl->window_scale, + xrects, n_rects, + ShapeSet, + YXBanded); + + g_free (xrects); + } +} + +static void +gdk_surface_x11_shape_combine_region (GdkSurface *window, + const cairo_region_t *shape_region, + gint offset_x, + gint offset_y) +{ + do_shape_combine_region (window, shape_region, offset_x, offset_y, ShapeBounding); +} + +static void +gdk_surface_x11_input_shape_combine_region (GdkSurface *window, + const cairo_region_t *shape_region, + gint offset_x, + gint offset_y) +{ +#ifdef ShapeInput + do_shape_combine_region (window, shape_region, offset_x, offset_y, ShapeInput); +#endif +} + +static void +gdk_x11_surface_set_accept_focus (GdkSurface *window, + gboolean accept_focus) +{ + accept_focus = accept_focus != FALSE; + + if (window->accept_focus != accept_focus) + { + window->accept_focus = accept_focus; + + if (!GDK_SURFACE_DESTROYED (window) && + WINDOW_IS_TOPLEVEL_OR_FOREIGN (window)) + update_wm_hints (window, FALSE); + } +} + +static void +gdk_x11_surface_set_focus_on_map (GdkSurface *window, + gboolean focus_on_map) +{ + focus_on_map = focus_on_map != FALSE; + + if (window->focus_on_map != focus_on_map) + { + window->focus_on_map = focus_on_map; + + if ((!GDK_SURFACE_DESTROYED (window)) && + (!window->focus_on_map) && + WINDOW_IS_TOPLEVEL_OR_FOREIGN (window)) + gdk_x11_surface_set_user_time (window, 0); + } +} + +/** + * gdk_x11_surface_set_user_time: + * @window: (type GdkX11Surface): A toplevel #GdkSurface + * @timestamp: An XServer timestamp to which the property should be set + * + * The application can use this call to update the _NET_WM_USER_TIME + * property on a toplevel window. This property stores an Xserver + * time which represents the time of the last user input event + * received for this window. This property may be used by the window + * manager to alter the focus, stacking, and/or placement behavior of + * windows when they are mapped depending on whether the new window + * was created by a user action or is a "pop-up" window activated by a + * timer or some other event. + * + * Note that this property is automatically updated by GDK, so this + * function should only be used by applications which handle input + * events bypassing GDK. + **/ +void +gdk_x11_surface_set_user_time (GdkSurface *window, + guint32 timestamp) +{ + GdkDisplay *display; + GdkX11Display *display_x11; + GdkToplevelX11 *toplevel; + glong timestamp_long = (glong)timestamp; + Window xid; + + if (GDK_SURFACE_DESTROYED (window) || + !WINDOW_IS_TOPLEVEL_OR_FOREIGN (window)) + return; + + display = gdk_surface_get_display (window); + display_x11 = GDK_X11_DISPLAY (display); + toplevel = _gdk_x11_surface_get_toplevel (window); + + if (!toplevel) + { + g_warning ("gdk_surface_set_user_time called on non-toplevel\n"); + return; + } + + if (toplevel->focus_window != None && + gdk_x11_screen_supports_net_wm_hint (GDK_SURFACE_SCREEN (window), + g_intern_static_string ("_NET_WM_USER_TIME_WINDOW"))) + xid = toplevel->focus_window; + else + xid = GDK_SURFACE_XID (window); + + XChangeProperty (GDK_DISPLAY_XDISPLAY (display), xid, + gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_USER_TIME"), + XA_CARDINAL, 32, PropModeReplace, + (guchar *)×tamp_long, 1); + + if (timestamp_long != GDK_CURRENT_TIME && + (display_x11->user_time == GDK_CURRENT_TIME || + XSERVER_TIME_IS_LATER (timestamp_long, display_x11->user_time))) + display_x11->user_time = timestamp_long; + + if (toplevel) + toplevel->user_time = timestamp_long; +} + +/** + * gdk_x11_surface_set_utf8_property: + * @window: (type GdkX11Surface): a #GdkSurface + * @name: Property name, will be interned as an X atom + * @value: (allow-none): Property value, or %NULL to delete + * + * This function modifies or removes an arbitrary X11 window + * property of type UTF8_STRING. If the given @window is + * not a toplevel window, it is ignored. + */ +void +gdk_x11_surface_set_utf8_property (GdkSurface *window, + const gchar *name, + const gchar *value) +{ + GdkDisplay *display; + + if (!WINDOW_IS_TOPLEVEL (window)) + return; + + display = gdk_surface_get_display (window); + + if (value != NULL) + { + XChangeProperty (GDK_DISPLAY_XDISPLAY (display), + GDK_SURFACE_XID (window), + gdk_x11_get_xatom_by_name_for_display (display, name), + gdk_x11_get_xatom_by_name_for_display (display, "UTF8_STRING"), 8, + PropModeReplace, (guchar *)value, strlen (value)); + } + else + { + XDeleteProperty (GDK_DISPLAY_XDISPLAY (display), + GDK_SURFACE_XID (window), + gdk_x11_get_xatom_by_name_for_display (display, name)); + } +} + +static void +gdk_x11_surface_set_shadow_width (GdkSurface *window, + int left, + int right, + int top, + int bottom) +{ + GdkSurfaceImplX11 *impl = GDK_SURFACE_IMPL_X11 (window->impl); + Atom frame_extents; + gulong data[4] = { + left * impl->window_scale, + right * impl->window_scale, + top * impl->window_scale, + bottom * impl->window_scale + }; + + frame_extents = gdk_x11_get_xatom_by_name_for_display (gdk_surface_get_display (window), + "_GTK_FRAME_EXTENTS"); + XChangeProperty (GDK_SURFACE_XDISPLAY (window), + GDK_SURFACE_XID (window), + frame_extents, XA_CARDINAL, + 32, PropModeReplace, + (guchar *) &data, 4); +} + +/** + * gdk_x11_surface_set_theme_variant: + * @window: (type GdkX11Surface): a #GdkSurface + * @variant: the theme variant to export + * + * GTK+ applications can request a dark theme variant. In order to + * make other applications - namely window managers using GTK+ for + * themeing - aware of this choice, GTK+ uses this function to + * export the requested theme variant as _GTK_THEME_VARIANT property + * on toplevel windows. + * + * Note that this property is automatically updated by GTK+, so this + * function should only be used by applications which do not use GTK+ + * to create toplevel windows. + */ +void +gdk_x11_surface_set_theme_variant (GdkSurface *window, + const char *variant) +{ + gdk_x11_surface_set_utf8_property (window, "_GTK_THEME_VARIANT", + variant ? variant : ""); +} + +#define GDK_SELECTION_MAX_SIZE(display) \ + MIN(262144, \ + XExtendedMaxRequestSize (GDK_DISPLAY_XDISPLAY (display)) == 0 \ + ? XMaxRequestSize (GDK_DISPLAY_XDISPLAY (display)) - 100 \ + : XExtendedMaxRequestSize (GDK_DISPLAY_XDISPLAY (display)) - 100) + +static void +gdk_surface_update_icon (GdkSurface *window, + GList *icon_list) +{ + GdkToplevelX11 *toplevel; + GdkTexture *best_icon; + GList *tmp_list; + int best_size; + + toplevel = _gdk_x11_surface_get_toplevel (window); + + if (toplevel->icon_pixmap != NULL) + { + cairo_surface_destroy (toplevel->icon_pixmap); + toplevel->icon_pixmap = NULL; + } + + if (toplevel->icon_mask != NULL) + { + cairo_surface_destroy (toplevel->icon_mask); + toplevel->icon_mask = NULL; + } + +#define IDEAL_SIZE 48 + + best_size = G_MAXINT; + best_icon = NULL; + for (tmp_list = icon_list; tmp_list; tmp_list = tmp_list->next) + { + GdkTexture *texture = tmp_list->data; + int this; + + /* average width and height - if someone passes in a rectangular + * icon they deserve what they get. + */ + this = gdk_texture_get_width (texture) + gdk_texture_get_height (texture); + this /= 2; + + if (best_icon == NULL) + { + best_icon = texture; + best_size = this; + } + else + { + /* icon is better if it's 32 pixels or larger, and closer to + * the ideal size than the current best. + */ + if (this >= 32 && + (ABS (best_size - IDEAL_SIZE) < + ABS (this - IDEAL_SIZE))) + { + best_icon = texture; + best_size = this; + } + } + } + + if (best_icon) + { + int width = gdk_texture_get_width (best_icon); + int height = gdk_texture_get_height (best_icon); + cairo_surface_t *surface; + cairo_t *cr; + + toplevel->icon_pixmap = gdk_x11_surface_create_pixmap_surface (window, width, height); + + surface = gdk_texture_download_surface (best_icon); + + cr = cairo_create (toplevel->icon_pixmap); + cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE); + cairo_set_source_surface (cr, surface, 0, 0); + if (cairo_surface_get_content (surface) == CAIRO_CONTENT_COLOR_ALPHA) + { + /* Saturate the image, so it has bilevel alpha */ + cairo_push_group_with_content (cr, CAIRO_CONTENT_COLOR_ALPHA); + cairo_paint (cr); + cairo_set_operator (cr, CAIRO_OPERATOR_SATURATE); + cairo_paint (cr); + cairo_pop_group_to_source (cr); + } + cairo_paint (cr); + cairo_destroy (cr); + + if (cairo_surface_get_content (surface) == CAIRO_CONTENT_COLOR_ALPHA) + { + GdkDisplay *display = gdk_surface_get_display (window); + + toplevel->icon_mask = _gdk_x11_display_create_bitmap_surface (display, width, height); + + cr = cairo_create (toplevel->icon_mask); + cairo_set_source_surface (cr, surface, 0, 0); + cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE); + cairo_paint (cr); + cairo_destroy (cr); + } + + cairo_surface_destroy (surface); + } + + update_wm_hints (window, FALSE); +} + +static void +gdk_x11_surface_set_icon_list (GdkSurface *window, + GList *textures) +{ + gulong *data; + gulong *p; + gint size; + GList *l; + gint width, height; + GdkTexture *texture; + GdkDisplay *display; + gint i, n; + + if (GDK_SURFACE_DESTROYED (window) || + !WINDOW_IS_TOPLEVEL_OR_FOREIGN (window)) + return; + + display = gdk_surface_get_display (window); + + size = 0; + n = 0; + for (l = textures; l != NULL; l = l->next) + { + texture = l->data; + + width = gdk_texture_get_width (texture); + height = gdk_texture_get_height (texture); + + /* silently ignore overlarge icons */ + if (size + 2 + width * height > GDK_SELECTION_MAX_SIZE(display)) + break; + + n++; + size += 2 + width * height; + } + + data = g_malloc (size * sizeof (gulong)); + + p = data; + for (l = textures; l != NULL && n > 0; l = l->next) + { + texture = l->data; + + width = gdk_texture_get_width (texture); + height = gdk_texture_get_height (texture); + + *p++ = width; + *p++ = height; + + gdk_texture_download (texture, (guchar *) p, width * 4); + if (sizeof (gulong) > 4) + { + i = width * height; + while (i-- > 0) + p[i] = ((guint32 *) p)[i]; + } + + p += width * height; + n--; + } + + if (size > 0) + { + XChangeProperty (GDK_DISPLAY_XDISPLAY (display), + GDK_SURFACE_XID (window), + gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_ICON"), + XA_CARDINAL, 32, + PropModeReplace, + (guchar*) data, size); + } + else + { + XDeleteProperty (GDK_DISPLAY_XDISPLAY (display), + GDK_SURFACE_XID (window), + gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_ICON")); + } + + g_free (data); + + gdk_surface_update_icon (window, textures); +} + +static gboolean +gdk_surface_icon_name_set (GdkSurface *window) +{ + return GPOINTER_TO_UINT (g_object_get_qdata (G_OBJECT (window), + g_quark_from_static_string ("gdk-icon-name-set"))); +} + +static void +gdk_x11_surface_set_icon_name (GdkSurface *window, + const gchar *name) +{ + GdkDisplay *display; + + if (GDK_SURFACE_DESTROYED (window) || + !WINDOW_IS_TOPLEVEL_OR_FOREIGN (window)) + return; + + display = gdk_surface_get_display (window); + + g_object_set_qdata (G_OBJECT (window), g_quark_from_static_string ("gdk-icon-name-set"), + GUINT_TO_POINTER (name != NULL)); + + if (name != NULL) + { + XChangeProperty (GDK_DISPLAY_XDISPLAY (display), + GDK_SURFACE_XID (window), + gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_ICON_NAME"), + gdk_x11_get_xatom_by_name_for_display (display, "UTF8_STRING"), 8, + PropModeReplace, (guchar *)name, strlen (name)); + + set_text_property (display, GDK_SURFACE_XID (window), + gdk_x11_get_xatom_by_name_for_display (display, "WM_ICON_NAME"), + name); + } + else + { + XDeleteProperty (GDK_DISPLAY_XDISPLAY (display), + GDK_SURFACE_XID (window), + gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_ICON_NAME")); + XDeleteProperty (GDK_DISPLAY_XDISPLAY (display), + GDK_SURFACE_XID (window), + gdk_x11_get_xatom_by_name_for_display (display, "WM_ICON_NAME")); + } +} + +static void +gdk_x11_surface_iconify (GdkSurface *window) +{ + if (GDK_SURFACE_DESTROYED (window) || + !WINDOW_IS_TOPLEVEL_OR_FOREIGN (window)) + return; + + if (GDK_SURFACE_IS_MAPPED (window)) + { + XIconifyWindow (GDK_SURFACE_XDISPLAY (window), + GDK_SURFACE_XID (window), + gdk_x11_screen_get_screen_number (GDK_SURFACE_SCREEN (window))); + } + else + { + /* Flip our client side flag, the real work happens on map. */ + gdk_synthesize_window_state (window, + 0, + GDK_SURFACE_STATE_ICONIFIED); + gdk_wmspec_change_state (TRUE, window, + g_intern_static_string ("_NET_WM_STATE_HIDDEN"), + NULL); + } +} + +static void +gdk_x11_surface_deiconify (GdkSurface *window) +{ + if (GDK_SURFACE_DESTROYED (window) || + !WINDOW_IS_TOPLEVEL_OR_FOREIGN (window)) + return; + + if (GDK_SURFACE_IS_MAPPED (window)) + { + gdk_surface_show (window); + gdk_wmspec_change_state (FALSE, window, + g_intern_static_string ("_NET_WM_STATE_HIDDEN"), + NULL); + } + else + { + /* Flip our client side flag, the real work happens on map. */ + gdk_synthesize_window_state (window, + GDK_SURFACE_STATE_ICONIFIED, + 0); + gdk_wmspec_change_state (FALSE, window, + g_intern_static_string ("_NET_WM_STATE_HIDDEN"), + NULL); + } +} + +static void +gdk_x11_surface_stick (GdkSurface *window) +{ + if (GDK_SURFACE_DESTROYED (window) || + !WINDOW_IS_TOPLEVEL_OR_FOREIGN (window)) + return; + + if (GDK_SURFACE_IS_MAPPED (window)) + { + /* "stick" means stick to all desktops _and_ do not scroll with the + * viewport. i.e. glue to the monitor glass in all cases. + */ + + XClientMessageEvent xclient; + + /* Request stick during viewport scroll */ + gdk_wmspec_change_state (TRUE, window, + g_intern_static_string ("_NET_WM_STATE_STICKY"), + NULL); + + /* Request desktop 0xFFFFFFFF */ + memset (&xclient, 0, sizeof (xclient)); + xclient.type = ClientMessage; + xclient.window = GDK_SURFACE_XID (window); + xclient.display = GDK_SURFACE_XDISPLAY (window); + xclient.message_type = gdk_x11_get_xatom_by_name_for_display (GDK_SURFACE_DISPLAY (window), + "_NET_WM_DESKTOP"); + xclient.format = 32; + + xclient.data.l[0] = 0xFFFFFFFF; + xclient.data.l[1] = 0; + xclient.data.l[2] = 0; + xclient.data.l[3] = 0; + xclient.data.l[4] = 0; + + XSendEvent (GDK_SURFACE_XDISPLAY (window), GDK_SURFACE_XROOTWIN (window), False, + SubstructureRedirectMask | SubstructureNotifyMask, + (XEvent *)&xclient); + } + else + { + /* Flip our client side flag, the real work happens on map. */ + gdk_synthesize_window_state (window, + 0, + GDK_SURFACE_STATE_STICKY); + } +} + +static void +gdk_x11_surface_unstick (GdkSurface *window) +{ + if (GDK_SURFACE_DESTROYED (window) || + !WINDOW_IS_TOPLEVEL_OR_FOREIGN (window)) + return; + + if (GDK_SURFACE_IS_MAPPED (window)) + { + /* Request unstick from viewport */ + gdk_wmspec_change_state (FALSE, window, + g_intern_static_string ("_NET_WM_STATE_STICKY"), + NULL); + + move_to_current_desktop (window); + } + else + { + /* Flip our client side flag, the real work happens on map. */ + gdk_synthesize_window_state (window, + GDK_SURFACE_STATE_STICKY, + 0); + + } +} + +static void +gdk_x11_surface_maximize (GdkSurface *window) +{ + if (GDK_SURFACE_DESTROYED (window) || + !WINDOW_IS_TOPLEVEL_OR_FOREIGN (window)) + return; + + if (GDK_SURFACE_IS_MAPPED (window)) + gdk_wmspec_change_state (TRUE, window, + g_intern_static_string ("_NET_WM_STATE_MAXIMIZED_VERT"), + g_intern_static_string ("_NET_WM_STATE_MAXIMIZED_HORZ")); + else + gdk_synthesize_window_state (window, + 0, + GDK_SURFACE_STATE_MAXIMIZED); +} + +static void +gdk_x11_surface_unmaximize (GdkSurface *window) +{ + if (GDK_SURFACE_DESTROYED (window) || + !WINDOW_IS_TOPLEVEL_OR_FOREIGN (window)) + return; + + if (GDK_SURFACE_IS_MAPPED (window)) + gdk_wmspec_change_state (FALSE, window, + g_intern_static_string ("_NET_WM_STATE_MAXIMIZED_VERT"), + g_intern_static_string ("_NET_WM_STATE_MAXIMIZED_HORZ")); + else + gdk_synthesize_window_state (window, + GDK_SURFACE_STATE_MAXIMIZED, + 0); +} + +static void +gdk_x11_surface_apply_fullscreen_mode (GdkSurface *window) +{ + if (GDK_SURFACE_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_SURFACE_IS_MAPPED (window)) + { + XClientMessageEvent xclient; + gint monitors[4]; + gint i; + + memset (&xclient, 0, sizeof (xclient)); + xclient.type = ClientMessage; + xclient.window = GDK_SURFACE_XID (window); + xclient.display = GDK_SURFACE_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_SURFACE_SCREEN (window), + &monitors[0], + &monitors[1], + &monitors[2], + &monitors[3]); + /* Translate all 4 monitors from the GDK set into XINERAMA indices */ + for (i = 0; i < 4; ++i) + { + xclient.data.l[i] = 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_surface_apply_fullscreen_mode: Invalid XINERAMA monitor index"); + return; + } + } + break; + + default: + g_warning ("gdk_x11_surface_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_SURFACE_DISPLAY (window), + "_NET_WM_FULLSCREEN_MONITORS"); + XSendEvent (GDK_SURFACE_XDISPLAY (window), GDK_SURFACE_XROOTWIN (window), False, + SubstructureRedirectMask | SubstructureNotifyMask, + (XEvent *)&xclient); + } +} + +static void +gdk_x11_surface_fullscreen (GdkSurface *window) +{ + if (GDK_SURFACE_DESTROYED (window) || + !WINDOW_IS_TOPLEVEL_OR_FOREIGN (window)) + return; + + if (GDK_SURFACE_IS_MAPPED (window)) + { + gdk_wmspec_change_state (TRUE, window, + g_intern_static_string ("_NET_WM_STATE_FULLSCREEN"), + NULL); + /* 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_surface_apply_fullscreen_mode (window); + } + else + gdk_synthesize_window_state (window, + 0, + GDK_SURFACE_STATE_FULLSCREEN); +} + +static void +gdk_x11_surface_fullscreen_on_monitor (GdkSurface *window, + GdkMonitor *monitor) +{ + GdkRectangle geom; + + if (GDK_SURFACE_DESTROYED (window) || + !WINDOW_IS_TOPLEVEL_OR_FOREIGN (window)) + return; + + gdk_monitor_get_geometry (monitor, &geom); + gdk_surface_move (window, geom.x, geom.y); + + gdk_surface_set_fullscreen_mode (window, GDK_FULLSCREEN_ON_CURRENT_MONITOR); + gdk_x11_surface_fullscreen (window); +} + +static void +gdk_x11_surface_unfullscreen (GdkSurface *window) +{ + if (GDK_SURFACE_DESTROYED (window) || + !WINDOW_IS_TOPLEVEL_OR_FOREIGN (window)) + return; + + if (GDK_SURFACE_IS_MAPPED (window)) + gdk_wmspec_change_state (FALSE, window, + g_intern_static_string ("_NET_WM_STATE_FULLSCREEN"), + NULL); + + else + gdk_synthesize_window_state (window, + GDK_SURFACE_STATE_FULLSCREEN, + 0); +} + +static void +gdk_x11_surface_set_keep_above (GdkSurface *window, + gboolean setting) +{ + g_return_if_fail (GDK_IS_SURFACE (window)); + + if (GDK_SURFACE_DESTROYED (window) || + !WINDOW_IS_TOPLEVEL_OR_FOREIGN (window)) + return; + + if (GDK_SURFACE_IS_MAPPED (window)) + { + if (setting) + gdk_wmspec_change_state (FALSE, window, + g_intern_static_string ("_NET_WM_STATE_BELOW"), + NULL); + gdk_wmspec_change_state (setting, window, + g_intern_static_string ("_NET_WM_STATE_ABOVE"), + NULL); + } + else + gdk_synthesize_window_state (window, + setting ? GDK_SURFACE_STATE_BELOW : GDK_SURFACE_STATE_ABOVE, + setting ? GDK_SURFACE_STATE_ABOVE : 0); +} + +static void +gdk_x11_surface_set_keep_below (GdkSurface *window, gboolean setting) +{ + g_return_if_fail (GDK_IS_SURFACE (window)); + + if (GDK_SURFACE_DESTROYED (window) || + !WINDOW_IS_TOPLEVEL_OR_FOREIGN (window)) + return; + + if (GDK_SURFACE_IS_MAPPED (window)) + { + if (setting) + gdk_wmspec_change_state (FALSE, window, + g_intern_static_string ("_NET_WM_STATE_ABOVE"), + NULL); + gdk_wmspec_change_state (setting, window, + g_intern_static_string ("_NET_WM_STATE_BELOW"), + NULL); + } + else + gdk_synthesize_window_state (window, + setting ? GDK_SURFACE_STATE_ABOVE : GDK_SURFACE_STATE_BELOW, + setting ? GDK_SURFACE_STATE_BELOW : 0); +} + +static GdkSurface * +gdk_x11_surface_get_group (GdkSurface *window) +{ + GdkToplevelX11 *toplevel; + + if (GDK_SURFACE_DESTROYED (window) || + !WINDOW_IS_TOPLEVEL (window)) + return NULL; + + toplevel = _gdk_x11_surface_get_toplevel (window); + + return toplevel->group_leader; +} + +static void +gdk_x11_surface_set_group (GdkSurface *window, + GdkSurface *leader) +{ + GdkToplevelX11 *toplevel; + + g_return_if_fail (GDK_IS_SURFACE (window)); + g_return_if_fail (GDK_SURFACE_TYPE (window) != GDK_SURFACE_CHILD); + g_return_if_fail (leader == NULL || GDK_IS_SURFACE (leader)); + + if (GDK_SURFACE_DESTROYED (window) || + (leader != NULL && GDK_SURFACE_DESTROYED (leader)) || + !WINDOW_IS_TOPLEVEL (window)) + return; + + toplevel = _gdk_x11_surface_get_toplevel (window); + + if (leader == NULL) + leader = gdk_display_get_default_group (gdk_surface_get_display (window)); + + if (toplevel->group_leader != leader) + { + if (toplevel->group_leader) + g_object_unref (toplevel->group_leader); + toplevel->group_leader = g_object_ref (leader); + (_gdk_x11_surface_get_toplevel (leader))->is_leader = TRUE; + } + + update_wm_hints (window, FALSE); +} + +static MotifWmHints * +gdk_surface_get_mwm_hints (GdkSurface *window) +{ + GdkDisplay *display; + Atom hints_atom = None; + guchar *data; + Atom type; + gint format; + gulong nitems; + gulong bytes_after; + + if (GDK_SURFACE_DESTROYED (window)) + return NULL; + + display = gdk_surface_get_display (window); + + hints_atom = gdk_x11_get_xatom_by_name_for_display (display, _XA_MOTIF_WM_HINTS); + + XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display), GDK_SURFACE_XID (window), + hints_atom, 0, sizeof (MotifWmHints)/sizeof (long), + False, AnyPropertyType, &type, &format, &nitems, + &bytes_after, &data); + + if (type == None) + return NULL; + + return (MotifWmHints *)data; +} + +static void +gdk_surface_set_mwm_hints (GdkSurface *window, + MotifWmHints *new_hints) +{ + GdkDisplay *display; + Atom hints_atom = None; + guchar *data; + MotifWmHints *hints; + Atom type; + gint format; + gulong nitems; + gulong bytes_after; + + if (GDK_SURFACE_DESTROYED (window)) + return; + + display = gdk_surface_get_display (window); + + hints_atom = gdk_x11_get_xatom_by_name_for_display (display, _XA_MOTIF_WM_HINTS); + + XGetWindowProperty (GDK_SURFACE_XDISPLAY (window), GDK_SURFACE_XID (window), + hints_atom, 0, sizeof (MotifWmHints)/sizeof (long), + False, AnyPropertyType, &type, &format, &nitems, + &bytes_after, &data); + + if (type == None) + hints = new_hints; + else + { + hints = (MotifWmHints *)data; + + if (new_hints->flags & MWM_HINTS_FUNCTIONS) + { + hints->flags |= MWM_HINTS_FUNCTIONS; + hints->functions = new_hints->functions; + } + if (new_hints->flags & MWM_HINTS_DECORATIONS) + { + hints->flags |= MWM_HINTS_DECORATIONS; + hints->decorations = new_hints->decorations; + } + } + + XChangeProperty (GDK_SURFACE_XDISPLAY (window), GDK_SURFACE_XID (window), + hints_atom, hints_atom, 32, PropModeReplace, + (guchar *)hints, sizeof (MotifWmHints)/sizeof (long)); + + if (hints != new_hints) + XFree (hints); +} + +static void +gdk_x11_surface_set_decorations (GdkSurface *window, + GdkWMDecoration decorations) +{ + MotifWmHints hints; + + if (GDK_SURFACE_DESTROYED (window) || + !WINDOW_IS_TOPLEVEL_OR_FOREIGN (window)) + return; + + /* initialize to zero to avoid writing uninitialized data to socket */ + memset(&hints, 0, sizeof(hints)); + hints.flags = MWM_HINTS_DECORATIONS; + hints.decorations = decorations; + + gdk_surface_set_mwm_hints (window, &hints); +} + +static gboolean +gdk_x11_surface_get_decorations(GdkSurface *window, + GdkWMDecoration *decorations) +{ + MotifWmHints *hints; + gboolean result = FALSE; + + if (GDK_SURFACE_DESTROYED (window) || + !WINDOW_IS_TOPLEVEL_OR_FOREIGN (window)) + return FALSE; + + hints = gdk_surface_get_mwm_hints (window); + + if (hints) + { + if (hints->flags & MWM_HINTS_DECORATIONS) + { + if (decorations) + *decorations = hints->decorations; + result = TRUE; + } + + XFree (hints); + } + + return result; +} + +static void +gdk_x11_surface_set_functions (GdkSurface *window, + GdkWMFunction functions) +{ + MotifWmHints hints; + + g_return_if_fail (GDK_IS_SURFACE (window)); + + if (GDK_SURFACE_DESTROYED (window) || + !WINDOW_IS_TOPLEVEL_OR_FOREIGN (window)) + return; + + /* initialize to zero to avoid writing uninitialized data to socket */ + memset(&hints, 0, sizeof(hints)); + hints.flags = MWM_HINTS_FUNCTIONS; + hints.functions = functions; + + gdk_surface_set_mwm_hints (window, &hints); +} + +cairo_region_t * +_gdk_x11_xwindow_get_shape (Display *xdisplay, + Window window, + gint scale, + gint shape_type) +{ + cairo_region_t *shape; + GdkRectangle *rl; + XRectangle *xrl; + gint rn, ord, i; + + shape = NULL; + rn = 0; + + /* Note that XShapeGetRectangles returns NULL in two situations: + * - the server doesn't support the SHAPE extension + * - the shape is empty + * + * Since we can't discriminate these here, we always return + * an empty shape. It is the callers responsibility to check + * whether the server supports the SHAPE extensions beforehand. + */ + xrl = XShapeGetRectangles (xdisplay, window, shape_type, &rn, &ord); + + if (rn == 0) + return cairo_region_create (); /* Empty */ + + if (ord != YXBanded) + { + /* This really shouldn't happen with any xserver, as they + * generally convert regions to YXBanded internally + */ + g_warning ("non YXBanded shape masks not supported"); + XFree (xrl); + return NULL; + } + + /* NOTE: The scale divisions here may lose some precision if someone + else set the shape to be non-scale precision */ + rl = g_new (GdkRectangle, rn); + for (i = 0; i < rn; i++) + { + rl[i].x = xrl[i].x / scale; + rl[i].y = xrl[i].y / scale; + rl[i].width = xrl[i].width / scale; + rl[i].height = xrl[i].height / scale; + } + XFree (xrl); + + shape = cairo_region_create_rectangles (rl, rn); + g_free (rl); + + return shape; +} + +/* From the WM spec */ +#define _NET_WM_MOVERESIZE_SIZE_TOPLEFT 0 +#define _NET_WM_MOVERESIZE_SIZE_TOP 1 +#define _NET_WM_MOVERESIZE_SIZE_TOPRIGHT 2 +#define _NET_WM_MOVERESIZE_SIZE_RIGHT 3 +#define _NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT 4 +#define _NET_WM_MOVERESIZE_SIZE_BOTTOM 5 +#define _NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT 6 +#define _NET_WM_MOVERESIZE_SIZE_LEFT 7 +#define _NET_WM_MOVERESIZE_MOVE 8 /* movement only */ +#define _NET_WM_MOVERESIZE_SIZE_KEYBOARD 9 /* size via keyboard */ +#define _NET_WM_MOVERESIZE_MOVE_KEYBOARD 10 /* move via keyboard */ +#define _NET_WM_MOVERESIZE_CANCEL 11 /* cancel operation */ + +static void +wmspec_send_message (GdkDisplay *display, + GdkSurface *window, + gint root_x, + gint root_y, + gint action, + gint button) +{ + GdkSurfaceImplX11 *impl = GDK_SURFACE_IMPL_X11 (window->impl); + XClientMessageEvent xclient; + + memset (&xclient, 0, sizeof (xclient)); + xclient.type = ClientMessage; + xclient.window = GDK_SURFACE_XID (window); + xclient.message_type = + gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_MOVERESIZE"); + xclient.format = 32; + xclient.data.l[0] = root_x * impl->window_scale; + xclient.data.l[1] = root_y * impl->window_scale; + xclient.data.l[2] = action; + xclient.data.l[3] = button; + xclient.data.l[4] = 1; /* source indication */ + + XSendEvent (GDK_DISPLAY_XDISPLAY (display), GDK_SURFACE_XROOTWIN (window), False, + SubstructureRedirectMask | SubstructureNotifyMask, + (XEvent *)&xclient); +} + +static void +handle_wmspec_button_release (GdkDisplay *display, + const XEvent *xevent) +{ + GdkX11Display *display_x11 = GDK_X11_DISPLAY (display); + GdkSurface *window; + +#if defined (HAVE_XGENERICEVENTS) && defined (XINPUT_2) + XIEvent *xiev = (XIEvent *) xevent->xcookie.data; + XIDeviceEvent *xidev = (XIDeviceEvent *) xiev; + + if (xevent->xany.type == GenericEvent) + window = gdk_x11_surface_lookup_for_display (display, xidev->event); + else +#endif + window = gdk_x11_surface_lookup_for_display (display, xevent->xany.window); + + if (display_x11->wm_moveresize_button != 0 && window != NULL) + { + if ((xevent->xany.type == ButtonRelease && + xevent->xbutton.button == display_x11->wm_moveresize_button) +#if defined (HAVE_XGENERICEVENTS) && defined (XINPUT_2) + || + (xevent->xany.type == GenericEvent && + xiev->evtype == XI_ButtonRelease && + xidev->detail == display_x11->wm_moveresize_button) +#endif + ) + { + display_x11->wm_moveresize_button = 0; + wmspec_send_message (display, window, 0, 0, _NET_WM_MOVERESIZE_CANCEL, 0); + } + } +} + +static void +wmspec_moveresize (GdkSurface *window, + gint direction, + GdkDevice *device, + gint button, + gint root_x, + gint root_y, + guint32 timestamp) +{ + GdkDisplay *display = GDK_SURFACE_DISPLAY (window); + + if (button != 0) + gdk_seat_ungrab (gdk_device_get_seat (device)); /* Release passive grab */ + GDK_X11_DISPLAY (display)->wm_moveresize_button = button; + + wmspec_send_message (display, window, root_x, root_y, direction, button); +} + +static void +wmspec_resize_drag (GdkSurface *window, + GdkSurfaceEdge edge, + GdkDevice *device, + gint button, + gint root_x, + gint root_y, + guint32 timestamp) +{ + gint direction; + + if (button == 0) + direction = _NET_WM_MOVERESIZE_SIZE_KEYBOARD; + else + switch (edge) + { + /* Let the compiler turn a switch into a table, instead + * of doing the table manually, this way is easier to verify. + */ + case GDK_SURFACE_EDGE_NORTH_WEST: + direction = _NET_WM_MOVERESIZE_SIZE_TOPLEFT; + break; + + case GDK_SURFACE_EDGE_NORTH: + direction = _NET_WM_MOVERESIZE_SIZE_TOP; + break; + + case GDK_SURFACE_EDGE_NORTH_EAST: + direction = _NET_WM_MOVERESIZE_SIZE_TOPRIGHT; + break; + + case GDK_SURFACE_EDGE_WEST: + direction = _NET_WM_MOVERESIZE_SIZE_LEFT; + break; + + case GDK_SURFACE_EDGE_EAST: + direction = _NET_WM_MOVERESIZE_SIZE_RIGHT; + break; + + case GDK_SURFACE_EDGE_SOUTH_WEST: + direction = _NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT; + break; + + case GDK_SURFACE_EDGE_SOUTH: + direction = _NET_WM_MOVERESIZE_SIZE_BOTTOM; + break; + + case GDK_SURFACE_EDGE_SOUTH_EAST: + direction = _NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT; + break; + + default: + g_warning ("gdk_surface_begin_resize_drag: bad resize edge %d!", + edge); + return; + } + + wmspec_moveresize (window, direction, device, button, root_x, root_y, timestamp); +} + +typedef struct _MoveResizeData MoveResizeData; + +struct _MoveResizeData +{ + GdkDisplay *display; + + GdkSurface *moveresize_window; + GdkSurface *moveresize_emulation_window; + gboolean is_resize; + GdkSurfaceEdge resize_edge; + GdkDevice *device; + gint moveresize_button; + gint moveresize_x; + gint moveresize_y; + gint moveresize_orig_x; + gint moveresize_orig_y; + gint moveresize_orig_width; + gint moveresize_orig_height; + GdkSurfaceHints moveresize_geom_mask; + GdkGeometry moveresize_geometry; + Time moveresize_process_time; + XEvent *moveresize_pending_event; +}; + +static MoveResizeData * +get_move_resize_data (GdkDisplay *display, + gboolean create) +{ + MoveResizeData *mv_resize; + static GQuark move_resize_quark = 0; + + if (!move_resize_quark) + move_resize_quark = g_quark_from_static_string ("gdk-window-moveresize"); + + mv_resize = g_object_get_qdata (G_OBJECT (display), move_resize_quark); + + if (!mv_resize && create) + { + mv_resize = g_new0 (MoveResizeData, 1); + mv_resize->display = display; + + g_object_set_qdata (G_OBJECT (display), move_resize_quark, mv_resize); + } + + return mv_resize; +} + +static void +check_maximize (MoveResizeData *mv_resize, + gdouble x_root, + gdouble y_root) +{ + GdkSurfaceState state; + gint y; + + if (mv_resize->is_resize) + return; + + state = gdk_surface_get_state (mv_resize->moveresize_window); + + if (state & GDK_SURFACE_STATE_MAXIMIZED) + return; + + y = mv_resize->moveresize_orig_y + (y_root - mv_resize->moveresize_y); + + if (y < 10) + gdk_surface_maximize (mv_resize->moveresize_window); +} + +static void +check_unmaximize (MoveResizeData *mv_resize, + gdouble x_root, + gdouble y_root) +{ + GdkSurfaceState state; + gint dx, dy; + + if (mv_resize->is_resize) + return; + + state = gdk_surface_get_state (mv_resize->moveresize_window); + + if ((state & (GDK_SURFACE_STATE_MAXIMIZED | GDK_SURFACE_STATE_TILED)) == 0) + return; + + dx = x_root - mv_resize->moveresize_x; + dy = y_root - mv_resize->moveresize_y; + + if (ABS (dx) > 20 || ABS (dy) > 20) + gdk_surface_unmaximize (mv_resize->moveresize_window); +} + +static void +update_pos (MoveResizeData *mv_resize, + gint new_root_x, + gint new_root_y) +{ + gint dx, dy; + + check_unmaximize (mv_resize, new_root_x, new_root_y); + dx = new_root_x - mv_resize->moveresize_x; + dy = new_root_y - mv_resize->moveresize_y; + + if (mv_resize->is_resize) + { + gint x, y, w, h; + + x = mv_resize->moveresize_orig_x; + y = mv_resize->moveresize_orig_y; + + w = mv_resize->moveresize_orig_width; + h = mv_resize->moveresize_orig_height; + + switch (mv_resize->resize_edge) + { + case GDK_SURFACE_EDGE_NORTH_WEST: + x += dx; + y += dy; + w -= dx; + h -= dy; + break; + case GDK_SURFACE_EDGE_NORTH: + y += dy; + h -= dy; + break; + case GDK_SURFACE_EDGE_NORTH_EAST: + y += dy; + h -= dy; + w += dx; + break; + case GDK_SURFACE_EDGE_SOUTH_WEST: + h += dy; + x += dx; + w -= dx; + break; + case GDK_SURFACE_EDGE_SOUTH_EAST: + w += dx; + h += dy; + break; + case GDK_SURFACE_EDGE_SOUTH: + h += dy; + break; + case GDK_SURFACE_EDGE_EAST: + w += dx; + break; + case GDK_SURFACE_EDGE_WEST: + x += dx; + w -= dx; + break; + default: + break; + } + + x = MAX (x, 0); + y = MAX (y, 0); + w = MAX (w, 1); + h = MAX (h, 1); + + if (mv_resize->moveresize_geom_mask) + { + gdk_surface_constrain_size (&mv_resize->moveresize_geometry, + mv_resize->moveresize_geom_mask, + w, h, &w, &h); + } + + gdk_surface_move_resize (mv_resize->moveresize_window, x, y, w, h); + } + else + { + gint x, y; + + x = mv_resize->moveresize_orig_x + dx; + y = mv_resize->moveresize_orig_y + dy; + + gdk_surface_move (mv_resize->moveresize_window, x, y); + } +} + +static void +finish_drag (MoveResizeData *mv_resize) +{ + gdk_surface_destroy (mv_resize->moveresize_emulation_window); + mv_resize->moveresize_emulation_window = NULL; + g_clear_object (&mv_resize->moveresize_window); + g_clear_pointer (&mv_resize->moveresize_pending_event, g_free); +} + +static int +lookahead_motion_predicate (Display *xdisplay, + XEvent *event, + XPointer arg) +{ + gboolean *seen_release = (gboolean *)arg; + GdkDisplay *display = gdk_x11_lookup_xdisplay (xdisplay); + MoveResizeData *mv_resize = get_move_resize_data (display, FALSE); + + if (*seen_release) + return False; + + switch (event->xany.type) + { + case ButtonRelease: + *seen_release = TRUE; + break; + case MotionNotify: + mv_resize->moveresize_process_time = event->xmotion.time; + break; + default: + break; + } + + return False; +} + +static gboolean +moveresize_lookahead (MoveResizeData *mv_resize, + const XEvent *event) +{ + XEvent tmp_event; + gboolean seen_release = FALSE; + + if (mv_resize->moveresize_process_time) + { + if (event->xmotion.time == mv_resize->moveresize_process_time) + { + mv_resize->moveresize_process_time = 0; + return TRUE; + } + else + return FALSE; + } + + XCheckIfEvent (event->xany.display, &tmp_event, + lookahead_motion_predicate, (XPointer) & seen_release); + + return mv_resize->moveresize_process_time == 0; +} + +gboolean +_gdk_x11_moveresize_handle_event (const XEvent *event) +{ + guint button_mask = 0; + GdkDisplay *display = gdk_x11_lookup_xdisplay (event->xany.display); + MoveResizeData *mv_resize = get_move_resize_data (display, FALSE); + GdkSurfaceImplX11 *impl; + + if (!mv_resize || !mv_resize->moveresize_window) + { + handle_wmspec_button_release (display, event); + return FALSE; + } + + impl = GDK_SURFACE_IMPL_X11 (mv_resize->moveresize_window->impl); + + if (mv_resize->moveresize_button != 0) + button_mask = GDK_BUTTON1_MASK << (mv_resize->moveresize_button - 1); + + switch (event->xany.type) + { + case MotionNotify: + if (mv_resize->moveresize_window->resize_count > 0) + { + if (mv_resize->moveresize_pending_event) + *mv_resize->moveresize_pending_event = *event; + else + mv_resize->moveresize_pending_event = + g_memdup (event, sizeof (XEvent)); + + break; + } + if (!moveresize_lookahead (mv_resize, event)) + break; + + update_pos (mv_resize, + event->xmotion.x_root / impl->window_scale, + event->xmotion.y_root / impl->window_scale); + + /* This should never be triggered in normal cases, but in the + * case where the drag started without an implicit grab being + * in effect, we could miss the release if it occurs before + * we grab the pointer; this ensures that we will never + * get a permanently stuck grab. + */ + if ((event->xmotion.state & button_mask) == 0) + { + check_maximize (mv_resize, + event->xmotion.x_root / impl->window_scale, + event->xmotion.y_root / impl->window_scale); + finish_drag (mv_resize); + } + break; + + case ButtonRelease: + update_pos (mv_resize, + event->xbutton.x_root / impl->window_scale, + event->xbutton.y_root / impl->window_scale); + + if (event->xbutton.button == mv_resize->moveresize_button) + { + check_maximize (mv_resize, + event->xmotion.x_root / impl->window_scale, + event->xmotion.y_root / impl->window_scale); + finish_drag (mv_resize); + } + break; + +#if defined (HAVE_XGENERICEVENTS) && defined (XINPUT_2) + case GenericEvent: + { + /* we just assume this is an XI2 event */ + XIEvent *ev = (XIEvent *) event->xcookie.data; + XIDeviceEvent *xev = (XIDeviceEvent *)ev; + gint state; + switch (ev->evtype) + { + case XI_Motion: + update_pos (mv_resize, xev->root_x / impl->window_scale, xev->root_y / impl->window_scale); + state = _gdk_x11_device_xi2_translate_state (&xev->mods, &xev->buttons, &xev->group); + if ((state & button_mask) == 0) + { + check_maximize (mv_resize, + xev->root_x / impl->window_scale, + xev->root_y / impl->window_scale); + finish_drag (mv_resize); + } + break; + + case XI_ButtonRelease: + update_pos (mv_resize, xev->root_x / impl->window_scale, xev->root_y / impl->window_scale); + if (xev->detail == mv_resize->moveresize_button) + { + check_maximize (mv_resize, + xev->root_x / impl->window_scale, + xev->root_y / impl->window_scale); + finish_drag (mv_resize); + } + break; + default: + break; + } + } + break; +#endif + + default: + break; + + } + return TRUE; +} + +gboolean +_gdk_x11_moveresize_configure_done (GdkDisplay *display, + GdkSurface *window) +{ + XEvent *tmp_event; + MoveResizeData *mv_resize = get_move_resize_data (display, FALSE); + + if (!mv_resize || window != mv_resize->moveresize_window) + return FALSE; + + if (mv_resize->moveresize_pending_event) + { + tmp_event = mv_resize->moveresize_pending_event; + mv_resize->moveresize_pending_event = NULL; + _gdk_x11_moveresize_handle_event (tmp_event); + g_free (tmp_event); + } + + return TRUE; +} + +static void +create_moveresize_window (MoveResizeData *mv_resize, + guint32 timestamp) +{ + GdkGrabStatus status; + + g_assert (mv_resize->moveresize_emulation_window == NULL); + + mv_resize->moveresize_emulation_window = gdk_surface_new_temp (mv_resize->display); + gdk_surface_show (mv_resize->moveresize_emulation_window); + + status = gdk_seat_grab (gdk_device_get_seat (mv_resize->device), + mv_resize->moveresize_emulation_window, + GDK_SEAT_CAPABILITY_POINTER, FALSE, + NULL, NULL, NULL, NULL); + + if (status != GDK_GRAB_SUCCESS) + { + /* If this fails, some other client has grabbed the window + * already. + */ + finish_drag (mv_resize); + } + + mv_resize->moveresize_process_time = 0; +} + +/* + Calculate mv_resize->moveresize_orig_x and mv_resize->moveresize_orig_y + so that calling XMoveWindow with these coordinates will not move the + window. + Note that this depends on the WM to implement ICCCM-compliant reference + point handling. +*/ +static void +calculate_unmoving_origin (MoveResizeData *mv_resize) +{ + GdkRectangle rect; + gint width, height; + + if (mv_resize->moveresize_geom_mask & GDK_HINT_WIN_GRAVITY && + mv_resize->moveresize_geometry.win_gravity == GDK_GRAVITY_STATIC) + { + gdk_surface_get_origin (mv_resize->moveresize_window, + &mv_resize->moveresize_orig_x, + &mv_resize->moveresize_orig_y); + } + else + { + gdk_surface_get_frame_extents (mv_resize->moveresize_window, &rect); + gdk_surface_get_geometry (mv_resize->moveresize_window, + NULL, NULL, &width, &height); + + switch (mv_resize->moveresize_geometry.win_gravity) + { + case GDK_GRAVITY_NORTH_WEST: + mv_resize->moveresize_orig_x = rect.x; + mv_resize->moveresize_orig_y = rect.y; + break; + case GDK_GRAVITY_NORTH: + mv_resize->moveresize_orig_x = rect.x + rect.width / 2 - width / 2; + mv_resize->moveresize_orig_y = rect.y; + break; + case GDK_GRAVITY_NORTH_EAST: + mv_resize->moveresize_orig_x = rect.x + rect.width - width; + mv_resize->moveresize_orig_y = rect.y; + break; + case GDK_GRAVITY_WEST: + mv_resize->moveresize_orig_x = rect.x; + mv_resize->moveresize_orig_y = rect.y + rect.height / 2 - height / 2; + break; + case GDK_GRAVITY_CENTER: + mv_resize->moveresize_orig_x = rect.x + rect.width / 2 - width / 2; + mv_resize->moveresize_orig_y = rect.y + rect.height / 2 - height / 2; + break; + case GDK_GRAVITY_EAST: + mv_resize->moveresize_orig_x = rect.x + rect.width - width; + mv_resize->moveresize_orig_y = rect.y + rect.height / 2 - height / 2; + break; + case GDK_GRAVITY_SOUTH_WEST: + mv_resize->moveresize_orig_x = rect.x; + mv_resize->moveresize_orig_y = rect.y + rect.height - height; + break; + case GDK_GRAVITY_SOUTH: + mv_resize->moveresize_orig_x = rect.x + rect.width / 2 - width / 2; + mv_resize->moveresize_orig_y = rect.y + rect.height - height; + break; + case GDK_GRAVITY_SOUTH_EAST: + mv_resize->moveresize_orig_x = rect.x + rect.width - width; + mv_resize->moveresize_orig_y = rect.y + rect.height - height; + break; + case GDK_GRAVITY_STATIC: + default: + mv_resize->moveresize_orig_x = rect.x; + mv_resize->moveresize_orig_y = rect.y; + break; + } + } +} + +static void +emulate_resize_drag (GdkSurface *window, + GdkSurfaceEdge edge, + GdkDevice *device, + gint button, + gint root_x, + gint root_y, + guint32 timestamp) +{ + MoveResizeData *mv_resize = get_move_resize_data (GDK_SURFACE_DISPLAY (window), TRUE); + + if (mv_resize->moveresize_window != NULL) + return; /* already a drag operation in progress */ + + mv_resize->is_resize = TRUE; + mv_resize->moveresize_button = button; + mv_resize->resize_edge = edge; + mv_resize->device = device; + mv_resize->moveresize_x = root_x; + mv_resize->moveresize_y = root_y; + mv_resize->moveresize_window = g_object_ref (window); + + mv_resize->moveresize_orig_width = gdk_surface_get_width (window); + mv_resize->moveresize_orig_height = gdk_surface_get_height (window); + + mv_resize->moveresize_geom_mask = 0; + gdk_surface_get_geometry_hints (window, + &mv_resize->moveresize_geometry, + &mv_resize->moveresize_geom_mask); + + calculate_unmoving_origin (mv_resize); + + create_moveresize_window (mv_resize, timestamp); +} + +static void +emulate_move_drag (GdkSurface *window, + GdkDevice *device, + gint button, + gint root_x, + gint root_y, + guint32 timestamp) +{ + MoveResizeData *mv_resize = get_move_resize_data (GDK_SURFACE_DISPLAY (window), TRUE); + + if (mv_resize->moveresize_window != NULL) + return; /* already a drag operation in progress */ + + mv_resize->is_resize = FALSE; + mv_resize->device = device; + mv_resize->moveresize_button = button; + mv_resize->moveresize_x = root_x; + mv_resize->moveresize_y = root_y; + + mv_resize->moveresize_window = g_object_ref (window); + + calculate_unmoving_origin (mv_resize); + + create_moveresize_window (mv_resize, timestamp); +} + +static gboolean +_should_perform_ewmh_drag (GdkSurface *window, + GdkDevice *device) +{ + GdkPointerSurfaceInfo *info; + GdkDisplay *display; + + display = gdk_surface_get_display (window); + info = _gdk_display_get_pointer_info (display, device); + + if ((!info->last_slave || gdk_device_get_source (info->last_slave) != GDK_SOURCE_TOUCHSCREEN) && + gdk_x11_screen_supports_net_wm_hint (GDK_SURFACE_SCREEN (window), + g_intern_static_string ("_NET_WM_MOVERESIZE"))) + return TRUE; + + return FALSE; +} + +static void +gdk_x11_surface_begin_resize_drag (GdkSurface *window, + GdkSurfaceEdge edge, + GdkDevice *device, + gint button, + gint root_x, + gint root_y, + guint32 timestamp) +{ + if (GDK_SURFACE_DESTROYED (window) || + !WINDOW_IS_TOPLEVEL_OR_FOREIGN (window)) + return; + + /* Avoid EWMH for touch devices */ + if (_should_perform_ewmh_drag (window, device)) + wmspec_resize_drag (window, edge, device, button, root_x, root_y, timestamp); + else + emulate_resize_drag (window, edge, device, button, root_x, root_y, timestamp); +} + +static void +gdk_x11_surface_begin_move_drag (GdkSurface *window, + GdkDevice *device, + gint button, + gint root_x, + gint root_y, + guint32 timestamp) +{ + gint direction; + + if (GDK_SURFACE_DESTROYED (window) || !WINDOW_IS_TOPLEVEL (window)) + return; + + if (button == 0) + direction = _NET_WM_MOVERESIZE_MOVE_KEYBOARD; + else + direction = _NET_WM_MOVERESIZE_MOVE; + + /* Avoid EWMH for touch devices */ + if (_should_perform_ewmh_drag (window, device)) + wmspec_moveresize (window, direction, device, button, root_x, root_y, timestamp); + else + emulate_move_drag (window, device, button, root_x, root_y, timestamp); +} + +static gboolean +gdk_x11_surface_beep (GdkSurface *window) +{ + GdkDisplay *display; + + display = GDK_SURFACE_DISPLAY (window); + +#ifdef HAVE_XKB + if (GDK_X11_DISPLAY (display)->use_xkb) + { + XkbBell (GDK_DISPLAY_XDISPLAY (display), + GDK_SURFACE_XID (window), + 0, + None); + return TRUE; + } +#endif + + return FALSE; +} + +static void +gdk_x11_surface_set_opacity (GdkSurface *window, + gdouble opacity) +{ + GdkDisplay *display; + gulong cardinal; + + g_return_if_fail (GDK_IS_SURFACE (window)); + + if (GDK_SURFACE_DESTROYED (window) || + !WINDOW_IS_TOPLEVEL (window)) + return; + + display = gdk_surface_get_display (window); + + if (opacity < 0) + opacity = 0; + else if (opacity > 1) + opacity = 1; + + cardinal = opacity * 0xffffffff; + + if (cardinal == 0xffffffff) + XDeleteProperty (GDK_DISPLAY_XDISPLAY (display), + GDK_SURFACE_XID (window), + gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_WINDOW_OPACITY")); + else + XChangeProperty (GDK_DISPLAY_XDISPLAY (display), + GDK_SURFACE_XID (window), + gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_WINDOW_OPACITY"), + XA_CARDINAL, 32, + PropModeReplace, + (guchar *) &cardinal, 1); +} + +static Bool +timestamp_predicate (Display *display, + XEvent *xevent, + XPointer arg) +{ + Window xwindow = GPOINTER_TO_UINT (arg); + GdkDisplay *gdk_display = gdk_x11_lookup_xdisplay (display); + + if (xevent->type == PropertyNotify && + xevent->xproperty.window == xwindow && + xevent->xproperty.atom == gdk_x11_get_xatom_by_name_for_display (gdk_display, + "GDK_TIMESTAMP_PROP")) + return True; + + return False; +} + +/** + * gdk_x11_get_server_time: + * @window: (type GdkX11Surface): a #GdkSurface, used for communication + * with the server. The window must have + * GDK_PROPERTY_CHANGE_MASK in its events mask or a hang will + * result. + * + * Routine to get the current X server time stamp. + * + * Returns: the time stamp. + **/ +guint32 +gdk_x11_get_server_time (GdkSurface *window) +{ + Display *xdisplay; + Window xwindow; + guchar c = 'a'; + XEvent xevent; + Atom timestamp_prop_atom; + + g_return_val_if_fail (GDK_IS_SURFACE (window), 0); + g_return_val_if_fail (!GDK_SURFACE_DESTROYED (window), 0); + + xdisplay = GDK_SURFACE_XDISPLAY (window); + xwindow = GDK_SURFACE_XID (window); + timestamp_prop_atom = + gdk_x11_get_xatom_by_name_for_display (GDK_SURFACE_DISPLAY (window), + "GDK_TIMESTAMP_PROP"); + + XChangeProperty (xdisplay, xwindow, timestamp_prop_atom, + timestamp_prop_atom, + 8, PropModeReplace, &c, 1); + + XIfEvent (xdisplay, &xevent, + timestamp_predicate, GUINT_TO_POINTER(xwindow)); + + return xevent.xproperty.time; +} + +/** + * gdk_x11_surface_get_xid: + * @window: (type GdkX11Surface): a native #GdkSurface. + * + * Returns the X resource (window) belonging to a #GdkSurface. + * + * Returns: the ID of @drawable’s X resource. + **/ +XID +gdk_x11_surface_get_xid (GdkSurface *window) +{ + if (!GDK_SURFACE_IS_X11 (window) || + !_gdk_surface_has_impl (window)) + { + g_warning (G_STRLOC " drawable is not a native X11 window"); + return None; + } + + return GDK_SURFACE_IMPL_X11 (window->impl)->xid; +} + +static gint +gdk_x11_surface_get_scale_factor (GdkSurface *window) +{ + GdkSurfaceImplX11 *impl = GDK_SURFACE_IMPL_X11 (window->impl); + + if (GDK_SURFACE_DESTROYED (window)) + return 1; + + return impl->window_scale; +} + +/** + * gdk_x11_surface_set_frame_sync_enabled: + * @window: (type GdkX11Surface): a native #GdkSurface + * @frame_sync_enabled: whether frame-synchronization should be enabled + * + * This function can be used to disable frame synchronization for a window. + * Normally frame synchronziation will be enabled or disabled based on whether + * the system has a compositor that supports frame synchronization, but if + * the window is not directly managed by the window manager, then frame + * synchronziation may need to be disabled. This is the case for a window + * embedded via the XEMBED protocol. + */ +void +gdk_x11_surface_set_frame_sync_enabled (GdkSurface *window, + gboolean frame_sync_enabled) +{ + if (!GDK_SURFACE_IS_X11 (window) || + !_gdk_surface_has_impl (window)) + { + g_warning (G_STRLOC " drawable is not a native X11 window"); + return; + } + + GDK_SURFACE_IMPL_X11 (window->impl)->frame_sync_enabled = FALSE; +} + +static void +gdk_x11_surface_set_opaque_region (GdkSurface *window, + cairo_region_t *region) +{ + GdkSurfaceImplX11 *impl = GDK_SURFACE_IMPL_X11 (window->impl); + GdkDisplay *display; + int nitems; + gulong *data; + + if (GDK_SURFACE_DESTROYED (window)) + return; + + if (region != NULL) + { + int i, nrects; + + nrects = cairo_region_num_rectangles (region); + nitems = nrects * 4; + data = g_new (gulong, nitems); + + for (i = 0; i < nrects; i++) + { + cairo_rectangle_int_t rect; + cairo_region_get_rectangle (region, i, &rect); + data[i*4+0] = rect.x * impl->window_scale; + data[i*4+1] = rect.y * impl->window_scale; + data[i*4+2] = rect.width * impl->window_scale; + data[i*4+3] = rect.height * impl->window_scale; + } + } + else + { + nitems = 0; + data = NULL; + } + + display = gdk_surface_get_display (window); + + XChangeProperty (GDK_DISPLAY_XDISPLAY (display), + GDK_SURFACE_XID (window), + gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_OPAQUE_REGION"), + XA_CARDINAL, 32, PropModeReplace, + (guchar *) data, nitems); + + g_free (data); +} + +static gboolean +gdk_x11_surface_show_window_menu (GdkSurface *window, + GdkEvent *event) +{ + GdkSurfaceImplX11 *impl = GDK_SURFACE_IMPL_X11 (window->impl); + GdkDisplay *display = GDK_SURFACE_DISPLAY (window); + GdkDevice *device; + int device_id; + double x_root, y_root; + XClientMessageEvent xclient = { 0 }; + + switch ((guint) event->any.type) + { + case GDK_BUTTON_PRESS: + case GDK_BUTTON_RELEASE: + break; + default: + return FALSE; + } + + if (!gdk_x11_screen_supports_net_wm_hint (GDK_SURFACE_SCREEN (window), + g_intern_static_string ("_GTK_SHOW_WINDOW_MENU"))) + return FALSE; + + gdk_event_get_root_coords (event, &x_root, &y_root); + device = gdk_event_get_device (event); + g_object_get (G_OBJECT (device), + "device-id", &device_id, + NULL); + + /* Ungrab the implicit grab */ + gdk_seat_ungrab (gdk_device_get_seat (device)); + + xclient.type = ClientMessage; + xclient.window = GDK_SURFACE_XID (window); + xclient.message_type = gdk_x11_get_xatom_by_name_for_display (display, "_GTK_SHOW_WINDOW_MENU"); + xclient.data.l[0] = device_id; + xclient.data.l[1] = x_root * impl->window_scale; + xclient.data.l[2] = y_root * impl->window_scale; + xclient.format = 32; + + XSendEvent (GDK_DISPLAY_XDISPLAY (display), GDK_SURFACE_XROOTWIN (window), False, + SubstructureRedirectMask | SubstructureNotifyMask, + (XEvent *)&xclient); + + return TRUE; +} + +static void +gdk_surface_impl_x11_class_init (GdkSurfaceImplX11Class *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GdkSurfaceImplClass *impl_class = GDK_SURFACE_IMPL_CLASS (klass); + + object_class->finalize = gdk_surface_impl_x11_finalize; + + impl_class->ref_cairo_surface = gdk_x11_ref_cairo_surface; + impl_class->show = gdk_surface_x11_show; + impl_class->hide = gdk_surface_x11_hide; + impl_class->withdraw = gdk_surface_x11_withdraw; + impl_class->set_events = gdk_surface_x11_set_events; + impl_class->get_events = gdk_surface_x11_get_events; + impl_class->raise = gdk_surface_x11_raise; + impl_class->lower = gdk_surface_x11_lower; + impl_class->restack_toplevel = gdk_surface_x11_restack_toplevel; + impl_class->move_resize = gdk_surface_x11_move_resize; + impl_class->get_geometry = gdk_surface_x11_get_geometry; + impl_class->get_root_coords = gdk_surface_x11_get_root_coords; + impl_class->get_device_state = gdk_surface_x11_get_device_state; + impl_class->shape_combine_region = gdk_surface_x11_shape_combine_region; + impl_class->input_shape_combine_region = gdk_surface_x11_input_shape_combine_region; + impl_class->queue_antiexpose = _gdk_x11_surface_queue_antiexpose; + impl_class->destroy = gdk_x11_surface_destroy; + impl_class->beep = gdk_x11_surface_beep; + + impl_class->focus = gdk_x11_surface_focus; + impl_class->set_type_hint = gdk_x11_surface_set_type_hint; + impl_class->get_type_hint = gdk_x11_surface_get_type_hint; + impl_class->set_modal_hint = gdk_x11_surface_set_modal_hint; + impl_class->set_skip_taskbar_hint = gdk_x11_surface_set_skip_taskbar_hint; + impl_class->set_skip_pager_hint = gdk_x11_surface_set_skip_pager_hint; + impl_class->set_urgency_hint = gdk_x11_surface_set_urgency_hint; + impl_class->set_geometry_hints = gdk_x11_surface_set_geometry_hints; + impl_class->set_title = gdk_x11_surface_set_title; + impl_class->set_role = gdk_x11_surface_set_role; + impl_class->set_startup_id = gdk_x11_surface_set_startup_id; + impl_class->set_transient_for = gdk_x11_surface_set_transient_for; + impl_class->get_frame_extents = gdk_x11_surface_get_frame_extents; + impl_class->set_accept_focus = gdk_x11_surface_set_accept_focus; + impl_class->set_focus_on_map = gdk_x11_surface_set_focus_on_map; + impl_class->set_icon_list = gdk_x11_surface_set_icon_list; + impl_class->set_icon_name = gdk_x11_surface_set_icon_name; + impl_class->iconify = gdk_x11_surface_iconify; + impl_class->deiconify = gdk_x11_surface_deiconify; + impl_class->stick = gdk_x11_surface_stick; + impl_class->unstick = gdk_x11_surface_unstick; + impl_class->maximize = gdk_x11_surface_maximize; + impl_class->unmaximize = gdk_x11_surface_unmaximize; + impl_class->fullscreen = gdk_x11_surface_fullscreen; + impl_class->fullscreen_on_monitor = gdk_x11_surface_fullscreen_on_monitor; + impl_class->apply_fullscreen_mode = gdk_x11_surface_apply_fullscreen_mode; + impl_class->unfullscreen = gdk_x11_surface_unfullscreen; + impl_class->set_keep_above = gdk_x11_surface_set_keep_above; + impl_class->set_keep_below = gdk_x11_surface_set_keep_below; + impl_class->get_group = gdk_x11_surface_get_group; + impl_class->set_group = gdk_x11_surface_set_group; + impl_class->set_decorations = gdk_x11_surface_set_decorations; + impl_class->get_decorations = gdk_x11_surface_get_decorations; + impl_class->set_functions = gdk_x11_surface_set_functions; + impl_class->begin_resize_drag = gdk_x11_surface_begin_resize_drag; + impl_class->begin_move_drag = gdk_x11_surface_begin_move_drag; + impl_class->set_opacity = gdk_x11_surface_set_opacity; + impl_class->destroy_notify = gdk_x11_surface_destroy_notify; + impl_class->register_dnd = _gdk_x11_surface_register_dnd; + impl_class->drag_begin = _gdk_x11_surface_drag_begin; + impl_class->get_scale_factor = gdk_x11_surface_get_scale_factor; + impl_class->set_opaque_region = gdk_x11_surface_set_opaque_region; + impl_class->set_shadow_width = gdk_x11_surface_set_shadow_width; + impl_class->show_window_menu = gdk_x11_surface_show_window_menu; + impl_class->create_gl_context = gdk_x11_surface_create_gl_context; + impl_class->get_unscaled_size = gdk_x11_surface_get_unscaled_size; + impl_class->supports_edge_constraints = gdk_x11_surface_supports_edge_constraints; +} diff --git a/gdk/x11/gdksurface-x11.h b/gdk/x11/gdksurface-x11.h new file mode 100644 index 0000000000..15993af589 --- /dev/null +++ b/gdk/x11/gdksurface-x11.h @@ -0,0 +1,199 @@ +/* GDK - The GIMP Drawing Kit + * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * 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, see . + */ + +/* + * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS + * file for a list of people on the GTK+ Team. See the ChangeLog + * files for a list of changes. These files are distributed with + * GTK+ at ftp://ftp.gtk.org/pub/gtk/. + */ + +#ifndef __GDK_SURFACE_X11_H__ +#define __GDK_SURFACE_X11_H__ + +#include "gdk/x11/gdkprivate-x11.h" +#include "gdk/gdksurfaceimpl.h" + +#include + +#ifdef HAVE_XDAMAGE +#include +#endif + +#ifdef HAVE_XSYNC +#include +#include +#endif + +G_BEGIN_DECLS + +typedef struct _GdkToplevelX11 GdkToplevelX11; +typedef struct _GdkSurfaceImplX11 GdkSurfaceImplX11; +typedef struct _GdkSurfaceImplX11Class GdkSurfaceImplX11Class; +typedef struct _GdkXPositionInfo GdkXPositionInfo; + +/* Window implementation for X11 + */ + +#define GDK_TYPE_SURFACE_IMPL_X11 (gdk_surface_impl_x11_get_type ()) +#define GDK_SURFACE_IMPL_X11(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), GDK_TYPE_SURFACE_IMPL_X11, GdkSurfaceImplX11)) +#define GDK_SURFACE_IMPL_X11_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GDK_TYPE_SURFACE_IMPL_X11, GdkSurfaceImplX11Class)) +#define GDK_IS_SURFACE_IMPL_X11(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), GDK_TYPE_SURFACE_IMPL_X11)) +#define GDK_IS_SURFACE_IMPL_X11_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GDK_TYPE_SURFACE_IMPL_X11)) +#define GDK_SURFACE_IMPL_X11_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GDK_TYPE_SURFACE_IMPL_X11, GdkSurfaceImplX11Class)) + +struct _GdkSurfaceImplX11 +{ + GdkSurfaceImpl parent_instance; + + GdkSurface *wrapper; + + Window xid; + + GdkToplevelX11 *toplevel; /* Toplevel-specific information */ + GdkCursor *cursor; + + guint no_bg : 1; /* Set when the window background is temporarily + * unset during resizing and scaling */ + guint override_redirect : 1; + guint frame_clock_connected : 1; + guint frame_sync_enabled : 1; + guint tracking_damage: 1; + + gint window_scale; + + /* Width and height not divided by window_scale - this matters in the + * corner-case where the window manager assigns us a size that isn't + * a multiple of window_scale - for example for a maximized window + * with an odd-sized title-bar. + */ + gint unscaled_width; + gint unscaled_height; + + cairo_surface_t *cairo_surface; + +#if defined (HAVE_XCOMPOSITE) && defined(HAVE_XDAMAGE) && defined (HAVE_XFIXES) + Damage damage; +#endif +}; + +struct _GdkSurfaceImplX11Class +{ + GdkSurfaceImplClass parent_class; +}; + +struct _GdkToplevelX11 +{ + + /* Set if the window, or any descendent of it, is the server's focus window + */ + guint has_focus_window : 1; + + /* Set if window->has_focus_window and the focus isn't grabbed elsewhere. + */ + guint has_focus : 1; + + /* Set if the pointer is inside this window. (This is needed for + * for focus tracking) + */ + guint has_pointer : 1; + + /* Set if the window is a descendent of the focus window and the pointer is + * inside it. (This is the case where the window will receive keystroke + * events even window->has_focus_window is FALSE) + */ + guint has_pointer_focus : 1; + + /* Set if we are requesting these hints */ + guint skip_taskbar_hint : 1; + guint skip_pager_hint : 1; + guint urgency_hint : 1; + + guint on_all_desktops : 1; /* _NET_WM_STICKY == 0xFFFFFFFF */ + + guint have_sticky : 1; /* _NET_WM_STATE_STICKY */ + guint have_maxvert : 1; /* _NET_WM_STATE_MAXIMIZED_VERT */ + guint have_maxhorz : 1; /* _NET_WM_STATE_MAXIMIZED_HORZ */ + guint have_fullscreen : 1; /* _NET_WM_STATE_FULLSCREEN */ + guint have_hidden : 1; /* _NET_WM_STATE_HIDDEN */ + + guint is_leader : 1; + + /* Set if the WM is presenting us as focused, i.e. with active decorations + */ + guint have_focused : 1; + + guint in_frame : 1; + + /* If we're expecting a response from the compositor after painting a frame */ + guint frame_pending : 1; + + /* Whether pending_counter_value/configure_counter_value are updates + * to the extended update counter */ + guint pending_counter_value_is_extended : 1; + guint configure_counter_value_is_extended : 1; + + gulong map_serial; /* Serial of last transition from unmapped */ + + cairo_surface_t *icon_pixmap; + cairo_surface_t *icon_mask; + GdkSurface *group_leader; + + /* Time of most recent user interaction. */ + gulong user_time; + + /* We use an extra X window for toplevel windows that we XSetInputFocus() + * to in order to avoid getting keyboard events redirected to subwindows + * that might not even be part of this app + */ + Window focus_window; + + GdkSurfaceHints last_geometry_hints_mask; + GdkGeometry last_geometry_hints; + + /* Constrained edge information */ + guint edge_constraints; + +#ifdef HAVE_XSYNC + XID update_counter; + XID extended_update_counter; + gint64 pending_counter_value; /* latest _NET_WM_SYNC_REQUEST value received */ + gint64 configure_counter_value; /* Latest _NET_WM_SYNC_REQUEST value received + * where we have also seen the corresponding + * ConfigureNotify + */ + gint64 current_counter_value; + + /* After a _NET_WM_FRAME_DRAWN message, this is the soonest that we think + * frame after will be presented */ + gint64 throttled_presentation_time; +#endif +}; + +GType gdk_surface_impl_x11_get_type (void); + +GdkToplevelX11 *_gdk_x11_surface_get_toplevel (GdkSurface *window); + +GdkCursor *_gdk_x11_surface_get_cursor (GdkSurface *window); + +void _gdk_x11_surface_update_size (GdkSurfaceImplX11 *impl); +void _gdk_x11_surface_set_window_scale (GdkSurface *window, + int scale); + +G_END_DECLS + +#endif /* __GDK_SURFACE_X11_H__ */ diff --git a/gdk/x11/gdkvulkancontext-x11.c b/gdk/x11/gdkvulkancontext-x11.c index 0e88ec6d63..b27c407bd4 100644 --- a/gdk/x11/gdkvulkancontext-x11.c +++ b/gdk/x11/gdkvulkancontext-x11.c @@ -28,7 +28,7 @@ #include "gdkinternals.h" #include "gdkdisplay-x11.h" -#include "gdkwindow-x11.h" +#include "gdksurface-x11.h" G_DEFINE_TYPE (GdkX11VulkanContext, gdk_x11_vulkan_context, GDK_TYPE_VULKAN_CONTEXT) diff --git a/gdk/x11/gdkwindow-x11.c b/gdk/x11/gdkwindow-x11.c deleted file mode 100644 index c78c5c1840..0000000000 --- a/gdk/x11/gdkwindow-x11.c +++ /dev/null @@ -1,4948 +0,0 @@ -/* GDK - The GIMP Drawing Kit - * Copyright (C) 1995-2007 Peter Mattis, Spencer Kimball, - * Josh MacDonald, Ryan Lortie - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * 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, see . - */ - -/* - * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS - * file for a list of people on the GTK+ Team. See the ChangeLog - * files for a list of changes. These files are distributed with - * GTK+ at ftp://ftp.gtk.org/pub/gtk/. - */ - -#include "config.h" - -#include "gdkwindow-x11.h" - -#include "gdkwindow.h" -#include "gdkwindowimpl.h" -#include "gdkvisual-x11.h" -#include "gdkinternals.h" -#include "gdkdeviceprivate.h" -#include "gdkframeclockprivate.h" -#include "gdkasync.h" -#include "gdkeventsource.h" -#include "gdkdisplay-x11.h" -#include "gdkglcontext-x11.h" -#include "gdkprivate-x11.h" -#include "gdktextureprivate.h" -#include "gdk-private.h" - -#include -#include -#include -#include -#include - -#include - -#include "MwmUtil.h" - -#include -#include -#include - -#include - -#ifdef HAVE_XKB -#include -#endif - -#ifdef HAVE_XCOMPOSITE -#include -#endif - -#ifdef HAVE_XFIXES -#include -#endif - -#ifdef HAVE_XDAMAGE -#include -#endif - -const int _gdk_x11_event_mask_table[21] = -{ - ExposureMask, - PointerMotionMask, - PointerMotionHintMask, - ButtonMotionMask, - Button1MotionMask, - Button2MotionMask, - Button3MotionMask, - ButtonPressMask, - ButtonReleaseMask, - KeyPressMask, - KeyReleaseMask, - EnterWindowMask, - LeaveWindowMask, - FocusChangeMask, - StructureNotifyMask, - PropertyChangeMask, - VisibilityChangeMask, - 0, /* PROXIMITY_IN */ - 0, /* PROXIMTY_OUT */ - SubstructureNotifyMask, - ButtonPressMask /* SCROLL; on X mouse wheel events is treated as mouse button 4/5 */ -}; - -const gint _gdk_x11_event_mask_table_size = G_N_ELEMENTS (_gdk_x11_event_mask_table); - -/* Forward declarations */ -static void gdk_x11_surface_apply_fullscreen_mode (GdkSurface *window); -static gboolean gdk_surface_icon_name_set (GdkSurface *window); -static void set_wm_name (GdkDisplay *display, - Window xwindow, - const gchar *name); -static void move_to_current_desktop (GdkSurface *window); - -static void gdk_surface_impl_x11_finalize (GObject *object); - -#define WINDOW_IS_TOPLEVEL_OR_FOREIGN(window) \ - (GDK_SURFACE_TYPE (window) == GDK_SURFACE_TOPLEVEL || \ - GDK_SURFACE_TYPE (window) == GDK_SURFACE_TEMP || \ - GDK_SURFACE_TYPE (window) == GDK_SURFACE_FOREIGN) - -#define WINDOW_IS_TOPLEVEL(window) \ - (GDK_SURFACE_TYPE (window) == GDK_SURFACE_TOPLEVEL || \ - GDK_SURFACE_TYPE (window) == GDK_SURFACE_TEMP) - -/* Return whether time1 is considered later than time2 as far as xserver - * time is concerned. Accounts for wraparound. - */ -#define XSERVER_TIME_IS_LATER(time1, time2) \ - ( (( time1 > time2 ) && ( time1 - time2 < ((guint32)-1)/2 )) || \ - (( time1 < time2 ) && ( time2 - time1 > ((guint32)-1)/2 )) \ - ) - -struct _GdkX11Surface { - GdkSurface parent; -}; - -struct _GdkX11SurfaceClass { - GdkSurfaceClass parent_class; -}; - -G_DEFINE_TYPE (GdkX11Surface, gdk_x11_surface, GDK_TYPE_SURFACE) - -static void -gdk_x11_surface_class_init (GdkX11SurfaceClass *x11_surface_class) -{ -} - -static void -gdk_x11_surface_init (GdkX11Surface *x11_surface) -{ -} - - -G_DEFINE_TYPE (GdkSurfaceImplX11, gdk_surface_impl_x11, GDK_TYPE_SURFACE_IMPL) - -static void -gdk_surface_impl_x11_init (GdkSurfaceImplX11 *impl) -{ - impl->window_scale = 1; - impl->frame_sync_enabled = TRUE; -} - -GdkToplevelX11 * -_gdk_x11_surface_get_toplevel (GdkSurface *window) -{ - GdkSurfaceImplX11 *impl; - - g_return_val_if_fail (GDK_IS_SURFACE (window), NULL); - - if (!WINDOW_IS_TOPLEVEL (window)) - return NULL; - - impl = GDK_SURFACE_IMPL_X11 (window->impl); - - if (!impl->toplevel) - { - impl->toplevel = g_new0 (GdkToplevelX11, 1); - impl->toplevel->have_focused = FALSE; - } - - return impl->toplevel; -} - -/** - * _gdk_x11_surface_update_size: - * @impl: a #GdkSurfaceImplX11. - * - * Updates the state of the window (in particular the drawable's - * cairo surface) when its size has changed. - **/ -void -_gdk_x11_surface_update_size (GdkSurfaceImplX11 *impl) -{ - if (impl->cairo_surface) - { - cairo_xlib_surface_set_size (impl->cairo_surface, - impl->unscaled_width, impl->unscaled_height); - } -} - -static void -gdk_x11_surface_get_unscaled_size (GdkSurface *window, - int *unscaled_width, - int *unscaled_height) -{ - GdkSurfaceImplX11 *impl = GDK_SURFACE_IMPL_X11 (window->impl); - - if (unscaled_width) - *unscaled_width = impl->unscaled_width; - - if (unscaled_height) - *unscaled_height = impl->unscaled_height; -} - -static gboolean -gdk_x11_surface_supports_edge_constraints (GdkSurface *window) -{ - return gdk_x11_screen_supports_net_wm_hint (GDK_SURFACE_SCREEN (window), - g_intern_static_string ("_GTK_EDGE_CONSTRAINTS")); -} - -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 (GdkSurface *window) -{ - GdkSurface *toplevel_window = gdk_surface_get_toplevel (window); - GdkSurfaceImplX11 *impl; - - if (!toplevel_window || !WINDOW_IS_TOPLEVEL (toplevel_window)) - return; - - impl = GDK_SURFACE_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_SURFACE_XDISPLAY (impl->wrapper), - impl->toplevel->extended_update_counter, - impl->toplevel->current_counter_value); - } -} - -static void -on_surface_changed (void *data) -{ - GdkSurface *window = data; - GdkSurfaceImplX11 *impl = GDK_SURFACE_IMPL_X11 (window->impl); - - if (impl->tracking_damage) - 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 (GdkSurface *window) -{ - GdkSurfaceImplX11 *impl = GDK_SURFACE_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); - impl->tracking_damage = 1; - } -} - -static void -unhook_surface_changed (GdkSurface *window) -{ - GdkSurfaceImplX11 *impl = GDK_SURFACE_IMPL_X11 (window->impl); - - if (impl->cairo_surface) - { - impl->tracking_damage = 0; - cairo_surface_set_mime_data (impl->cairo_surface, - "x-gdk/change-notify", - NULL, 0, - NULL, NULL); - } -} - -static void -gdk_x11_surface_predict_presentation_time (GdkSurface *window) -{ - GdkSurfaceImplX11 *impl = GDK_SURFACE_IMPL_X11 (window->impl); - GdkFrameClock *clock; - GdkFrameTimings *timings; - gint64 presentation_time; - gint64 refresh_interval; - - if (!WINDOW_IS_TOPLEVEL (window)) - return; - - clock = gdk_surface_get_frame_clock (window); - - timings = gdk_frame_clock_get_current_timings (clock); - - gdk_frame_clock_get_refresh_info (clock, - timings->frame_time, - &refresh_interval, &presentation_time); - - if (presentation_time != 0) - { - if (timings->slept_before) - { - presentation_time += refresh_interval; - } - else - { - if (presentation_time < timings->frame_time + refresh_interval / 2) - presentation_time += refresh_interval; - } - } - else - { - if (timings->slept_before) - presentation_time = timings->frame_time + refresh_interval + refresh_interval / 2; - else - presentation_time = timings->frame_time + refresh_interval; - } - - if (presentation_time < impl->toplevel->throttled_presentation_time) - presentation_time = impl->toplevel->throttled_presentation_time; - - timings->predicted_presentation_time = presentation_time; -} - -static void -gdk_x11_surface_begin_frame (GdkSurface *window, - gboolean force_frame) -{ - GdkSurfaceImplX11 *impl; - - g_return_if_fail (GDK_IS_SURFACE (window)); - - impl = GDK_SURFACE_IMPL_X11 (window->impl); - - if (!WINDOW_IS_TOPLEVEL (window) || - impl->toplevel->extended_update_counter == None) - return; - - impl->toplevel->in_frame = TRUE; - - if (impl->toplevel->configure_counter_value != 0 && - impl->toplevel->configure_counter_value_is_extended) - { - impl->toplevel->current_counter_value = impl->toplevel->configure_counter_value; - if ((impl->toplevel->current_counter_value % 2) == 1) - impl->toplevel->current_counter_value += 1; - - impl->toplevel->configure_counter_value = 0; - - window_pre_damage (window); - } - else if (force_frame) - { - /* When mapping the window, we really want to freeze the - rendering of the window by the compositor until we've - actually painted something into the window's buffer. */ - window_pre_damage (window); - } - else - { - hook_surface_changed (window); - } -} - -static void -gdk_x11_surface_end_frame (GdkSurface *window) -{ - GdkFrameClock *clock; - GdkFrameTimings *timings; - GdkSurfaceImplX11 *impl; - - g_return_if_fail (GDK_IS_SURFACE (window)); - - impl = GDK_SURFACE_IMPL_X11 (window->impl); - - if (!WINDOW_IS_TOPLEVEL (window) || - impl->toplevel->extended_update_counter == None || - !impl->toplevel->in_frame) - return; - - clock = gdk_surface_get_frame_clock (window); - timings = gdk_frame_clock_get_current_timings (clock); - - impl->toplevel->in_frame = FALSE; - - if (impl->toplevel->current_counter_value % 2 == 1) - { - if (GDK_DISPLAY_DEBUG_CHECK (gdk_surface_get_display (window), FRAMES)) - { - XImage *image = XGetImage (GDK_SURFACE_XDISPLAY (window), - GDK_SURFACE_XID (window), - 0, 0, 1, 1, - (1 << 24) - 1, - ZPixmap); - XDestroyImage (image); - } - - /* An increment of 3 means that the frame was not drawn as fast as possible, - * but rather at a particular time. This can trigger different handling from - * the compositor. - */ - if (timings->slept_before) - impl->toplevel->current_counter_value += 3; - else - impl->toplevel->current_counter_value += 1; - - set_sync_counter(GDK_SURFACE_XDISPLAY (impl->wrapper), - impl->toplevel->extended_update_counter, - impl->toplevel->current_counter_value); - - if (impl->frame_sync_enabled && - gdk_x11_screen_supports_net_wm_hint (GDK_SURFACE_SCREEN (window), - g_intern_static_string ("_NET_WM_FRAME_DRAWN"))) - { - impl->toplevel->frame_pending = TRUE; - _gdk_frame_clock_freeze (gdk_surface_get_frame_clock (window)); - timings->cookie = impl->toplevel->current_counter_value; - } - } - - unhook_surface_changed (window); - - if (impl->toplevel->configure_counter_value != 0 && - !impl->toplevel->configure_counter_value_is_extended) - { - set_sync_counter (GDK_SURFACE_XDISPLAY (window), - impl->toplevel->update_counter, - impl->toplevel->configure_counter_value); - - impl->toplevel->configure_counter_value = 0; - } - - if (!impl->toplevel->frame_pending) - timings->complete = TRUE; -} - -/***************************************************** - * X11 specific implementations of generic functions * - *****************************************************/ - -static cairo_surface_t * -gdk_x11_create_cairo_surface (GdkSurfaceImplX11 *impl, - int width, - int height) -{ - Visual *visual; - - visual = gdk_x11_display_get_window_visual (GDK_X11_DISPLAY (gdk_surface_get_display (impl->wrapper))); - return cairo_xlib_surface_create (GDK_SURFACE_XDISPLAY (impl->wrapper), - GDK_SURFACE_IMPL_X11 (impl)->xid, - visual, - width, height); -} - -static cairo_surface_t * -gdk_x11_ref_cairo_surface (GdkSurface *window) -{ - GdkSurfaceImplX11 *impl = GDK_SURFACE_IMPL_X11 (window->impl); - - if (GDK_SURFACE_DESTROYED (window)) - return NULL; - - if (!impl->cairo_surface) - { - impl->cairo_surface = gdk_x11_create_cairo_surface (impl, - gdk_surface_get_width (window) * impl->window_scale, - gdk_surface_get_height (window) * impl->window_scale); - cairo_surface_set_device_scale (impl->cairo_surface, impl->window_scale, impl->window_scale); - - if (WINDOW_IS_TOPLEVEL (window) && impl->toplevel->in_frame) - hook_surface_changed (window); - } - - cairo_surface_reference (impl->cairo_surface); - - return impl->cairo_surface; -} - -static void -gdk_surface_impl_x11_finalize (GObject *object) -{ - GdkSurface *wrapper; - GdkSurfaceImplX11 *impl; - - g_return_if_fail (GDK_IS_SURFACE_IMPL_X11 (object)); - - impl = GDK_SURFACE_IMPL_X11 (object); - - wrapper = impl->wrapper; - - if (WINDOW_IS_TOPLEVEL (wrapper) && impl->toplevel->in_frame) - unhook_surface_changed (wrapper); - - _gdk_x11_surface_grab_check_destroy (wrapper); - - if (!GDK_SURFACE_DESTROYED (wrapper)) - { - GdkDisplay *display = GDK_SURFACE_DISPLAY (wrapper); - - _gdk_x11_display_remove_window (display, impl->xid); - if (impl->toplevel && impl->toplevel->focus_window) - _gdk_x11_display_remove_window (display, impl->toplevel->focus_window); - } - - g_free (impl->toplevel); - - if (impl->cursor) - g_object_unref (impl->cursor); - - G_OBJECT_CLASS (gdk_surface_impl_x11_parent_class)->finalize (object); -} - -typedef struct { - GdkDisplay *display; - Pixmap pixmap; -} FreePixmapData; - -static void -free_pixmap (gpointer datap) -{ - FreePixmapData *data = datap; - - if (!gdk_display_is_closed (data->display)) - { - XFreePixmap (GDK_DISPLAY_XDISPLAY (data->display), - data->pixmap); - } - - g_object_unref (data->display); - g_slice_free (FreePixmapData, data); -} - -static void -attach_free_pixmap_handler (cairo_surface_t *surface, - GdkDisplay *display, - Pixmap pixmap) -{ - static const cairo_user_data_key_t key; - FreePixmapData *data; - - data = g_slice_new (FreePixmapData); - data->display = g_object_ref (display); - data->pixmap = pixmap; - - cairo_surface_set_user_data (surface, &key, data, free_pixmap); -} - -/* Cairo does not guarantee we get an xlib surface if we call - * cairo_surface_create_similar(). In some cases however, we must use a - * pixmap or bitmap in the X11 API. - * These functions ensure an Xlib surface. - */ -cairo_surface_t * -_gdk_x11_display_create_bitmap_surface (GdkDisplay *display, - int width, - int height) -{ - cairo_surface_t *surface; - Pixmap pixmap; - - pixmap = XCreatePixmap (GDK_DISPLAY_XDISPLAY (display), - GDK_SCREEN_XROOTWIN (GDK_X11_DISPLAY (display)->screen), - width, height, 1); - surface = cairo_xlib_surface_create_for_bitmap (GDK_DISPLAY_XDISPLAY (display), - pixmap, - GDK_X11_SCREEN (GDK_X11_DISPLAY (display)->screen)->xscreen, - width, height); - attach_free_pixmap_handler (surface, display, pixmap); - - return surface; -} - -/* Create a surface backed with a pixmap without alpha on the same screen as window */ -static cairo_surface_t * -gdk_x11_surface_create_pixmap_surface (GdkSurface *window, - int width, - int height) -{ - GdkDisplay *display; - Display *dpy; - cairo_surface_t *surface; - Pixmap pixmap; - - display = gdk_surface_get_display (window); - dpy = GDK_DISPLAY_XDISPLAY (display); - - pixmap = XCreatePixmap (dpy, - GDK_SURFACE_XID (window), - width, height, - DefaultDepth (dpy, DefaultScreen (dpy))); - surface = cairo_xlib_surface_create (dpy, - pixmap, - DefaultVisual (dpy, DefaultScreen (dpy)), - width, height); - attach_free_pixmap_handler (surface, display, pixmap); - - return surface; -} - -static void -set_wm_protocols (GdkSurface *window) -{ - GdkDisplay *display = gdk_surface_get_display (window); - Atom protocols[4]; - int n = 0; - - protocols[n++] = gdk_x11_get_xatom_by_name_for_display (display, "WM_DELETE_WINDOW"); - protocols[n++] = gdk_x11_get_xatom_by_name_for_display (display, "WM_TAKE_FOCUS"); - protocols[n++] = gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_PING"); - -#ifdef HAVE_XSYNC - if (GDK_X11_DISPLAY (display)->use_sync) - protocols[n++] = gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_SYNC_REQUEST"); -#endif - - XSetWMProtocols (GDK_DISPLAY_XDISPLAY (display), GDK_SURFACE_XID (window), protocols, n); -} - -static const gchar * -get_default_title (void) -{ - const char *title; - - title = g_get_application_name (); - if (!title) - title = g_get_prgname (); - if (!title) - title = ""; - - return title; -} - -static void -check_leader_window_title (GdkDisplay *display) -{ - GdkX11Display *display_x11 = GDK_X11_DISPLAY (display); - - if (display_x11->leader_window && !display_x11->leader_window_title_set) - { - set_wm_name (display, - display_x11->leader_window, - get_default_title ()); - - display_x11->leader_window_title_set = TRUE; - } -} - -static Window -create_focus_window (GdkDisplay *display, - XID parent) -{ - GdkX11Display *display_x11; - GdkEventMask event_mask; - Display *xdisplay; - Window focus_window; - XSetWindowAttributes attrs; - - xdisplay = GDK_DISPLAY_XDISPLAY (display); - display_x11 = GDK_X11_DISPLAY (display); - - focus_window = XCreateWindow (xdisplay, parent, - -1, -1, 1, 1, 0, - 0, /* depth */ - InputOnly, - CopyFromParent, - 0, &attrs); - - event_mask = (GDK_KEY_PRESS_MASK | - GDK_KEY_RELEASE_MASK | - GDK_FOCUS_CHANGE_MASK); - - gdk_x11_event_source_select_events ((GdkEventSource *) display_x11->event_source, - focus_window, - event_mask, 0); - - XMapWindow (xdisplay, focus_window); - - return focus_window; -} - -static void -ensure_sync_counter (GdkSurface *window) -{ -#ifdef HAVE_XSYNC - if (!GDK_SURFACE_DESTROYED (window)) - { - GdkDisplay *display = GDK_SURFACE_DISPLAY (window); - GdkToplevelX11 *toplevel = _gdk_x11_surface_get_toplevel (window); - - 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_SURFACE_XID (window), - atom, XA_CARDINAL, - 32, PropModeReplace, - (guchar *)counters, 2); - - toplevel->current_counter_value = 0; - } - } -#endif -} - -static void -setup_toplevel_window (GdkSurface *window, - GdkX11Screen *x11_screen) -{ - GdkToplevelX11 *toplevel = _gdk_x11_surface_get_toplevel (window); - GdkSurfaceImplX11 *impl = GDK_SURFACE_IMPL_X11 (window->impl); - GdkDisplay *display = gdk_surface_get_display (window); - Display *xdisplay = GDK_SURFACE_XDISPLAY (window); - XID xid = GDK_SURFACE_XID (window); - XSizeHints size_hints; - long pid; - Window leader_window; - - set_wm_protocols (window); - - if (!window->input_only) - { - /* The focus window is off the visible area, and serves to receive key - * press events so they don't get sent to child windows. - */ - toplevel->focus_window = create_focus_window (display, xid); - _gdk_x11_display_add_window (x11_screen->display, - &toplevel->focus_window, - window); - } - - check_leader_window_title (x11_screen->display); - - /* FIXME: Is there any point in doing this? Do any WM's pay - * attention to PSize, and even if they do, is this the - * correct value??? - */ - size_hints.flags = PSize; - size_hints.width = window->width * impl->window_scale; - size_hints.height = window->height * impl->window_scale; - - XSetWMNormalHints (xdisplay, xid, &size_hints); - - /* This will set WM_CLIENT_MACHINE and WM_LOCALE_NAME */ - XSetWMProperties (xdisplay, xid, NULL, NULL, NULL, 0, NULL, NULL, NULL); - - pid = getpid (); - XChangeProperty (xdisplay, xid, - gdk_x11_get_xatom_by_name_for_display (x11_screen->display, "_NET_WM_PID"), - XA_CARDINAL, 32, - PropModeReplace, - (guchar *)&pid, 1); - - leader_window = GDK_X11_DISPLAY (x11_screen->display)->leader_window; - if (!leader_window) - leader_window = xid; - XChangeProperty (xdisplay, xid, - gdk_x11_get_xatom_by_name_for_display (x11_screen->display, "WM_CLIENT_LEADER"), - XA_WINDOW, 32, PropModeReplace, - (guchar *) &leader_window, 1); - - if (toplevel->focus_window != None) - XChangeProperty (xdisplay, xid, - gdk_x11_get_xatom_by_name_for_display (x11_screen->display, "_NET_WM_USER_TIME_WINDOW"), - XA_WINDOW, 32, PropModeReplace, - (guchar *) &toplevel->focus_window, 1); - - if (!window->focus_on_map) - gdk_x11_surface_set_user_time (window, 0); - else if (GDK_X11_DISPLAY (x11_screen->display)->user_time != 0) - gdk_x11_surface_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_surface_begin_frame (window, TRUE); -} - -static void -on_frame_clock_before_paint (GdkFrameClock *clock, - GdkSurface *window) -{ - gdk_x11_surface_predict_presentation_time (window); - gdk_x11_surface_begin_frame (window, FALSE); -} - -static void -on_frame_clock_after_paint (GdkFrameClock *clock, - GdkSurface *window) -{ - gdk_x11_surface_end_frame (window); - -} - -static void -connect_frame_clock (GdkSurface *window) -{ - GdkSurfaceImplX11 *impl; - - impl = GDK_SURFACE_IMPL_X11 (window->impl); - if (WINDOW_IS_TOPLEVEL (window) && !impl->frame_clock_connected) - { - GdkFrameClock *frame_clock = gdk_surface_get_frame_clock (window); - - g_signal_connect (frame_clock, "before-paint", - G_CALLBACK (on_frame_clock_before_paint), window); - g_signal_connect (frame_clock, "after-paint", - G_CALLBACK (on_frame_clock_after_paint), window); - - impl->frame_clock_connected = TRUE; - } -} - -void -_gdk_x11_display_create_window_impl (GdkDisplay *display, - GdkSurface *window, - GdkSurface *real_parent, - GdkEventMask event_mask, - GdkSurfaceAttr *attributes) -{ - GdkSurfaceImplX11 *impl; - GdkX11Screen *x11_screen; - GdkX11Display *display_x11; - - Window xparent; - Visual *xvisual; - Display *xdisplay; - - XSetWindowAttributes xattributes; - long xattributes_mask; - XClassHint *class_hint; - - unsigned int class; - int depth; - - int abs_x; - int abs_y; - - display_x11 = GDK_X11_DISPLAY (display); - x11_screen = GDK_X11_SCREEN (display_x11->screen); - if (real_parent) - xparent = GDK_SURFACE_XID (real_parent); - else - xparent = GDK_SCREEN_XROOTWIN (x11_screen); - - impl = g_object_new (GDK_TYPE_SURFACE_IMPL_X11, NULL); - window->impl = GDK_SURFACE_IMPL (impl); - impl->wrapper = GDK_SURFACE (window); - impl->window_scale = x11_screen->window_scale; - - xdisplay = x11_screen->xdisplay; - - xattributes_mask = 0; - - xvisual = gdk_x11_display_get_window_visual (display_x11); - - impl->override_redirect = FALSE; - - /* Sanity checks */ - switch (window->window_type) - { - case GDK_SURFACE_TOPLEVEL: - case GDK_SURFACE_TEMP: - if (window->parent) - { - /* The common code warns for this case */ - xparent = GDK_SCREEN_XROOTWIN (x11_screen); - } - break; - - case GDK_SURFACE_CHILD: - default: - g_assert_not_reached (); - break; - } - - if (!window->input_only) - { - class = InputOutput; - - xattributes.background_pixel = BlackPixel (xdisplay, x11_screen->screen_num); - - xattributes.border_pixel = BlackPixel (xdisplay, x11_screen->screen_num); - xattributes_mask |= CWBorderPixel | CWBackPixel; - - xattributes.bit_gravity = NorthWestGravity; - xattributes_mask |= CWBitGravity; - - xattributes.colormap = gdk_x11_display_get_window_colormap (display_x11); - xattributes_mask |= CWColormap; - - if (window->window_type == GDK_SURFACE_TEMP) - { - xattributes.save_under = True; - xattributes.override_redirect = True; - xattributes.cursor = None; - xattributes_mask |= CWSaveUnder | CWOverrideRedirect; - - impl->override_redirect = TRUE; - } - - depth = gdk_x11_display_get_window_depth (display_x11); - } - else - { - class = InputOnly; - - if (window->window_type == GDK_SURFACE_TEMP) - { - xattributes.override_redirect = True; - xattributes_mask |= CWOverrideRedirect; - - impl->override_redirect = TRUE; - } - - depth = 0; - } - - if (window->width * impl->window_scale > 32767 || - window->height * impl->window_scale > 32767) - { - g_warning ("Native Windows wider or taller than 32767 pixels are not supported"); - - if (window->width * impl->window_scale > 32767) - window->width = 32767 / impl->window_scale; - if (window->height * impl->window_scale > 32767) - window->height = 32767 / impl->window_scale; - } - - impl->unscaled_width = window->width * impl->window_scale; - impl->unscaled_height = window->height * impl->window_scale; - - if (window->parent) - { - abs_x = window->parent->abs_x; - abs_y = window->parent->abs_y; - } - else - { - abs_x = 0; - abs_y = 0; - } - - impl->xid = XCreateWindow (xdisplay, xparent, - (window->x + abs_x) * impl->window_scale, - (window->y + abs_y) * impl->window_scale, - window->width * impl->window_scale, window->height * impl->window_scale, - 0, depth, class, xvisual, - xattributes_mask, &xattributes); - - g_object_ref (window); - _gdk_x11_display_add_window (x11_screen->display, &impl->xid, window); - - switch (GDK_SURFACE_TYPE (window)) - { - case GDK_SURFACE_TOPLEVEL: - case GDK_SURFACE_TEMP: - gdk_surface_set_title (window, get_default_title ()); - - class_hint = XAllocClassHint (); - class_hint->res_name = (char *) g_get_prgname (); - class_hint->res_class = (char *) display_x11->program_class; - XSetClassHint (xdisplay, impl->xid, class_hint); - XFree (class_hint); - - setup_toplevel_window (window, x11_screen); - break; - - case GDK_SURFACE_CHILD: - default: - break; - } - - gdk_x11_event_source_select_events ((GdkEventSource *) display_x11->event_source, - GDK_SURFACE_XID (window), event_mask, - StructureNotifyMask | PropertyChangeMask); - - connect_frame_clock (window); - - gdk_surface_freeze_toplevel_updates (window); -} - -static GdkEventMask -x_event_mask_to_gdk_event_mask (long mask) -{ - GdkEventMask event_mask = 0; - int i; - - for (i = 0; i < _gdk_x11_event_mask_table_size; i++) - { - if (mask & _gdk_x11_event_mask_table[i]) - event_mask |= 1 << (i + 1); - } - - return event_mask; -} - -/** - * gdk_x11_surface_foreign_new_for_display: - * @display: (type GdkX11Display): the #GdkDisplay where the window handle comes from. - * @window: an Xlib Window - * - * Wraps a native window in a #GdkSurface. The function will try to - * look up the window using gdk_x11_surface_lookup_for_display() first. - * If it does not find it there, it will create a new window. - * - * This may fail if the window has been destroyed. If the window - * was already known to GDK, a new reference to the existing - * #GdkSurface is returned. - * - * Returns: (transfer full): a #GdkSurface wrapper for the native - * window, or %NULL if the window has been destroyed. The wrapper - * will be newly created, if one doesn’t exist already. - */ -GdkSurface * -gdk_x11_surface_foreign_new_for_display (GdkDisplay *display, - Window window) -{ - GdkX11Screen *screen; - GdkSurface *win; - GdkSurfaceImplX11 *impl; - GdkX11Display *display_x11; - XWindowAttributes attrs; - Window root, parent; - Window *children = NULL; - guint nchildren; - gboolean result; - - g_return_val_if_fail (GDK_IS_DISPLAY (display), NULL); - - display_x11 = GDK_X11_DISPLAY (display); - - if ((win = gdk_x11_surface_lookup_for_display (display, window)) != NULL) - return g_object_ref (win); - - gdk_x11_display_error_trap_push (display); - result = XGetWindowAttributes (display_x11->xdisplay, window, &attrs); - if (gdk_x11_display_error_trap_pop (display) || !result) - return NULL; - - /* FIXME: This is pretty expensive. - * Maybe the caller should supply the parent - */ - gdk_x11_display_error_trap_push (display); - result = XQueryTree (display_x11->xdisplay, window, &root, &parent, &children, &nchildren); - if (gdk_x11_display_error_trap_pop (display) || !result) - return NULL; - - if (children) - XFree (children); - - screen = _gdk_x11_display_screen_for_xrootwin (display, root); - if (screen == NULL) - return NULL; - - win = _gdk_display_create_window (display); - win->impl = g_object_new (GDK_TYPE_SURFACE_IMPL_X11, NULL); - win->impl_window = win; - - impl = GDK_SURFACE_IMPL_X11 (win->impl); - impl->wrapper = win; - impl->window_scale = GDK_X11_SCREEN (screen)->window_scale; - - /* Always treat foreigns as toplevels */ - win->parent = NULL; - - impl->xid = window; - - win->x = attrs.x / impl->window_scale; - win->y = attrs.y / impl->window_scale; - impl->unscaled_width = attrs.width; - impl->unscaled_height = attrs.height; - win->width = attrs.width / impl->window_scale; - win->height = attrs.height / impl->window_scale; - win->window_type = GDK_SURFACE_FOREIGN; - win->destroyed = FALSE; - - win->event_mask = x_event_mask_to_gdk_event_mask (attrs.your_event_mask); - - if (attrs.map_state == IsUnmapped) - win->state = GDK_SURFACE_STATE_WITHDRAWN; - else - win->state = 0; - win->viewable = TRUE; - - g_object_ref (win); - _gdk_x11_display_add_window (display, &GDK_SURFACE_XID (win), win); - - /* Update the clip region, etc */ - _gdk_surface_update_size (win); - - return win; -} - -static void -gdk_toplevel_x11_free_contents (GdkDisplay *display, - GdkToplevelX11 *toplevel) -{ - if (toplevel->icon_pixmap) - { - cairo_surface_destroy (toplevel->icon_pixmap); - toplevel->icon_pixmap = NULL; - } - if (toplevel->icon_mask) - { - cairo_surface_destroy (toplevel->icon_mask); - toplevel->icon_mask = NULL; - } - if (toplevel->group_leader) - { - g_object_unref (toplevel->group_leader); - toplevel->group_leader = NULL; - } -#ifdef HAVE_XSYNC - if (toplevel->update_counter != None) - { - XSyncDestroyCounter (GDK_DISPLAY_XDISPLAY (display), - toplevel->update_counter); - XSyncDestroyCounter (GDK_DISPLAY_XDISPLAY (display), - toplevel->extended_update_counter); - toplevel->update_counter = None; - toplevel->extended_update_counter = None; - - toplevel->current_counter_value = 0; - } -#endif -} - -static void -gdk_x11_surface_destroy (GdkSurface *window, - gboolean recursing, - gboolean foreign_destroy) -{ - GdkSurfaceImplX11 *impl = GDK_SURFACE_IMPL_X11 (window->impl); - GdkToplevelX11 *toplevel; - - g_return_if_fail (GDK_IS_SURFACE (window)); - - toplevel = _gdk_x11_surface_get_toplevel (window); - if (toplevel) - gdk_toplevel_x11_free_contents (GDK_SURFACE_DISPLAY (window), toplevel); - - unhook_surface_changed (window); - - if (impl->cairo_surface) - { - cairo_surface_finish (impl->cairo_surface); - cairo_surface_destroy (impl->cairo_surface); - impl->cairo_surface = NULL; - } - - if (!recursing && !foreign_destroy) - XDestroyWindow (GDK_SURFACE_XDISPLAY (window), GDK_SURFACE_XID (window)); -} - -/* This function is called when the XWindow is really gone. - */ -static void -gdk_x11_surface_destroy_notify (GdkSurface *window) -{ - GdkSurfaceImplX11 *window_impl; - - window_impl = GDK_SURFACE_IMPL_X11 ((window)->impl); - - if (!GDK_SURFACE_DESTROYED (window)) - { - if (GDK_SURFACE_TYPE(window) != GDK_SURFACE_FOREIGN) - g_warning ("GdkSurface %#lx unexpectedly destroyed", GDK_SURFACE_XID (window)); - - _gdk_surface_destroy (window, TRUE); - } - - _gdk_x11_display_remove_window (GDK_SURFACE_DISPLAY (window), GDK_SURFACE_XID (window)); - if (window_impl->toplevel && window_impl->toplevel->focus_window) - _gdk_x11_display_remove_window (GDK_SURFACE_DISPLAY (window), window_impl->toplevel->focus_window); - - _gdk_x11_surface_grab_check_destroy (window); - - g_object_unref (window); -} - -static void -update_wm_hints (GdkSurface *window, - gboolean force) -{ - GdkToplevelX11 *toplevel = _gdk_x11_surface_get_toplevel (window); - GdkDisplay *display = GDK_SURFACE_DISPLAY (window); - XWMHints wm_hints; - - if (!force && - !toplevel->is_leader && - window->state & GDK_SURFACE_STATE_WITHDRAWN) - return; - - wm_hints.flags = StateHint | InputHint; - wm_hints.input = window->accept_focus ? True : False; - wm_hints.initial_state = NormalState; - - if (window->state & GDK_SURFACE_STATE_ICONIFIED) - { - wm_hints.flags |= StateHint; - wm_hints.initial_state = IconicState; - } - - if (toplevel->icon_pixmap) - { - wm_hints.flags |= IconPixmapHint; - wm_hints.icon_pixmap = cairo_xlib_surface_get_drawable (toplevel->icon_pixmap); - } - - if (toplevel->icon_mask) - { - wm_hints.flags |= IconMaskHint; - wm_hints.icon_mask = cairo_xlib_surface_get_drawable (toplevel->icon_mask); - } - - wm_hints.flags |= WindowGroupHint; - if (toplevel->group_leader && !GDK_SURFACE_DESTROYED (toplevel->group_leader)) - { - wm_hints.flags |= WindowGroupHint; - wm_hints.window_group = GDK_SURFACE_XID (toplevel->group_leader); - } - else - wm_hints.window_group = GDK_X11_DISPLAY (display)->leader_window; - - if (toplevel->urgency_hint) - wm_hints.flags |= XUrgencyHint; - - XSetWMHints (GDK_SURFACE_XDISPLAY (window), - GDK_SURFACE_XID (window), - &wm_hints); -} - -static void -set_initial_hints (GdkSurface *window) -{ - GdkDisplay *display = GDK_SURFACE_DISPLAY (window); - Display *xdisplay = GDK_DISPLAY_XDISPLAY (display); - Window xwindow = GDK_SURFACE_XID (window); - GdkToplevelX11 *toplevel; - Atom atoms[9]; - gint i; - - toplevel = _gdk_x11_surface_get_toplevel (window); - - if (!toplevel) - return; - - update_wm_hints (window, TRUE); - - /* We set the spec hints regardless of whether the spec is supported, - * since it can't hurt and it's kind of expensive to check whether - * it's supported. - */ - - i = 0; - - if (window->state & GDK_SURFACE_STATE_MAXIMIZED) - { - atoms[i] = gdk_x11_get_xatom_by_name_for_display (display, - "_NET_WM_STATE_MAXIMIZED_VERT"); - ++i; - atoms[i] = gdk_x11_get_xatom_by_name_for_display (display, - "_NET_WM_STATE_MAXIMIZED_HORZ"); - ++i; - toplevel->have_maxhorz = toplevel->have_maxvert = TRUE; - } - - if (window->state & GDK_SURFACE_STATE_ABOVE) - { - atoms[i] = gdk_x11_get_xatom_by_name_for_display (display, - "_NET_WM_STATE_ABOVE"); - ++i; - } - - if (window->state & GDK_SURFACE_STATE_BELOW) - { - atoms[i] = gdk_x11_get_xatom_by_name_for_display (display, - "_NET_WM_STATE_BELOW"); - ++i; - } - - if (window->state & GDK_SURFACE_STATE_STICKY) - { - atoms[i] = gdk_x11_get_xatom_by_name_for_display (display, - "_NET_WM_STATE_STICKY"); - ++i; - toplevel->have_sticky = TRUE; - } - - if (window->state & GDK_SURFACE_STATE_FULLSCREEN) - { - atoms[i] = gdk_x11_get_xatom_by_name_for_display (display, - "_NET_WM_STATE_FULLSCREEN"); - ++i; - toplevel->have_fullscreen = TRUE; - } - - if (window->modal_hint) - { - atoms[i] = gdk_x11_get_xatom_by_name_for_display (display, - "_NET_WM_STATE_MODAL"); - ++i; - } - - if (toplevel->skip_taskbar_hint) - { - atoms[i] = gdk_x11_get_xatom_by_name_for_display (display, - "_NET_WM_STATE_SKIP_TASKBAR"); - ++i; - } - - if (toplevel->skip_pager_hint) - { - atoms[i] = gdk_x11_get_xatom_by_name_for_display (display, - "_NET_WM_STATE_SKIP_PAGER"); - ++i; - } - - if (window->state & GDK_SURFACE_STATE_ICONIFIED) - { - atoms[i] = gdk_x11_get_xatom_by_name_for_display (display, - "_NET_WM_STATE_HIDDEN"); - ++i; - toplevel->have_hidden = TRUE; - } - - if (i > 0) - { - XChangeProperty (xdisplay, - xwindow, - gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_STATE"), - XA_ATOM, 32, PropModeReplace, - (guchar*) atoms, i); - } - else - { - XDeleteProperty (xdisplay, - xwindow, - gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_STATE")); - } - - if (window->state & GDK_SURFACE_STATE_STICKY) - { - atoms[0] = 0xFFFFFFFF; - XChangeProperty (xdisplay, - xwindow, - gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_DESKTOP"), - XA_CARDINAL, 32, PropModeReplace, - (guchar*) atoms, 1); - toplevel->on_all_desktops = TRUE; - } - else - { - XDeleteProperty (xdisplay, - xwindow, - gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_DESKTOP")); - } - - toplevel->map_serial = NextRequest (xdisplay); -} - -static void -gdk_surface_x11_show (GdkSurface *window, gboolean already_mapped) -{ - GdkDisplay *display; - GdkX11Display *display_x11; - GdkToplevelX11 *toplevel; - Display *xdisplay = GDK_SURFACE_XDISPLAY (window); - Window xwindow = GDK_SURFACE_XID (window); - - if (!already_mapped) - set_initial_hints (window); - - if (WINDOW_IS_TOPLEVEL (window)) - { - display = gdk_surface_get_display (window); - display_x11 = GDK_X11_DISPLAY (display); - toplevel = _gdk_x11_surface_get_toplevel (window); - - if (toplevel->user_time != 0 && - display_x11->user_time != 0 && - XSERVER_TIME_IS_LATER (display_x11->user_time, toplevel->user_time)) - gdk_x11_surface_set_user_time (window, display_x11->user_time); - } - - XMapWindow (xdisplay, xwindow); - - /* 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_surface_apply_fullscreen_mode (window); -} - -static void -gdk_surface_x11_hide (GdkSurface *window) -{ - /* We'll get the unmap notify eventually, and handle it then, - * but checking here makes things more consistent if we are - * just doing stuff ourself. - */ - _gdk_x11_surface_grab_check_unmap (window, - NextRequest (GDK_SURFACE_XDISPLAY (window))); - - /* You can't simply unmap toplevel windows. */ - switch (window->window_type) - { - case GDK_SURFACE_TOPLEVEL: - case GDK_SURFACE_TEMP: /* ? */ - gdk_surface_withdraw (window); - return; - - case GDK_SURFACE_FOREIGN: - case GDK_SURFACE_CHILD: - default: - break; - } - - _gdk_surface_clear_update_area (window); - - XUnmapWindow (GDK_SURFACE_XDISPLAY (window), - GDK_SURFACE_XID (window)); -} - -static void -gdk_surface_x11_withdraw (GdkSurface *window) -{ - if (!window->destroyed) - { - if (GDK_SURFACE_IS_MAPPED (window)) - gdk_synthesize_window_state (window, - 0, - GDK_SURFACE_STATE_WITHDRAWN); - - g_assert (!GDK_SURFACE_IS_MAPPED (window)); - - XWithdrawWindow (GDK_SURFACE_XDISPLAY (window), - GDK_SURFACE_XID (window), 0); - } -} - -static inline void -window_x11_move (GdkSurface *window, - gint x, - gint y) -{ - GdkSurfaceImplX11 *impl = GDK_SURFACE_IMPL_X11 (window->impl); - - XMoveWindow (GDK_SURFACE_XDISPLAY (window), - GDK_SURFACE_XID (window), - x * impl->window_scale, y * impl->window_scale); - - if (impl->override_redirect) - { - window->x = x; - window->y = y; - } -} - -static inline void -window_x11_resize (GdkSurface *window, - gint width, - gint height) -{ - GdkSurfaceImplX11 *impl = GDK_SURFACE_IMPL_X11 (window->impl); - - if (width < 1) - width = 1; - - if (height < 1) - height = 1; - - window_pre_damage (window); - - XResizeWindow (GDK_SURFACE_XDISPLAY (window), - GDK_SURFACE_XID (window), - width * impl->window_scale, height * impl->window_scale); - - if (impl->override_redirect) - { - impl->unscaled_width = width * impl->window_scale; - impl->unscaled_height = height * impl->window_scale; - window->width = width; - window->height = height; - _gdk_x11_surface_update_size (GDK_SURFACE_IMPL_X11 (window->impl)); - } - else - { - if (width * impl->window_scale != impl->unscaled_width || height * impl->window_scale != impl->unscaled_height) - window->resize_count += 1; - } -} - -static inline void -window_x11_move_resize (GdkSurface *window, - gint x, - gint y, - gint width, - gint height) -{ - GdkSurfaceImplX11 *impl = GDK_SURFACE_IMPL_X11 (window->impl); - - if (width < 1) - width = 1; - - if (height < 1) - height = 1; - - window_pre_damage (window); - - XMoveResizeWindow (GDK_SURFACE_XDISPLAY (window), - GDK_SURFACE_XID (window), - x * impl->window_scale, y * impl->window_scale, - width * impl->window_scale, height * impl->window_scale); - - if (impl->override_redirect) - { - window->x = x; - window->y = y; - - impl->unscaled_width = width * impl->window_scale; - impl->unscaled_height = height * impl->window_scale; - window->width = width; - window->height = height; - - _gdk_x11_surface_update_size (GDK_SURFACE_IMPL_X11 (window->impl)); - } - else - { - if (width * impl->window_scale != impl->unscaled_width || height * impl->window_scale != impl->unscaled_height) - window->resize_count += 1; - } -} - -static void -gdk_surface_x11_move_resize (GdkSurface *window, - gboolean with_move, - gint x, - gint y, - gint width, - gint height) -{ - if (with_move && (width < 0 && height < 0)) - window_x11_move (window, x, y); - else - { - if (with_move) - window_x11_move_resize (window, x, y, width, height); - else - window_x11_resize (window, width, height); - } -} - -void -_gdk_x11_surface_set_window_scale (GdkSurface *window, - int scale) -{ - GdkSurfaceImplX11 *impl; - GdkToplevelX11 *toplevel; - GdkSurfaceHints geom_mask; - - impl = GDK_SURFACE_IMPL_X11 (window->impl); - - impl->window_scale = scale; - if (impl->cairo_surface) - cairo_surface_set_device_scale (impl->cairo_surface, impl->window_scale, impl->window_scale); - _gdk_surface_update_size (window); - - toplevel = _gdk_x11_surface_get_toplevel (window); - if (toplevel && window->window_type != GDK_SURFACE_FOREIGN) - { - /* These are affected by window scale: */ - geom_mask = toplevel->last_geometry_hints_mask & - (GDK_HINT_MIN_SIZE | GDK_HINT_MAX_SIZE | GDK_HINT_BASE_SIZE | GDK_HINT_RESIZE_INC); - if (geom_mask) - gdk_surface_set_geometry_hints (window, - &toplevel->last_geometry_hints, - geom_mask); - } - - if (window->window_type == GDK_SURFACE_FOREIGN) - XMoveWindow (GDK_SURFACE_XDISPLAY (window), - GDK_SURFACE_XID (window), - window->x * impl->window_scale, - window->y * impl->window_scale); - else - { - if (impl->override_redirect) - { - impl->unscaled_width = window->width * impl->window_scale; - impl->unscaled_height = window->height * impl->window_scale; - } - - XResizeWindow (GDK_SURFACE_XDISPLAY (window), - GDK_SURFACE_XID (window), - window->width * impl->window_scale, - window->height * impl->window_scale); - } - - gdk_surface_invalidate_rect (window, NULL, TRUE); -} - -static void -gdk_surface_x11_raise (GdkSurface *window) -{ - XRaiseWindow (GDK_SURFACE_XDISPLAY (window), GDK_SURFACE_XID (window)); -} - -static void -gdk_surface_x11_restack_toplevel (GdkSurface *window, - GdkSurface *sibling, - gboolean above) -{ - XWindowChanges changes; - - changes.sibling = GDK_SURFACE_XID (sibling); - changes.stack_mode = above ? Above : Below; - XReconfigureWMWindow (GDK_SURFACE_XDISPLAY (window), - GDK_SURFACE_XID (window), - gdk_x11_screen_get_screen_number (GDK_SURFACE_SCREEN (window)), - CWStackMode | CWSibling, &changes); -} - -static void -gdk_surface_x11_lower (GdkSurface *window) -{ - XLowerWindow (GDK_SURFACE_XDISPLAY (window), GDK_SURFACE_XID (window)); -} - -/** - * gdk_x11_surface_move_to_current_desktop: - * @window: (type GdkX11Surface): a #GdkSurface - * - * Moves the window to the correct workspace when running under a - * window manager that supports multiple workspaces, as described - * in the [Extended Window Manager Hints](http://www.freedesktop.org/Standards/wm-spec) specification. - * Will not do anything if the window is already on all workspaces. - */ -void -gdk_x11_surface_move_to_current_desktop (GdkSurface *window) -{ - GdkToplevelX11 *toplevel; - - g_return_if_fail (GDK_IS_SURFACE (window)); - g_return_if_fail (GDK_SURFACE_TYPE (window) != GDK_SURFACE_CHILD); - - toplevel = _gdk_x11_surface_get_toplevel (window); - - if (toplevel->on_all_desktops) - return; - - move_to_current_desktop (window); -} - -static void -move_to_current_desktop (GdkSurface *window) -{ - guint32 desktop; - - desktop = gdk_x11_screen_get_current_desktop (GDK_SURFACE_SCREEN (window)); - gdk_x11_surface_move_to_desktop (window, desktop); -} - -static guint32 -get_netwm_cardinal_property (GdkSurface *window, - const gchar *name) -{ - GdkX11Screen *x11_screen = GDK_SURFACE_SCREEN (window); - GdkAtom atom; - guint32 prop = 0; - Atom type; - gint format; - gulong nitems; - gulong bytes_after; - guchar *data; - - atom = g_intern_static_string (name); - - if (!gdk_x11_screen_supports_net_wm_hint (x11_screen, atom)) - return 0; - - XGetWindowProperty (x11_screen->xdisplay, - GDK_SURFACE_XID (window), - gdk_x11_get_xatom_by_name_for_display (GDK_SURFACE_DISPLAY (window), name), - 0, G_MAXLONG, - False, XA_CARDINAL, &type, &format, &nitems, - &bytes_after, &data); - if (type == XA_CARDINAL) - { - prop = *(gulong *)data; - XFree (data); - } - - return prop; -} - -/** - * gdk_x11_surface_get_desktop: - * @window: (type GdkX11Surface): a #GdkSurface - * - * Gets the number of the workspace @window is on. - * - * Returns: the current workspace of @window - */ -guint32 -gdk_x11_surface_get_desktop (GdkSurface *window) -{ - g_return_val_if_fail (GDK_IS_SURFACE (window), 0); - - return get_netwm_cardinal_property (window, "_NET_WM_DESKTOP"); -} - -/** - * gdk_x11_surface_move_to_desktop: - * @window: (type GdkX11Surface): a #GdkSurface - * @desktop: the number of the workspace to move the window to - * - * Moves the window to the given workspace when running unde a - * window manager that supports multiple workspaces, as described - * in the [Extended Window Manager Hints](http://www.freedesktop.org/Standards/wm-spec) specification. - */ -void -gdk_x11_surface_move_to_desktop (GdkSurface *window, - guint32 desktop) -{ - GdkAtom atom; - XClientMessageEvent xclient; - - g_return_if_fail (GDK_IS_SURFACE (window)); - - atom = g_intern_static_string ("_NET_WM_DESKTOP"); - if (!gdk_x11_screen_supports_net_wm_hint (GDK_SURFACE_SCREEN (window), atom)) - return; - - memset (&xclient, 0, sizeof (xclient)); - xclient.type = ClientMessage; - xclient.serial = 0; - xclient.send_event = True; - xclient.window = GDK_SURFACE_XID (window); - xclient.message_type = gdk_x11_atom_to_xatom_for_display (GDK_SURFACE_DISPLAY (window), atom); - xclient.format = 32; - - xclient.data.l[0] = desktop; - xclient.data.l[1] = 1; /* source indication */ - xclient.data.l[2] = 0; - xclient.data.l[3] = 0; - xclient.data.l[4] = 0; - - XSendEvent (GDK_SURFACE_XDISPLAY (window), - GDK_SURFACE_XROOTWIN (window), - False, - SubstructureRedirectMask | SubstructureNotifyMask, - (XEvent *)&xclient); -} - -static void -gdk_x11_surface_focus (GdkSurface *window, - guint32 timestamp) -{ - GdkDisplay *display; - - g_return_if_fail (GDK_IS_SURFACE (window)); - - if (GDK_SURFACE_DESTROYED (window) || - !WINDOW_IS_TOPLEVEL_OR_FOREIGN (window)) - return; - - display = GDK_SURFACE_DISPLAY (window); - - if (gdk_x11_screen_supports_net_wm_hint (GDK_SURFACE_SCREEN (window), - g_intern_static_string ("_NET_ACTIVE_WINDOW"))) - { - XClientMessageEvent xclient; - - memset (&xclient, 0, sizeof (xclient)); - xclient.type = ClientMessage; - xclient.window = GDK_SURFACE_XID (window); - xclient.message_type = gdk_x11_get_xatom_by_name_for_display (display, - "_NET_ACTIVE_WINDOW"); - xclient.format = 32; - xclient.data.l[0] = 1; /* requestor type; we're an app */ - xclient.data.l[1] = timestamp; - xclient.data.l[2] = None; /* currently active window */ - xclient.data.l[3] = 0; - xclient.data.l[4] = 0; - - XSendEvent (GDK_DISPLAY_XDISPLAY (display), GDK_SURFACE_XROOTWIN (window), False, - SubstructureRedirectMask | SubstructureNotifyMask, - (XEvent *)&xclient); - } - else - { - XRaiseWindow (GDK_DISPLAY_XDISPLAY (display), GDK_SURFACE_XID (window)); - - /* There is no way of knowing reliably whether we are viewable; - * so trap errors asynchronously around the XSetInputFocus call - */ - gdk_x11_display_error_trap_push (display); - XSetInputFocus (GDK_DISPLAY_XDISPLAY (display), - GDK_SURFACE_XID (window), - RevertToParent, - timestamp); - gdk_x11_display_error_trap_pop_ignored (display); - } -} - -static void -gdk_x11_surface_set_type_hint (GdkSurface *window, - GdkSurfaceTypeHint hint) -{ - GdkDisplay *display; - Atom atom; - - if (GDK_SURFACE_DESTROYED (window) || - !WINDOW_IS_TOPLEVEL_OR_FOREIGN (window)) - return; - - display = gdk_surface_get_display (window); - - switch (hint) - { - case GDK_SURFACE_TYPE_HINT_DIALOG: - atom = gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_WINDOW_TYPE_DIALOG"); - break; - case GDK_SURFACE_TYPE_HINT_MENU: - atom = gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_WINDOW_TYPE_MENU"); - break; - case GDK_SURFACE_TYPE_HINT_TOOLBAR: - atom = gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_WINDOW_TYPE_TOOLBAR"); - break; - case GDK_SURFACE_TYPE_HINT_UTILITY: - atom = gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_WINDOW_TYPE_UTILITY"); - break; - case GDK_SURFACE_TYPE_HINT_SPLASHSCREEN: - atom = gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_WINDOW_TYPE_SPLASH"); - break; - case GDK_SURFACE_TYPE_HINT_DOCK: - atom = gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_WINDOW_TYPE_DOCK"); - break; - case GDK_SURFACE_TYPE_HINT_DESKTOP: - atom = gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_WINDOW_TYPE_DESKTOP"); - break; - case GDK_SURFACE_TYPE_HINT_DROPDOWN_MENU: - atom = gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_WINDOW_TYPE_DROPDOWN_MENU"); - break; - case GDK_SURFACE_TYPE_HINT_POPUP_MENU: - atom = gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_WINDOW_TYPE_POPUP_MENU"); - break; - case GDK_SURFACE_TYPE_HINT_TOOLTIP: - atom = gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_WINDOW_TYPE_TOOLTIP"); - break; - case GDK_SURFACE_TYPE_HINT_NOTIFICATION: - atom = gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_WINDOW_TYPE_NOTIFICATION"); - break; - case GDK_SURFACE_TYPE_HINT_COMBO: - atom = gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_WINDOW_TYPE_COMBO"); - break; - case GDK_SURFACE_TYPE_HINT_DND: - atom = gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_WINDOW_TYPE_DND"); - break; - default: - g_warning ("Unknown hint %d passed to gdk_surface_set_type_hint", hint); - /* Fall thru */ - case GDK_SURFACE_TYPE_HINT_NORMAL: - atom = gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_WINDOW_TYPE_NORMAL"); - break; - } - - XChangeProperty (GDK_DISPLAY_XDISPLAY (display), GDK_SURFACE_XID (window), - gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_WINDOW_TYPE"), - XA_ATOM, 32, PropModeReplace, - (guchar *)&atom, 1); -} - -static GdkSurfaceTypeHint -gdk_x11_surface_get_type_hint (GdkSurface *window) -{ - GdkDisplay *display; - GdkSurfaceTypeHint type; - Atom type_return; - gint format_return; - gulong nitems_return; - gulong bytes_after_return; - guchar *data = NULL; - - g_return_val_if_fail (GDK_IS_SURFACE (window), GDK_SURFACE_TYPE_HINT_NORMAL); - - if (GDK_SURFACE_DESTROYED (window) || - !WINDOW_IS_TOPLEVEL_OR_FOREIGN (window)) - return GDK_SURFACE_TYPE_HINT_NORMAL; - - type = GDK_SURFACE_TYPE_HINT_NORMAL; - - display = gdk_surface_get_display (window); - - if (XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display), GDK_SURFACE_XID (window), - gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_WINDOW_TYPE"), - 0, G_MAXLONG, False, XA_ATOM, &type_return, - &format_return, &nitems_return, &bytes_after_return, - &data) == Success) - { - if ((type_return == XA_ATOM) && (format_return == 32) && - (data) && (nitems_return == 1)) - { - Atom atom = *(Atom*)data; - - if (atom == gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_WINDOW_TYPE_DIALOG")) - type = GDK_SURFACE_TYPE_HINT_DIALOG; - else if (atom == gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_WINDOW_TYPE_MENU")) - type = GDK_SURFACE_TYPE_HINT_MENU; - else if (atom == gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_WINDOW_TYPE_TOOLBAR")) - type = GDK_SURFACE_TYPE_HINT_TOOLBAR; - else if (atom == gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_WINDOW_TYPE_UTILITY")) - type = GDK_SURFACE_TYPE_HINT_UTILITY; - else if (atom == gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_WINDOW_TYPE_SPLASH")) - type = GDK_SURFACE_TYPE_HINT_SPLASHSCREEN; - else if (atom == gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_WINDOW_TYPE_DOCK")) - type = GDK_SURFACE_TYPE_HINT_DOCK; - else if (atom == gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_WINDOW_TYPE_DESKTOP")) - type = GDK_SURFACE_TYPE_HINT_DESKTOP; - else if (atom == gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_WINDOW_TYPE_DROPDOWN_MENU")) - type = GDK_SURFACE_TYPE_HINT_DROPDOWN_MENU; - else if (atom == gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_WINDOW_TYPE_POPUP_MENU")) - type = GDK_SURFACE_TYPE_HINT_POPUP_MENU; - else if (atom == gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_WINDOW_TYPE_TOOLTIP")) - type = GDK_SURFACE_TYPE_HINT_TOOLTIP; - else if (atom == gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_WINDOW_TYPE_NOTIFICATION")) - type = GDK_SURFACE_TYPE_HINT_NOTIFICATION; - else if (atom == gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_WINDOW_TYPE_COMBO")) - type = GDK_SURFACE_TYPE_HINT_COMBO; - else if (atom == gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_WINDOW_TYPE_DND")) - type = GDK_SURFACE_TYPE_HINT_DND; - } - - if (type_return != None && data != NULL) - XFree (data); - } - - return type; -} - -static void -gdk_wmspec_change_state (gboolean add, - GdkSurface *window, - GdkAtom state1, - GdkAtom state2) -{ - GdkDisplay *display = GDK_SURFACE_DISPLAY (window); - XClientMessageEvent xclient; - -#define _NET_WM_STATE_REMOVE 0 /* remove/unset property */ -#define _NET_WM_STATE_ADD 1 /* add/set property */ -#define _NET_WM_STATE_TOGGLE 2 /* toggle property */ - - memset (&xclient, 0, sizeof (xclient)); - xclient.type = ClientMessage; - xclient.window = GDK_SURFACE_XID (window); - xclient.message_type = gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_STATE"); - xclient.format = 32; - xclient.data.l[0] = add ? _NET_WM_STATE_ADD : _NET_WM_STATE_REMOVE; - xclient.data.l[1] = gdk_x11_atom_to_xatom_for_display (display, state1); - xclient.data.l[2] = gdk_x11_atom_to_xatom_for_display (display, state2); - xclient.data.l[3] = 1; /* source indication */ - xclient.data.l[4] = 0; - - XSendEvent (GDK_SURFACE_XDISPLAY (window), GDK_SURFACE_XROOTWIN (window), False, - SubstructureRedirectMask | SubstructureNotifyMask, - (XEvent *)&xclient); -} - -static void -gdk_x11_surface_set_modal_hint (GdkSurface *window, - gboolean modal) -{ - if (GDK_SURFACE_DESTROYED (window) || - !WINDOW_IS_TOPLEVEL_OR_FOREIGN (window)) - return; - - window->modal_hint = modal; - - if (GDK_SURFACE_IS_MAPPED (window)) - gdk_wmspec_change_state (modal, window, - g_intern_static_string ("_NET_WM_STATE_MODAL"), - NULL); -} - -static void -gdk_x11_surface_set_skip_taskbar_hint (GdkSurface *window, - gboolean skips_taskbar) -{ - GdkToplevelX11 *toplevel; - - g_return_if_fail (GDK_SURFACE_TYPE (window) != GDK_SURFACE_CHILD); - - if (GDK_SURFACE_DESTROYED (window) || - !WINDOW_IS_TOPLEVEL_OR_FOREIGN (window)) - return; - - toplevel = _gdk_x11_surface_get_toplevel (window); - toplevel->skip_taskbar_hint = skips_taskbar; - - if (GDK_SURFACE_IS_MAPPED (window)) - gdk_wmspec_change_state (skips_taskbar, window, - g_intern_static_string ("_NET_WM_STATE_SKIP_TASKBAR"), - NULL); -} - -static void -gdk_x11_surface_set_skip_pager_hint (GdkSurface *window, - gboolean skips_pager) -{ - GdkToplevelX11 *toplevel; - - g_return_if_fail (GDK_SURFACE_TYPE (window) != GDK_SURFACE_CHILD); - - if (GDK_SURFACE_DESTROYED (window) || - !WINDOW_IS_TOPLEVEL_OR_FOREIGN (window)) - return; - - toplevel = _gdk_x11_surface_get_toplevel (window); - toplevel->skip_pager_hint = skips_pager; - - if (GDK_SURFACE_IS_MAPPED (window)) - gdk_wmspec_change_state (skips_pager, window, - g_intern_static_string ("_NET_WM_STATE_SKIP_PAGER"), - NULL); -} - -static void -gdk_x11_surface_set_urgency_hint (GdkSurface *window, - gboolean urgent) -{ - GdkToplevelX11 *toplevel; - - g_return_if_fail (GDK_SURFACE_TYPE (window) != GDK_SURFACE_CHILD); - - if (GDK_SURFACE_DESTROYED (window) || - !WINDOW_IS_TOPLEVEL_OR_FOREIGN (window)) - return; - - toplevel = _gdk_x11_surface_get_toplevel (window); - toplevel->urgency_hint = urgent; - - update_wm_hints (window, FALSE); -} - -static void -gdk_x11_surface_set_geometry_hints (GdkSurface *window, - const GdkGeometry *geometry, - GdkSurfaceHints geom_mask) -{ - GdkSurfaceImplX11 *impl = GDK_SURFACE_IMPL_X11 (window->impl); - XSizeHints size_hints; - GdkToplevelX11 *toplevel; - - if (GDK_SURFACE_DESTROYED (window) || - !WINDOW_IS_TOPLEVEL_OR_FOREIGN (window)) - return; - - toplevel = _gdk_x11_surface_get_toplevel (window); - if (toplevel) - { - if (geometry) - toplevel->last_geometry_hints = *geometry; - toplevel->last_geometry_hints_mask = geom_mask; - } - - size_hints.flags = 0; - - if (geom_mask & GDK_HINT_POS) - { - size_hints.flags |= PPosition; - /* We need to initialize the following obsolete fields because KWM - * apparently uses these fields if they are non-zero. - * #@#!#!$!. - */ - size_hints.x = 0; - size_hints.y = 0; - } - - if (geom_mask & GDK_HINT_USER_POS) - { - size_hints.flags |= USPosition; - } - - if (geom_mask & GDK_HINT_USER_SIZE) - { - size_hints.flags |= USSize; - } - - if (geom_mask & GDK_HINT_MIN_SIZE) - { - size_hints.flags |= PMinSize; - size_hints.min_width = geometry->min_width * impl->window_scale; - size_hints.min_height = geometry->min_height * impl->window_scale; - } - - if (geom_mask & GDK_HINT_MAX_SIZE) - { - size_hints.flags |= PMaxSize; - size_hints.max_width = MAX (geometry->max_width, 1) * impl->window_scale; - size_hints.max_height = MAX (geometry->max_height, 1) * impl->window_scale; - } - - if (geom_mask & GDK_HINT_BASE_SIZE) - { - size_hints.flags |= PBaseSize; - size_hints.base_width = geometry->base_width * impl->window_scale; - size_hints.base_height = geometry->base_height * impl->window_scale; - } - - if (geom_mask & GDK_HINT_RESIZE_INC) - { - size_hints.flags |= PResizeInc; - size_hints.width_inc = geometry->width_inc * impl->window_scale; - size_hints.height_inc = geometry->height_inc * impl->window_scale; - } - else if (impl->window_scale > 1) - { - size_hints.flags |= PResizeInc; - size_hints.width_inc = impl->window_scale; - size_hints.height_inc = impl->window_scale; - } - - if (geom_mask & GDK_HINT_ASPECT) - { - size_hints.flags |= PAspect; - if (geometry->min_aspect <= 1) - { - size_hints.min_aspect.x = 65536 * geometry->min_aspect; - size_hints.min_aspect.y = 65536; - } - else - { - size_hints.min_aspect.x = 65536; - size_hints.min_aspect.y = 65536 / geometry->min_aspect;; - } - if (geometry->max_aspect <= 1) - { - size_hints.max_aspect.x = 65536 * geometry->max_aspect; - size_hints.max_aspect.y = 65536; - } - else - { - size_hints.max_aspect.x = 65536; - size_hints.max_aspect.y = 65536 / geometry->max_aspect;; - } - } - - if (geom_mask & GDK_HINT_WIN_GRAVITY) - { - size_hints.flags |= PWinGravity; - size_hints.win_gravity = geometry->win_gravity; - } - - /* FIXME: Would it be better to delete this property if - * geom_mask == 0? It would save space on the server - */ - XSetWMNormalHints (GDK_SURFACE_XDISPLAY (window), - GDK_SURFACE_XID (window), - &size_hints); -} - -static void -gdk_surface_get_geometry_hints (GdkSurface *window, - GdkGeometry *geometry, - GdkSurfaceHints *geom_mask) -{ - GdkSurfaceImplX11 *impl; - XSizeHints *size_hints; - glong junk_supplied_mask = 0; - - g_return_if_fail (GDK_IS_SURFACE (window)); - g_return_if_fail (geometry != NULL); - g_return_if_fail (geom_mask != NULL); - - *geom_mask = 0; - - if (GDK_SURFACE_DESTROYED (window) || - !WINDOW_IS_TOPLEVEL_OR_FOREIGN (window)) - return; - - impl = GDK_SURFACE_IMPL_X11 (window->impl); - - size_hints = XAllocSizeHints (); - if (!size_hints) - return; - - if (!XGetWMNormalHints (GDK_SURFACE_XDISPLAY (window), - GDK_SURFACE_XID (window), - size_hints, - &junk_supplied_mask)) - size_hints->flags = 0; - - if (size_hints->flags & PMinSize) - { - *geom_mask |= GDK_HINT_MIN_SIZE; - geometry->min_width = size_hints->min_width / impl->window_scale; - geometry->min_height = size_hints->min_height / impl->window_scale; - } - - if (size_hints->flags & PMaxSize) - { - *geom_mask |= GDK_HINT_MAX_SIZE; - geometry->max_width = MAX (size_hints->max_width, 1) / impl->window_scale; - geometry->max_height = MAX (size_hints->max_height, 1) / impl->window_scale; - } - - if (size_hints->flags & PResizeInc) - { - *geom_mask |= GDK_HINT_RESIZE_INC; - geometry->width_inc = size_hints->width_inc / impl->window_scale; - geometry->height_inc = size_hints->height_inc / impl->window_scale; - } - - if (size_hints->flags & PAspect) - { - *geom_mask |= GDK_HINT_ASPECT; - - geometry->min_aspect = (gdouble) size_hints->min_aspect.x / (gdouble) size_hints->min_aspect.y; - geometry->max_aspect = (gdouble) size_hints->max_aspect.x / (gdouble) size_hints->max_aspect.y; - } - - if (size_hints->flags & PWinGravity) - { - *geom_mask |= GDK_HINT_WIN_GRAVITY; - geometry->win_gravity = size_hints->win_gravity; - } - - XFree (size_hints); -} - -static gboolean -utf8_is_latin1 (const gchar *str) -{ - const char *p = str; - - while (*p) - { - gunichar ch = g_utf8_get_char (p); - - if (ch > 0xff) - return FALSE; - - p = g_utf8_next_char (p); - } - - return TRUE; -} - -/* Set the property to @utf8_str as STRING if the @utf8_str is fully - * convertable to STRING, otherwise, set it as compound text - */ -static void -set_text_property (GdkDisplay *display, - Window xwindow, - Atom property, - const gchar *utf8_str) -{ - gchar *prop_text = NULL; - Atom prop_type; - gint prop_length; - gint prop_format; - gboolean is_compound_text; - - if (utf8_is_latin1 (utf8_str)) - { - prop_type = XA_STRING; - prop_text = _gdk_x11_display_utf8_to_string_target (display, utf8_str); - prop_length = prop_text ? strlen (prop_text) : 0; - prop_format = 8; - is_compound_text = FALSE; - } - else - { - GdkAtom gdk_type; - - gdk_x11_display_utf8_to_compound_text (display, - utf8_str, &gdk_type, &prop_format, - (guchar **)&prop_text, &prop_length); - prop_type = gdk_x11_atom_to_xatom_for_display (display, gdk_type); - is_compound_text = TRUE; - } - - if (prop_text) - { - XChangeProperty (GDK_DISPLAY_XDISPLAY (display), - xwindow, - property, - prop_type, prop_format, - PropModeReplace, (guchar *)prop_text, - prop_length); - - if (is_compound_text) - gdk_x11_free_compound_text ((guchar *)prop_text); - else - g_free (prop_text); - } -} - -/* Set WM_NAME and _NET_WM_NAME - */ -static void -set_wm_name (GdkDisplay *display, - Window xwindow, - const gchar *name) -{ - XChangeProperty (GDK_DISPLAY_XDISPLAY (display), xwindow, - gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_NAME"), - gdk_x11_get_xatom_by_name_for_display (display, "UTF8_STRING"), 8, - PropModeReplace, (guchar *)name, strlen (name)); - - set_text_property (display, xwindow, - gdk_x11_get_xatom_by_name_for_display (display, "WM_NAME"), - name); -} - -static void -gdk_x11_surface_set_title (GdkSurface *window, - const gchar *title) -{ - GdkDisplay *display; - Display *xdisplay; - Window xwindow; - - g_return_if_fail (title != NULL); - - if (GDK_SURFACE_DESTROYED (window) || - !WINDOW_IS_TOPLEVEL_OR_FOREIGN (window)) - return; - - display = gdk_surface_get_display (window); - xdisplay = GDK_DISPLAY_XDISPLAY (display); - xwindow = GDK_SURFACE_XID (window); - - set_wm_name (display, xwindow, title); - - if (!gdk_surface_icon_name_set (window)) - { - XChangeProperty (xdisplay, xwindow, - gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_ICON_NAME"), - gdk_x11_get_xatom_by_name_for_display (display, "UTF8_STRING"), 8, - PropModeReplace, (guchar *)title, strlen (title)); - - set_text_property (display, xwindow, - gdk_x11_get_xatom_by_name_for_display (display, "WM_ICON_NAME"), - title); - } -} - -static void -gdk_x11_surface_set_role (GdkSurface *window, - const gchar *role) -{ - GdkDisplay *display; - - display = gdk_surface_get_display (window); - - if (GDK_SURFACE_DESTROYED (window) || - !WINDOW_IS_TOPLEVEL_OR_FOREIGN (window)) - return; - - if (role) - XChangeProperty (GDK_DISPLAY_XDISPLAY (display), GDK_SURFACE_XID (window), - gdk_x11_get_xatom_by_name_for_display (display, "WM_WINDOW_ROLE"), - XA_STRING, 8, PropModeReplace, (guchar *)role, strlen (role)); - else - XDeleteProperty (GDK_DISPLAY_XDISPLAY (display), GDK_SURFACE_XID (window), - gdk_x11_get_xatom_by_name_for_display (display, "WM_WINDOW_ROLE")); -} - -static void -gdk_x11_surface_set_startup_id (GdkSurface *window, - const gchar *startup_id) -{ - GdkDisplay *display; - - g_return_if_fail (GDK_IS_SURFACE (window)); - - display = gdk_surface_get_display (window); - - if (GDK_SURFACE_DESTROYED (window) || - !WINDOW_IS_TOPLEVEL_OR_FOREIGN (window)) - return; - - if (startup_id) - XChangeProperty (GDK_DISPLAY_XDISPLAY (display), GDK_SURFACE_XID (window), - gdk_x11_get_xatom_by_name_for_display (display, "_NET_STARTUP_ID"), - gdk_x11_get_xatom_by_name_for_display (display, "UTF8_STRING"), 8, - PropModeReplace, (unsigned char *)startup_id, strlen (startup_id)); - else - XDeleteProperty (GDK_DISPLAY_XDISPLAY (display), GDK_SURFACE_XID (window), - gdk_x11_get_xatom_by_name_for_display (display, "_NET_STARTUP_ID")); -} - -static void -gdk_x11_surface_set_transient_for (GdkSurface *window, - GdkSurface *parent) -{ - if (GDK_SURFACE_DESTROYED (window) || - !WINDOW_IS_TOPLEVEL_OR_FOREIGN (window)) - return; - - /* XSetTransientForHint() doesn't allow unsetting, so do it manually */ - if (parent && !GDK_SURFACE_DESTROYED (parent)) - XSetTransientForHint (GDK_SURFACE_XDISPLAY (window), - GDK_SURFACE_XID (window), - GDK_SURFACE_XID (parent)); - else - XDeleteProperty (GDK_SURFACE_XDISPLAY (window), - GDK_SURFACE_XID (window), - gdk_x11_get_xatom_by_name_for_display (GDK_SURFACE_DISPLAY (window), "WM_TRANSIENT_FOR")); -} - -GdkCursor * -_gdk_x11_surface_get_cursor (GdkSurface *window) -{ - GdkSurfaceImplX11 *impl; - - g_return_val_if_fail (GDK_IS_SURFACE (window), NULL); - - impl = GDK_SURFACE_IMPL_X11 (window->impl); - - return impl->cursor; -} - -static void -gdk_surface_x11_get_geometry (GdkSurface *window, - gint *x, - gint *y, - gint *width, - gint *height) -{ - GdkSurfaceImplX11 *impl; - Window root; - gint tx; - gint ty; - guint twidth; - guint theight; - guint tborder_width; - guint tdepth; - - if (!GDK_SURFACE_DESTROYED (window)) - { - impl = GDK_SURFACE_IMPL_X11 (window->impl); - - XGetGeometry (GDK_SURFACE_XDISPLAY (window), - GDK_SURFACE_XID (window), - &root, &tx, &ty, &twidth, &theight, &tborder_width, &tdepth); - - if (x) - *x = tx / impl->window_scale; - if (y) - *y = ty / impl->window_scale; - if (width) - *width = twidth / impl->window_scale; - if (height) - *height = theight / impl->window_scale; - } -} - -static void -gdk_surface_x11_get_root_coords (GdkSurface *window, - gint x, - gint y, - gint *root_x, - gint *root_y) -{ - GdkSurfaceImplX11 *impl = GDK_SURFACE_IMPL_X11 (window->impl); - Window child; - gint tx; - gint ty; - - XTranslateCoordinates (GDK_SURFACE_XDISPLAY (window), - GDK_SURFACE_XID (window), - GDK_SURFACE_XROOTWIN (window), - x * impl->window_scale, y * impl->window_scale, &tx, &ty, - &child); - - if (root_x) - *root_x = tx / impl->window_scale; - if (root_y) - *root_y = ty / impl->window_scale; -} - -static void -gdk_x11_surface_get_frame_extents (GdkSurface *window, - GdkRectangle *rect) -{ - GdkDisplay *display; - GdkSurfaceImplX11 *impl; - Window xwindow; - Window xparent; - Window root; - Window child; - Window *children; - guchar *data; - Window *vroots; - Atom type_return; - guint nchildren; - guint nvroots; - gulong nitems_return; - gulong bytes_after_return; - gint format_return; - gint i; - guint ww, wh, wb, wd; - gint wx, wy; - gboolean got_frame_extents = FALSE; - - g_return_if_fail (rect != NULL); - - rect->x = 0; - rect->y = 0; - rect->width = 1; - rect->height = 1; - - while (window->parent && (window->parent)->parent) - window = window->parent; - - impl = GDK_SURFACE_IMPL_X11 (window->impl); - - /* Refine our fallback answer a bit using local information */ - rect->x = window->x * impl->window_scale; - rect->y = window->y * impl->window_scale; - rect->width = window->width * impl->window_scale; - rect->height = window->height * impl->window_scale; - - if (GDK_SURFACE_DESTROYED (window) || impl->override_redirect) - return; - - nvroots = 0; - vroots = NULL; - - display = gdk_surface_get_display (window); - - gdk_x11_display_error_trap_push (display); - - xwindow = GDK_SURFACE_XID (window); - - /* first try: use _NET_FRAME_EXTENTS */ - if (gdk_x11_screen_supports_net_wm_hint (GDK_SURFACE_SCREEN (window), - g_intern_static_string ("_NET_FRAME_EXTENTS")) && - XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display), xwindow, - gdk_x11_get_xatom_by_name_for_display (display, - "_NET_FRAME_EXTENTS"), - 0, G_MAXLONG, False, XA_CARDINAL, &type_return, - &format_return, &nitems_return, &bytes_after_return, - &data) - == Success) - { - if ((type_return == XA_CARDINAL) && (format_return == 32) && - (nitems_return == 4) && (data)) - { - gulong *ldata = (gulong *) data; - got_frame_extents = TRUE; - - /* try to get the real client window geometry */ - if (XGetGeometry (GDK_DISPLAY_XDISPLAY (display), xwindow, - &root, &wx, &wy, &ww, &wh, &wb, &wd) && - XTranslateCoordinates (GDK_DISPLAY_XDISPLAY (display), - xwindow, root, 0, 0, &wx, &wy, &child)) - { - rect->x = wx; - rect->y = wy; - rect->width = ww; - rect->height = wh; - } - - /* _NET_FRAME_EXTENTS format is left, right, top, bottom */ - rect->x -= ldata[0]; - rect->y -= ldata[2]; - rect->width += ldata[0] + ldata[1]; - rect->height += ldata[2] + ldata[3]; - } - - if (data) - XFree (data); - } - - if (got_frame_extents) - goto out; - - /* no frame extents property available, which means we either have a WM that - is not EWMH compliant or is broken - try fallback and walk up the window - tree to get our window's parent which hopefully is the window frame */ - - /* use NETWM_VIRTUAL_ROOTS if available */ - root = GDK_SURFACE_XROOTWIN (window); - - if (gdk_x11_screen_supports_net_wm_hint (GDK_SURFACE_SCREEN (window), - g_intern_static_string ("_NET_VIRTUAL_ROOTS")) && - XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display), root, - gdk_x11_get_xatom_by_name_for_display (display, - "_NET_VIRTUAL_ROOTS"), - 0, G_MAXLONG, False, XA_WINDOW, &type_return, - &format_return, &nitems_return, &bytes_after_return, - &data) - == Success) - { - if ((type_return == XA_WINDOW) && (format_return == 32) && (data)) - { - nvroots = nitems_return; - vroots = (Window *)data; - } - } - - xparent = GDK_SURFACE_XID (window); - - do - { - xwindow = xparent; - - if (!XQueryTree (GDK_DISPLAY_XDISPLAY (display), xwindow, - &root, &xparent, - &children, &nchildren)) - goto out; - - if (children) - XFree (children); - - /* check virtual roots */ - for (i = 0; i < nvroots; i++) - { - if (xparent == vroots[i]) - { - root = xparent; - break; - } - } - } - while (xparent != root); - - if (XGetGeometry (GDK_DISPLAY_XDISPLAY (display), xwindow, - &root, &wx, &wy, &ww, &wh, &wb, &wd)) - { - rect->x = wx; - rect->y = wy; - rect->width = ww; - rect->height = wh; - } - - out: - if (vroots) - XFree (vroots); - - /* Here we extend the size to include the extra pixels if we round x/y down - as well as round the size up when we divide by scale so that the returned - size is guaranteed to cover the real pixels, but it may overshoot a bit - in case the window is not positioned/sized according to the scale */ - rect->width = (rect->width + rect->x % impl->window_scale + impl->window_scale - 1) / impl->window_scale; - rect->height = (rect->height + rect->y % impl->window_scale + impl->window_scale - 1) / impl->window_scale; - rect->x = rect->x / impl->window_scale; - rect->y = rect->y / impl->window_scale; - gdk_x11_display_error_trap_pop_ignored (display); -} - -static gboolean -gdk_surface_x11_get_device_state (GdkSurface *window, - GdkDevice *device, - gdouble *x, - gdouble *y, - GdkModifierType *mask) -{ - GdkSurface *child; - - if (GDK_SURFACE_DESTROYED (window)) - return FALSE; - - /*HIDPI: handle coords here?*/ - GDK_DEVICE_GET_CLASS (device)->query_state (device, window, - &child, - NULL, NULL, - x, y, mask); - return child != NULL; -} - -static GdkEventMask -gdk_surface_x11_get_events (GdkSurface *window) -{ - XWindowAttributes attrs; - GdkEventMask event_mask; - GdkEventMask filtered; - - if (GDK_SURFACE_DESTROYED (window)) - return 0; - else - { - XGetWindowAttributes (GDK_SURFACE_XDISPLAY (window), - GDK_SURFACE_XID (window), - &attrs); - event_mask = x_event_mask_to_gdk_event_mask (attrs.your_event_mask); - /* if property change was filtered out before, keep it filtered out */ - filtered = GDK_STRUCTURE_MASK | GDK_PROPERTY_CHANGE_MASK; - window->event_mask = event_mask & ((window->event_mask & filtered) | ~filtered); - - return event_mask; - } -} -static void -gdk_surface_x11_set_events (GdkSurface *window, - GdkEventMask event_mask) -{ - long xevent_mask = 0; - - if (!GDK_SURFACE_DESTROYED (window)) - { - GdkX11Display *display_x11; - - if (GDK_SURFACE_XID (window) != GDK_SURFACE_XROOTWIN (window)) - xevent_mask = StructureNotifyMask | PropertyChangeMask; - - display_x11 = GDK_X11_DISPLAY (gdk_surface_get_display (window)); - gdk_x11_event_source_select_events ((GdkEventSource *) display_x11->event_source, - GDK_SURFACE_XID (window), event_mask, - xevent_mask); - } -} - -static inline void -do_shape_combine_region (GdkSurface *window, - const cairo_region_t *shape_region, - gint offset_x, - gint offset_y, - gint shape) -{ - GdkSurfaceImplX11 *impl = GDK_SURFACE_IMPL_X11 (window->impl); - - if (GDK_SURFACE_DESTROYED (window)) - return; - - if (shape_region == NULL) - { - /* Use NULL mask to unset the shape */ - if (shape == ShapeBounding - ? gdk_display_supports_shapes (GDK_SURFACE_DISPLAY (window)) - : gdk_display_supports_input_shapes (GDK_SURFACE_DISPLAY (window))) - { - XShapeCombineMask (GDK_SURFACE_XDISPLAY (window), - GDK_SURFACE_XID (window), - shape, - 0, 0, - None, - ShapeSet); - } - return; - } - - if (shape == ShapeBounding - ? gdk_display_supports_shapes (GDK_SURFACE_DISPLAY (window)) - : gdk_display_supports_input_shapes (GDK_SURFACE_DISPLAY (window))) - { - gint n_rects = 0; - XRectangle *xrects = NULL; - - _gdk_x11_region_get_xrectangles (shape_region, - 0, 0, impl->window_scale, - &xrects, &n_rects); - - XShapeCombineRectangles (GDK_SURFACE_XDISPLAY (window), - GDK_SURFACE_XID (window), - shape, - offset_x * impl->window_scale, - offset_y * impl->window_scale, - xrects, n_rects, - ShapeSet, - YXBanded); - - g_free (xrects); - } -} - -static void -gdk_surface_x11_shape_combine_region (GdkSurface *window, - const cairo_region_t *shape_region, - gint offset_x, - gint offset_y) -{ - do_shape_combine_region (window, shape_region, offset_x, offset_y, ShapeBounding); -} - -static void -gdk_surface_x11_input_shape_combine_region (GdkSurface *window, - const cairo_region_t *shape_region, - gint offset_x, - gint offset_y) -{ -#ifdef ShapeInput - do_shape_combine_region (window, shape_region, offset_x, offset_y, ShapeInput); -#endif -} - -static void -gdk_x11_surface_set_accept_focus (GdkSurface *window, - gboolean accept_focus) -{ - accept_focus = accept_focus != FALSE; - - if (window->accept_focus != accept_focus) - { - window->accept_focus = accept_focus; - - if (!GDK_SURFACE_DESTROYED (window) && - WINDOW_IS_TOPLEVEL_OR_FOREIGN (window)) - update_wm_hints (window, FALSE); - } -} - -static void -gdk_x11_surface_set_focus_on_map (GdkSurface *window, - gboolean focus_on_map) -{ - focus_on_map = focus_on_map != FALSE; - - if (window->focus_on_map != focus_on_map) - { - window->focus_on_map = focus_on_map; - - if ((!GDK_SURFACE_DESTROYED (window)) && - (!window->focus_on_map) && - WINDOW_IS_TOPLEVEL_OR_FOREIGN (window)) - gdk_x11_surface_set_user_time (window, 0); - } -} - -/** - * gdk_x11_surface_set_user_time: - * @window: (type GdkX11Surface): A toplevel #GdkSurface - * @timestamp: An XServer timestamp to which the property should be set - * - * The application can use this call to update the _NET_WM_USER_TIME - * property on a toplevel window. This property stores an Xserver - * time which represents the time of the last user input event - * received for this window. This property may be used by the window - * manager to alter the focus, stacking, and/or placement behavior of - * windows when they are mapped depending on whether the new window - * was created by a user action or is a "pop-up" window activated by a - * timer or some other event. - * - * Note that this property is automatically updated by GDK, so this - * function should only be used by applications which handle input - * events bypassing GDK. - **/ -void -gdk_x11_surface_set_user_time (GdkSurface *window, - guint32 timestamp) -{ - GdkDisplay *display; - GdkX11Display *display_x11; - GdkToplevelX11 *toplevel; - glong timestamp_long = (glong)timestamp; - Window xid; - - if (GDK_SURFACE_DESTROYED (window) || - !WINDOW_IS_TOPLEVEL_OR_FOREIGN (window)) - return; - - display = gdk_surface_get_display (window); - display_x11 = GDK_X11_DISPLAY (display); - toplevel = _gdk_x11_surface_get_toplevel (window); - - if (!toplevel) - { - g_warning ("gdk_surface_set_user_time called on non-toplevel\n"); - return; - } - - if (toplevel->focus_window != None && - gdk_x11_screen_supports_net_wm_hint (GDK_SURFACE_SCREEN (window), - g_intern_static_string ("_NET_WM_USER_TIME_WINDOW"))) - xid = toplevel->focus_window; - else - xid = GDK_SURFACE_XID (window); - - XChangeProperty (GDK_DISPLAY_XDISPLAY (display), xid, - gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_USER_TIME"), - XA_CARDINAL, 32, PropModeReplace, - (guchar *)×tamp_long, 1); - - if (timestamp_long != GDK_CURRENT_TIME && - (display_x11->user_time == GDK_CURRENT_TIME || - XSERVER_TIME_IS_LATER (timestamp_long, display_x11->user_time))) - display_x11->user_time = timestamp_long; - - if (toplevel) - toplevel->user_time = timestamp_long; -} - -/** - * gdk_x11_surface_set_utf8_property: - * @window: (type GdkX11Surface): a #GdkSurface - * @name: Property name, will be interned as an X atom - * @value: (allow-none): Property value, or %NULL to delete - * - * This function modifies or removes an arbitrary X11 window - * property of type UTF8_STRING. If the given @window is - * not a toplevel window, it is ignored. - */ -void -gdk_x11_surface_set_utf8_property (GdkSurface *window, - const gchar *name, - const gchar *value) -{ - GdkDisplay *display; - - if (!WINDOW_IS_TOPLEVEL (window)) - return; - - display = gdk_surface_get_display (window); - - if (value != NULL) - { - XChangeProperty (GDK_DISPLAY_XDISPLAY (display), - GDK_SURFACE_XID (window), - gdk_x11_get_xatom_by_name_for_display (display, name), - gdk_x11_get_xatom_by_name_for_display (display, "UTF8_STRING"), 8, - PropModeReplace, (guchar *)value, strlen (value)); - } - else - { - XDeleteProperty (GDK_DISPLAY_XDISPLAY (display), - GDK_SURFACE_XID (window), - gdk_x11_get_xatom_by_name_for_display (display, name)); - } -} - -static void -gdk_x11_surface_set_shadow_width (GdkSurface *window, - int left, - int right, - int top, - int bottom) -{ - GdkSurfaceImplX11 *impl = GDK_SURFACE_IMPL_X11 (window->impl); - Atom frame_extents; - gulong data[4] = { - left * impl->window_scale, - right * impl->window_scale, - top * impl->window_scale, - bottom * impl->window_scale - }; - - frame_extents = gdk_x11_get_xatom_by_name_for_display (gdk_surface_get_display (window), - "_GTK_FRAME_EXTENTS"); - XChangeProperty (GDK_SURFACE_XDISPLAY (window), - GDK_SURFACE_XID (window), - frame_extents, XA_CARDINAL, - 32, PropModeReplace, - (guchar *) &data, 4); -} - -/** - * gdk_x11_surface_set_theme_variant: - * @window: (type GdkX11Surface): a #GdkSurface - * @variant: the theme variant to export - * - * GTK+ applications can request a dark theme variant. In order to - * make other applications - namely window managers using GTK+ for - * themeing - aware of this choice, GTK+ uses this function to - * export the requested theme variant as _GTK_THEME_VARIANT property - * on toplevel windows. - * - * Note that this property is automatically updated by GTK+, so this - * function should only be used by applications which do not use GTK+ - * to create toplevel windows. - */ -void -gdk_x11_surface_set_theme_variant (GdkSurface *window, - const char *variant) -{ - gdk_x11_surface_set_utf8_property (window, "_GTK_THEME_VARIANT", - variant ? variant : ""); -} - -#define GDK_SELECTION_MAX_SIZE(display) \ - MIN(262144, \ - XExtendedMaxRequestSize (GDK_DISPLAY_XDISPLAY (display)) == 0 \ - ? XMaxRequestSize (GDK_DISPLAY_XDISPLAY (display)) - 100 \ - : XExtendedMaxRequestSize (GDK_DISPLAY_XDISPLAY (display)) - 100) - -static void -gdk_surface_update_icon (GdkSurface *window, - GList *icon_list) -{ - GdkToplevelX11 *toplevel; - GdkTexture *best_icon; - GList *tmp_list; - int best_size; - - toplevel = _gdk_x11_surface_get_toplevel (window); - - if (toplevel->icon_pixmap != NULL) - { - cairo_surface_destroy (toplevel->icon_pixmap); - toplevel->icon_pixmap = NULL; - } - - if (toplevel->icon_mask != NULL) - { - cairo_surface_destroy (toplevel->icon_mask); - toplevel->icon_mask = NULL; - } - -#define IDEAL_SIZE 48 - - best_size = G_MAXINT; - best_icon = NULL; - for (tmp_list = icon_list; tmp_list; tmp_list = tmp_list->next) - { - GdkTexture *texture = tmp_list->data; - int this; - - /* average width and height - if someone passes in a rectangular - * icon they deserve what they get. - */ - this = gdk_texture_get_width (texture) + gdk_texture_get_height (texture); - this /= 2; - - if (best_icon == NULL) - { - best_icon = texture; - best_size = this; - } - else - { - /* icon is better if it's 32 pixels or larger, and closer to - * the ideal size than the current best. - */ - if (this >= 32 && - (ABS (best_size - IDEAL_SIZE) < - ABS (this - IDEAL_SIZE))) - { - best_icon = texture; - best_size = this; - } - } - } - - if (best_icon) - { - int width = gdk_texture_get_width (best_icon); - int height = gdk_texture_get_height (best_icon); - cairo_surface_t *surface; - cairo_t *cr; - - toplevel->icon_pixmap = gdk_x11_surface_create_pixmap_surface (window, width, height); - - surface = gdk_texture_download_surface (best_icon); - - cr = cairo_create (toplevel->icon_pixmap); - cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE); - cairo_set_source_surface (cr, surface, 0, 0); - if (cairo_surface_get_content (surface) == CAIRO_CONTENT_COLOR_ALPHA) - { - /* Saturate the image, so it has bilevel alpha */ - cairo_push_group_with_content (cr, CAIRO_CONTENT_COLOR_ALPHA); - cairo_paint (cr); - cairo_set_operator (cr, CAIRO_OPERATOR_SATURATE); - cairo_paint (cr); - cairo_pop_group_to_source (cr); - } - cairo_paint (cr); - cairo_destroy (cr); - - if (cairo_surface_get_content (surface) == CAIRO_CONTENT_COLOR_ALPHA) - { - GdkDisplay *display = gdk_surface_get_display (window); - - toplevel->icon_mask = _gdk_x11_display_create_bitmap_surface (display, width, height); - - cr = cairo_create (toplevel->icon_mask); - cairo_set_source_surface (cr, surface, 0, 0); - cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE); - cairo_paint (cr); - cairo_destroy (cr); - } - - cairo_surface_destroy (surface); - } - - update_wm_hints (window, FALSE); -} - -static void -gdk_x11_surface_set_icon_list (GdkSurface *window, - GList *textures) -{ - gulong *data; - gulong *p; - gint size; - GList *l; - gint width, height; - GdkTexture *texture; - GdkDisplay *display; - gint i, n; - - if (GDK_SURFACE_DESTROYED (window) || - !WINDOW_IS_TOPLEVEL_OR_FOREIGN (window)) - return; - - display = gdk_surface_get_display (window); - - size = 0; - n = 0; - for (l = textures; l != NULL; l = l->next) - { - texture = l->data; - - width = gdk_texture_get_width (texture); - height = gdk_texture_get_height (texture); - - /* silently ignore overlarge icons */ - if (size + 2 + width * height > GDK_SELECTION_MAX_SIZE(display)) - break; - - n++; - size += 2 + width * height; - } - - data = g_malloc (size * sizeof (gulong)); - - p = data; - for (l = textures; l != NULL && n > 0; l = l->next) - { - texture = l->data; - - width = gdk_texture_get_width (texture); - height = gdk_texture_get_height (texture); - - *p++ = width; - *p++ = height; - - gdk_texture_download (texture, (guchar *) p, width * 4); - if (sizeof (gulong) > 4) - { - i = width * height; - while (i-- > 0) - p[i] = ((guint32 *) p)[i]; - } - - p += width * height; - n--; - } - - if (size > 0) - { - XChangeProperty (GDK_DISPLAY_XDISPLAY (display), - GDK_SURFACE_XID (window), - gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_ICON"), - XA_CARDINAL, 32, - PropModeReplace, - (guchar*) data, size); - } - else - { - XDeleteProperty (GDK_DISPLAY_XDISPLAY (display), - GDK_SURFACE_XID (window), - gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_ICON")); - } - - g_free (data); - - gdk_surface_update_icon (window, textures); -} - -static gboolean -gdk_surface_icon_name_set (GdkSurface *window) -{ - return GPOINTER_TO_UINT (g_object_get_qdata (G_OBJECT (window), - g_quark_from_static_string ("gdk-icon-name-set"))); -} - -static void -gdk_x11_surface_set_icon_name (GdkSurface *window, - const gchar *name) -{ - GdkDisplay *display; - - if (GDK_SURFACE_DESTROYED (window) || - !WINDOW_IS_TOPLEVEL_OR_FOREIGN (window)) - return; - - display = gdk_surface_get_display (window); - - g_object_set_qdata (G_OBJECT (window), g_quark_from_static_string ("gdk-icon-name-set"), - GUINT_TO_POINTER (name != NULL)); - - if (name != NULL) - { - XChangeProperty (GDK_DISPLAY_XDISPLAY (display), - GDK_SURFACE_XID (window), - gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_ICON_NAME"), - gdk_x11_get_xatom_by_name_for_display (display, "UTF8_STRING"), 8, - PropModeReplace, (guchar *)name, strlen (name)); - - set_text_property (display, GDK_SURFACE_XID (window), - gdk_x11_get_xatom_by_name_for_display (display, "WM_ICON_NAME"), - name); - } - else - { - XDeleteProperty (GDK_DISPLAY_XDISPLAY (display), - GDK_SURFACE_XID (window), - gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_ICON_NAME")); - XDeleteProperty (GDK_DISPLAY_XDISPLAY (display), - GDK_SURFACE_XID (window), - gdk_x11_get_xatom_by_name_for_display (display, "WM_ICON_NAME")); - } -} - -static void -gdk_x11_surface_iconify (GdkSurface *window) -{ - if (GDK_SURFACE_DESTROYED (window) || - !WINDOW_IS_TOPLEVEL_OR_FOREIGN (window)) - return; - - if (GDK_SURFACE_IS_MAPPED (window)) - { - XIconifyWindow (GDK_SURFACE_XDISPLAY (window), - GDK_SURFACE_XID (window), - gdk_x11_screen_get_screen_number (GDK_SURFACE_SCREEN (window))); - } - else - { - /* Flip our client side flag, the real work happens on map. */ - gdk_synthesize_window_state (window, - 0, - GDK_SURFACE_STATE_ICONIFIED); - gdk_wmspec_change_state (TRUE, window, - g_intern_static_string ("_NET_WM_STATE_HIDDEN"), - NULL); - } -} - -static void -gdk_x11_surface_deiconify (GdkSurface *window) -{ - if (GDK_SURFACE_DESTROYED (window) || - !WINDOW_IS_TOPLEVEL_OR_FOREIGN (window)) - return; - - if (GDK_SURFACE_IS_MAPPED (window)) - { - gdk_surface_show (window); - gdk_wmspec_change_state (FALSE, window, - g_intern_static_string ("_NET_WM_STATE_HIDDEN"), - NULL); - } - else - { - /* Flip our client side flag, the real work happens on map. */ - gdk_synthesize_window_state (window, - GDK_SURFACE_STATE_ICONIFIED, - 0); - gdk_wmspec_change_state (FALSE, window, - g_intern_static_string ("_NET_WM_STATE_HIDDEN"), - NULL); - } -} - -static void -gdk_x11_surface_stick (GdkSurface *window) -{ - if (GDK_SURFACE_DESTROYED (window) || - !WINDOW_IS_TOPLEVEL_OR_FOREIGN (window)) - return; - - if (GDK_SURFACE_IS_MAPPED (window)) - { - /* "stick" means stick to all desktops _and_ do not scroll with the - * viewport. i.e. glue to the monitor glass in all cases. - */ - - XClientMessageEvent xclient; - - /* Request stick during viewport scroll */ - gdk_wmspec_change_state (TRUE, window, - g_intern_static_string ("_NET_WM_STATE_STICKY"), - NULL); - - /* Request desktop 0xFFFFFFFF */ - memset (&xclient, 0, sizeof (xclient)); - xclient.type = ClientMessage; - xclient.window = GDK_SURFACE_XID (window); - xclient.display = GDK_SURFACE_XDISPLAY (window); - xclient.message_type = gdk_x11_get_xatom_by_name_for_display (GDK_SURFACE_DISPLAY (window), - "_NET_WM_DESKTOP"); - xclient.format = 32; - - xclient.data.l[0] = 0xFFFFFFFF; - xclient.data.l[1] = 0; - xclient.data.l[2] = 0; - xclient.data.l[3] = 0; - xclient.data.l[4] = 0; - - XSendEvent (GDK_SURFACE_XDISPLAY (window), GDK_SURFACE_XROOTWIN (window), False, - SubstructureRedirectMask | SubstructureNotifyMask, - (XEvent *)&xclient); - } - else - { - /* Flip our client side flag, the real work happens on map. */ - gdk_synthesize_window_state (window, - 0, - GDK_SURFACE_STATE_STICKY); - } -} - -static void -gdk_x11_surface_unstick (GdkSurface *window) -{ - if (GDK_SURFACE_DESTROYED (window) || - !WINDOW_IS_TOPLEVEL_OR_FOREIGN (window)) - return; - - if (GDK_SURFACE_IS_MAPPED (window)) - { - /* Request unstick from viewport */ - gdk_wmspec_change_state (FALSE, window, - g_intern_static_string ("_NET_WM_STATE_STICKY"), - NULL); - - move_to_current_desktop (window); - } - else - { - /* Flip our client side flag, the real work happens on map. */ - gdk_synthesize_window_state (window, - GDK_SURFACE_STATE_STICKY, - 0); - - } -} - -static void -gdk_x11_surface_maximize (GdkSurface *window) -{ - if (GDK_SURFACE_DESTROYED (window) || - !WINDOW_IS_TOPLEVEL_OR_FOREIGN (window)) - return; - - if (GDK_SURFACE_IS_MAPPED (window)) - gdk_wmspec_change_state (TRUE, window, - g_intern_static_string ("_NET_WM_STATE_MAXIMIZED_VERT"), - g_intern_static_string ("_NET_WM_STATE_MAXIMIZED_HORZ")); - else - gdk_synthesize_window_state (window, - 0, - GDK_SURFACE_STATE_MAXIMIZED); -} - -static void -gdk_x11_surface_unmaximize (GdkSurface *window) -{ - if (GDK_SURFACE_DESTROYED (window) || - !WINDOW_IS_TOPLEVEL_OR_FOREIGN (window)) - return; - - if (GDK_SURFACE_IS_MAPPED (window)) - gdk_wmspec_change_state (FALSE, window, - g_intern_static_string ("_NET_WM_STATE_MAXIMIZED_VERT"), - g_intern_static_string ("_NET_WM_STATE_MAXIMIZED_HORZ")); - else - gdk_synthesize_window_state (window, - GDK_SURFACE_STATE_MAXIMIZED, - 0); -} - -static void -gdk_x11_surface_apply_fullscreen_mode (GdkSurface *window) -{ - if (GDK_SURFACE_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_SURFACE_IS_MAPPED (window)) - { - XClientMessageEvent xclient; - gint monitors[4]; - gint i; - - memset (&xclient, 0, sizeof (xclient)); - xclient.type = ClientMessage; - xclient.window = GDK_SURFACE_XID (window); - xclient.display = GDK_SURFACE_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_SURFACE_SCREEN (window), - &monitors[0], - &monitors[1], - &monitors[2], - &monitors[3]); - /* Translate all 4 monitors from the GDK set into XINERAMA indices */ - for (i = 0; i < 4; ++i) - { - xclient.data.l[i] = 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_surface_apply_fullscreen_mode: Invalid XINERAMA monitor index"); - return; - } - } - break; - - default: - g_warning ("gdk_x11_surface_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_SURFACE_DISPLAY (window), - "_NET_WM_FULLSCREEN_MONITORS"); - XSendEvent (GDK_SURFACE_XDISPLAY (window), GDK_SURFACE_XROOTWIN (window), False, - SubstructureRedirectMask | SubstructureNotifyMask, - (XEvent *)&xclient); - } -} - -static void -gdk_x11_surface_fullscreen (GdkSurface *window) -{ - if (GDK_SURFACE_DESTROYED (window) || - !WINDOW_IS_TOPLEVEL_OR_FOREIGN (window)) - return; - - if (GDK_SURFACE_IS_MAPPED (window)) - { - gdk_wmspec_change_state (TRUE, window, - g_intern_static_string ("_NET_WM_STATE_FULLSCREEN"), - NULL); - /* 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_surface_apply_fullscreen_mode (window); - } - else - gdk_synthesize_window_state (window, - 0, - GDK_SURFACE_STATE_FULLSCREEN); -} - -static void -gdk_x11_surface_fullscreen_on_monitor (GdkSurface *window, - GdkMonitor *monitor) -{ - GdkRectangle geom; - - if (GDK_SURFACE_DESTROYED (window) || - !WINDOW_IS_TOPLEVEL_OR_FOREIGN (window)) - return; - - gdk_monitor_get_geometry (monitor, &geom); - gdk_surface_move (window, geom.x, geom.y); - - gdk_surface_set_fullscreen_mode (window, GDK_FULLSCREEN_ON_CURRENT_MONITOR); - gdk_x11_surface_fullscreen (window); -} - -static void -gdk_x11_surface_unfullscreen (GdkSurface *window) -{ - if (GDK_SURFACE_DESTROYED (window) || - !WINDOW_IS_TOPLEVEL_OR_FOREIGN (window)) - return; - - if (GDK_SURFACE_IS_MAPPED (window)) - gdk_wmspec_change_state (FALSE, window, - g_intern_static_string ("_NET_WM_STATE_FULLSCREEN"), - NULL); - - else - gdk_synthesize_window_state (window, - GDK_SURFACE_STATE_FULLSCREEN, - 0); -} - -static void -gdk_x11_surface_set_keep_above (GdkSurface *window, - gboolean setting) -{ - g_return_if_fail (GDK_IS_SURFACE (window)); - - if (GDK_SURFACE_DESTROYED (window) || - !WINDOW_IS_TOPLEVEL_OR_FOREIGN (window)) - return; - - if (GDK_SURFACE_IS_MAPPED (window)) - { - if (setting) - gdk_wmspec_change_state (FALSE, window, - g_intern_static_string ("_NET_WM_STATE_BELOW"), - NULL); - gdk_wmspec_change_state (setting, window, - g_intern_static_string ("_NET_WM_STATE_ABOVE"), - NULL); - } - else - gdk_synthesize_window_state (window, - setting ? GDK_SURFACE_STATE_BELOW : GDK_SURFACE_STATE_ABOVE, - setting ? GDK_SURFACE_STATE_ABOVE : 0); -} - -static void -gdk_x11_surface_set_keep_below (GdkSurface *window, gboolean setting) -{ - g_return_if_fail (GDK_IS_SURFACE (window)); - - if (GDK_SURFACE_DESTROYED (window) || - !WINDOW_IS_TOPLEVEL_OR_FOREIGN (window)) - return; - - if (GDK_SURFACE_IS_MAPPED (window)) - { - if (setting) - gdk_wmspec_change_state (FALSE, window, - g_intern_static_string ("_NET_WM_STATE_ABOVE"), - NULL); - gdk_wmspec_change_state (setting, window, - g_intern_static_string ("_NET_WM_STATE_BELOW"), - NULL); - } - else - gdk_synthesize_window_state (window, - setting ? GDK_SURFACE_STATE_ABOVE : GDK_SURFACE_STATE_BELOW, - setting ? GDK_SURFACE_STATE_BELOW : 0); -} - -static GdkSurface * -gdk_x11_surface_get_group (GdkSurface *window) -{ - GdkToplevelX11 *toplevel; - - if (GDK_SURFACE_DESTROYED (window) || - !WINDOW_IS_TOPLEVEL (window)) - return NULL; - - toplevel = _gdk_x11_surface_get_toplevel (window); - - return toplevel->group_leader; -} - -static void -gdk_x11_surface_set_group (GdkSurface *window, - GdkSurface *leader) -{ - GdkToplevelX11 *toplevel; - - g_return_if_fail (GDK_IS_SURFACE (window)); - g_return_if_fail (GDK_SURFACE_TYPE (window) != GDK_SURFACE_CHILD); - g_return_if_fail (leader == NULL || GDK_IS_SURFACE (leader)); - - if (GDK_SURFACE_DESTROYED (window) || - (leader != NULL && GDK_SURFACE_DESTROYED (leader)) || - !WINDOW_IS_TOPLEVEL (window)) - return; - - toplevel = _gdk_x11_surface_get_toplevel (window); - - if (leader == NULL) - leader = gdk_display_get_default_group (gdk_surface_get_display (window)); - - if (toplevel->group_leader != leader) - { - if (toplevel->group_leader) - g_object_unref (toplevel->group_leader); - toplevel->group_leader = g_object_ref (leader); - (_gdk_x11_surface_get_toplevel (leader))->is_leader = TRUE; - } - - update_wm_hints (window, FALSE); -} - -static MotifWmHints * -gdk_surface_get_mwm_hints (GdkSurface *window) -{ - GdkDisplay *display; - Atom hints_atom = None; - guchar *data; - Atom type; - gint format; - gulong nitems; - gulong bytes_after; - - if (GDK_SURFACE_DESTROYED (window)) - return NULL; - - display = gdk_surface_get_display (window); - - hints_atom = gdk_x11_get_xatom_by_name_for_display (display, _XA_MOTIF_WM_HINTS); - - XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display), GDK_SURFACE_XID (window), - hints_atom, 0, sizeof (MotifWmHints)/sizeof (long), - False, AnyPropertyType, &type, &format, &nitems, - &bytes_after, &data); - - if (type == None) - return NULL; - - return (MotifWmHints *)data; -} - -static void -gdk_surface_set_mwm_hints (GdkSurface *window, - MotifWmHints *new_hints) -{ - GdkDisplay *display; - Atom hints_atom = None; - guchar *data; - MotifWmHints *hints; - Atom type; - gint format; - gulong nitems; - gulong bytes_after; - - if (GDK_SURFACE_DESTROYED (window)) - return; - - display = gdk_surface_get_display (window); - - hints_atom = gdk_x11_get_xatom_by_name_for_display (display, _XA_MOTIF_WM_HINTS); - - XGetWindowProperty (GDK_SURFACE_XDISPLAY (window), GDK_SURFACE_XID (window), - hints_atom, 0, sizeof (MotifWmHints)/sizeof (long), - False, AnyPropertyType, &type, &format, &nitems, - &bytes_after, &data); - - if (type == None) - hints = new_hints; - else - { - hints = (MotifWmHints *)data; - - if (new_hints->flags & MWM_HINTS_FUNCTIONS) - { - hints->flags |= MWM_HINTS_FUNCTIONS; - hints->functions = new_hints->functions; - } - if (new_hints->flags & MWM_HINTS_DECORATIONS) - { - hints->flags |= MWM_HINTS_DECORATIONS; - hints->decorations = new_hints->decorations; - } - } - - XChangeProperty (GDK_SURFACE_XDISPLAY (window), GDK_SURFACE_XID (window), - hints_atom, hints_atom, 32, PropModeReplace, - (guchar *)hints, sizeof (MotifWmHints)/sizeof (long)); - - if (hints != new_hints) - XFree (hints); -} - -static void -gdk_x11_surface_set_decorations (GdkSurface *window, - GdkWMDecoration decorations) -{ - MotifWmHints hints; - - if (GDK_SURFACE_DESTROYED (window) || - !WINDOW_IS_TOPLEVEL_OR_FOREIGN (window)) - return; - - /* initialize to zero to avoid writing uninitialized data to socket */ - memset(&hints, 0, sizeof(hints)); - hints.flags = MWM_HINTS_DECORATIONS; - hints.decorations = decorations; - - gdk_surface_set_mwm_hints (window, &hints); -} - -static gboolean -gdk_x11_surface_get_decorations(GdkSurface *window, - GdkWMDecoration *decorations) -{ - MotifWmHints *hints; - gboolean result = FALSE; - - if (GDK_SURFACE_DESTROYED (window) || - !WINDOW_IS_TOPLEVEL_OR_FOREIGN (window)) - return FALSE; - - hints = gdk_surface_get_mwm_hints (window); - - if (hints) - { - if (hints->flags & MWM_HINTS_DECORATIONS) - { - if (decorations) - *decorations = hints->decorations; - result = TRUE; - } - - XFree (hints); - } - - return result; -} - -static void -gdk_x11_surface_set_functions (GdkSurface *window, - GdkWMFunction functions) -{ - MotifWmHints hints; - - g_return_if_fail (GDK_IS_SURFACE (window)); - - if (GDK_SURFACE_DESTROYED (window) || - !WINDOW_IS_TOPLEVEL_OR_FOREIGN (window)) - return; - - /* initialize to zero to avoid writing uninitialized data to socket */ - memset(&hints, 0, sizeof(hints)); - hints.flags = MWM_HINTS_FUNCTIONS; - hints.functions = functions; - - gdk_surface_set_mwm_hints (window, &hints); -} - -cairo_region_t * -_gdk_x11_xwindow_get_shape (Display *xdisplay, - Window window, - gint scale, - gint shape_type) -{ - cairo_region_t *shape; - GdkRectangle *rl; - XRectangle *xrl; - gint rn, ord, i; - - shape = NULL; - rn = 0; - - /* Note that XShapeGetRectangles returns NULL in two situations: - * - the server doesn't support the SHAPE extension - * - the shape is empty - * - * Since we can't discriminate these here, we always return - * an empty shape. It is the callers responsibility to check - * whether the server supports the SHAPE extensions beforehand. - */ - xrl = XShapeGetRectangles (xdisplay, window, shape_type, &rn, &ord); - - if (rn == 0) - return cairo_region_create (); /* Empty */ - - if (ord != YXBanded) - { - /* This really shouldn't happen with any xserver, as they - * generally convert regions to YXBanded internally - */ - g_warning ("non YXBanded shape masks not supported"); - XFree (xrl); - return NULL; - } - - /* NOTE: The scale divisions here may lose some precision if someone - else set the shape to be non-scale precision */ - rl = g_new (GdkRectangle, rn); - for (i = 0; i < rn; i++) - { - rl[i].x = xrl[i].x / scale; - rl[i].y = xrl[i].y / scale; - rl[i].width = xrl[i].width / scale; - rl[i].height = xrl[i].height / scale; - } - XFree (xrl); - - shape = cairo_region_create_rectangles (rl, rn); - g_free (rl); - - return shape; -} - -/* From the WM spec */ -#define _NET_WM_MOVERESIZE_SIZE_TOPLEFT 0 -#define _NET_WM_MOVERESIZE_SIZE_TOP 1 -#define _NET_WM_MOVERESIZE_SIZE_TOPRIGHT 2 -#define _NET_WM_MOVERESIZE_SIZE_RIGHT 3 -#define _NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT 4 -#define _NET_WM_MOVERESIZE_SIZE_BOTTOM 5 -#define _NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT 6 -#define _NET_WM_MOVERESIZE_SIZE_LEFT 7 -#define _NET_WM_MOVERESIZE_MOVE 8 /* movement only */ -#define _NET_WM_MOVERESIZE_SIZE_KEYBOARD 9 /* size via keyboard */ -#define _NET_WM_MOVERESIZE_MOVE_KEYBOARD 10 /* move via keyboard */ -#define _NET_WM_MOVERESIZE_CANCEL 11 /* cancel operation */ - -static void -wmspec_send_message (GdkDisplay *display, - GdkSurface *window, - gint root_x, - gint root_y, - gint action, - gint button) -{ - GdkSurfaceImplX11 *impl = GDK_SURFACE_IMPL_X11 (window->impl); - XClientMessageEvent xclient; - - memset (&xclient, 0, sizeof (xclient)); - xclient.type = ClientMessage; - xclient.window = GDK_SURFACE_XID (window); - xclient.message_type = - gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_MOVERESIZE"); - xclient.format = 32; - xclient.data.l[0] = root_x * impl->window_scale; - xclient.data.l[1] = root_y * impl->window_scale; - xclient.data.l[2] = action; - xclient.data.l[3] = button; - xclient.data.l[4] = 1; /* source indication */ - - XSendEvent (GDK_DISPLAY_XDISPLAY (display), GDK_SURFACE_XROOTWIN (window), False, - SubstructureRedirectMask | SubstructureNotifyMask, - (XEvent *)&xclient); -} - -static void -handle_wmspec_button_release (GdkDisplay *display, - const XEvent *xevent) -{ - GdkX11Display *display_x11 = GDK_X11_DISPLAY (display); - GdkSurface *window; - -#if defined (HAVE_XGENERICEVENTS) && defined (XINPUT_2) - XIEvent *xiev = (XIEvent *) xevent->xcookie.data; - XIDeviceEvent *xidev = (XIDeviceEvent *) xiev; - - if (xevent->xany.type == GenericEvent) - window = gdk_x11_surface_lookup_for_display (display, xidev->event); - else -#endif - window = gdk_x11_surface_lookup_for_display (display, xevent->xany.window); - - if (display_x11->wm_moveresize_button != 0 && window != NULL) - { - if ((xevent->xany.type == ButtonRelease && - xevent->xbutton.button == display_x11->wm_moveresize_button) -#if defined (HAVE_XGENERICEVENTS) && defined (XINPUT_2) - || - (xevent->xany.type == GenericEvent && - xiev->evtype == XI_ButtonRelease && - xidev->detail == display_x11->wm_moveresize_button) -#endif - ) - { - display_x11->wm_moveresize_button = 0; - wmspec_send_message (display, window, 0, 0, _NET_WM_MOVERESIZE_CANCEL, 0); - } - } -} - -static void -wmspec_moveresize (GdkSurface *window, - gint direction, - GdkDevice *device, - gint button, - gint root_x, - gint root_y, - guint32 timestamp) -{ - GdkDisplay *display = GDK_SURFACE_DISPLAY (window); - - if (button != 0) - gdk_seat_ungrab (gdk_device_get_seat (device)); /* Release passive grab */ - GDK_X11_DISPLAY (display)->wm_moveresize_button = button; - - wmspec_send_message (display, window, root_x, root_y, direction, button); -} - -static void -wmspec_resize_drag (GdkSurface *window, - GdkSurfaceEdge edge, - GdkDevice *device, - gint button, - gint root_x, - gint root_y, - guint32 timestamp) -{ - gint direction; - - if (button == 0) - direction = _NET_WM_MOVERESIZE_SIZE_KEYBOARD; - else - switch (edge) - { - /* Let the compiler turn a switch into a table, instead - * of doing the table manually, this way is easier to verify. - */ - case GDK_SURFACE_EDGE_NORTH_WEST: - direction = _NET_WM_MOVERESIZE_SIZE_TOPLEFT; - break; - - case GDK_SURFACE_EDGE_NORTH: - direction = _NET_WM_MOVERESIZE_SIZE_TOP; - break; - - case GDK_SURFACE_EDGE_NORTH_EAST: - direction = _NET_WM_MOVERESIZE_SIZE_TOPRIGHT; - break; - - case GDK_SURFACE_EDGE_WEST: - direction = _NET_WM_MOVERESIZE_SIZE_LEFT; - break; - - case GDK_SURFACE_EDGE_EAST: - direction = _NET_WM_MOVERESIZE_SIZE_RIGHT; - break; - - case GDK_SURFACE_EDGE_SOUTH_WEST: - direction = _NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT; - break; - - case GDK_SURFACE_EDGE_SOUTH: - direction = _NET_WM_MOVERESIZE_SIZE_BOTTOM; - break; - - case GDK_SURFACE_EDGE_SOUTH_EAST: - direction = _NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT; - break; - - default: - g_warning ("gdk_surface_begin_resize_drag: bad resize edge %d!", - edge); - return; - } - - wmspec_moveresize (window, direction, device, button, root_x, root_y, timestamp); -} - -typedef struct _MoveResizeData MoveResizeData; - -struct _MoveResizeData -{ - GdkDisplay *display; - - GdkSurface *moveresize_window; - GdkSurface *moveresize_emulation_window; - gboolean is_resize; - GdkSurfaceEdge resize_edge; - GdkDevice *device; - gint moveresize_button; - gint moveresize_x; - gint moveresize_y; - gint moveresize_orig_x; - gint moveresize_orig_y; - gint moveresize_orig_width; - gint moveresize_orig_height; - GdkSurfaceHints moveresize_geom_mask; - GdkGeometry moveresize_geometry; - Time moveresize_process_time; - XEvent *moveresize_pending_event; -}; - -static MoveResizeData * -get_move_resize_data (GdkDisplay *display, - gboolean create) -{ - MoveResizeData *mv_resize; - static GQuark move_resize_quark = 0; - - if (!move_resize_quark) - move_resize_quark = g_quark_from_static_string ("gdk-window-moveresize"); - - mv_resize = g_object_get_qdata (G_OBJECT (display), move_resize_quark); - - if (!mv_resize && create) - { - mv_resize = g_new0 (MoveResizeData, 1); - mv_resize->display = display; - - g_object_set_qdata (G_OBJECT (display), move_resize_quark, mv_resize); - } - - return mv_resize; -} - -static void -check_maximize (MoveResizeData *mv_resize, - gdouble x_root, - gdouble y_root) -{ - GdkSurfaceState state; - gint y; - - if (mv_resize->is_resize) - return; - - state = gdk_surface_get_state (mv_resize->moveresize_window); - - if (state & GDK_SURFACE_STATE_MAXIMIZED) - return; - - y = mv_resize->moveresize_orig_y + (y_root - mv_resize->moveresize_y); - - if (y < 10) - gdk_surface_maximize (mv_resize->moveresize_window); -} - -static void -check_unmaximize (MoveResizeData *mv_resize, - gdouble x_root, - gdouble y_root) -{ - GdkSurfaceState state; - gint dx, dy; - - if (mv_resize->is_resize) - return; - - state = gdk_surface_get_state (mv_resize->moveresize_window); - - if ((state & (GDK_SURFACE_STATE_MAXIMIZED | GDK_SURFACE_STATE_TILED)) == 0) - return; - - dx = x_root - mv_resize->moveresize_x; - dy = y_root - mv_resize->moveresize_y; - - if (ABS (dx) > 20 || ABS (dy) > 20) - gdk_surface_unmaximize (mv_resize->moveresize_window); -} - -static void -update_pos (MoveResizeData *mv_resize, - gint new_root_x, - gint new_root_y) -{ - gint dx, dy; - - check_unmaximize (mv_resize, new_root_x, new_root_y); - dx = new_root_x - mv_resize->moveresize_x; - dy = new_root_y - mv_resize->moveresize_y; - - if (mv_resize->is_resize) - { - gint x, y, w, h; - - x = mv_resize->moveresize_orig_x; - y = mv_resize->moveresize_orig_y; - - w = mv_resize->moveresize_orig_width; - h = mv_resize->moveresize_orig_height; - - switch (mv_resize->resize_edge) - { - case GDK_SURFACE_EDGE_NORTH_WEST: - x += dx; - y += dy; - w -= dx; - h -= dy; - break; - case GDK_SURFACE_EDGE_NORTH: - y += dy; - h -= dy; - break; - case GDK_SURFACE_EDGE_NORTH_EAST: - y += dy; - h -= dy; - w += dx; - break; - case GDK_SURFACE_EDGE_SOUTH_WEST: - h += dy; - x += dx; - w -= dx; - break; - case GDK_SURFACE_EDGE_SOUTH_EAST: - w += dx; - h += dy; - break; - case GDK_SURFACE_EDGE_SOUTH: - h += dy; - break; - case GDK_SURFACE_EDGE_EAST: - w += dx; - break; - case GDK_SURFACE_EDGE_WEST: - x += dx; - w -= dx; - break; - default: - break; - } - - x = MAX (x, 0); - y = MAX (y, 0); - w = MAX (w, 1); - h = MAX (h, 1); - - if (mv_resize->moveresize_geom_mask) - { - gdk_surface_constrain_size (&mv_resize->moveresize_geometry, - mv_resize->moveresize_geom_mask, - w, h, &w, &h); - } - - gdk_surface_move_resize (mv_resize->moveresize_window, x, y, w, h); - } - else - { - gint x, y; - - x = mv_resize->moveresize_orig_x + dx; - y = mv_resize->moveresize_orig_y + dy; - - gdk_surface_move (mv_resize->moveresize_window, x, y); - } -} - -static void -finish_drag (MoveResizeData *mv_resize) -{ - gdk_surface_destroy (mv_resize->moveresize_emulation_window); - mv_resize->moveresize_emulation_window = NULL; - g_clear_object (&mv_resize->moveresize_window); - g_clear_pointer (&mv_resize->moveresize_pending_event, g_free); -} - -static int -lookahead_motion_predicate (Display *xdisplay, - XEvent *event, - XPointer arg) -{ - gboolean *seen_release = (gboolean *)arg; - GdkDisplay *display = gdk_x11_lookup_xdisplay (xdisplay); - MoveResizeData *mv_resize = get_move_resize_data (display, FALSE); - - if (*seen_release) - return False; - - switch (event->xany.type) - { - case ButtonRelease: - *seen_release = TRUE; - break; - case MotionNotify: - mv_resize->moveresize_process_time = event->xmotion.time; - break; - default: - break; - } - - return False; -} - -static gboolean -moveresize_lookahead (MoveResizeData *mv_resize, - const XEvent *event) -{ - XEvent tmp_event; - gboolean seen_release = FALSE; - - if (mv_resize->moveresize_process_time) - { - if (event->xmotion.time == mv_resize->moveresize_process_time) - { - mv_resize->moveresize_process_time = 0; - return TRUE; - } - else - return FALSE; - } - - XCheckIfEvent (event->xany.display, &tmp_event, - lookahead_motion_predicate, (XPointer) & seen_release); - - return mv_resize->moveresize_process_time == 0; -} - -gboolean -_gdk_x11_moveresize_handle_event (const XEvent *event) -{ - guint button_mask = 0; - GdkDisplay *display = gdk_x11_lookup_xdisplay (event->xany.display); - MoveResizeData *mv_resize = get_move_resize_data (display, FALSE); - GdkSurfaceImplX11 *impl; - - if (!mv_resize || !mv_resize->moveresize_window) - { - handle_wmspec_button_release (display, event); - return FALSE; - } - - impl = GDK_SURFACE_IMPL_X11 (mv_resize->moveresize_window->impl); - - if (mv_resize->moveresize_button != 0) - button_mask = GDK_BUTTON1_MASK << (mv_resize->moveresize_button - 1); - - switch (event->xany.type) - { - case MotionNotify: - if (mv_resize->moveresize_window->resize_count > 0) - { - if (mv_resize->moveresize_pending_event) - *mv_resize->moveresize_pending_event = *event; - else - mv_resize->moveresize_pending_event = - g_memdup (event, sizeof (XEvent)); - - break; - } - if (!moveresize_lookahead (mv_resize, event)) - break; - - update_pos (mv_resize, - event->xmotion.x_root / impl->window_scale, - event->xmotion.y_root / impl->window_scale); - - /* This should never be triggered in normal cases, but in the - * case where the drag started without an implicit grab being - * in effect, we could miss the release if it occurs before - * we grab the pointer; this ensures that we will never - * get a permanently stuck grab. - */ - if ((event->xmotion.state & button_mask) == 0) - { - check_maximize (mv_resize, - event->xmotion.x_root / impl->window_scale, - event->xmotion.y_root / impl->window_scale); - finish_drag (mv_resize); - } - break; - - case ButtonRelease: - update_pos (mv_resize, - event->xbutton.x_root / impl->window_scale, - event->xbutton.y_root / impl->window_scale); - - if (event->xbutton.button == mv_resize->moveresize_button) - { - check_maximize (mv_resize, - event->xmotion.x_root / impl->window_scale, - event->xmotion.y_root / impl->window_scale); - finish_drag (mv_resize); - } - break; - -#if defined (HAVE_XGENERICEVENTS) && defined (XINPUT_2) - case GenericEvent: - { - /* we just assume this is an XI2 event */ - XIEvent *ev = (XIEvent *) event->xcookie.data; - XIDeviceEvent *xev = (XIDeviceEvent *)ev; - gint state; - switch (ev->evtype) - { - case XI_Motion: - update_pos (mv_resize, xev->root_x / impl->window_scale, xev->root_y / impl->window_scale); - state = _gdk_x11_device_xi2_translate_state (&xev->mods, &xev->buttons, &xev->group); - if ((state & button_mask) == 0) - { - check_maximize (mv_resize, - xev->root_x / impl->window_scale, - xev->root_y / impl->window_scale); - finish_drag (mv_resize); - } - break; - - case XI_ButtonRelease: - update_pos (mv_resize, xev->root_x / impl->window_scale, xev->root_y / impl->window_scale); - if (xev->detail == mv_resize->moveresize_button) - { - check_maximize (mv_resize, - xev->root_x / impl->window_scale, - xev->root_y / impl->window_scale); - finish_drag (mv_resize); - } - break; - default: - break; - } - } - break; -#endif - - default: - break; - - } - return TRUE; -} - -gboolean -_gdk_x11_moveresize_configure_done (GdkDisplay *display, - GdkSurface *window) -{ - XEvent *tmp_event; - MoveResizeData *mv_resize = get_move_resize_data (display, FALSE); - - if (!mv_resize || window != mv_resize->moveresize_window) - return FALSE; - - if (mv_resize->moveresize_pending_event) - { - tmp_event = mv_resize->moveresize_pending_event; - mv_resize->moveresize_pending_event = NULL; - _gdk_x11_moveresize_handle_event (tmp_event); - g_free (tmp_event); - } - - return TRUE; -} - -static void -create_moveresize_window (MoveResizeData *mv_resize, - guint32 timestamp) -{ - GdkGrabStatus status; - - g_assert (mv_resize->moveresize_emulation_window == NULL); - - mv_resize->moveresize_emulation_window = gdk_surface_new_temp (mv_resize->display); - gdk_surface_show (mv_resize->moveresize_emulation_window); - - status = gdk_seat_grab (gdk_device_get_seat (mv_resize->device), - mv_resize->moveresize_emulation_window, - GDK_SEAT_CAPABILITY_POINTER, FALSE, - NULL, NULL, NULL, NULL); - - if (status != GDK_GRAB_SUCCESS) - { - /* If this fails, some other client has grabbed the window - * already. - */ - finish_drag (mv_resize); - } - - mv_resize->moveresize_process_time = 0; -} - -/* - Calculate mv_resize->moveresize_orig_x and mv_resize->moveresize_orig_y - so that calling XMoveWindow with these coordinates will not move the - window. - Note that this depends on the WM to implement ICCCM-compliant reference - point handling. -*/ -static void -calculate_unmoving_origin (MoveResizeData *mv_resize) -{ - GdkRectangle rect; - gint width, height; - - if (mv_resize->moveresize_geom_mask & GDK_HINT_WIN_GRAVITY && - mv_resize->moveresize_geometry.win_gravity == GDK_GRAVITY_STATIC) - { - gdk_surface_get_origin (mv_resize->moveresize_window, - &mv_resize->moveresize_orig_x, - &mv_resize->moveresize_orig_y); - } - else - { - gdk_surface_get_frame_extents (mv_resize->moveresize_window, &rect); - gdk_surface_get_geometry (mv_resize->moveresize_window, - NULL, NULL, &width, &height); - - switch (mv_resize->moveresize_geometry.win_gravity) - { - case GDK_GRAVITY_NORTH_WEST: - mv_resize->moveresize_orig_x = rect.x; - mv_resize->moveresize_orig_y = rect.y; - break; - case GDK_GRAVITY_NORTH: - mv_resize->moveresize_orig_x = rect.x + rect.width / 2 - width / 2; - mv_resize->moveresize_orig_y = rect.y; - break; - case GDK_GRAVITY_NORTH_EAST: - mv_resize->moveresize_orig_x = rect.x + rect.width - width; - mv_resize->moveresize_orig_y = rect.y; - break; - case GDK_GRAVITY_WEST: - mv_resize->moveresize_orig_x = rect.x; - mv_resize->moveresize_orig_y = rect.y + rect.height / 2 - height / 2; - break; - case GDK_GRAVITY_CENTER: - mv_resize->moveresize_orig_x = rect.x + rect.width / 2 - width / 2; - mv_resize->moveresize_orig_y = rect.y + rect.height / 2 - height / 2; - break; - case GDK_GRAVITY_EAST: - mv_resize->moveresize_orig_x = rect.x + rect.width - width; - mv_resize->moveresize_orig_y = rect.y + rect.height / 2 - height / 2; - break; - case GDK_GRAVITY_SOUTH_WEST: - mv_resize->moveresize_orig_x = rect.x; - mv_resize->moveresize_orig_y = rect.y + rect.height - height; - break; - case GDK_GRAVITY_SOUTH: - mv_resize->moveresize_orig_x = rect.x + rect.width / 2 - width / 2; - mv_resize->moveresize_orig_y = rect.y + rect.height - height; - break; - case GDK_GRAVITY_SOUTH_EAST: - mv_resize->moveresize_orig_x = rect.x + rect.width - width; - mv_resize->moveresize_orig_y = rect.y + rect.height - height; - break; - case GDK_GRAVITY_STATIC: - default: - mv_resize->moveresize_orig_x = rect.x; - mv_resize->moveresize_orig_y = rect.y; - break; - } - } -} - -static void -emulate_resize_drag (GdkSurface *window, - GdkSurfaceEdge edge, - GdkDevice *device, - gint button, - gint root_x, - gint root_y, - guint32 timestamp) -{ - MoveResizeData *mv_resize = get_move_resize_data (GDK_SURFACE_DISPLAY (window), TRUE); - - if (mv_resize->moveresize_window != NULL) - return; /* already a drag operation in progress */ - - mv_resize->is_resize = TRUE; - mv_resize->moveresize_button = button; - mv_resize->resize_edge = edge; - mv_resize->device = device; - mv_resize->moveresize_x = root_x; - mv_resize->moveresize_y = root_y; - mv_resize->moveresize_window = g_object_ref (window); - - mv_resize->moveresize_orig_width = gdk_surface_get_width (window); - mv_resize->moveresize_orig_height = gdk_surface_get_height (window); - - mv_resize->moveresize_geom_mask = 0; - gdk_surface_get_geometry_hints (window, - &mv_resize->moveresize_geometry, - &mv_resize->moveresize_geom_mask); - - calculate_unmoving_origin (mv_resize); - - create_moveresize_window (mv_resize, timestamp); -} - -static void -emulate_move_drag (GdkSurface *window, - GdkDevice *device, - gint button, - gint root_x, - gint root_y, - guint32 timestamp) -{ - MoveResizeData *mv_resize = get_move_resize_data (GDK_SURFACE_DISPLAY (window), TRUE); - - if (mv_resize->moveresize_window != NULL) - return; /* already a drag operation in progress */ - - mv_resize->is_resize = FALSE; - mv_resize->device = device; - mv_resize->moveresize_button = button; - mv_resize->moveresize_x = root_x; - mv_resize->moveresize_y = root_y; - - mv_resize->moveresize_window = g_object_ref (window); - - calculate_unmoving_origin (mv_resize); - - create_moveresize_window (mv_resize, timestamp); -} - -static gboolean -_should_perform_ewmh_drag (GdkSurface *window, - GdkDevice *device) -{ - GdkPointerSurfaceInfo *info; - GdkDisplay *display; - - display = gdk_surface_get_display (window); - info = _gdk_display_get_pointer_info (display, device); - - if ((!info->last_slave || gdk_device_get_source (info->last_slave) != GDK_SOURCE_TOUCHSCREEN) && - gdk_x11_screen_supports_net_wm_hint (GDK_SURFACE_SCREEN (window), - g_intern_static_string ("_NET_WM_MOVERESIZE"))) - return TRUE; - - return FALSE; -} - -static void -gdk_x11_surface_begin_resize_drag (GdkSurface *window, - GdkSurfaceEdge edge, - GdkDevice *device, - gint button, - gint root_x, - gint root_y, - guint32 timestamp) -{ - if (GDK_SURFACE_DESTROYED (window) || - !WINDOW_IS_TOPLEVEL_OR_FOREIGN (window)) - return; - - /* Avoid EWMH for touch devices */ - if (_should_perform_ewmh_drag (window, device)) - wmspec_resize_drag (window, edge, device, button, root_x, root_y, timestamp); - else - emulate_resize_drag (window, edge, device, button, root_x, root_y, timestamp); -} - -static void -gdk_x11_surface_begin_move_drag (GdkSurface *window, - GdkDevice *device, - gint button, - gint root_x, - gint root_y, - guint32 timestamp) -{ - gint direction; - - if (GDK_SURFACE_DESTROYED (window) || !WINDOW_IS_TOPLEVEL (window)) - return; - - if (button == 0) - direction = _NET_WM_MOVERESIZE_MOVE_KEYBOARD; - else - direction = _NET_WM_MOVERESIZE_MOVE; - - /* Avoid EWMH for touch devices */ - if (_should_perform_ewmh_drag (window, device)) - wmspec_moveresize (window, direction, device, button, root_x, root_y, timestamp); - else - emulate_move_drag (window, device, button, root_x, root_y, timestamp); -} - -static gboolean -gdk_x11_surface_beep (GdkSurface *window) -{ - GdkDisplay *display; - - display = GDK_SURFACE_DISPLAY (window); - -#ifdef HAVE_XKB - if (GDK_X11_DISPLAY (display)->use_xkb) - { - XkbBell (GDK_DISPLAY_XDISPLAY (display), - GDK_SURFACE_XID (window), - 0, - None); - return TRUE; - } -#endif - - return FALSE; -} - -static void -gdk_x11_surface_set_opacity (GdkSurface *window, - gdouble opacity) -{ - GdkDisplay *display; - gulong cardinal; - - g_return_if_fail (GDK_IS_SURFACE (window)); - - if (GDK_SURFACE_DESTROYED (window) || - !WINDOW_IS_TOPLEVEL (window)) - return; - - display = gdk_surface_get_display (window); - - if (opacity < 0) - opacity = 0; - else if (opacity > 1) - opacity = 1; - - cardinal = opacity * 0xffffffff; - - if (cardinal == 0xffffffff) - XDeleteProperty (GDK_DISPLAY_XDISPLAY (display), - GDK_SURFACE_XID (window), - gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_WINDOW_OPACITY")); - else - XChangeProperty (GDK_DISPLAY_XDISPLAY (display), - GDK_SURFACE_XID (window), - gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_WINDOW_OPACITY"), - XA_CARDINAL, 32, - PropModeReplace, - (guchar *) &cardinal, 1); -} - -static Bool -timestamp_predicate (Display *display, - XEvent *xevent, - XPointer arg) -{ - Window xwindow = GPOINTER_TO_UINT (arg); - GdkDisplay *gdk_display = gdk_x11_lookup_xdisplay (display); - - if (xevent->type == PropertyNotify && - xevent->xproperty.window == xwindow && - xevent->xproperty.atom == gdk_x11_get_xatom_by_name_for_display (gdk_display, - "GDK_TIMESTAMP_PROP")) - return True; - - return False; -} - -/** - * gdk_x11_get_server_time: - * @window: (type GdkX11Surface): a #GdkSurface, used for communication - * with the server. The window must have - * GDK_PROPERTY_CHANGE_MASK in its events mask or a hang will - * result. - * - * Routine to get the current X server time stamp. - * - * Returns: the time stamp. - **/ -guint32 -gdk_x11_get_server_time (GdkSurface *window) -{ - Display *xdisplay; - Window xwindow; - guchar c = 'a'; - XEvent xevent; - Atom timestamp_prop_atom; - - g_return_val_if_fail (GDK_IS_SURFACE (window), 0); - g_return_val_if_fail (!GDK_SURFACE_DESTROYED (window), 0); - - xdisplay = GDK_SURFACE_XDISPLAY (window); - xwindow = GDK_SURFACE_XID (window); - timestamp_prop_atom = - gdk_x11_get_xatom_by_name_for_display (GDK_SURFACE_DISPLAY (window), - "GDK_TIMESTAMP_PROP"); - - XChangeProperty (xdisplay, xwindow, timestamp_prop_atom, - timestamp_prop_atom, - 8, PropModeReplace, &c, 1); - - XIfEvent (xdisplay, &xevent, - timestamp_predicate, GUINT_TO_POINTER(xwindow)); - - return xevent.xproperty.time; -} - -/** - * gdk_x11_surface_get_xid: - * @window: (type GdkX11Surface): a native #GdkSurface. - * - * Returns the X resource (window) belonging to a #GdkSurface. - * - * Returns: the ID of @drawable’s X resource. - **/ -XID -gdk_x11_surface_get_xid (GdkSurface *window) -{ - if (!GDK_SURFACE_IS_X11 (window) || - !_gdk_surface_has_impl (window)) - { - g_warning (G_STRLOC " drawable is not a native X11 window"); - return None; - } - - return GDK_SURFACE_IMPL_X11 (window->impl)->xid; -} - -static gint -gdk_x11_surface_get_scale_factor (GdkSurface *window) -{ - GdkSurfaceImplX11 *impl = GDK_SURFACE_IMPL_X11 (window->impl); - - if (GDK_SURFACE_DESTROYED (window)) - return 1; - - return impl->window_scale; -} - -/** - * gdk_x11_surface_set_frame_sync_enabled: - * @window: (type GdkX11Surface): a native #GdkSurface - * @frame_sync_enabled: whether frame-synchronization should be enabled - * - * This function can be used to disable frame synchronization for a window. - * Normally frame synchronziation will be enabled or disabled based on whether - * the system has a compositor that supports frame synchronization, but if - * the window is not directly managed by the window manager, then frame - * synchronziation may need to be disabled. This is the case for a window - * embedded via the XEMBED protocol. - */ -void -gdk_x11_surface_set_frame_sync_enabled (GdkSurface *window, - gboolean frame_sync_enabled) -{ - if (!GDK_SURFACE_IS_X11 (window) || - !_gdk_surface_has_impl (window)) - { - g_warning (G_STRLOC " drawable is not a native X11 window"); - return; - } - - GDK_SURFACE_IMPL_X11 (window->impl)->frame_sync_enabled = FALSE; -} - -static void -gdk_x11_surface_set_opaque_region (GdkSurface *window, - cairo_region_t *region) -{ - GdkSurfaceImplX11 *impl = GDK_SURFACE_IMPL_X11 (window->impl); - GdkDisplay *display; - int nitems; - gulong *data; - - if (GDK_SURFACE_DESTROYED (window)) - return; - - if (region != NULL) - { - int i, nrects; - - nrects = cairo_region_num_rectangles (region); - nitems = nrects * 4; - data = g_new (gulong, nitems); - - for (i = 0; i < nrects; i++) - { - cairo_rectangle_int_t rect; - cairo_region_get_rectangle (region, i, &rect); - data[i*4+0] = rect.x * impl->window_scale; - data[i*4+1] = rect.y * impl->window_scale; - data[i*4+2] = rect.width * impl->window_scale; - data[i*4+3] = rect.height * impl->window_scale; - } - } - else - { - nitems = 0; - data = NULL; - } - - display = gdk_surface_get_display (window); - - XChangeProperty (GDK_DISPLAY_XDISPLAY (display), - GDK_SURFACE_XID (window), - gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_OPAQUE_REGION"), - XA_CARDINAL, 32, PropModeReplace, - (guchar *) data, nitems); - - g_free (data); -} - -static gboolean -gdk_x11_surface_show_window_menu (GdkSurface *window, - GdkEvent *event) -{ - GdkSurfaceImplX11 *impl = GDK_SURFACE_IMPL_X11 (window->impl); - GdkDisplay *display = GDK_SURFACE_DISPLAY (window); - GdkDevice *device; - int device_id; - double x_root, y_root; - XClientMessageEvent xclient = { 0 }; - - switch ((guint) event->any.type) - { - case GDK_BUTTON_PRESS: - case GDK_BUTTON_RELEASE: - break; - default: - return FALSE; - } - - if (!gdk_x11_screen_supports_net_wm_hint (GDK_SURFACE_SCREEN (window), - g_intern_static_string ("_GTK_SHOW_WINDOW_MENU"))) - return FALSE; - - gdk_event_get_root_coords (event, &x_root, &y_root); - device = gdk_event_get_device (event); - g_object_get (G_OBJECT (device), - "device-id", &device_id, - NULL); - - /* Ungrab the implicit grab */ - gdk_seat_ungrab (gdk_device_get_seat (device)); - - xclient.type = ClientMessage; - xclient.window = GDK_SURFACE_XID (window); - xclient.message_type = gdk_x11_get_xatom_by_name_for_display (display, "_GTK_SHOW_WINDOW_MENU"); - xclient.data.l[0] = device_id; - xclient.data.l[1] = x_root * impl->window_scale; - xclient.data.l[2] = y_root * impl->window_scale; - xclient.format = 32; - - XSendEvent (GDK_DISPLAY_XDISPLAY (display), GDK_SURFACE_XROOTWIN (window), False, - SubstructureRedirectMask | SubstructureNotifyMask, - (XEvent *)&xclient); - - return TRUE; -} - -static void -gdk_surface_impl_x11_class_init (GdkSurfaceImplX11Class *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - GdkSurfaceImplClass *impl_class = GDK_SURFACE_IMPL_CLASS (klass); - - object_class->finalize = gdk_surface_impl_x11_finalize; - - impl_class->ref_cairo_surface = gdk_x11_ref_cairo_surface; - impl_class->show = gdk_surface_x11_show; - impl_class->hide = gdk_surface_x11_hide; - impl_class->withdraw = gdk_surface_x11_withdraw; - impl_class->set_events = gdk_surface_x11_set_events; - impl_class->get_events = gdk_surface_x11_get_events; - impl_class->raise = gdk_surface_x11_raise; - impl_class->lower = gdk_surface_x11_lower; - impl_class->restack_toplevel = gdk_surface_x11_restack_toplevel; - impl_class->move_resize = gdk_surface_x11_move_resize; - impl_class->get_geometry = gdk_surface_x11_get_geometry; - impl_class->get_root_coords = gdk_surface_x11_get_root_coords; - impl_class->get_device_state = gdk_surface_x11_get_device_state; - impl_class->shape_combine_region = gdk_surface_x11_shape_combine_region; - impl_class->input_shape_combine_region = gdk_surface_x11_input_shape_combine_region; - impl_class->queue_antiexpose = _gdk_x11_surface_queue_antiexpose; - impl_class->destroy = gdk_x11_surface_destroy; - impl_class->beep = gdk_x11_surface_beep; - - impl_class->focus = gdk_x11_surface_focus; - impl_class->set_type_hint = gdk_x11_surface_set_type_hint; - impl_class->get_type_hint = gdk_x11_surface_get_type_hint; - impl_class->set_modal_hint = gdk_x11_surface_set_modal_hint; - impl_class->set_skip_taskbar_hint = gdk_x11_surface_set_skip_taskbar_hint; - impl_class->set_skip_pager_hint = gdk_x11_surface_set_skip_pager_hint; - impl_class->set_urgency_hint = gdk_x11_surface_set_urgency_hint; - impl_class->set_geometry_hints = gdk_x11_surface_set_geometry_hints; - impl_class->set_title = gdk_x11_surface_set_title; - impl_class->set_role = gdk_x11_surface_set_role; - impl_class->set_startup_id = gdk_x11_surface_set_startup_id; - impl_class->set_transient_for = gdk_x11_surface_set_transient_for; - impl_class->get_frame_extents = gdk_x11_surface_get_frame_extents; - impl_class->set_accept_focus = gdk_x11_surface_set_accept_focus; - impl_class->set_focus_on_map = gdk_x11_surface_set_focus_on_map; - impl_class->set_icon_list = gdk_x11_surface_set_icon_list; - impl_class->set_icon_name = gdk_x11_surface_set_icon_name; - impl_class->iconify = gdk_x11_surface_iconify; - impl_class->deiconify = gdk_x11_surface_deiconify; - impl_class->stick = gdk_x11_surface_stick; - impl_class->unstick = gdk_x11_surface_unstick; - impl_class->maximize = gdk_x11_surface_maximize; - impl_class->unmaximize = gdk_x11_surface_unmaximize; - impl_class->fullscreen = gdk_x11_surface_fullscreen; - impl_class->fullscreen_on_monitor = gdk_x11_surface_fullscreen_on_monitor; - impl_class->apply_fullscreen_mode = gdk_x11_surface_apply_fullscreen_mode; - impl_class->unfullscreen = gdk_x11_surface_unfullscreen; - impl_class->set_keep_above = gdk_x11_surface_set_keep_above; - impl_class->set_keep_below = gdk_x11_surface_set_keep_below; - impl_class->get_group = gdk_x11_surface_get_group; - impl_class->set_group = gdk_x11_surface_set_group; - impl_class->set_decorations = gdk_x11_surface_set_decorations; - impl_class->get_decorations = gdk_x11_surface_get_decorations; - impl_class->set_functions = gdk_x11_surface_set_functions; - impl_class->begin_resize_drag = gdk_x11_surface_begin_resize_drag; - impl_class->begin_move_drag = gdk_x11_surface_begin_move_drag; - impl_class->set_opacity = gdk_x11_surface_set_opacity; - impl_class->destroy_notify = gdk_x11_surface_destroy_notify; - impl_class->register_dnd = _gdk_x11_surface_register_dnd; - impl_class->drag_begin = _gdk_x11_surface_drag_begin; - impl_class->get_scale_factor = gdk_x11_surface_get_scale_factor; - impl_class->set_opaque_region = gdk_x11_surface_set_opaque_region; - impl_class->set_shadow_width = gdk_x11_surface_set_shadow_width; - impl_class->show_window_menu = gdk_x11_surface_show_window_menu; - impl_class->create_gl_context = gdk_x11_surface_create_gl_context; - impl_class->get_unscaled_size = gdk_x11_surface_get_unscaled_size; - impl_class->supports_edge_constraints = gdk_x11_surface_supports_edge_constraints; -} diff --git a/gdk/x11/gdkwindow-x11.h b/gdk/x11/gdkwindow-x11.h deleted file mode 100644 index b824240ffc..0000000000 --- a/gdk/x11/gdkwindow-x11.h +++ /dev/null @@ -1,199 +0,0 @@ -/* GDK - The GIMP Drawing Kit - * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * 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, see . - */ - -/* - * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS - * file for a list of people on the GTK+ Team. See the ChangeLog - * files for a list of changes. These files are distributed with - * GTK+ at ftp://ftp.gtk.org/pub/gtk/. - */ - -#ifndef __GDK_SURFACE_X11_H__ -#define __GDK_SURFACE_X11_H__ - -#include "gdk/x11/gdkprivate-x11.h" -#include "gdk/gdkwindowimpl.h" - -#include - -#ifdef HAVE_XDAMAGE -#include -#endif - -#ifdef HAVE_XSYNC -#include -#include -#endif - -G_BEGIN_DECLS - -typedef struct _GdkToplevelX11 GdkToplevelX11; -typedef struct _GdkSurfaceImplX11 GdkSurfaceImplX11; -typedef struct _GdkSurfaceImplX11Class GdkSurfaceImplX11Class; -typedef struct _GdkXPositionInfo GdkXPositionInfo; - -/* Window implementation for X11 - */ - -#define GDK_TYPE_SURFACE_IMPL_X11 (gdk_surface_impl_x11_get_type ()) -#define GDK_SURFACE_IMPL_X11(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), GDK_TYPE_SURFACE_IMPL_X11, GdkSurfaceImplX11)) -#define GDK_SURFACE_IMPL_X11_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GDK_TYPE_SURFACE_IMPL_X11, GdkSurfaceImplX11Class)) -#define GDK_IS_SURFACE_IMPL_X11(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), GDK_TYPE_SURFACE_IMPL_X11)) -#define GDK_IS_SURFACE_IMPL_X11_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GDK_TYPE_SURFACE_IMPL_X11)) -#define GDK_SURFACE_IMPL_X11_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GDK_TYPE_SURFACE_IMPL_X11, GdkSurfaceImplX11Class)) - -struct _GdkSurfaceImplX11 -{ - GdkSurfaceImpl parent_instance; - - GdkSurface *wrapper; - - Window xid; - - GdkToplevelX11 *toplevel; /* Toplevel-specific information */ - GdkCursor *cursor; - - guint no_bg : 1; /* Set when the window background is temporarily - * unset during resizing and scaling */ - guint override_redirect : 1; - guint frame_clock_connected : 1; - guint frame_sync_enabled : 1; - guint tracking_damage: 1; - - gint window_scale; - - /* Width and height not divided by window_scale - this matters in the - * corner-case where the window manager assigns us a size that isn't - * a multiple of window_scale - for example for a maximized window - * with an odd-sized title-bar. - */ - gint unscaled_width; - gint unscaled_height; - - cairo_surface_t *cairo_surface; - -#if defined (HAVE_XCOMPOSITE) && defined(HAVE_XDAMAGE) && defined (HAVE_XFIXES) - Damage damage; -#endif -}; - -struct _GdkSurfaceImplX11Class -{ - GdkSurfaceImplClass parent_class; -}; - -struct _GdkToplevelX11 -{ - - /* Set if the window, or any descendent of it, is the server's focus window - */ - guint has_focus_window : 1; - - /* Set if window->has_focus_window and the focus isn't grabbed elsewhere. - */ - guint has_focus : 1; - - /* Set if the pointer is inside this window. (This is needed for - * for focus tracking) - */ - guint has_pointer : 1; - - /* Set if the window is a descendent of the focus window and the pointer is - * inside it. (This is the case where the window will receive keystroke - * events even window->has_focus_window is FALSE) - */ - guint has_pointer_focus : 1; - - /* Set if we are requesting these hints */ - guint skip_taskbar_hint : 1; - guint skip_pager_hint : 1; - guint urgency_hint : 1; - - guint on_all_desktops : 1; /* _NET_WM_STICKY == 0xFFFFFFFF */ - - guint have_sticky : 1; /* _NET_WM_STATE_STICKY */ - guint have_maxvert : 1; /* _NET_WM_STATE_MAXIMIZED_VERT */ - guint have_maxhorz : 1; /* _NET_WM_STATE_MAXIMIZED_HORZ */ - guint have_fullscreen : 1; /* _NET_WM_STATE_FULLSCREEN */ - guint have_hidden : 1; /* _NET_WM_STATE_HIDDEN */ - - guint is_leader : 1; - - /* Set if the WM is presenting us as focused, i.e. with active decorations - */ - guint have_focused : 1; - - guint in_frame : 1; - - /* If we're expecting a response from the compositor after painting a frame */ - guint frame_pending : 1; - - /* Whether pending_counter_value/configure_counter_value are updates - * to the extended update counter */ - guint pending_counter_value_is_extended : 1; - guint configure_counter_value_is_extended : 1; - - gulong map_serial; /* Serial of last transition from unmapped */ - - cairo_surface_t *icon_pixmap; - cairo_surface_t *icon_mask; - GdkSurface *group_leader; - - /* Time of most recent user interaction. */ - gulong user_time; - - /* We use an extra X window for toplevel windows that we XSetInputFocus() - * to in order to avoid getting keyboard events redirected to subwindows - * that might not even be part of this app - */ - Window focus_window; - - GdkSurfaceHints last_geometry_hints_mask; - GdkGeometry last_geometry_hints; - - /* Constrained edge information */ - guint edge_constraints; - -#ifdef HAVE_XSYNC - XID update_counter; - XID extended_update_counter; - gint64 pending_counter_value; /* latest _NET_WM_SYNC_REQUEST value received */ - gint64 configure_counter_value; /* Latest _NET_WM_SYNC_REQUEST value received - * where we have also seen the corresponding - * ConfigureNotify - */ - gint64 current_counter_value; - - /* After a _NET_WM_FRAME_DRAWN message, this is the soonest that we think - * frame after will be presented */ - gint64 throttled_presentation_time; -#endif -}; - -GType gdk_surface_impl_x11_get_type (void); - -GdkToplevelX11 *_gdk_x11_surface_get_toplevel (GdkSurface *window); - -GdkCursor *_gdk_x11_surface_get_cursor (GdkSurface *window); - -void _gdk_x11_surface_update_size (GdkSurfaceImplX11 *impl); -void _gdk_x11_surface_set_window_scale (GdkSurface *window, - int scale); - -G_END_DECLS - -#endif /* __GDK_SURFACE_X11_H__ */ diff --git a/gdk/x11/gdkx.h b/gdk/x11/gdkx.h index 153b4a501e..9fa9cb0711 100644 --- a/gdk/x11/gdkx.h +++ b/gdk/x11/gdkx.h @@ -48,7 +48,7 @@ #include #include #include -#include +#include #include diff --git a/gdk/x11/gdkx11surface.h b/gdk/x11/gdkx11surface.h new file mode 100644 index 0000000000..d2fc1e4a60 --- /dev/null +++ b/gdk/x11/gdkx11surface.h @@ -0,0 +1,113 @@ +/* GDK - The GIMP Drawing Kit + * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * 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, see . + */ + +/* + * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS + * file for a list of people on the GTK+ Team. See the ChangeLog + * files for a list of changes. These files are distributed with + * GTK+ at ftp://ftp.gtk.org/pub/gtk/. + */ + +#ifndef __GDK_X11_SURFACE_H__ +#define __GDK_X11_SURFACE_H__ + +#if !defined (__GDKX_H_INSIDE__) && !defined (GDK_COMPILATION) +#error "Only can be included directly." +#endif + +#include + +#include +#include + +G_BEGIN_DECLS + +#define GDK_TYPE_X11_SURFACE (gdk_x11_surface_get_type ()) +#define GDK_X11_SURFACE(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), GDK_TYPE_X11_SURFACE, GdkX11Surface)) +#define GDK_X11_SURFACE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GDK_TYPE_X11_SURFACE, GdkX11SurfaceClass)) +#define GDK_IS_X11_SURFACE(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), GDK_TYPE_X11_SURFACE)) +#define GDK_IS_X11_SURFACE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GDK_TYPE_X11_SURFACE)) +#define GDK_X11_SURFACE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GDK_TYPE_X11_SURFACE, GdkX11SurfaceClass)) + +#ifdef GDK_COMPILATION +typedef struct _GdkX11Surface GdkX11Surface; +#else +typedef GdkSurface GdkX11Surface; +#endif +typedef struct _GdkX11SurfaceClass GdkX11SurfaceClass; + +GDK_AVAILABLE_IN_ALL +GType gdk_x11_surface_get_type (void); + +GDK_AVAILABLE_IN_ALL +Window gdk_x11_surface_get_xid (GdkSurface *window); +GDK_AVAILABLE_IN_ALL +void gdk_x11_surface_set_user_time (GdkSurface *window, + guint32 timestamp); +GDK_AVAILABLE_IN_ALL +void gdk_x11_surface_set_utf8_property (GdkSurface *window, + const gchar *name, + const gchar *value); +GDK_AVAILABLE_IN_ALL +void gdk_x11_surface_set_theme_variant (GdkSurface *window, + const char *variant); +GDK_AVAILABLE_IN_ALL +void gdk_x11_surface_move_to_current_desktop (GdkSurface *window); + +GDK_AVAILABLE_IN_ALL +guint32 gdk_x11_surface_get_desktop (GdkSurface *window); +GDK_AVAILABLE_IN_ALL +void gdk_x11_surface_move_to_desktop (GdkSurface *window, + guint32 desktop); + +GDK_AVAILABLE_IN_ALL +void gdk_x11_surface_set_frame_sync_enabled (GdkSurface *window, + gboolean frame_sync_enabled); + +/** + * GDK_SURFACE_XDISPLAY: + * @win: a #GdkSurface. + * + * Returns the display of a #GdkSurface. + * + * Returns: an Xlib Display*. + */ +#define GDK_SURFACE_XDISPLAY(win) (GDK_DISPLAY_XDISPLAY (gdk_surface_get_display (win))) + +/** + * GDK_SURFACE_XID: + * @win: a #GdkSurface. + * + * Returns the X window belonging to a #GdkSurface. + * + * Returns: the Xlib Window of @win. + */ +#define GDK_SURFACE_XID(win) (gdk_x11_surface_get_xid (win)) + +GDK_AVAILABLE_IN_ALL +guint32 gdk_x11_get_server_time (GdkSurface *window); + +GDK_AVAILABLE_IN_ALL +GdkSurface *gdk_x11_surface_foreign_new_for_display (GdkDisplay *display, + Window window); +GDK_AVAILABLE_IN_ALL +GdkSurface *gdk_x11_surface_lookup_for_display (GdkDisplay *display, + Window window); + +G_END_DECLS + +#endif /* __GDK_X11_SURFACE_H__ */ diff --git a/gdk/x11/gdkx11window.h b/gdk/x11/gdkx11window.h deleted file mode 100644 index d2fc1e4a60..0000000000 --- a/gdk/x11/gdkx11window.h +++ /dev/null @@ -1,113 +0,0 @@ -/* GDK - The GIMP Drawing Kit - * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * 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, see . - */ - -/* - * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS - * file for a list of people on the GTK+ Team. See the ChangeLog - * files for a list of changes. These files are distributed with - * GTK+ at ftp://ftp.gtk.org/pub/gtk/. - */ - -#ifndef __GDK_X11_SURFACE_H__ -#define __GDK_X11_SURFACE_H__ - -#if !defined (__GDKX_H_INSIDE__) && !defined (GDK_COMPILATION) -#error "Only can be included directly." -#endif - -#include - -#include -#include - -G_BEGIN_DECLS - -#define GDK_TYPE_X11_SURFACE (gdk_x11_surface_get_type ()) -#define GDK_X11_SURFACE(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), GDK_TYPE_X11_SURFACE, GdkX11Surface)) -#define GDK_X11_SURFACE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GDK_TYPE_X11_SURFACE, GdkX11SurfaceClass)) -#define GDK_IS_X11_SURFACE(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), GDK_TYPE_X11_SURFACE)) -#define GDK_IS_X11_SURFACE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GDK_TYPE_X11_SURFACE)) -#define GDK_X11_SURFACE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GDK_TYPE_X11_SURFACE, GdkX11SurfaceClass)) - -#ifdef GDK_COMPILATION -typedef struct _GdkX11Surface GdkX11Surface; -#else -typedef GdkSurface GdkX11Surface; -#endif -typedef struct _GdkX11SurfaceClass GdkX11SurfaceClass; - -GDK_AVAILABLE_IN_ALL -GType gdk_x11_surface_get_type (void); - -GDK_AVAILABLE_IN_ALL -Window gdk_x11_surface_get_xid (GdkSurface *window); -GDK_AVAILABLE_IN_ALL -void gdk_x11_surface_set_user_time (GdkSurface *window, - guint32 timestamp); -GDK_AVAILABLE_IN_ALL -void gdk_x11_surface_set_utf8_property (GdkSurface *window, - const gchar *name, - const gchar *value); -GDK_AVAILABLE_IN_ALL -void gdk_x11_surface_set_theme_variant (GdkSurface *window, - const char *variant); -GDK_AVAILABLE_IN_ALL -void gdk_x11_surface_move_to_current_desktop (GdkSurface *window); - -GDK_AVAILABLE_IN_ALL -guint32 gdk_x11_surface_get_desktop (GdkSurface *window); -GDK_AVAILABLE_IN_ALL -void gdk_x11_surface_move_to_desktop (GdkSurface *window, - guint32 desktop); - -GDK_AVAILABLE_IN_ALL -void gdk_x11_surface_set_frame_sync_enabled (GdkSurface *window, - gboolean frame_sync_enabled); - -/** - * GDK_SURFACE_XDISPLAY: - * @win: a #GdkSurface. - * - * Returns the display of a #GdkSurface. - * - * Returns: an Xlib Display*. - */ -#define GDK_SURFACE_XDISPLAY(win) (GDK_DISPLAY_XDISPLAY (gdk_surface_get_display (win))) - -/** - * GDK_SURFACE_XID: - * @win: a #GdkSurface. - * - * Returns the X window belonging to a #GdkSurface. - * - * Returns: the Xlib Window of @win. - */ -#define GDK_SURFACE_XID(win) (gdk_x11_surface_get_xid (win)) - -GDK_AVAILABLE_IN_ALL -guint32 gdk_x11_get_server_time (GdkSurface *window); - -GDK_AVAILABLE_IN_ALL -GdkSurface *gdk_x11_surface_foreign_new_for_display (GdkDisplay *display, - Window window); -GDK_AVAILABLE_IN_ALL -GdkSurface *gdk_x11_surface_lookup_for_display (GdkDisplay *display, - Window window); - -G_END_DECLS - -#endif /* __GDK_X11_SURFACE_H__ */ diff --git a/gdk/x11/meson.build b/gdk/x11/meson.build index 53213818fb..3912031c18 100644 --- a/gdk/x11/meson.build +++ b/gdk/x11/meson.build @@ -26,7 +26,7 @@ gdk_x11_sources = files([ 'gdktextlistconverter-x11.c', 'gdkvisual-x11.c', 'gdkvulkancontext-x11.c', - 'gdkwindow-x11.c', + 'gdksurface-x11.c', 'gdkxftdefaults.c', 'gdkxid.c', 'xsettings-client.c', @@ -50,7 +50,7 @@ gdk_x11_public_headers = files([ 'gdkx11screen.h', 'gdkx11selection.h', 'gdkx11utils.h', - 'gdkx11window.h', + 'gdkx11surface.h', ]) install_headers(gdk_x11_public_headers, subdir: 'gtk-4.0/gdk/x11/') diff --git a/gdk/x11/xsettings-client.c b/gdk/x11/xsettings-client.c index 5713882320..9adfcf27d4 100644 --- a/gdk/x11/xsettings-client.c +++ b/gdk/x11/xsettings-client.c @@ -28,7 +28,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/gtk/gtkdnd-quartz.c b/gtk/gtkdnd-quartz.c index 01cb0238bf..a91235b7d8 100644 --- a/gtk/gtkdnd-quartz.c +++ b/gtk/gtkdnd-quartz.c @@ -1091,7 +1091,7 @@ gtk_drag_begin_idle (gpointer arg) } /* Fake protocol to let us call GdkNSView gdkWindow without including * gdk/GdkNSView.h (which we can’t because it pulls in the internal-only - * gdkwindow.h). + * gdksurface.h). */ @protocol GdkNSView - (GdkSurface *)gdkWindow; diff --git a/gtk/gtkwindow.c b/gtk/gtkwindow.c index 2331030054..ec5ded38f4 100644 --- a/gtk/gtkwindow.c +++ b/gtk/gtkwindow.c @@ -5974,7 +5974,7 @@ gtk_window_finalize (GObject *object) G_OBJECT_CLASS (gtk_window_parent_class)->finalize (object); } -/* copied from gdkwindow-x11.c */ +/* copied from gdksurface-x11.c */ static const gchar * get_default_title (void) {