forked from AuroraMiddleware/gtk
972134abe4
With the vendor provided Nvidia driver there is a small window of time after drawing to a GL surface before the updates to that surface can be used by the compositor. Drawing is already coordinated with the compositor through the frame synchronization protocol detailed here: https://fishsoup.net/misc/wm-spec-synchronization.html Unfortunately, at the moment, GdkX11Surface tells the compositor the frame is ready immediately after drawing to the surface, not later, when it's consumable by the compositor. This commit defers announcing the frame as ready until it's consumable by the compositor. It does this by listening for the X server to announce damage events associated with the frame drawing. It tries to find the right damage event by waiting until fence placed at buffer swap time signals.
5193 lines
148 KiB
C
5193 lines
148 KiB
C
/* 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 <http://www.gnu.org/licenses/>.
|
||
*/
|
||
|
||
/*
|
||
* 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 "gdksurfaceprivate.h"
|
||
#include "gdkpopupprivate.h"
|
||
#include "gdktoplevelprivate.h"
|
||
#include "gdkdragsurfaceprivate.h"
|
||
#include "gdkvisual-x11.h"
|
||
#include "gdkinternals.h"
|
||
#include "gdkdeviceprivate.h"
|
||
#include "gdkframeclockidleprivate.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 <stdlib.h>
|
||
#include <stdio.h>
|
||
#include <string.h>
|
||
#include <netinet/in.h>
|
||
#include <unistd.h>
|
||
|
||
#include <cairo-xlib.h>
|
||
|
||
#include "MwmUtil.h"
|
||
|
||
#include <X11/Xlib.h>
|
||
#include <X11/Xutil.h>
|
||
#include <X11/Xatom.h>
|
||
|
||
#include <X11/extensions/shape.h>
|
||
|
||
#ifdef HAVE_XKB
|
||
#include <X11/XKBlib.h>
|
||
#endif
|
||
|
||
#ifdef HAVE_XCOMPOSITE
|
||
#include <X11/extensions/Xcomposite.h>
|
||
#endif
|
||
|
||
#ifdef HAVE_XFIXES
|
||
#include <X11/extensions/Xfixes.h>
|
||
#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 *surface);
|
||
static gboolean gdk_surface_icon_name_set (GdkSurface *surface);
|
||
static void set_wm_name (GdkDisplay *display,
|
||
Window xwindow,
|
||
const gchar *name);
|
||
static void move_to_current_desktop (GdkSurface *surface);
|
||
static void gdk_x11_toplevel_state_callback (GdkSurface *surface);
|
||
static gboolean gdk_x11_toplevel_event_callback (GdkSurface *surface,
|
||
GdkEvent *gdk_event);
|
||
|
||
/* 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 )) \
|
||
)
|
||
|
||
G_DEFINE_TYPE (GdkX11Surface, gdk_x11_surface, GDK_TYPE_SURFACE)
|
||
|
||
GType gdk_x11_toplevel_get_type (void) G_GNUC_CONST;
|
||
GType gdk_x11_popup_get_type (void) G_GNUC_CONST;
|
||
GType gdk_x11_drag_surface_get_type (void) G_GNUC_CONST;
|
||
|
||
#define GDK_TYPE_X11_TOPLEVEL (gdk_x11_toplevel_get_type ())
|
||
#define GDK_TYPE_X11_POPUP (gdk_x11_popup_get_type ())
|
||
#define GDK_TYPE_X11_DRAG_SURFACE (gdk_x11_drag_surface_get_type ())
|
||
|
||
static void
|
||
gdk_x11_surface_init (GdkX11Surface *impl)
|
||
{
|
||
impl->surface_scale = 1;
|
||
impl->frame_sync_enabled = TRUE;
|
||
impl->surface_is_on_monitor = NULL;
|
||
}
|
||
|
||
GdkToplevelX11 *
|
||
_gdk_x11_surface_get_toplevel (GdkSurface *surface)
|
||
{
|
||
GdkX11Surface *impl;
|
||
|
||
g_assert (GDK_IS_SURFACE (surface));
|
||
|
||
impl = GDK_X11_SURFACE (surface);
|
||
|
||
if (!impl->toplevel)
|
||
{
|
||
impl->toplevel = g_new0 (GdkToplevelX11, 1);
|
||
impl->toplevel->have_focused = FALSE;
|
||
g_signal_connect (surface, "notify::state",
|
||
G_CALLBACK (gdk_x11_toplevel_state_callback),
|
||
NULL);
|
||
g_signal_connect (surface, "event",
|
||
G_CALLBACK (gdk_x11_toplevel_event_callback),
|
||
NULL);
|
||
}
|
||
|
||
return impl->toplevel;
|
||
}
|
||
|
||
/**
|
||
* _gdk_x11_surface_update_size:
|
||
* @impl: a #GdkX11Surface.
|
||
*
|
||
* Updates the state of the surface (in particular the drawable's
|
||
* cairo surface) when its size has changed.
|
||
**/
|
||
void
|
||
_gdk_x11_surface_update_size (GdkX11Surface *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 *surface,
|
||
int *unscaled_width,
|
||
int *unscaled_height)
|
||
{
|
||
GdkX11Surface *impl = GDK_X11_SURFACE (surface);
|
||
|
||
if (unscaled_width)
|
||
*unscaled_width = impl->unscaled_width;
|
||
|
||
if (unscaled_height)
|
||
*unscaled_height = impl->unscaled_height;
|
||
}
|
||
|
||
gboolean
|
||
gdk_x11_surface_supports_edge_constraints (GdkSurface *surface)
|
||
{
|
||
return gdk_x11_screen_supports_net_wm_hint (GDK_SURFACE_SCREEN (surface),
|
||
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);
|
||
}
|
||
|
||
void
|
||
gdk_x11_surface_pre_damage (GdkSurface *surface)
|
||
{
|
||
GdkX11Surface *impl;
|
||
|
||
impl = GDK_X11_SURFACE (surface);
|
||
|
||
if (impl->toplevel->in_frame &&
|
||
impl->toplevel->current_counter_value % 2 == 0)
|
||
{
|
||
impl->toplevel->current_counter_value += 1;
|
||
set_sync_counter (GDK_SURFACE_XDISPLAY (surface),
|
||
impl->toplevel->extended_update_counter,
|
||
impl->toplevel->current_counter_value);
|
||
}
|
||
}
|
||
|
||
static void
|
||
on_surface_changed (void *data)
|
||
{
|
||
GdkSurface *surface = data;
|
||
GdkX11Surface *impl = GDK_X11_SURFACE (surface);
|
||
|
||
if (impl->tracking_damage)
|
||
gdk_x11_surface_pre_damage (surface);
|
||
}
|
||
|
||
/* 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 *surface)
|
||
{
|
||
GdkX11Surface *impl = GDK_X11_SURFACE (surface);
|
||
|
||
if (impl->cairo_surface)
|
||
{
|
||
cairo_surface_set_mime_data (impl->cairo_surface,
|
||
"x-gdk/change-notify",
|
||
(unsigned char *)"X",
|
||
1,
|
||
on_surface_changed,
|
||
surface);
|
||
impl->tracking_damage = 1;
|
||
}
|
||
}
|
||
|
||
static void
|
||
unhook_surface_changed (GdkSurface *surface)
|
||
{
|
||
GdkX11Surface *impl = GDK_X11_SURFACE (surface);
|
||
|
||
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 *surface)
|
||
{
|
||
GdkX11Surface *impl = GDK_X11_SURFACE (surface);
|
||
GdkFrameClock *clock;
|
||
GdkFrameTimings *timings;
|
||
gint64 presentation_time;
|
||
gint64 refresh_interval;
|
||
|
||
clock = gdk_surface_get_frame_clock (surface);
|
||
|
||
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 *surface,
|
||
gboolean force_frame)
|
||
{
|
||
GdkX11Surface *impl;
|
||
|
||
g_return_if_fail (GDK_IS_SURFACE (surface));
|
||
|
||
impl = GDK_X11_SURFACE (surface);
|
||
|
||
if (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;
|
||
|
||
gdk_x11_surface_pre_damage (surface);
|
||
}
|
||
else if (force_frame)
|
||
{
|
||
/* When mapping the surface, we really want to freeze the
|
||
rendering of the surface by the compositor until we've
|
||
actually painted something into the surface's buffer. */
|
||
gdk_x11_surface_pre_damage (surface);
|
||
}
|
||
else
|
||
{
|
||
hook_surface_changed (surface);
|
||
}
|
||
}
|
||
|
||
static gboolean
|
||
should_sync_frame_drawing (GdkSurface *surface)
|
||
{
|
||
GdkX11Surface *impl = GDK_X11_SURFACE (surface);
|
||
|
||
/* disabled client side */
|
||
if (!impl->frame_sync_enabled)
|
||
return FALSE;
|
||
|
||
/* disabled compositor side */
|
||
if (!gdk_x11_screen_supports_net_wm_hint (GDK_SURFACE_SCREEN (surface),
|
||
g_intern_static_string ("_NET_WM_FRAME_DRAWN")))
|
||
return FALSE;
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
static void
|
||
sync_counter_for_end_frame (GdkSurface *surface)
|
||
{
|
||
GdkX11Surface *impl = GDK_X11_SURFACE (surface);
|
||
|
||
g_assert (!impl->toplevel->in_frame);
|
||
g_assert (impl->toplevel->extended_update_counter != None);
|
||
g_assert ((impl->toplevel->current_counter_value % 2) == 0);
|
||
|
||
set_sync_counter (GDK_SURFACE_XDISPLAY (surface),
|
||
impl->toplevel->extended_update_counter,
|
||
impl->toplevel->current_counter_value);
|
||
}
|
||
|
||
static void
|
||
maybe_sync_counter_for_end_frame (GdkSurface *surface)
|
||
{
|
||
GdkX11Surface *impl = GDK_X11_SURFACE (surface);
|
||
gboolean frame_sync_negotiated = should_sync_frame_drawing (surface);
|
||
gboolean frame_done_painting = !impl->toplevel->frame_pending;
|
||
|
||
#ifdef HAVE_XDAMAGE
|
||
frame_done_painting = !impl->toplevel->frame_still_painting && frame_sync_negotiated;
|
||
#endif
|
||
|
||
if (!impl->toplevel->frame_pending)
|
||
{
|
||
if (!frame_sync_negotiated || frame_done_painting)
|
||
sync_counter_for_end_frame (surface);
|
||
}
|
||
else
|
||
{
|
||
if (frame_done_painting)
|
||
sync_counter_for_end_frame (surface);
|
||
}
|
||
}
|
||
|
||
#ifdef HAVE_XDAMAGE
|
||
void
|
||
_gdk_x11_surface_set_frame_still_painting (GdkSurface *surface,
|
||
gboolean painting)
|
||
{
|
||
GdkX11Surface *impl = GDK_X11_SURFACE (surface);
|
||
|
||
if (impl->toplevel->frame_still_painting == painting)
|
||
return;
|
||
|
||
impl->toplevel->frame_still_painting = painting;
|
||
|
||
if (!impl->toplevel->frame_still_painting)
|
||
maybe_sync_counter_for_end_frame (surface);
|
||
}
|
||
#endif
|
||
|
||
static void
|
||
gdk_x11_surface_end_frame (GdkSurface *surface)
|
||
{
|
||
GdkFrameClock *clock;
|
||
GdkFrameTimings *timings;
|
||
GdkX11Surface *impl;
|
||
|
||
g_return_if_fail (GDK_IS_SURFACE (surface));
|
||
|
||
impl = GDK_X11_SURFACE (surface);
|
||
|
||
if (impl->toplevel->extended_update_counter == None ||
|
||
!impl->toplevel->in_frame)
|
||
return;
|
||
|
||
clock = gdk_surface_get_frame_clock (surface);
|
||
timings = gdk_frame_clock_get_current_timings (clock);
|
||
|
||
/* Make sure we request timing updates even if nothing was damaged.
|
||
* We want the frame clock to be accurate. */
|
||
gdk_x11_surface_pre_damage (surface);
|
||
|
||
impl->toplevel->in_frame = FALSE;
|
||
|
||
if (impl->toplevel->current_counter_value % 2 == 1)
|
||
{
|
||
if (GDK_DISPLAY_DEBUG_CHECK (gdk_surface_get_display (surface), FRAMES))
|
||
{
|
||
XImage *image = XGetImage (GDK_SURFACE_XDISPLAY (surface),
|
||
GDK_SURFACE_XID (surface),
|
||
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;
|
||
|
||
maybe_sync_counter_for_end_frame (surface);
|
||
|
||
if (should_sync_frame_drawing (surface))
|
||
{
|
||
impl->toplevel->frame_pending = TRUE;
|
||
gdk_surface_freeze_updates (surface);
|
||
timings->cookie = impl->toplevel->current_counter_value;
|
||
}
|
||
}
|
||
|
||
unhook_surface_changed (surface);
|
||
|
||
if (impl->toplevel->configure_counter_value != 0 &&
|
||
!impl->toplevel->configure_counter_value_is_extended)
|
||
{
|
||
set_sync_counter (GDK_SURFACE_XDISPLAY (surface),
|
||
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 void
|
||
gdk_x11_surface_finalize (GObject *object)
|
||
{
|
||
GdkX11Surface *impl;
|
||
|
||
g_return_if_fail (GDK_IS_X11_SURFACE (object));
|
||
|
||
impl = GDK_X11_SURFACE (object);
|
||
|
||
if (impl->toplevel->in_frame)
|
||
unhook_surface_changed (GDK_SURFACE (impl));
|
||
|
||
g_signal_handlers_disconnect_by_func (GDK_SURFACE (impl),
|
||
gdk_x11_toplevel_state_callback,
|
||
NULL);
|
||
g_signal_handlers_disconnect_by_func (GDK_SURFACE (impl),
|
||
gdk_x11_toplevel_event_callback,
|
||
NULL);
|
||
|
||
_gdk_x11_surface_grab_check_destroy (GDK_SURFACE (impl));
|
||
|
||
if (!GDK_SURFACE_DESTROYED (impl))
|
||
{
|
||
GdkDisplay *display = GDK_SURFACE_DISPLAY (GDK_SURFACE (impl));
|
||
|
||
_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_clear_pointer (&impl->surface_is_on_monitor, g_list_free);
|
||
|
||
g_free (impl->toplevel);
|
||
|
||
if (impl->cursor)
|
||
g_object_unref (impl->cursor);
|
||
|
||
G_OBJECT_CLASS (gdk_x11_surface_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 surface */
|
||
static cairo_surface_t *
|
||
gdk_x11_surface_create_pixmap_surface (GdkSurface *surface,
|
||
int width,
|
||
int height)
|
||
{
|
||
GdkDisplay *display;
|
||
Display *dpy;
|
||
cairo_surface_t *cairo_surface;
|
||
Pixmap pixmap;
|
||
|
||
display = gdk_surface_get_display (surface);
|
||
dpy = GDK_DISPLAY_XDISPLAY (display);
|
||
|
||
pixmap = XCreatePixmap (dpy,
|
||
GDK_SURFACE_XID (surface),
|
||
width, height,
|
||
DefaultDepth (dpy, DefaultScreen (dpy)));
|
||
cairo_surface = cairo_xlib_surface_create (dpy,
|
||
pixmap,
|
||
DefaultVisual (dpy, DefaultScreen (dpy)),
|
||
width, height);
|
||
attach_free_pixmap_handler (cairo_surface, display, pixmap);
|
||
|
||
return cairo_surface;
|
||
}
|
||
|
||
static void
|
||
set_wm_protocols (GdkSurface *surface)
|
||
{
|
||
GdkDisplay *display = gdk_surface_get_display (surface);
|
||
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 (surface), 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 *surface)
|
||
{
|
||
#ifdef HAVE_XSYNC
|
||
if (!GDK_SURFACE_DESTROYED (surface))
|
||
{
|
||
GdkDisplay *display = GDK_SURFACE_DISPLAY (surface);
|
||
GdkToplevelX11 *toplevel = _gdk_x11_surface_get_toplevel (surface);
|
||
|
||
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 (surface),
|
||
atom, XA_CARDINAL,
|
||
32, PropModeReplace,
|
||
(guchar *)counters, 2);
|
||
|
||
toplevel->current_counter_value = 0;
|
||
}
|
||
}
|
||
#endif
|
||
}
|
||
|
||
static void
|
||
setup_toplevel_window (GdkSurface *surface,
|
||
GdkX11Screen *x11_screen)
|
||
{
|
||
GdkToplevelX11 *toplevel = _gdk_x11_surface_get_toplevel (surface);
|
||
GdkX11Surface *impl = GDK_X11_SURFACE (surface);
|
||
GdkDisplay *display = gdk_surface_get_display (surface);
|
||
Display *xdisplay = GDK_SURFACE_XDISPLAY (surface);
|
||
XID xid = GDK_SURFACE_XID (surface);
|
||
XSizeHints size_hints;
|
||
Window leader_window;
|
||
|
||
set_wm_protocols (surface);
|
||
|
||
/* The focus surface is off the visible area, and serves to receive key
|
||
* press events so they don't get sent to child surfaces.
|
||
*/
|
||
toplevel->focus_window = create_focus_window (display, xid);
|
||
_gdk_x11_display_add_window (x11_screen->display,
|
||
&toplevel->focus_window,
|
||
surface);
|
||
|
||
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 = surface->width * impl->surface_scale;
|
||
size_hints.height = surface->height * impl->surface_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);
|
||
|
||
if (!gdk_running_in_sandbox ())
|
||
{
|
||
/* if sandboxed, we're likely in a pid namespace and would only confuse the wm with this */
|
||
long 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 (GDK_X11_DISPLAY (x11_screen->display)->user_time != 0)
|
||
gdk_x11_surface_set_user_time (surface, GDK_X11_DISPLAY (x11_screen->display)->user_time);
|
||
|
||
ensure_sync_counter (surface);
|
||
|
||
/* Start off in a frozen state - we'll finish this when we first paint */
|
||
gdk_x11_surface_begin_frame (surface, TRUE);
|
||
}
|
||
|
||
static void
|
||
on_frame_clock_before_paint (GdkFrameClock *clock,
|
||
GdkSurface *surface)
|
||
{
|
||
if (surface->update_freeze_count > 0)
|
||
return;
|
||
|
||
gdk_x11_surface_predict_presentation_time (surface);
|
||
gdk_x11_surface_begin_frame (surface, FALSE);
|
||
}
|
||
|
||
static void
|
||
on_frame_clock_after_paint (GdkFrameClock *clock,
|
||
GdkSurface *surface)
|
||
{
|
||
if (surface->update_freeze_count > 0)
|
||
return;
|
||
|
||
gdk_x11_surface_end_frame (surface);
|
||
}
|
||
|
||
static void
|
||
connect_frame_clock (GdkSurface *surface)
|
||
{
|
||
GdkX11Surface *impl;
|
||
|
||
impl = GDK_X11_SURFACE (surface);
|
||
if (!impl->frame_clock_connected)
|
||
{
|
||
GdkFrameClock *frame_clock = gdk_surface_get_frame_clock (surface);
|
||
|
||
g_signal_connect (frame_clock, "before-paint",
|
||
G_CALLBACK (on_frame_clock_before_paint), surface);
|
||
g_signal_connect (frame_clock, "after-paint",
|
||
G_CALLBACK (on_frame_clock_after_paint), surface);
|
||
|
||
impl->frame_clock_connected = TRUE;
|
||
}
|
||
}
|
||
|
||
static void
|
||
disconnect_frame_clock (GdkSurface *surface)
|
||
{
|
||
GdkX11Surface *impl;
|
||
|
||
impl = GDK_X11_SURFACE (surface);
|
||
if (impl->frame_clock_connected)
|
||
{
|
||
GdkFrameClock *frame_clock = gdk_surface_get_frame_clock (surface);
|
||
|
||
g_signal_handlers_disconnect_by_func (frame_clock,
|
||
on_frame_clock_before_paint, surface);
|
||
g_signal_handlers_disconnect_by_func (frame_clock,
|
||
on_frame_clock_after_paint, surface);
|
||
|
||
impl->frame_clock_connected = FALSE;
|
||
}
|
||
}
|
||
|
||
static void gdk_x11_surface_set_title (GdkSurface *surface,
|
||
const char *title);
|
||
static void gdk_x11_surface_set_type_hint (GdkSurface *surface,
|
||
GdkSurfaceTypeHint hint);
|
||
|
||
GdkSurface *
|
||
_gdk_x11_display_create_surface (GdkDisplay *display,
|
||
GdkSurfaceType surface_type,
|
||
GdkSurface *parent,
|
||
int x,
|
||
int y,
|
||
int width,
|
||
int height)
|
||
{
|
||
GdkSurface *surface;
|
||
GdkFrameClock *frame_clock;
|
||
GdkX11Surface *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);
|
||
xparent = GDK_SCREEN_XROOTWIN (x11_screen);
|
||
|
||
if (parent)
|
||
frame_clock = g_object_ref (gdk_surface_get_frame_clock (parent));
|
||
else
|
||
frame_clock = _gdk_frame_clock_idle_new ();
|
||
|
||
switch (surface_type)
|
||
{
|
||
case GDK_SURFACE_TOPLEVEL:
|
||
surface = g_object_new (GDK_TYPE_X11_TOPLEVEL,
|
||
"display", display,
|
||
"frame-clock", frame_clock,
|
||
NULL);
|
||
break;
|
||
case GDK_SURFACE_POPUP:
|
||
surface = g_object_new (GDK_TYPE_X11_POPUP,
|
||
"parent", parent,
|
||
"display", display,
|
||
"frame-clock", frame_clock,
|
||
NULL);
|
||
break;
|
||
case GDK_SURFACE_TEMP:
|
||
surface = g_object_new (GDK_TYPE_X11_DRAG_SURFACE,
|
||
"display", display,
|
||
"frame-clock", frame_clock,
|
||
NULL);
|
||
break;
|
||
default:
|
||
g_assert_not_reached ();
|
||
break;
|
||
}
|
||
|
||
g_object_unref (frame_clock);
|
||
|
||
surface->x = x;
|
||
surface->y = y;
|
||
surface->width = width;
|
||
surface->height = height;
|
||
|
||
impl = GDK_X11_SURFACE (surface);
|
||
impl->surface_scale = x11_screen->surface_scale;
|
||
|
||
xdisplay = x11_screen->xdisplay;
|
||
|
||
xattributes_mask = 0;
|
||
|
||
xvisual = gdk_x11_display_get_window_visual (display_x11);
|
||
|
||
impl->override_redirect = FALSE;
|
||
|
||
class = InputOutput;
|
||
|
||
xattributes.background_pixmap = None;
|
||
xattributes_mask |= CWBackPixmap;
|
||
|
||
xattributes.border_pixel = BlackPixel (xdisplay, x11_screen->screen_num);
|
||
xattributes_mask |= CWBorderPixel;
|
||
|
||
xattributes.bit_gravity = NorthWestGravity;
|
||
xattributes_mask |= CWBitGravity;
|
||
|
||
xattributes.colormap = gdk_x11_display_get_window_colormap (display_x11);
|
||
xattributes_mask |= CWColormap;
|
||
|
||
if (surface_type == GDK_SURFACE_TEMP ||
|
||
surface_type == GDK_SURFACE_POPUP)
|
||
{
|
||
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);
|
||
|
||
if (surface->width * impl->surface_scale > 32767 ||
|
||
surface->height * impl->surface_scale > 32767)
|
||
{
|
||
g_warning ("Native Windows wider or taller than 32767 pixels are not supported");
|
||
|
||
if (surface->width * impl->surface_scale > 32767)
|
||
surface->width = 32767 / impl->surface_scale;
|
||
if (surface->height * impl->surface_scale > 32767)
|
||
surface->height = 32767 / impl->surface_scale;
|
||
}
|
||
|
||
impl->unscaled_width = surface->width * impl->surface_scale;
|
||
impl->unscaled_height = surface->height * impl->surface_scale;
|
||
|
||
abs_x = 0;
|
||
abs_y = 0;
|
||
|
||
impl->xid = XCreateWindow (xdisplay, xparent,
|
||
(surface->x + abs_x) * impl->surface_scale,
|
||
(surface->y + abs_y) * impl->surface_scale,
|
||
surface->width * impl->surface_scale, surface->height * impl->surface_scale,
|
||
0, depth, class, xvisual,
|
||
xattributes_mask, &xattributes);
|
||
|
||
g_object_ref (surface);
|
||
_gdk_x11_display_add_window (x11_screen->display, &impl->xid, surface);
|
||
|
||
gdk_x11_surface_set_title (surface, get_default_title ());
|
||
if (surface_type == GDK_SURFACE_TOPLEVEL)
|
||
gdk_x11_surface_set_type_hint (surface, GDK_SURFACE_TYPE_HINT_NORMAL);
|
||
else if (surface_type == GDK_SURFACE_POPUP)
|
||
gdk_x11_surface_set_type_hint (surface, GDK_SURFACE_TYPE_HINT_MENU);
|
||
|
||
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 (surface, x11_screen);
|
||
|
||
gdk_x11_event_source_select_events ((GdkEventSource *) display_x11->event_source,
|
||
GDK_SURFACE_XID (surface), GDK_ALL_EVENTS_MASK,
|
||
StructureNotifyMask | PropertyChangeMask);
|
||
|
||
_gdk_x11_surface_register_dnd (surface);
|
||
|
||
connect_frame_clock (surface);
|
||
|
||
gdk_surface_freeze_updates (surface);
|
||
|
||
return surface;
|
||
}
|
||
|
||
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 *surface,
|
||
gboolean foreign_destroy)
|
||
{
|
||
GdkX11Surface *impl = GDK_X11_SURFACE (surface);
|
||
GdkToplevelX11 *toplevel;
|
||
|
||
g_return_if_fail (GDK_IS_SURFACE (surface));
|
||
|
||
toplevel = _gdk_x11_surface_get_toplevel (surface);
|
||
if (toplevel)
|
||
gdk_toplevel_x11_free_contents (GDK_SURFACE_DISPLAY (surface), toplevel);
|
||
|
||
unhook_surface_changed (surface);
|
||
disconnect_frame_clock (surface);
|
||
|
||
if (impl->cairo_surface)
|
||
{
|
||
cairo_surface_finish (impl->cairo_surface);
|
||
cairo_surface_destroy (impl->cairo_surface);
|
||
impl->cairo_surface = NULL;
|
||
}
|
||
|
||
if (!foreign_destroy)
|
||
XDestroyWindow (GDK_SURFACE_XDISPLAY (surface), GDK_SURFACE_XID (surface));
|
||
}
|
||
|
||
/* This function is called when the XWindow is really gone.
|
||
*/
|
||
static void
|
||
gdk_x11_surface_destroy_notify (GdkSurface *surface)
|
||
{
|
||
GdkX11Surface *surface_impl;
|
||
|
||
surface_impl = GDK_X11_SURFACE (surface);
|
||
|
||
if (!GDK_SURFACE_DESTROYED (surface))
|
||
{
|
||
g_warning ("GdkSurface %#lx unexpectedly destroyed", GDK_SURFACE_XID (surface));
|
||
|
||
_gdk_surface_destroy (surface, TRUE);
|
||
}
|
||
|
||
_gdk_x11_display_remove_window (GDK_SURFACE_DISPLAY (surface), GDK_SURFACE_XID (surface));
|
||
if (surface_impl->toplevel && surface_impl->toplevel->focus_window)
|
||
_gdk_x11_display_remove_window (GDK_SURFACE_DISPLAY (surface), surface_impl->toplevel->focus_window);
|
||
|
||
_gdk_x11_surface_grab_check_destroy (surface);
|
||
|
||
g_object_unref (surface);
|
||
}
|
||
|
||
static void
|
||
update_wm_hints (GdkSurface *surface,
|
||
gboolean force)
|
||
{
|
||
GdkToplevelX11 *toplevel = _gdk_x11_surface_get_toplevel (surface);
|
||
GdkDisplay *display = GDK_SURFACE_DISPLAY (surface);
|
||
XWMHints wm_hints;
|
||
|
||
if (!force &&
|
||
!toplevel->is_leader &&
|
||
surface->state & GDK_SURFACE_STATE_WITHDRAWN)
|
||
return;
|
||
|
||
wm_hints.flags = StateHint | InputHint;
|
||
wm_hints.input = True;
|
||
wm_hints.initial_state = NormalState;
|
||
|
||
if (surface->state & GDK_SURFACE_STATE_MINIMIZED)
|
||
{
|
||
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 (surface),
|
||
GDK_SURFACE_XID (surface),
|
||
&wm_hints);
|
||
}
|
||
|
||
static void
|
||
set_initial_hints (GdkSurface *surface)
|
||
{
|
||
GdkDisplay *display = GDK_SURFACE_DISPLAY (surface);
|
||
Display *xdisplay = GDK_DISPLAY_XDISPLAY (display);
|
||
Window xwindow = GDK_SURFACE_XID (surface);
|
||
GdkToplevelX11 *toplevel;
|
||
Atom atoms[9];
|
||
gint i;
|
||
|
||
toplevel = _gdk_x11_surface_get_toplevel (surface);
|
||
|
||
if (!toplevel)
|
||
return;
|
||
|
||
update_wm_hints (surface, 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 (surface->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 (surface->state & GDK_SURFACE_STATE_ABOVE)
|
||
{
|
||
atoms[i] = gdk_x11_get_xatom_by_name_for_display (display,
|
||
"_NET_WM_STATE_ABOVE");
|
||
++i;
|
||
}
|
||
|
||
if (surface->state & GDK_SURFACE_STATE_BELOW)
|
||
{
|
||
atoms[i] = gdk_x11_get_xatom_by_name_for_display (display,
|
||
"_NET_WM_STATE_BELOW");
|
||
++i;
|
||
}
|
||
|
||
if (surface->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 (surface->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 (surface->state & GDK_SURFACE_STATE_MINIMIZED)
|
||
{
|
||
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 (surface->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);
|
||
}
|
||
|
||
void
|
||
gdk_x11_surface_show (GdkSurface *surface, gboolean already_mapped)
|
||
{
|
||
GdkDisplay *display;
|
||
GdkX11Display *display_x11;
|
||
GdkToplevelX11 *toplevel;
|
||
Display *xdisplay = GDK_SURFACE_XDISPLAY (surface);
|
||
Window xwindow = GDK_SURFACE_XID (surface);
|
||
GdkX11Surface *impl = GDK_X11_SURFACE (surface);
|
||
|
||
if (!already_mapped)
|
||
set_initial_hints (surface);
|
||
|
||
display = gdk_surface_get_display (surface);
|
||
display_x11 = GDK_X11_DISPLAY (display);
|
||
toplevel = _gdk_x11_surface_get_toplevel (surface);
|
||
|
||
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 (surface, display_x11->user_time);
|
||
|
||
if (GDK_PROFILER_IS_RUNNING)
|
||
{
|
||
if (impl->map_time == 0)
|
||
impl->map_time = g_get_monotonic_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 (surface->fullscreen_mode != GDK_FULLSCREEN_ON_CURRENT_MONITOR)
|
||
gdk_x11_surface_apply_fullscreen_mode (surface);
|
||
}
|
||
|
||
static void
|
||
gdk_x11_surface_withdraw (GdkSurface *surface)
|
||
{
|
||
if (!surface->destroyed)
|
||
{
|
||
if (GDK_SURFACE_IS_MAPPED (surface))
|
||
gdk_synthesize_surface_state (surface,
|
||
0,
|
||
GDK_SURFACE_STATE_WITHDRAWN);
|
||
|
||
g_assert (!GDK_SURFACE_IS_MAPPED (surface));
|
||
XWithdrawWindow (GDK_SURFACE_XDISPLAY (surface),
|
||
GDK_SURFACE_XID (surface), 0);
|
||
}
|
||
}
|
||
|
||
static void
|
||
gdk_x11_surface_hide (GdkSurface *surface)
|
||
{
|
||
/* 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 (surface,
|
||
NextRequest (GDK_SURFACE_XDISPLAY (surface)));
|
||
|
||
gdk_x11_surface_withdraw (surface);
|
||
}
|
||
|
||
static inline void
|
||
x11_surface_move (GdkSurface *surface,
|
||
gint x,
|
||
gint y)
|
||
{
|
||
GdkX11Surface *impl = GDK_X11_SURFACE (surface);
|
||
|
||
XMoveWindow (GDK_SURFACE_XDISPLAY (surface),
|
||
GDK_SURFACE_XID (surface),
|
||
x * impl->surface_scale, y * impl->surface_scale);
|
||
|
||
if (impl->override_redirect)
|
||
{
|
||
impl->abs_x = x;
|
||
impl->abs_y = y;
|
||
|
||
if (surface->parent)
|
||
{
|
||
surface->x = impl->abs_x - GDK_X11_SURFACE (surface->parent)->abs_x;
|
||
surface->y = impl->abs_y - GDK_X11_SURFACE (surface->parent)->abs_y;
|
||
}
|
||
else
|
||
{
|
||
surface->x = x;
|
||
surface->y = y;
|
||
}
|
||
}
|
||
}
|
||
|
||
static inline void
|
||
x11_surface_resize (GdkSurface *surface,
|
||
gint width,
|
||
gint height)
|
||
{
|
||
GdkX11Surface *impl = GDK_X11_SURFACE (surface);
|
||
|
||
if (width < 1)
|
||
width = 1;
|
||
|
||
if (height < 1)
|
||
height = 1;
|
||
|
||
gdk_x11_surface_pre_damage (surface);
|
||
|
||
XResizeWindow (GDK_SURFACE_XDISPLAY (surface),
|
||
GDK_SURFACE_XID (surface),
|
||
width * impl->surface_scale, height * impl->surface_scale);
|
||
|
||
if (impl->override_redirect)
|
||
{
|
||
impl->unscaled_width = width * impl->surface_scale;
|
||
impl->unscaled_height = height * impl->surface_scale;
|
||
surface->width = width;
|
||
surface->height = height;
|
||
_gdk_surface_update_size (surface);
|
||
_gdk_x11_surface_update_size (GDK_X11_SURFACE (surface));
|
||
}
|
||
else
|
||
{
|
||
if (width * impl->surface_scale != impl->unscaled_width || height * impl->surface_scale != impl->unscaled_height)
|
||
surface->resize_count += 1;
|
||
}
|
||
}
|
||
|
||
static inline void
|
||
x11_surface_move_resize (GdkSurface *surface,
|
||
gint x,
|
||
gint y,
|
||
gint width,
|
||
gint height)
|
||
{
|
||
GdkX11Surface *impl = GDK_X11_SURFACE (surface);
|
||
|
||
if (width < 1)
|
||
width = 1;
|
||
|
||
if (height < 1)
|
||
height = 1;
|
||
|
||
gdk_x11_surface_pre_damage (surface);
|
||
|
||
XMoveResizeWindow (GDK_SURFACE_XDISPLAY (surface),
|
||
GDK_SURFACE_XID (surface),
|
||
x * impl->surface_scale, y * impl->surface_scale,
|
||
width * impl->surface_scale, height * impl->surface_scale);
|
||
|
||
if (impl->override_redirect)
|
||
{
|
||
impl->abs_x = x;
|
||
impl->abs_y = y;
|
||
|
||
impl->unscaled_width = width * impl->surface_scale;
|
||
impl->unscaled_height = height * impl->surface_scale;
|
||
surface->width = width;
|
||
surface->height = height;
|
||
|
||
_gdk_x11_surface_update_size (GDK_X11_SURFACE (surface));
|
||
|
||
if (surface->parent)
|
||
{
|
||
surface->x = impl->abs_x - GDK_X11_SURFACE (surface->parent)->abs_x;
|
||
surface->y = impl->abs_y - GDK_X11_SURFACE (surface->parent)->abs_y;
|
||
}
|
||
else
|
||
{
|
||
surface->x = x;
|
||
surface->y = y;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
if (width * impl->surface_scale != impl->unscaled_width || height * impl->surface_scale != impl->unscaled_height)
|
||
surface->resize_count += 1;
|
||
}
|
||
}
|
||
|
||
static void
|
||
gdk_x11_surface_move_resize (GdkSurface *surface,
|
||
gboolean with_move,
|
||
gint x,
|
||
gint y,
|
||
gint width,
|
||
gint height)
|
||
{
|
||
if (with_move && (width < 0 && height < 0))
|
||
x11_surface_move (surface, x, y);
|
||
else
|
||
{
|
||
if (with_move)
|
||
x11_surface_move_resize (surface, x, y, width, height);
|
||
else
|
||
x11_surface_resize (surface, width, height);
|
||
}
|
||
}
|
||
|
||
static void
|
||
gdk_x11_surface_toplevel_resize (GdkSurface *surface,
|
||
gint width,
|
||
gint height)
|
||
{
|
||
x11_surface_resize (surface, width, height);
|
||
}
|
||
|
||
void
|
||
gdk_x11_surface_move (GdkSurface *surface,
|
||
gint x,
|
||
gint y)
|
||
{
|
||
gdk_x11_surface_move_resize (surface, TRUE, x, y, -1, -1);
|
||
}
|
||
|
||
static void
|
||
gdk_x11_surface_layout_popup (GdkSurface *surface,
|
||
int width,
|
||
int height,
|
||
GdkPopupLayout *layout)
|
||
{
|
||
GdkRectangle final_rect;
|
||
int x, y;
|
||
|
||
gdk_surface_layout_popup_helper (surface,
|
||
width,
|
||
height,
|
||
layout,
|
||
&final_rect);
|
||
|
||
gdk_surface_get_origin (surface->parent, &x, &y);
|
||
x += final_rect.x;
|
||
y += final_rect.y;
|
||
|
||
if (final_rect.width != surface->width ||
|
||
final_rect.height != surface->height)
|
||
{
|
||
gdk_x11_surface_move_resize (surface,
|
||
TRUE,
|
||
x,
|
||
y,
|
||
final_rect.width,
|
||
final_rect.height);
|
||
}
|
||
else
|
||
{
|
||
gdk_x11_surface_move (surface, x, y);
|
||
}
|
||
}
|
||
|
||
static void
|
||
show_popup (GdkSurface *surface)
|
||
{
|
||
gdk_x11_surface_raise (surface);
|
||
gdk_synthesize_surface_state (surface, GDK_SURFACE_STATE_WITHDRAWN, 0);
|
||
gdk_x11_surface_show (surface, FALSE);
|
||
gdk_surface_invalidate_rect (surface, NULL);
|
||
}
|
||
|
||
static void
|
||
show_grabbing_popup (GdkSeat *seat,
|
||
GdkSurface *surface,
|
||
gpointer user_data)
|
||
{
|
||
show_popup (surface);
|
||
}
|
||
|
||
static gboolean
|
||
gdk_x11_surface_present_popup (GdkSurface *surface,
|
||
int width,
|
||
int height,
|
||
GdkPopupLayout *layout)
|
||
{
|
||
gdk_x11_surface_layout_popup (surface, width, height, layout);
|
||
|
||
if (GDK_SURFACE_IS_MAPPED (surface))
|
||
return TRUE;
|
||
|
||
if (surface->autohide)
|
||
{
|
||
gdk_seat_grab (gdk_display_get_default_seat (surface->display),
|
||
surface,
|
||
GDK_SEAT_CAPABILITY_ALL,
|
||
TRUE,
|
||
NULL, NULL,
|
||
show_grabbing_popup, NULL);
|
||
}
|
||
else
|
||
{
|
||
show_popup (surface);
|
||
}
|
||
|
||
return GDK_SURFACE_IS_MAPPED (surface);
|
||
}
|
||
|
||
static void gdk_x11_surface_restack_toplevel (GdkSurface *surface,
|
||
GdkSurface *sibling,
|
||
gboolean above);
|
||
|
||
void
|
||
gdk_x11_surface_update_popups (GdkSurface *parent)
|
||
{
|
||
GList *l;
|
||
|
||
for (l = parent->children; l; l = l->next)
|
||
{
|
||
GdkX11Surface *popup_impl = l->data;
|
||
GdkSurface *popup = GDK_SURFACE (popup_impl);
|
||
int new_x = GDK_X11_SURFACE (parent)->abs_x + popup->x;
|
||
int new_y = GDK_X11_SURFACE (parent)->abs_y + popup->y;
|
||
|
||
if (new_x != popup_impl->abs_x || new_y != popup_impl->abs_y)
|
||
x11_surface_move (popup, new_x, new_y);
|
||
gdk_x11_surface_restack_toplevel (popup, parent, TRUE);
|
||
}
|
||
}
|
||
|
||
static void
|
||
gdk_x11_surface_set_is_on_monitor (GdkSurface *surface,
|
||
GdkMonitor *monitor,
|
||
gboolean is_on_monitor)
|
||
{
|
||
GdkX11Surface *impl = GDK_X11_SURFACE (surface);
|
||
GList *was_on_monitor;
|
||
|
||
was_on_monitor = g_list_find (impl->surface_is_on_monitor, monitor);
|
||
|
||
if (!was_on_monitor && is_on_monitor)
|
||
{
|
||
impl->surface_is_on_monitor = g_list_append (impl->surface_is_on_monitor,
|
||
monitor);
|
||
gdk_surface_enter_monitor (surface, monitor);
|
||
}
|
||
else if (was_on_monitor && !is_on_monitor)
|
||
{
|
||
impl->surface_is_on_monitor = g_list_remove (impl->surface_is_on_monitor,
|
||
monitor);
|
||
gdk_surface_leave_monitor (surface, monitor);
|
||
}
|
||
}
|
||
|
||
void
|
||
gdk_x11_surface_check_monitor (GdkSurface *surface,
|
||
GdkMonitor *monitor)
|
||
{
|
||
GdkRectangle monitor_geometry;
|
||
GdkRectangle surface_geometry;
|
||
gboolean is_on_monitor;
|
||
|
||
gdk_monitor_get_geometry (monitor, &monitor_geometry);
|
||
gdk_surface_get_geometry (surface,
|
||
&surface_geometry.x,
|
||
&surface_geometry.y,
|
||
&surface_geometry.width,
|
||
&surface_geometry.height);
|
||
|
||
is_on_monitor = gdk_rectangle_intersect (&surface_geometry,
|
||
&monitor_geometry,
|
||
NULL);
|
||
|
||
gdk_x11_surface_set_is_on_monitor (surface, monitor, is_on_monitor);
|
||
}
|
||
|
||
void
|
||
gdk_x11_surface_enter_leave_monitors (GdkSurface *surface)
|
||
{
|
||
GdkDisplay *display = gdk_surface_get_display (surface);
|
||
GListModel *monitors;
|
||
guint i;
|
||
|
||
monitors = gdk_display_get_monitors (display);
|
||
for (i = 0; i < g_list_model_get_n_items (monitors); i++)
|
||
{
|
||
GdkMonitor *monitor = g_list_model_get_item (monitors, i);
|
||
gdk_x11_surface_check_monitor (surface, monitor);
|
||
g_object_unref (monitor);
|
||
}
|
||
}
|
||
|
||
static void gdk_x11_surface_set_geometry_hints (GdkSurface *surface,
|
||
const GdkGeometry *geometry,
|
||
GdkSurfaceHints geom_mask);
|
||
|
||
void
|
||
_gdk_x11_surface_set_surface_scale (GdkSurface *surface,
|
||
int scale)
|
||
{
|
||
GdkX11Surface *impl;
|
||
GdkToplevelX11 *toplevel;
|
||
GdkSurfaceHints geom_mask;
|
||
|
||
impl = GDK_X11_SURFACE (surface);
|
||
|
||
impl->surface_scale = scale;
|
||
if (impl->cairo_surface)
|
||
cairo_surface_set_device_scale (impl->cairo_surface, impl->surface_scale, impl->surface_scale);
|
||
_gdk_surface_update_size (surface);
|
||
|
||
toplevel = _gdk_x11_surface_get_toplevel (surface);
|
||
if (toplevel)
|
||
{
|
||
/* These are affected by surface 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_x11_surface_set_geometry_hints (surface,
|
||
&toplevel->last_geometry_hints,
|
||
geom_mask);
|
||
}
|
||
|
||
if (impl->override_redirect)
|
||
{
|
||
impl->unscaled_width = surface->width * impl->surface_scale;
|
||
impl->unscaled_height = surface->height * impl->surface_scale;
|
||
}
|
||
|
||
XResizeWindow (GDK_SURFACE_XDISPLAY (surface),
|
||
GDK_SURFACE_XID (surface),
|
||
surface->width * impl->surface_scale,
|
||
surface->height * impl->surface_scale);
|
||
|
||
gdk_surface_invalidate_rect (surface, NULL);
|
||
}
|
||
|
||
void
|
||
gdk_x11_surface_raise (GdkSurface *surface)
|
||
{
|
||
XRaiseWindow (GDK_SURFACE_XDISPLAY (surface), GDK_SURFACE_XID (surface));
|
||
}
|
||
|
||
static void
|
||
gdk_x11_surface_restack_toplevel (GdkSurface *surface,
|
||
GdkSurface *sibling,
|
||
gboolean above)
|
||
{
|
||
XWindowChanges changes;
|
||
|
||
changes.sibling = GDK_SURFACE_XID (sibling);
|
||
changes.stack_mode = above ? Above : Below;
|
||
XReconfigureWMWindow (GDK_SURFACE_XDISPLAY (surface),
|
||
GDK_SURFACE_XID (surface),
|
||
gdk_x11_screen_get_screen_number (GDK_SURFACE_SCREEN (surface)),
|
||
CWStackMode | CWSibling, &changes);
|
||
}
|
||
|
||
static void
|
||
gdk_x11_surface_lower (GdkSurface *surface)
|
||
{
|
||
XLowerWindow (GDK_SURFACE_XDISPLAY (surface), GDK_SURFACE_XID (surface));
|
||
}
|
||
|
||
/**
|
||
* gdk_x11_surface_move_to_current_desktop:
|
||
* @surface: (type GdkX11Surface): a #GdkSurface
|
||
*
|
||
* Moves the surface 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 surface is already on all workspaces.
|
||
*/
|
||
void
|
||
gdk_x11_surface_move_to_current_desktop (GdkSurface *surface)
|
||
{
|
||
GdkToplevelX11 *toplevel;
|
||
|
||
g_return_if_fail (GDK_IS_SURFACE (surface));
|
||
|
||
toplevel = _gdk_x11_surface_get_toplevel (surface);
|
||
|
||
if (toplevel->on_all_desktops)
|
||
return;
|
||
|
||
move_to_current_desktop (surface);
|
||
}
|
||
|
||
static void
|
||
move_to_current_desktop (GdkSurface *surface)
|
||
{
|
||
guint32 desktop;
|
||
|
||
desktop = gdk_x11_screen_get_current_desktop (GDK_SURFACE_SCREEN (surface));
|
||
gdk_x11_surface_move_to_desktop (surface, desktop);
|
||
}
|
||
|
||
static guint32
|
||
get_netwm_cardinal_property (GdkSurface *surface,
|
||
const gchar *name)
|
||
{
|
||
GdkX11Screen *x11_screen = GDK_SURFACE_SCREEN (surface);
|
||
guint32 prop = 0;
|
||
Atom type;
|
||
gint format;
|
||
gulong nitems;
|
||
gulong bytes_after;
|
||
guchar *data;
|
||
|
||
if (!gdk_x11_screen_supports_net_wm_hint (x11_screen, name))
|
||
return 0;
|
||
|
||
XGetWindowProperty (x11_screen->xdisplay,
|
||
GDK_SURFACE_XID (surface),
|
||
gdk_x11_get_xatom_by_name_for_display (GDK_SURFACE_DISPLAY (surface), 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:
|
||
* @surface: (type GdkX11Surface): a #GdkSurface
|
||
*
|
||
* Gets the number of the workspace @surface is on.
|
||
*
|
||
* Returns: the current workspace of @surface
|
||
*/
|
||
guint32
|
||
gdk_x11_surface_get_desktop (GdkSurface *surface)
|
||
{
|
||
g_return_val_if_fail (GDK_IS_SURFACE (surface), 0);
|
||
|
||
return get_netwm_cardinal_property (surface, "_NET_WM_DESKTOP");
|
||
}
|
||
|
||
/**
|
||
* gdk_x11_surface_move_to_desktop:
|
||
* @surface: (type GdkX11Surface): a #GdkSurface
|
||
* @desktop: the number of the workspace to move the surface to
|
||
*
|
||
* Moves the surface 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 *surface,
|
||
guint32 desktop)
|
||
{
|
||
const char *atom_name = "_NET_WM_DESKTOP";
|
||
XClientMessageEvent xclient;
|
||
|
||
g_return_if_fail (GDK_IS_SURFACE (surface));
|
||
|
||
if (!gdk_x11_screen_supports_net_wm_hint (GDK_SURFACE_SCREEN (surface), atom_name))
|
||
return;
|
||
|
||
memset (&xclient, 0, sizeof (xclient));
|
||
xclient.type = ClientMessage;
|
||
xclient.serial = 0;
|
||
xclient.send_event = True;
|
||
xclient.window = GDK_SURFACE_XID (surface);
|
||
xclient.message_type = gdk_x11_get_xatom_by_name_for_display (GDK_SURFACE_DISPLAY (surface), atom_name);
|
||
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 (surface),
|
||
GDK_SURFACE_XROOTWIN (surface),
|
||
False,
|
||
SubstructureRedirectMask | SubstructureNotifyMask,
|
||
(XEvent *)&xclient);
|
||
}
|
||
|
||
static void
|
||
gdk_x11_surface_focus (GdkSurface *surface,
|
||
guint32 timestamp)
|
||
{
|
||
GdkDisplay *display;
|
||
|
||
g_return_if_fail (GDK_IS_SURFACE (surface));
|
||
|
||
if (GDK_SURFACE_DESTROYED (surface))
|
||
return;
|
||
|
||
display = GDK_SURFACE_DISPLAY (surface);
|
||
|
||
if (gdk_x11_screen_supports_net_wm_hint (GDK_SURFACE_SCREEN (surface),
|
||
g_intern_static_string ("_NET_ACTIVE_WINDOW")))
|
||
{
|
||
XClientMessageEvent xclient;
|
||
|
||
memset (&xclient, 0, sizeof (xclient));
|
||
xclient.type = ClientMessage;
|
||
xclient.window = GDK_SURFACE_XID (surface);
|
||
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 (surface), False,
|
||
SubstructureRedirectMask | SubstructureNotifyMask,
|
||
(XEvent *)&xclient);
|
||
}
|
||
else
|
||
{
|
||
XRaiseWindow (GDK_DISPLAY_XDISPLAY (display), GDK_SURFACE_XID (surface));
|
||
|
||
/* 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 (surface),
|
||
RevertToParent,
|
||
timestamp);
|
||
gdk_x11_display_error_trap_pop_ignored (display);
|
||
}
|
||
}
|
||
|
||
static void
|
||
gdk_x11_surface_set_type_hint (GdkSurface *surface,
|
||
GdkSurfaceTypeHint hint)
|
||
{
|
||
GdkDisplay *display;
|
||
Atom atom;
|
||
|
||
if (GDK_SURFACE_DESTROYED (surface))
|
||
return;
|
||
|
||
display = gdk_surface_get_display (surface);
|
||
|
||
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);
|
||
G_GNUC_FALLTHROUGH;
|
||
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 (surface),
|
||
gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_WINDOW_TYPE"),
|
||
XA_ATOM, 32, PropModeReplace,
|
||
(guchar *)&atom, 1);
|
||
}
|
||
|
||
static void
|
||
gdk_wmspec_change_state (gboolean add,
|
||
GdkSurface *surface,
|
||
const char *state1,
|
||
const char *state2)
|
||
{
|
||
GdkDisplay *display = GDK_SURFACE_DISPLAY (surface);
|
||
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 (surface);
|
||
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_get_xatom_by_name_for_display (display, state1);
|
||
xclient.data.l[2] = gdk_x11_get_xatom_by_name_for_display (display, state2);
|
||
xclient.data.l[3] = 1; /* source indication */
|
||
xclient.data.l[4] = 0;
|
||
|
||
XSendEvent (GDK_SURFACE_XDISPLAY (surface), GDK_SURFACE_XROOTWIN (surface), False,
|
||
SubstructureRedirectMask | SubstructureNotifyMask,
|
||
(XEvent *)&xclient);
|
||
}
|
||
|
||
static void
|
||
gdk_x11_surface_set_modal_hint (GdkSurface *surface,
|
||
gboolean modal)
|
||
{
|
||
if (GDK_SURFACE_DESTROYED (surface))
|
||
return;
|
||
|
||
surface->modal_hint = modal;
|
||
|
||
if (GDK_SURFACE_IS_MAPPED (surface))
|
||
gdk_wmspec_change_state (modal, surface,
|
||
"_NET_WM_STATE_MODAL",
|
||
NULL);
|
||
}
|
||
|
||
/**
|
||
* gdk_x11_surface_set_skip_taskbar_hint:
|
||
* @surface: (type GdkX11Surface): a native #GdkSurface
|
||
* @skips_taskbar: %TRUE to skip taskbars
|
||
*
|
||
* Sets a hint on @surface that taskbars should not
|
||
* display it. See the EWMH for details.
|
||
*/
|
||
void
|
||
gdk_x11_surface_set_skip_taskbar_hint (GdkSurface *surface,
|
||
gboolean skips_taskbar)
|
||
{
|
||
GdkToplevelX11 *toplevel;
|
||
|
||
if (GDK_SURFACE_DESTROYED (surface))
|
||
return;
|
||
|
||
toplevel = _gdk_x11_surface_get_toplevel (surface);
|
||
toplevel->skip_taskbar_hint = skips_taskbar;
|
||
|
||
if (GDK_SURFACE_IS_MAPPED (surface))
|
||
gdk_wmspec_change_state (skips_taskbar, surface,
|
||
"_NET_WM_STATE_SKIP_TASKBAR",
|
||
NULL);
|
||
}
|
||
|
||
/**
|
||
* gdk_x11_surface_set_skip_pager_hint:
|
||
* @surface: (type GdkX11Surface): a #GdkSurface
|
||
* @skips_pager: %TRUE to skip pagers
|
||
*
|
||
* Sets a hint on @surface that pagers should not
|
||
* display it. See the EWMH for details.
|
||
*/
|
||
void
|
||
gdk_x11_surface_set_skip_pager_hint (GdkSurface *surface,
|
||
gboolean skips_pager)
|
||
{
|
||
GdkToplevelX11 *toplevel;
|
||
|
||
if (GDK_SURFACE_DESTROYED (surface))
|
||
return;
|
||
|
||
toplevel = _gdk_x11_surface_get_toplevel (surface);
|
||
toplevel->skip_pager_hint = skips_pager;
|
||
|
||
if (GDK_SURFACE_IS_MAPPED (surface))
|
||
gdk_wmspec_change_state (skips_pager, surface,
|
||
"_NET_WM_STATE_SKIP_PAGER",
|
||
NULL);
|
||
}
|
||
|
||
/**
|
||
* gdk_x11_surface_set_urgency_hint:
|
||
* @surface: (type GdkX11Surface): a native #GdkSurface
|
||
* @urgent: %TRUE to indicate urgenct attention needed
|
||
*
|
||
* Sets a hint on @surface that it needs user attention.
|
||
* See the ICCCM for details.
|
||
*/
|
||
void
|
||
gdk_x11_surface_set_urgency_hint (GdkSurface *surface,
|
||
gboolean urgent)
|
||
{
|
||
GdkToplevelX11 *toplevel;
|
||
|
||
if (GDK_SURFACE_DESTROYED (surface))
|
||
return;
|
||
|
||
toplevel = _gdk_x11_surface_get_toplevel (surface);
|
||
toplevel->urgency_hint = urgent;
|
||
|
||
update_wm_hints (surface, FALSE);
|
||
}
|
||
|
||
static void
|
||
gdk_x11_surface_set_geometry_hints (GdkSurface *surface,
|
||
const GdkGeometry *geometry,
|
||
GdkSurfaceHints geom_mask)
|
||
{
|
||
GdkX11Surface *impl = GDK_X11_SURFACE (surface);
|
||
XSizeHints size_hints;
|
||
GdkToplevelX11 *toplevel;
|
||
|
||
if (GDK_SURFACE_DESTROYED (surface))
|
||
return;
|
||
|
||
toplevel = _gdk_x11_surface_get_toplevel (surface);
|
||
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->surface_scale;
|
||
size_hints.min_height = geometry->min_height * impl->surface_scale;
|
||
}
|
||
|
||
if (geom_mask & GDK_HINT_MAX_SIZE)
|
||
{
|
||
size_hints.flags |= PMaxSize;
|
||
size_hints.max_width = MAX (geometry->max_width, 1) * impl->surface_scale;
|
||
size_hints.max_height = MAX (geometry->max_height, 1) * impl->surface_scale;
|
||
}
|
||
|
||
if (geom_mask & GDK_HINT_BASE_SIZE)
|
||
{
|
||
size_hints.flags |= PBaseSize;
|
||
size_hints.base_width = geometry->base_width * impl->surface_scale;
|
||
size_hints.base_height = geometry->base_height * impl->surface_scale;
|
||
}
|
||
|
||
if (geom_mask & GDK_HINT_RESIZE_INC)
|
||
{
|
||
size_hints.flags |= PResizeInc;
|
||
size_hints.width_inc = geometry->width_inc * impl->surface_scale;
|
||
size_hints.height_inc = geometry->height_inc * impl->surface_scale;
|
||
}
|
||
else if (impl->surface_scale > 1)
|
||
{
|
||
size_hints.flags |= PResizeInc;
|
||
size_hints.width_inc = impl->surface_scale;
|
||
size_hints.height_inc = impl->surface_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 (surface),
|
||
GDK_SURFACE_XID (surface),
|
||
&size_hints);
|
||
}
|
||
|
||
static void
|
||
gdk_surface_get_geometry_hints (GdkSurface *surface,
|
||
GdkGeometry *geometry,
|
||
GdkSurfaceHints *geom_mask)
|
||
{
|
||
GdkX11Surface *impl;
|
||
XSizeHints *size_hints;
|
||
glong junk_supplied_mask = 0;
|
||
|
||
g_return_if_fail (GDK_IS_SURFACE (surface));
|
||
g_return_if_fail (geometry != NULL);
|
||
g_return_if_fail (geom_mask != NULL);
|
||
|
||
*geom_mask = 0;
|
||
|
||
if (GDK_SURFACE_DESTROYED (surface))
|
||
return;
|
||
|
||
impl = GDK_X11_SURFACE (surface);
|
||
|
||
size_hints = XAllocSizeHints ();
|
||
if (!size_hints)
|
||
return;
|
||
|
||
if (!XGetWMNormalHints (GDK_SURFACE_XDISPLAY (surface),
|
||
GDK_SURFACE_XID (surface),
|
||
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->surface_scale;
|
||
geometry->min_height = size_hints->min_height / impl->surface_scale;
|
||
}
|
||
|
||
if (size_hints->flags & PMaxSize)
|
||
{
|
||
*geom_mask |= GDK_HINT_MAX_SIZE;
|
||
geometry->max_width = MAX (size_hints->max_width, 1) / impl->surface_scale;
|
||
geometry->max_height = MAX (size_hints->max_height, 1) / impl->surface_scale;
|
||
}
|
||
|
||
if (size_hints->flags & PResizeInc)
|
||
{
|
||
*geom_mask |= GDK_HINT_RESIZE_INC;
|
||
geometry->width_inc = size_hints->width_inc / impl->surface_scale;
|
||
geometry->height_inc = size_hints->height_inc / impl->surface_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
|
||
* convertible 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_utf8_to_string_target (utf8_str, TRUE);
|
||
prop_length = prop_text ? strlen (prop_text) : 0;
|
||
prop_format = 8;
|
||
is_compound_text = FALSE;
|
||
}
|
||
else
|
||
{
|
||
const char *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_get_xatom_by_name_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 *surface,
|
||
const gchar *title)
|
||
{
|
||
GdkDisplay *display;
|
||
Display *xdisplay;
|
||
Window xwindow;
|
||
|
||
g_return_if_fail (title != NULL);
|
||
|
||
if (GDK_SURFACE_DESTROYED (surface))
|
||
return;
|
||
|
||
display = gdk_surface_get_display (surface);
|
||
xdisplay = GDK_DISPLAY_XDISPLAY (display);
|
||
xwindow = GDK_SURFACE_XID (surface);
|
||
|
||
set_wm_name (display, xwindow, title);
|
||
|
||
if (!gdk_surface_icon_name_set (surface))
|
||
{
|
||
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_startup_id (GdkSurface *surface,
|
||
const gchar *startup_id)
|
||
{
|
||
GdkDisplay *display;
|
||
|
||
g_return_if_fail (GDK_IS_SURFACE (surface));
|
||
|
||
display = gdk_surface_get_display (surface);
|
||
|
||
if (GDK_SURFACE_DESTROYED (surface))
|
||
return;
|
||
|
||
if (startup_id)
|
||
XChangeProperty (GDK_DISPLAY_XDISPLAY (display), GDK_SURFACE_XID (surface),
|
||
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 (surface),
|
||
gdk_x11_get_xatom_by_name_for_display (display, "_NET_STARTUP_ID"));
|
||
}
|
||
|
||
static void
|
||
gdk_x11_surface_set_transient_for (GdkSurface *surface,
|
||
GdkSurface *parent)
|
||
{
|
||
if (GDK_SURFACE_DESTROYED (surface))
|
||
return;
|
||
|
||
/* XSetTransientForHint() doesn't allow unsetting, so do it manually */
|
||
if (parent && !GDK_SURFACE_DESTROYED (parent))
|
||
{
|
||
XSetTransientForHint (GDK_SURFACE_XDISPLAY (surface),
|
||
GDK_SURFACE_XID (surface),
|
||
GDK_SURFACE_XID (parent));
|
||
gdk_x11_surface_set_type_hint (surface, GDK_SURFACE_TYPE_HINT_DIALOG);
|
||
}
|
||
else
|
||
{
|
||
XDeleteProperty (GDK_SURFACE_XDISPLAY (surface),
|
||
GDK_SURFACE_XID (surface),
|
||
gdk_x11_get_xatom_by_name_for_display (GDK_SURFACE_DISPLAY (surface), "WM_TRANSIENT_FOR"));
|
||
gdk_x11_surface_set_type_hint (surface, GDK_SURFACE_TYPE_HINT_NORMAL);
|
||
}
|
||
}
|
||
|
||
GdkCursor *
|
||
_gdk_x11_surface_get_cursor (GdkSurface *surface)
|
||
{
|
||
GdkX11Surface *impl;
|
||
|
||
g_return_val_if_fail (GDK_IS_SURFACE (surface), NULL);
|
||
|
||
impl = GDK_X11_SURFACE (surface);
|
||
|
||
return impl->cursor;
|
||
}
|
||
|
||
static void
|
||
gdk_x11_surface_get_geometry (GdkSurface *surface,
|
||
gint *x,
|
||
gint *y,
|
||
gint *width,
|
||
gint *height)
|
||
{
|
||
GdkX11Surface *impl;
|
||
Window root;
|
||
gint tx;
|
||
gint ty;
|
||
guint twidth;
|
||
guint theight;
|
||
guint tborder_width;
|
||
guint tdepth;
|
||
|
||
if (!GDK_SURFACE_DESTROYED (surface))
|
||
{
|
||
impl = GDK_X11_SURFACE (surface);
|
||
|
||
XGetGeometry (GDK_SURFACE_XDISPLAY (surface),
|
||
GDK_SURFACE_XID (surface),
|
||
&root, &tx, &ty, &twidth, &theight, &tborder_width, &tdepth);
|
||
|
||
if (x)
|
||
*x = tx / impl->surface_scale;
|
||
if (y)
|
||
*y = ty / impl->surface_scale;
|
||
if (width)
|
||
*width = twidth / impl->surface_scale;
|
||
if (height)
|
||
*height = theight / impl->surface_scale;
|
||
}
|
||
}
|
||
|
||
void
|
||
gdk_x11_surface_get_root_coords (GdkSurface *surface,
|
||
gint x,
|
||
gint y,
|
||
gint *root_x,
|
||
gint *root_y)
|
||
{
|
||
GdkX11Surface *impl = GDK_X11_SURFACE (surface);
|
||
Window child;
|
||
gint tx;
|
||
gint ty;
|
||
|
||
XTranslateCoordinates (GDK_SURFACE_XDISPLAY (surface),
|
||
GDK_SURFACE_XID (surface),
|
||
GDK_SURFACE_XROOTWIN (surface),
|
||
x * impl->surface_scale, y * impl->surface_scale, &tx, &ty,
|
||
&child);
|
||
|
||
if (root_x)
|
||
*root_x = tx / impl->surface_scale;
|
||
if (root_y)
|
||
*root_y = ty / impl->surface_scale;
|
||
}
|
||
|
||
static void
|
||
gdk_x11_surface_get_frame_extents (GdkSurface *surface,
|
||
GdkRectangle *rect)
|
||
{
|
||
GdkDisplay *display;
|
||
GdkX11Surface *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;
|
||
|
||
impl = GDK_X11_SURFACE (surface);
|
||
|
||
/* Refine our fallback answer a bit using local information */
|
||
rect->x = impl->abs_x * impl->surface_scale;
|
||
rect->y = impl->abs_y * impl->surface_scale;
|
||
rect->width = surface->width * impl->surface_scale;
|
||
rect->height = surface->height * impl->surface_scale;
|
||
|
||
if (GDK_SURFACE_DESTROYED (surface) || impl->override_redirect)
|
||
return;
|
||
|
||
nvroots = 0;
|
||
vroots = NULL;
|
||
|
||
display = gdk_surface_get_display (surface);
|
||
|
||
gdk_x11_display_error_trap_push (display);
|
||
|
||
xwindow = GDK_SURFACE_XID (surface);
|
||
|
||
/* first try: use _NET_FRAME_EXTENTS */
|
||
if (gdk_x11_screen_supports_net_wm_hint (GDK_SURFACE_SCREEN (surface),
|
||
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 (surface);
|
||
|
||
if (gdk_x11_screen_supports_net_wm_hint (GDK_SURFACE_SCREEN (surface),
|
||
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 (surface);
|
||
|
||
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->surface_scale + impl->surface_scale - 1) / impl->surface_scale;
|
||
rect->height = (rect->height + rect->y % impl->surface_scale + impl->surface_scale - 1) / impl->surface_scale;
|
||
rect->x = rect->x / impl->surface_scale;
|
||
rect->y = rect->y / impl->surface_scale;
|
||
gdk_x11_display_error_trap_pop_ignored (display);
|
||
}
|
||
|
||
static gboolean
|
||
gdk_x11_surface_get_device_state (GdkSurface *surface,
|
||
GdkDevice *device,
|
||
gdouble *x,
|
||
gdouble *y,
|
||
GdkModifierType *mask)
|
||
{
|
||
GdkSurface *child;
|
||
|
||
if (GDK_SURFACE_DESTROYED (surface))
|
||
return FALSE;
|
||
|
||
/*HIDPI: handle coords here?*/
|
||
GDK_DEVICE_GET_CLASS (device)->query_state (device, surface,
|
||
&child,
|
||
x, y, mask);
|
||
return child != NULL;
|
||
}
|
||
|
||
static void
|
||
gdk_x11_surface_set_input_region (GdkSurface *surface,
|
||
cairo_region_t *input_region)
|
||
{
|
||
#ifdef ShapeInput
|
||
GdkX11Surface *impl = GDK_X11_SURFACE (surface);
|
||
|
||
if (GDK_SURFACE_DESTROYED (surface))
|
||
return;
|
||
|
||
if (!gdk_display_supports_input_shapes (GDK_SURFACE_DISPLAY (surface)))
|
||
return;
|
||
|
||
if (input_region == NULL)
|
||
{
|
||
XShapeCombineMask (GDK_SURFACE_XDISPLAY (surface),
|
||
GDK_SURFACE_XID (surface),
|
||
ShapeInput,
|
||
0, 0,
|
||
None,
|
||
ShapeSet);
|
||
return;
|
||
}
|
||
else
|
||
{
|
||
gint n_rects = 0;
|
||
XRectangle *xrects = NULL;
|
||
|
||
_gdk_x11_region_get_xrectangles (input_region,
|
||
0, 0, impl->surface_scale,
|
||
&xrects, &n_rects);
|
||
|
||
XShapeCombineRectangles (GDK_SURFACE_XDISPLAY (surface),
|
||
GDK_SURFACE_XID (surface),
|
||
ShapeInput,
|
||
0, 0,
|
||
xrects, n_rects,
|
||
ShapeSet,
|
||
YXBanded);
|
||
|
||
g_free (xrects);
|
||
}
|
||
#endif
|
||
}
|
||
|
||
/**
|
||
* gdk_x11_surface_set_user_time:
|
||
* @surface: (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 surface. This property stores an Xserver
|
||
* time which represents the time of the last user input event
|
||
* received for this surface. This property may be used by the window
|
||
* manager to alter the focus, stacking, and/or placement behavior of
|
||
* surfaces when they are mapped depending on whether the new surface
|
||
* was created by a user action or is a "pop-up" surface 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 *surface,
|
||
guint32 timestamp)
|
||
{
|
||
GdkDisplay *display;
|
||
GdkX11Display *display_x11;
|
||
GdkToplevelX11 *toplevel;
|
||
glong timestamp_long = (glong)timestamp;
|
||
Window xid;
|
||
|
||
if (GDK_SURFACE_DESTROYED (surface))
|
||
return;
|
||
|
||
display = gdk_surface_get_display (surface);
|
||
display_x11 = GDK_X11_DISPLAY (display);
|
||
toplevel = _gdk_x11_surface_get_toplevel (surface);
|
||
|
||
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 (surface),
|
||
g_intern_static_string ("_NET_WM_USER_TIME_WINDOW")))
|
||
xid = toplevel->focus_window;
|
||
else
|
||
xid = GDK_SURFACE_XID (surface);
|
||
|
||
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:
|
||
* @surface: (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 @surface is
|
||
* not a toplevel surface, it is ignored.
|
||
*/
|
||
void
|
||
gdk_x11_surface_set_utf8_property (GdkSurface *surface,
|
||
const gchar *name,
|
||
const gchar *value)
|
||
{
|
||
GdkDisplay *display;
|
||
|
||
display = gdk_surface_get_display (surface);
|
||
|
||
if (value != NULL)
|
||
{
|
||
XChangeProperty (GDK_DISPLAY_XDISPLAY (display),
|
||
GDK_SURFACE_XID (surface),
|
||
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 (surface),
|
||
gdk_x11_get_xatom_by_name_for_display (display, name));
|
||
}
|
||
}
|
||
|
||
static void
|
||
gdk_x11_surface_set_shadow_width (GdkSurface *surface,
|
||
int left,
|
||
int right,
|
||
int top,
|
||
int bottom)
|
||
{
|
||
GdkX11Surface *impl = GDK_X11_SURFACE (surface);
|
||
Atom frame_extents;
|
||
gulong data[4] = {
|
||
left * impl->surface_scale,
|
||
right * impl->surface_scale,
|
||
top * impl->surface_scale,
|
||
bottom * impl->surface_scale
|
||
};
|
||
|
||
frame_extents = gdk_x11_get_xatom_by_name_for_display (gdk_surface_get_display (surface),
|
||
"_GTK_FRAME_EXTENTS");
|
||
XChangeProperty (GDK_SURFACE_XDISPLAY (surface),
|
||
GDK_SURFACE_XID (surface),
|
||
frame_extents, XA_CARDINAL,
|
||
32, PropModeReplace,
|
||
(guchar *) &data, 4);
|
||
}
|
||
|
||
/**
|
||
* gdk_x11_surface_set_theme_variant:
|
||
* @surface: (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 surfaces.
|
||
*
|
||
* 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 surfaces.
|
||
*/
|
||
void
|
||
gdk_x11_surface_set_theme_variant (GdkSurface *surface,
|
||
const char *variant)
|
||
{
|
||
gdk_x11_surface_set_utf8_property (surface, "_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 *surface,
|
||
GList *icon_list)
|
||
{
|
||
GdkToplevelX11 *toplevel;
|
||
GdkTexture *best_icon;
|
||
GList *tmp_list;
|
||
int best_size;
|
||
|
||
toplevel = _gdk_x11_surface_get_toplevel (surface);
|
||
|
||
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 *cairo_surface;
|
||
cairo_t *cr;
|
||
|
||
toplevel->icon_pixmap = gdk_x11_surface_create_pixmap_surface (surface, width, height);
|
||
|
||
cairo_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, cairo_surface, 0, 0);
|
||
if (cairo_surface_get_content (cairo_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 (cairo_surface) == CAIRO_CONTENT_COLOR_ALPHA)
|
||
{
|
||
GdkDisplay *display = gdk_surface_get_display (surface);
|
||
|
||
toplevel->icon_mask = _gdk_x11_display_create_bitmap_surface (display, width, height);
|
||
|
||
cr = cairo_create (toplevel->icon_mask);
|
||
cairo_set_source_surface (cr, cairo_surface, 0, 0);
|
||
cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
|
||
cairo_paint (cr);
|
||
cairo_destroy (cr);
|
||
}
|
||
|
||
cairo_surface_destroy (cairo_surface);
|
||
}
|
||
|
||
update_wm_hints (surface, FALSE);
|
||
}
|
||
|
||
static void
|
||
gdk_x11_surface_set_icon_list (GdkSurface *surface,
|
||
GList *textures)
|
||
{
|
||
gulong *data;
|
||
gulong *p;
|
||
gint size;
|
||
GList *l;
|
||
gint width, height;
|
||
GdkTexture *texture;
|
||
GdkDisplay *display;
|
||
gint i, n;
|
||
|
||
if (GDK_SURFACE_DESTROYED (surface))
|
||
return;
|
||
|
||
display = gdk_surface_get_display (surface);
|
||
|
||
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 (surface),
|
||
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 (surface),
|
||
gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_ICON"));
|
||
}
|
||
|
||
g_free (data);
|
||
|
||
gdk_surface_update_icon (surface, textures);
|
||
}
|
||
|
||
static gboolean
|
||
gdk_surface_icon_name_set (GdkSurface *surface)
|
||
{
|
||
return GPOINTER_TO_UINT (g_object_get_qdata (G_OBJECT (surface),
|
||
g_quark_from_static_string ("gdk-icon-name-set")));
|
||
}
|
||
|
||
static void
|
||
gdk_x11_surface_minimize (GdkSurface *surface)
|
||
{
|
||
if (GDK_SURFACE_DESTROYED (surface))
|
||
return;
|
||
|
||
if (GDK_SURFACE_IS_MAPPED (surface))
|
||
{
|
||
XIconifyWindow (GDK_SURFACE_XDISPLAY (surface),
|
||
GDK_SURFACE_XID (surface),
|
||
gdk_x11_screen_get_screen_number (GDK_SURFACE_SCREEN (surface)));
|
||
}
|
||
else
|
||
{
|
||
/* Flip our client side flag, the real work happens on map. */
|
||
gdk_synthesize_surface_state (surface, 0, GDK_SURFACE_STATE_MINIMIZED);
|
||
gdk_wmspec_change_state (TRUE, surface,
|
||
"_NET_WM_STATE_HIDDEN",
|
||
NULL);
|
||
}
|
||
}
|
||
|
||
static void
|
||
gdk_x11_surface_unminimize (GdkSurface *surface)
|
||
{
|
||
if (GDK_SURFACE_DESTROYED (surface))
|
||
return;
|
||
|
||
if (GDK_SURFACE_IS_MAPPED (surface))
|
||
{
|
||
gdk_x11_surface_show (surface, TRUE);
|
||
gdk_wmspec_change_state (FALSE, surface,
|
||
"_NET_WM_STATE_HIDDEN",
|
||
NULL);
|
||
}
|
||
else
|
||
{
|
||
/* Flip our client side flag, the real work happens on map. */
|
||
gdk_synthesize_surface_state (surface, GDK_SURFACE_STATE_MINIMIZED, 0);
|
||
gdk_wmspec_change_state (FALSE, surface,
|
||
"_NET_WM_STATE_HIDDEN",
|
||
NULL);
|
||
}
|
||
}
|
||
|
||
static void
|
||
gdk_x11_surface_maximize (GdkSurface *surface)
|
||
{
|
||
if (GDK_SURFACE_DESTROYED (surface))
|
||
return;
|
||
|
||
if (GDK_SURFACE_IS_MAPPED (surface))
|
||
gdk_wmspec_change_state (TRUE, surface,
|
||
"_NET_WM_STATE_MAXIMIZED_VERT",
|
||
"_NET_WM_STATE_MAXIMIZED_HORZ");
|
||
else
|
||
gdk_synthesize_surface_state (surface,
|
||
0,
|
||
GDK_SURFACE_STATE_MAXIMIZED);
|
||
}
|
||
|
||
static void
|
||
gdk_x11_surface_unmaximize (GdkSurface *surface)
|
||
{
|
||
if (GDK_SURFACE_DESTROYED (surface))
|
||
return;
|
||
|
||
if (GDK_SURFACE_IS_MAPPED (surface))
|
||
gdk_wmspec_change_state (FALSE, surface,
|
||
"_NET_WM_STATE_MAXIMIZED_VERT",
|
||
"_NET_WM_STATE_MAXIMIZED_HORZ");
|
||
else
|
||
gdk_synthesize_surface_state (surface,
|
||
GDK_SURFACE_STATE_MAXIMIZED,
|
||
0);
|
||
}
|
||
|
||
static void
|
||
gdk_x11_surface_apply_fullscreen_mode (GdkSurface *surface)
|
||
{
|
||
if (GDK_SURFACE_DESTROYED (surface))
|
||
return;
|
||
|
||
/* _NET_WM_FULLSCREEN_MONITORS gives an indication to the window manager as
|
||
* to which monitors so span across when the surface is fullscreen, but it's
|
||
* not a state in itself so this would have no effect if the surface is not
|
||
* mapped.
|
||
*/
|
||
|
||
if (GDK_SURFACE_IS_MAPPED (surface))
|
||
{
|
||
XClientMessageEvent xclient;
|
||
gint monitors[4];
|
||
gint i;
|
||
|
||
memset (&xclient, 0, sizeof (xclient));
|
||
xclient.type = ClientMessage;
|
||
xclient.window = GDK_SURFACE_XID (surface);
|
||
xclient.display = GDK_SURFACE_XDISPLAY (surface);
|
||
xclient.format = 32;
|
||
|
||
switch (surface->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 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 (surface),
|
||
&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",
|
||
surface->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 (surface),
|
||
"_NET_WM_FULLSCREEN_MONITORS");
|
||
XSendEvent (GDK_SURFACE_XDISPLAY (surface), GDK_SURFACE_XROOTWIN (surface), False,
|
||
SubstructureRedirectMask | SubstructureNotifyMask,
|
||
(XEvent *)&xclient);
|
||
}
|
||
}
|
||
|
||
static void
|
||
gdk_x11_surface_fullscreen (GdkSurface *surface)
|
||
{
|
||
if (GDK_SURFACE_DESTROYED (surface))
|
||
return;
|
||
|
||
if (GDK_SURFACE_IS_MAPPED (surface))
|
||
{
|
||
gdk_wmspec_change_state (TRUE, surface,
|
||
"_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 (surface->fullscreen_mode == GDK_FULLSCREEN_ON_ALL_MONITORS)
|
||
gdk_x11_surface_apply_fullscreen_mode (surface);
|
||
}
|
||
else
|
||
gdk_synthesize_surface_state (surface,
|
||
0,
|
||
GDK_SURFACE_STATE_FULLSCREEN);
|
||
}
|
||
|
||
static void
|
||
gdk_x11_surface_fullscreen_on_monitor (GdkSurface *surface,
|
||
GdkMonitor *monitor)
|
||
{
|
||
GdkRectangle geom;
|
||
|
||
if (GDK_SURFACE_DESTROYED (surface))
|
||
return;
|
||
|
||
gdk_monitor_get_geometry (monitor, &geom);
|
||
gdk_x11_surface_move (surface, geom.x, geom.y);
|
||
|
||
surface->fullscreen_mode = GDK_FULLSCREEN_ON_CURRENT_MONITOR;
|
||
g_object_notify (G_OBJECT (surface), "fullscreen-mode");
|
||
gdk_x11_surface_fullscreen (surface);
|
||
}
|
||
|
||
static void
|
||
gdk_x11_surface_unfullscreen (GdkSurface *surface)
|
||
{
|
||
if (GDK_SURFACE_DESTROYED (surface))
|
||
return;
|
||
|
||
if (GDK_SURFACE_IS_MAPPED (surface))
|
||
gdk_wmspec_change_state (FALSE, surface,
|
||
"_NET_WM_STATE_FULLSCREEN",
|
||
NULL);
|
||
|
||
else
|
||
gdk_synthesize_surface_state (surface,
|
||
GDK_SURFACE_STATE_FULLSCREEN,
|
||
0);
|
||
}
|
||
|
||
/**
|
||
* gdk_x11_surface_get_group:
|
||
* @surface: The #GdkSurface
|
||
*
|
||
* Returns the group this surface belongs to.
|
||
*
|
||
* Returns: (transfer none): The group of this surface;
|
||
*/
|
||
GdkSurface *
|
||
gdk_x11_surface_get_group (GdkSurface *surface)
|
||
{
|
||
GdkToplevelX11 *toplevel;
|
||
|
||
if (GDK_SURFACE_DESTROYED (surface))
|
||
return NULL;
|
||
|
||
toplevel = _gdk_x11_surface_get_toplevel (surface);
|
||
|
||
return toplevel->group_leader;
|
||
}
|
||
|
||
/**
|
||
* gdk_x11_surface_set_group:
|
||
* @surface: (type GdkX11Surface): a native #GdkSurface
|
||
* @leader: a #GdkSurface
|
||
*
|
||
* Sets the group leader of @surface to be @leader.
|
||
* See the ICCCM for details.
|
||
*/
|
||
void
|
||
gdk_x11_surface_set_group (GdkSurface *surface,
|
||
GdkSurface *leader)
|
||
{
|
||
GdkToplevelX11 *toplevel;
|
||
|
||
g_return_if_fail (GDK_IS_SURFACE (surface));
|
||
g_return_if_fail (leader == NULL || GDK_IS_SURFACE (leader));
|
||
|
||
if (GDK_SURFACE_DESTROYED (surface) ||
|
||
(leader != NULL && GDK_SURFACE_DESTROYED (leader)))
|
||
return;
|
||
|
||
toplevel = _gdk_x11_surface_get_toplevel (surface);
|
||
|
||
if (leader == NULL)
|
||
leader = gdk_display_get_default_group (gdk_surface_get_display (surface));
|
||
|
||
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 (surface, FALSE);
|
||
}
|
||
|
||
static MotifWmHints *
|
||
gdk_surface_get_mwm_hints (GdkSurface *surface)
|
||
{
|
||
GdkDisplay *display;
|
||
Atom hints_atom = None;
|
||
guchar *data;
|
||
Atom type;
|
||
gint format;
|
||
gulong nitems;
|
||
gulong bytes_after;
|
||
|
||
if (GDK_SURFACE_DESTROYED (surface))
|
||
return NULL;
|
||
|
||
display = gdk_surface_get_display (surface);
|
||
|
||
hints_atom = gdk_x11_get_xatom_by_name_for_display (display, _XA_MOTIF_WM_HINTS);
|
||
|
||
XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display), GDK_SURFACE_XID (surface),
|
||
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 *surface,
|
||
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 (surface))
|
||
return;
|
||
|
||
display = gdk_surface_get_display (surface);
|
||
|
||
hints_atom = gdk_x11_get_xatom_by_name_for_display (display, _XA_MOTIF_WM_HINTS);
|
||
|
||
XGetWindowProperty (GDK_SURFACE_XDISPLAY (surface), GDK_SURFACE_XID (surface),
|
||
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 (surface), GDK_SURFACE_XID (surface),
|
||
hints_atom, hints_atom, 32, PropModeReplace,
|
||
(guchar *)hints, sizeof (MotifWmHints)/sizeof (long));
|
||
|
||
if (hints != new_hints)
|
||
XFree (hints);
|
||
}
|
||
|
||
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;
|
||
|
||
static void
|
||
gdk_x11_surface_set_decorations (GdkSurface *surface,
|
||
GdkWMDecoration decorations)
|
||
{
|
||
MotifWmHints hints;
|
||
|
||
if (GDK_SURFACE_DESTROYED (surface))
|
||
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 (surface, &hints);
|
||
}
|
||
|
||
static gboolean
|
||
gdk_x11_surface_get_decorations(GdkSurface *surface,
|
||
GdkWMDecoration *decorations)
|
||
{
|
||
MotifWmHints *hints;
|
||
gboolean result = FALSE;
|
||
|
||
if (GDK_SURFACE_DESTROYED (surface))
|
||
return FALSE;
|
||
|
||
hints = gdk_surface_get_mwm_hints (surface);
|
||
|
||
if (hints)
|
||
{
|
||
if (hints->flags & MWM_HINTS_DECORATIONS)
|
||
{
|
||
if (decorations)
|
||
*decorations = hints->decorations;
|
||
result = TRUE;
|
||
}
|
||
|
||
XFree (hints);
|
||
}
|
||
|
||
return result;
|
||
}
|
||
|
||
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;
|
||
|
||
static void
|
||
gdk_x11_surface_set_functions (GdkSurface *surface,
|
||
GdkWMFunction functions)
|
||
{
|
||
MotifWmHints hints;
|
||
|
||
g_return_if_fail (GDK_IS_SURFACE (surface));
|
||
|
||
if (GDK_SURFACE_DESTROYED (surface))
|
||
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 (surface, &hints);
|
||
}
|
||
|
||
static gboolean
|
||
gdk_x11_surface_get_functions (GdkSurface *surface,
|
||
GdkWMFunction *functions)
|
||
{
|
||
MotifWmHints *hints;
|
||
gboolean result = FALSE;
|
||
|
||
if (GDK_SURFACE_DESTROYED (surface))
|
||
return FALSE;
|
||
|
||
hints = gdk_surface_get_mwm_hints (surface);
|
||
|
||
if (hints)
|
||
{
|
||
if (hints->flags & MWM_HINTS_DECORATIONS)
|
||
{
|
||
if (functions)
|
||
*functions = hints->functions;
|
||
result = TRUE;
|
||
}
|
||
|
||
XFree (hints);
|
||
}
|
||
|
||
return result;
|
||
}
|
||
|
||
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 *surface,
|
||
gint root_x,
|
||
gint root_y,
|
||
gint action,
|
||
gint button)
|
||
{
|
||
GdkX11Surface *impl = GDK_X11_SURFACE (surface);
|
||
XClientMessageEvent xclient;
|
||
|
||
memset (&xclient, 0, sizeof (xclient));
|
||
xclient.type = ClientMessage;
|
||
xclient.window = GDK_SURFACE_XID (surface);
|
||
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->surface_scale;
|
||
xclient.data.l[1] = root_y * impl->surface_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 (surface), False,
|
||
SubstructureRedirectMask | SubstructureNotifyMask,
|
||
(XEvent *)&xclient);
|
||
}
|
||
|
||
static void
|
||
handle_wmspec_button_release (GdkDisplay *display,
|
||
const XEvent *xevent)
|
||
{
|
||
GdkX11Display *display_x11 = GDK_X11_DISPLAY (display);
|
||
GdkSurface *surface;
|
||
|
||
XIEvent *xiev = (XIEvent *) xevent->xcookie.data;
|
||
XIDeviceEvent *xidev = (XIDeviceEvent *) xiev;
|
||
|
||
if (xevent->xany.type == GenericEvent)
|
||
surface = gdk_x11_surface_lookup_for_display (display, xidev->event);
|
||
else
|
||
surface = gdk_x11_surface_lookup_for_display (display, xevent->xany.window);
|
||
|
||
if (display_x11->wm_moveresize_button != 0 && surface != NULL)
|
||
{
|
||
if ((xevent->xany.type == ButtonRelease &&
|
||
xevent->xbutton.button == display_x11->wm_moveresize_button) ||
|
||
(xevent->xany.type == GenericEvent &&
|
||
xiev->evtype == XI_ButtonRelease &&
|
||
xidev->detail == display_x11->wm_moveresize_button))
|
||
{
|
||
display_x11->wm_moveresize_button = 0;
|
||
wmspec_send_message (display, surface, 0, 0, _NET_WM_MOVERESIZE_CANCEL, 0);
|
||
}
|
||
}
|
||
}
|
||
|
||
static void
|
||
wmspec_moveresize (GdkSurface *surface,
|
||
gint direction,
|
||
GdkDevice *device,
|
||
gint button,
|
||
gint root_x,
|
||
gint root_y,
|
||
guint32 timestamp)
|
||
{
|
||
GdkDisplay *display = GDK_SURFACE_DISPLAY (surface);
|
||
|
||
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, surface, root_x, root_y, direction, button);
|
||
}
|
||
|
||
static void
|
||
wmspec_resize_drag (GdkSurface *surface,
|
||
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_toplevel_begin_resize: bad resize edge %d!",
|
||
edge);
|
||
return;
|
||
}
|
||
|
||
wmspec_moveresize (surface, direction, device, button, root_x, root_y, timestamp);
|
||
}
|
||
|
||
typedef struct _MoveResizeData MoveResizeData;
|
||
|
||
struct _MoveResizeData
|
||
{
|
||
GdkDisplay *display;
|
||
|
||
GdkSurface *moveresize_surface;
|
||
GdkSurface *moveresize_emulation_surface;
|
||
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-surface-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_toplevel_get_state (GDK_TOPLEVEL (mv_resize->moveresize_surface));
|
||
|
||
if (state & GDK_SURFACE_STATE_MAXIMIZED)
|
||
return;
|
||
|
||
y = mv_resize->moveresize_orig_y + (y_root - mv_resize->moveresize_y);
|
||
|
||
if (y < 10)
|
||
gdk_x11_surface_maximize (mv_resize->moveresize_surface);
|
||
}
|
||
|
||
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_toplevel_get_state (GDK_TOPLEVEL (mv_resize->moveresize_surface));
|
||
|
||
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_x11_surface_unmaximize (mv_resize->moveresize_surface);
|
||
}
|
||
|
||
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_x11_surface_move_resize (mv_resize->moveresize_surface, TRUE,
|
||
x, y, w, h);
|
||
}
|
||
else
|
||
{
|
||
gint x, y;
|
||
|
||
x = mv_resize->moveresize_orig_x + dx;
|
||
y = mv_resize->moveresize_orig_y + dy;
|
||
|
||
gdk_x11_surface_move (mv_resize->moveresize_surface, x, y);
|
||
}
|
||
}
|
||
|
||
static void
|
||
finish_drag (MoveResizeData *mv_resize)
|
||
{
|
||
gdk_surface_destroy (mv_resize->moveresize_emulation_surface);
|
||
mv_resize->moveresize_emulation_surface = NULL;
|
||
g_clear_object (&mv_resize->moveresize_surface);
|
||
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);
|
||
GdkX11Surface *impl;
|
||
|
||
if (!mv_resize || !mv_resize->moveresize_surface)
|
||
{
|
||
handle_wmspec_button_release (display, event);
|
||
return FALSE;
|
||
}
|
||
|
||
impl = GDK_X11_SURFACE (mv_resize->moveresize_surface);
|
||
|
||
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_surface->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->surface_scale,
|
||
event->xmotion.y_root / impl->surface_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->surface_scale,
|
||
event->xmotion.y_root / impl->surface_scale);
|
||
finish_drag (mv_resize);
|
||
}
|
||
break;
|
||
|
||
case ButtonRelease:
|
||
update_pos (mv_resize,
|
||
event->xbutton.x_root / impl->surface_scale,
|
||
event->xbutton.y_root / impl->surface_scale);
|
||
|
||
if (event->xbutton.button == mv_resize->moveresize_button)
|
||
{
|
||
check_maximize (mv_resize,
|
||
event->xmotion.x_root / impl->surface_scale,
|
||
event->xmotion.y_root / impl->surface_scale);
|
||
finish_drag (mv_resize);
|
||
}
|
||
break;
|
||
|
||
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->surface_scale, xev->root_y / impl->surface_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->surface_scale,
|
||
xev->root_y / impl->surface_scale);
|
||
finish_drag (mv_resize);
|
||
}
|
||
break;
|
||
|
||
case XI_ButtonRelease:
|
||
update_pos (mv_resize, xev->root_x / impl->surface_scale, xev->root_y / impl->surface_scale);
|
||
if (xev->detail == mv_resize->moveresize_button)
|
||
{
|
||
check_maximize (mv_resize,
|
||
xev->root_x / impl->surface_scale,
|
||
xev->root_y / impl->surface_scale);
|
||
finish_drag (mv_resize);
|
||
}
|
||
break;
|
||
default:
|
||
break;
|
||
}
|
||
}
|
||
break;
|
||
|
||
default:
|
||
break;
|
||
|
||
}
|
||
return TRUE;
|
||
}
|
||
|
||
gboolean
|
||
_gdk_x11_moveresize_configure_done (GdkDisplay *display,
|
||
GdkSurface *surface)
|
||
{
|
||
XEvent *tmp_event;
|
||
MoveResizeData *mv_resize = get_move_resize_data (display, FALSE);
|
||
|
||
if (!mv_resize || surface != mv_resize->moveresize_surface)
|
||
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_surface (MoveResizeData *mv_resize,
|
||
guint32 timestamp)
|
||
{
|
||
GdkGrabStatus status;
|
||
GdkRectangle rect = { -100, -100, 1, 1 };
|
||
|
||
g_assert (mv_resize->moveresize_emulation_surface == NULL);
|
||
|
||
mv_resize->moveresize_emulation_surface = gdk_surface_new_temp (mv_resize->display, &rect);
|
||
gdk_x11_surface_show (mv_resize->moveresize_emulation_surface, FALSE);
|
||
|
||
status = gdk_seat_grab (gdk_device_get_seat (mv_resize->device),
|
||
mv_resize->moveresize_emulation_surface,
|
||
GDK_SEAT_CAPABILITY_POINTER, FALSE,
|
||
NULL, NULL, NULL, NULL);
|
||
|
||
if (status != GDK_GRAB_SUCCESS)
|
||
{
|
||
/* If this fails, some other client has grabbed the surface
|
||
* 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
|
||
surface.
|
||
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_surface,
|
||
&mv_resize->moveresize_orig_x,
|
||
&mv_resize->moveresize_orig_y);
|
||
}
|
||
else
|
||
{
|
||
gdk_x11_surface_get_frame_extents (mv_resize->moveresize_surface, &rect);
|
||
gdk_surface_get_geometry (mv_resize->moveresize_surface,
|
||
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 *surface,
|
||
GdkSurfaceEdge edge,
|
||
GdkDevice *device,
|
||
gint button,
|
||
gint root_x,
|
||
gint root_y,
|
||
guint32 timestamp)
|
||
{
|
||
MoveResizeData *mv_resize = get_move_resize_data (GDK_SURFACE_DISPLAY (surface), TRUE);
|
||
|
||
if (mv_resize->moveresize_surface != 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_surface = g_object_ref (surface);
|
||
|
||
mv_resize->moveresize_orig_width = gdk_surface_get_width (surface);
|
||
mv_resize->moveresize_orig_height = gdk_surface_get_height (surface);
|
||
|
||
mv_resize->moveresize_geom_mask = 0;
|
||
gdk_surface_get_geometry_hints (surface,
|
||
&mv_resize->moveresize_geometry,
|
||
&mv_resize->moveresize_geom_mask);
|
||
|
||
calculate_unmoving_origin (mv_resize);
|
||
|
||
create_moveresize_surface (mv_resize, timestamp);
|
||
}
|
||
|
||
static void
|
||
emulate_move_drag (GdkSurface *surface,
|
||
GdkDevice *device,
|
||
gint button,
|
||
gint root_x,
|
||
gint root_y,
|
||
guint32 timestamp)
|
||
{
|
||
MoveResizeData *mv_resize = get_move_resize_data (GDK_SURFACE_DISPLAY (surface), TRUE);
|
||
|
||
if (mv_resize->moveresize_surface != 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_surface = g_object_ref (surface);
|
||
|
||
calculate_unmoving_origin (mv_resize);
|
||
|
||
create_moveresize_surface (mv_resize, timestamp);
|
||
}
|
||
|
||
static gboolean
|
||
_should_perform_ewmh_drag (GdkSurface *surface,
|
||
GdkDevice *device)
|
||
{
|
||
GdkPointerSurfaceInfo *info;
|
||
GdkDisplay *display;
|
||
|
||
display = gdk_surface_get_display (surface);
|
||
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 (surface),
|
||
g_intern_static_string ("_NET_WM_MOVERESIZE")))
|
||
return TRUE;
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
static void
|
||
gdk_x11_toplevel_begin_resize (GdkToplevel *toplevel,
|
||
GdkSurfaceEdge edge,
|
||
GdkDevice *device,
|
||
int button,
|
||
double x,
|
||
double y,
|
||
guint32 timestamp)
|
||
{
|
||
GdkSurface *surface = GDK_SURFACE (toplevel);
|
||
int root_x, root_y;
|
||
|
||
if (GDK_SURFACE_DESTROYED (surface))
|
||
return;
|
||
|
||
gdk_x11_surface_get_root_coords (surface, x, y, &root_x, &root_y);
|
||
|
||
/* Avoid EWMH for touch devices */
|
||
if (_should_perform_ewmh_drag (surface, device))
|
||
wmspec_resize_drag (surface, edge, device, button, root_x, root_y, timestamp);
|
||
else
|
||
emulate_resize_drag (surface, edge, device, button, root_x, root_y, timestamp);
|
||
}
|
||
|
||
static void
|
||
gdk_x11_toplevel_begin_move (GdkToplevel *toplevel,
|
||
GdkDevice *device,
|
||
int button,
|
||
double x,
|
||
double y,
|
||
guint32 timestamp)
|
||
{
|
||
GdkSurface *surface = GDK_SURFACE (toplevel);
|
||
int root_x, root_y;
|
||
gint direction;
|
||
|
||
if (GDK_SURFACE_DESTROYED (surface))
|
||
return;
|
||
|
||
if (button == 0)
|
||
direction = _NET_WM_MOVERESIZE_MOVE_KEYBOARD;
|
||
else
|
||
direction = _NET_WM_MOVERESIZE_MOVE;
|
||
|
||
gdk_x11_surface_get_root_coords (surface, x, y, &root_x, &root_y);
|
||
|
||
/* Avoid EWMH for touch devices */
|
||
if (_should_perform_ewmh_drag (surface, device))
|
||
wmspec_moveresize (surface, direction, device, button, root_x, root_y, timestamp);
|
||
else
|
||
emulate_move_drag (surface, device, button, root_x, root_y, timestamp);
|
||
}
|
||
|
||
static gboolean
|
||
gdk_x11_surface_beep (GdkSurface *surface)
|
||
{
|
||
GdkDisplay *display;
|
||
|
||
display = GDK_SURFACE_DISPLAY (surface);
|
||
|
||
#ifdef HAVE_XKB
|
||
if (GDK_X11_DISPLAY (display)->use_xkb)
|
||
{
|
||
XkbBell (GDK_DISPLAY_XDISPLAY (display),
|
||
GDK_SURFACE_XID (surface),
|
||
0,
|
||
None);
|
||
return TRUE;
|
||
}
|
||
#endif
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
void
|
||
gdk_x11_surface_set_opacity (GdkSurface *surface,
|
||
double opacity)
|
||
{
|
||
GdkDisplay *display;
|
||
gulong cardinal;
|
||
|
||
g_return_if_fail (GDK_IS_SURFACE (surface));
|
||
|
||
if (GDK_SURFACE_DESTROYED (surface))
|
||
return;
|
||
|
||
display = gdk_surface_get_display (surface);
|
||
|
||
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 (surface),
|
||
gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_WINDOW_OPACITY"));
|
||
else
|
||
XChangeProperty (GDK_DISPLAY_XDISPLAY (display),
|
||
GDK_SURFACE_XID (surface),
|
||
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:
|
||
* @surface: (type GdkX11Surface): a #GdkSurface, used for communication
|
||
* with the server. The surface 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 *surface)
|
||
{
|
||
Display *xdisplay;
|
||
Window xwindow;
|
||
guchar c = 'a';
|
||
XEvent xevent;
|
||
Atom timestamp_prop_atom;
|
||
|
||
g_return_val_if_fail (GDK_IS_SURFACE (surface), 0);
|
||
g_return_val_if_fail (!GDK_SURFACE_DESTROYED (surface), 0);
|
||
|
||
xdisplay = GDK_SURFACE_XDISPLAY (surface);
|
||
xwindow = GDK_SURFACE_XID (surface);
|
||
timestamp_prop_atom =
|
||
gdk_x11_get_xatom_by_name_for_display (GDK_SURFACE_DISPLAY (surface),
|
||
"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:
|
||
* @surface: (type GdkX11Surface): a native #GdkSurface.
|
||
*
|
||
* Returns the X resource (surface) belonging to a #GdkSurface.
|
||
*
|
||
* Returns: the ID of @drawable’s X resource.
|
||
**/
|
||
XID
|
||
gdk_x11_surface_get_xid (GdkSurface *surface)
|
||
{
|
||
return GDK_X11_SURFACE (surface)->xid;
|
||
}
|
||
|
||
static gint
|
||
gdk_x11_surface_get_scale_factor (GdkSurface *surface)
|
||
{
|
||
GdkX11Surface *impl = GDK_X11_SURFACE (surface);
|
||
|
||
if (GDK_SURFACE_DESTROYED (surface))
|
||
return 1;
|
||
|
||
return impl->surface_scale;
|
||
}
|
||
|
||
/**
|
||
* gdk_x11_surface_set_frame_sync_enabled:
|
||
* @surface: (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 surface.
|
||
* Normally frame synchronziation will be enabled or disabled based on whether
|
||
* the system has a compositor that supports frame synchronization, but if
|
||
* the surface is not directly managed by the window manager, then frame
|
||
* synchronziation may need to be disabled. This is the case for a surface
|
||
* embedded via the XEMBED protocol.
|
||
*/
|
||
void
|
||
gdk_x11_surface_set_frame_sync_enabled (GdkSurface *surface,
|
||
gboolean frame_sync_enabled)
|
||
{
|
||
GDK_X11_SURFACE (surface)->frame_sync_enabled = FALSE;
|
||
}
|
||
|
||
static void
|
||
gdk_x11_surface_set_opaque_region (GdkSurface *surface,
|
||
cairo_region_t *region)
|
||
{
|
||
GdkX11Surface *impl = GDK_X11_SURFACE (surface);
|
||
GdkDisplay *display;
|
||
int nitems;
|
||
gulong *data;
|
||
|
||
if (GDK_SURFACE_DESTROYED (surface))
|
||
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->surface_scale;
|
||
data[i*4+1] = rect.y * impl->surface_scale;
|
||
data[i*4+2] = rect.width * impl->surface_scale;
|
||
data[i*4+3] = rect.height * impl->surface_scale;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
nitems = 0;
|
||
data = NULL;
|
||
}
|
||
|
||
display = gdk_surface_get_display (surface);
|
||
|
||
XChangeProperty (GDK_DISPLAY_XDISPLAY (display),
|
||
GDK_SURFACE_XID (surface),
|
||
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 *surface,
|
||
GdkEvent *event)
|
||
{
|
||
GdkX11Surface *impl = GDK_X11_SURFACE (surface);
|
||
GdkDisplay *display = GDK_SURFACE_DISPLAY (surface);
|
||
GdkDevice *device;
|
||
int device_id;
|
||
double x, y;
|
||
int x_root, y_root;
|
||
XClientMessageEvent xclient = { 0 };
|
||
|
||
GdkEventType event_type = gdk_event_get_event_type (event);
|
||
|
||
switch ((guint) event_type)
|
||
{
|
||
case GDK_BUTTON_PRESS:
|
||
case GDK_BUTTON_RELEASE:
|
||
break;
|
||
default:
|
||
return FALSE;
|
||
}
|
||
|
||
if (!gdk_x11_screen_supports_net_wm_hint (GDK_SURFACE_SCREEN (surface),
|
||
g_intern_static_string ("_GTK_SHOW_WINDOW_MENU")))
|
||
return FALSE;
|
||
|
||
gdk_event_get_position (event, &x, &y);
|
||
gdk_x11_surface_get_root_coords (surface, x, y, &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 (surface);
|
||
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->surface_scale;
|
||
xclient.data.l[2] = y_root * impl->surface_scale;
|
||
xclient.format = 32;
|
||
|
||
XSendEvent (GDK_DISPLAY_XDISPLAY (display), GDK_SURFACE_XROOTWIN (surface), False,
|
||
SubstructureRedirectMask | SubstructureNotifyMask,
|
||
(XEvent *)&xclient);
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
static void
|
||
gdk_x11_surface_class_init (GdkX11SurfaceClass *klass)
|
||
{
|
||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||
GdkSurfaceClass *impl_class = GDK_SURFACE_CLASS (klass);
|
||
|
||
object_class->finalize = gdk_x11_surface_finalize;
|
||
|
||
impl_class->hide = gdk_x11_surface_hide;
|
||
impl_class->get_geometry = gdk_x11_surface_get_geometry;
|
||
impl_class->get_root_coords = gdk_x11_surface_get_root_coords;
|
||
impl_class->get_device_state = gdk_x11_surface_get_device_state;
|
||
impl_class->set_input_region = gdk_x11_surface_set_input_region;
|
||
impl_class->destroy = gdk_x11_surface_destroy;
|
||
impl_class->beep = gdk_x11_surface_beep;
|
||
|
||
impl_class->destroy_notify = gdk_x11_surface_destroy_notify;
|
||
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->create_gl_context = gdk_x11_surface_create_gl_context;
|
||
impl_class->get_unscaled_size = gdk_x11_surface_get_unscaled_size;
|
||
}
|
||
|
||
#define LAST_PROP 1
|
||
|
||
typedef struct {
|
||
GdkX11Surface parent_instance;
|
||
} GdkX11Popup;
|
||
|
||
typedef struct {
|
||
GdkX11SurfaceClass parent_class;
|
||
} GdkX11PopupClass;
|
||
|
||
static void gdk_x11_popup_iface_init (GdkPopupInterface *iface);
|
||
|
||
G_DEFINE_TYPE_WITH_CODE (GdkX11Popup, gdk_x11_popup, GDK_TYPE_X11_SURFACE,
|
||
G_IMPLEMENT_INTERFACE (GDK_TYPE_POPUP,
|
||
gdk_x11_popup_iface_init))
|
||
|
||
static void
|
||
gdk_x11_popup_init (GdkX11Popup *popup)
|
||
{
|
||
}
|
||
|
||
static void
|
||
gdk_x11_popup_get_property (GObject *object,
|
||
guint prop_id,
|
||
GValue *value,
|
||
GParamSpec *pspec)
|
||
{
|
||
GdkSurface *surface = GDK_SURFACE (object);
|
||
|
||
switch (prop_id)
|
||
{
|
||
case LAST_PROP + GDK_POPUP_PROP_PARENT:
|
||
g_value_set_object (value, surface->parent);
|
||
break;
|
||
|
||
case LAST_PROP + GDK_POPUP_PROP_AUTOHIDE:
|
||
g_value_set_boolean (value, surface->autohide);
|
||
break;
|
||
|
||
default:
|
||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||
break;
|
||
}
|
||
}
|
||
|
||
static void
|
||
gdk_x11_popup_set_property (GObject *object,
|
||
guint prop_id,
|
||
const GValue *value,
|
||
GParamSpec *pspec)
|
||
{
|
||
GdkSurface *surface = GDK_SURFACE (object);
|
||
|
||
switch (prop_id)
|
||
{
|
||
case LAST_PROP + GDK_POPUP_PROP_PARENT:
|
||
surface->parent = g_value_dup_object (value);
|
||
if (surface->parent != NULL)
|
||
surface->parent->children = g_list_prepend (surface->parent->children, surface);
|
||
break;
|
||
|
||
case LAST_PROP + GDK_POPUP_PROP_AUTOHIDE:
|
||
surface->autohide = g_value_get_boolean (value);
|
||
break;
|
||
|
||
default:
|
||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||
break;
|
||
}
|
||
}
|
||
|
||
|
||
static void
|
||
gdk_x11_popup_class_init (GdkX11PopupClass *class)
|
||
{
|
||
GObjectClass *object_class = G_OBJECT_CLASS (class);
|
||
|
||
object_class->get_property = gdk_x11_popup_get_property;
|
||
object_class->set_property = gdk_x11_popup_set_property;
|
||
|
||
gdk_popup_install_properties (object_class, 1);
|
||
}
|
||
|
||
static gboolean
|
||
gdk_x11_popup_present (GdkPopup *popup,
|
||
int width,
|
||
int height,
|
||
GdkPopupLayout *layout)
|
||
{
|
||
return gdk_x11_surface_present_popup (GDK_SURFACE (popup), width, height, layout);
|
||
}
|
||
|
||
static GdkGravity
|
||
gdk_x11_popup_get_surface_anchor (GdkPopup *popup)
|
||
{
|
||
return GDK_SURFACE (popup)->popup.surface_anchor;
|
||
}
|
||
|
||
static GdkGravity
|
||
gdk_x11_popup_get_rect_anchor (GdkPopup *popup)
|
||
{
|
||
return GDK_SURFACE (popup)->popup.rect_anchor;
|
||
}
|
||
|
||
static int
|
||
gdk_x11_popup_get_position_x (GdkPopup *popup)
|
||
{
|
||
return GDK_SURFACE (popup)->x;
|
||
}
|
||
|
||
static int
|
||
gdk_x11_popup_get_position_y (GdkPopup *popup)
|
||
{
|
||
return GDK_SURFACE (popup)->y;
|
||
}
|
||
|
||
static void
|
||
gdk_x11_popup_iface_init (GdkPopupInterface *iface)
|
||
{
|
||
iface->present = gdk_x11_popup_present;
|
||
iface->get_surface_anchor = gdk_x11_popup_get_surface_anchor;
|
||
iface->get_rect_anchor = gdk_x11_popup_get_rect_anchor;
|
||
iface->get_position_x = gdk_x11_popup_get_position_x;
|
||
iface->get_position_y = gdk_x11_popup_get_position_y;
|
||
}
|
||
|
||
typedef struct {
|
||
GdkX11Surface parent_instance;
|
||
} GdkX11Toplevel;
|
||
|
||
typedef struct {
|
||
GdkX11SurfaceClass parent_class;
|
||
} GdkX11ToplevelClass;
|
||
|
||
static void gdk_x11_toplevel_iface_init (GdkToplevelInterface *iface);
|
||
|
||
G_DEFINE_TYPE_WITH_CODE (GdkX11Toplevel, gdk_x11_toplevel, GDK_TYPE_X11_SURFACE,
|
||
G_IMPLEMENT_INTERFACE (GDK_TYPE_TOPLEVEL,
|
||
gdk_x11_toplevel_iface_init))
|
||
|
||
static void
|
||
gdk_x11_toplevel_init (GdkX11Toplevel *toplevel)
|
||
{
|
||
}
|
||
static void
|
||
gdk_x11_toplevel_set_property (GObject *object,
|
||
guint prop_id,
|
||
const GValue *value,
|
||
GParamSpec *pspec)
|
||
{
|
||
GdkSurface *surface = GDK_SURFACE (object);
|
||
|
||
switch (prop_id)
|
||
{
|
||
case LAST_PROP + GDK_TOPLEVEL_PROP_TITLE:
|
||
gdk_x11_surface_set_title (surface, g_value_get_string (value));
|
||
g_object_notify_by_pspec (G_OBJECT (surface), pspec);
|
||
break;
|
||
|
||
case LAST_PROP + GDK_TOPLEVEL_PROP_STARTUP_ID:
|
||
gdk_x11_surface_set_startup_id (surface, g_value_get_string (value));
|
||
g_object_notify_by_pspec (G_OBJECT (surface), pspec);
|
||
break;
|
||
|
||
case LAST_PROP + GDK_TOPLEVEL_PROP_TRANSIENT_FOR:
|
||
gdk_x11_surface_set_transient_for (surface, g_value_get_object (value));
|
||
g_object_notify_by_pspec (G_OBJECT (surface), pspec);
|
||
break;
|
||
|
||
case LAST_PROP + GDK_TOPLEVEL_PROP_MODAL:
|
||
gdk_x11_surface_set_modal_hint (surface, g_value_get_boolean (value));
|
||
g_object_notify_by_pspec (G_OBJECT (surface), pspec);
|
||
break;
|
||
|
||
case LAST_PROP + GDK_TOPLEVEL_PROP_ICON_LIST:
|
||
gdk_x11_surface_set_icon_list (surface, g_value_get_pointer (value));
|
||
g_object_notify_by_pspec (G_OBJECT (surface), pspec);
|
||
break;
|
||
|
||
case LAST_PROP + GDK_TOPLEVEL_PROP_DECORATED:
|
||
gdk_x11_surface_set_decorations (surface, g_value_get_boolean (value) ? GDK_DECOR_ALL : 0);
|
||
g_object_notify_by_pspec (G_OBJECT (surface), pspec);
|
||
break;
|
||
|
||
case LAST_PROP + GDK_TOPLEVEL_PROP_DELETABLE:
|
||
gdk_x11_surface_set_functions (surface, g_value_get_boolean (value) ? GDK_FUNC_ALL : GDK_FUNC_ALL | GDK_FUNC_CLOSE);
|
||
g_object_notify_by_pspec (G_OBJECT (surface), pspec);
|
||
break;
|
||
|
||
case LAST_PROP + GDK_TOPLEVEL_PROP_FULLSCREEN_MODE:
|
||
surface->fullscreen_mode = g_value_get_enum (value);
|
||
gdk_x11_surface_apply_fullscreen_mode (surface);
|
||
g_object_notify_by_pspec (G_OBJECT (surface), pspec);
|
||
break;
|
||
|
||
case LAST_PROP + GDK_TOPLEVEL_PROP_SHORTCUTS_INHIBITED:
|
||
break;
|
||
|
||
default:
|
||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||
break;
|
||
}
|
||
}
|
||
|
||
static void
|
||
gdk_x11_toplevel_get_property (GObject *object,
|
||
guint prop_id,
|
||
GValue *value,
|
||
GParamSpec *pspec)
|
||
{
|
||
GdkSurface *surface = GDK_SURFACE (object);
|
||
|
||
switch (prop_id)
|
||
{
|
||
case LAST_PROP + GDK_TOPLEVEL_PROP_STATE:
|
||
g_value_set_flags (value, surface->state);
|
||
break;
|
||
|
||
case LAST_PROP + GDK_TOPLEVEL_PROP_TITLE:
|
||
g_value_set_string (value, "");
|
||
break;
|
||
|
||
case LAST_PROP + GDK_TOPLEVEL_PROP_STARTUP_ID:
|
||
g_value_set_string (value, "");
|
||
break;
|
||
|
||
case LAST_PROP + GDK_TOPLEVEL_PROP_TRANSIENT_FOR:
|
||
g_value_set_object (value, surface->transient_for);
|
||
break;
|
||
|
||
case LAST_PROP + GDK_TOPLEVEL_PROP_MODAL:
|
||
g_value_set_boolean (value, surface->modal_hint);
|
||
break;
|
||
|
||
case LAST_PROP + GDK_TOPLEVEL_PROP_ICON_LIST:
|
||
g_value_set_pointer (value, NULL);
|
||
break;
|
||
|
||
case LAST_PROP + GDK_TOPLEVEL_PROP_DECORATED:
|
||
{
|
||
GdkWMDecoration decorations = GDK_DECOR_ALL;
|
||
gdk_x11_surface_get_decorations (surface, &decorations);
|
||
g_value_set_boolean (value, decorations != 0);
|
||
}
|
||
break;
|
||
|
||
case LAST_PROP + GDK_TOPLEVEL_PROP_DELETABLE:
|
||
{
|
||
GdkWMFunction functions = GDK_FUNC_ALL;
|
||
gdk_x11_surface_get_functions (surface, &functions);
|
||
g_value_set_boolean (value, functions == GDK_FUNC_ALL);
|
||
}
|
||
break;
|
||
|
||
case LAST_PROP + GDK_TOPLEVEL_PROP_FULLSCREEN_MODE:
|
||
g_value_set_enum (value, surface->fullscreen_mode);
|
||
break;
|
||
|
||
case LAST_PROP + GDK_TOPLEVEL_PROP_SHORTCUTS_INHIBITED:
|
||
g_value_set_boolean (value, surface->shortcuts_inhibited);
|
||
break;
|
||
|
||
default:
|
||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||
break;
|
||
}
|
||
}
|
||
|
||
static void
|
||
gdk_x11_toplevel_class_init (GdkX11ToplevelClass *class)
|
||
{
|
||
GObjectClass *object_class = G_OBJECT_CLASS (class);
|
||
|
||
object_class->get_property = gdk_x11_toplevel_get_property;
|
||
object_class->set_property = gdk_x11_toplevel_set_property;
|
||
|
||
gdk_toplevel_install_properties (object_class, LAST_PROP);
|
||
}
|
||
|
||
static gboolean
|
||
gdk_x11_toplevel_present (GdkToplevel *toplevel,
|
||
int width,
|
||
int height,
|
||
GdkToplevelLayout *layout)
|
||
{
|
||
GdkSurface *surface = GDK_SURFACE (toplevel);
|
||
GdkGeometry geometry;
|
||
GdkSurfaceHints mask;
|
||
gboolean was_mapped;
|
||
|
||
gdk_x11_surface_unminimize (surface);
|
||
|
||
if (gdk_toplevel_layout_get_resizable (layout))
|
||
{
|
||
geometry.min_width = gdk_toplevel_layout_get_min_width (layout);
|
||
geometry.min_height = gdk_toplevel_layout_get_min_height (layout);
|
||
mask = GDK_HINT_MIN_SIZE;
|
||
}
|
||
else
|
||
{
|
||
geometry.max_width = geometry.min_width = width;
|
||
geometry.max_height = geometry.min_height = height;
|
||
mask = GDK_HINT_MIN_SIZE | GDK_HINT_MAX_SIZE;
|
||
}
|
||
gdk_x11_surface_set_geometry_hints (surface, &geometry, mask);
|
||
gdk_surface_constrain_size (&geometry, mask, width, height, &width, &height);
|
||
gdk_x11_surface_toplevel_resize (surface, width, height);
|
||
|
||
if (gdk_toplevel_layout_get_maximized (layout))
|
||
gdk_x11_surface_maximize (surface);
|
||
else
|
||
gdk_x11_surface_unmaximize (surface);
|
||
|
||
if (gdk_toplevel_layout_get_fullscreen (layout))
|
||
{
|
||
GdkMonitor *monitor = gdk_toplevel_layout_get_fullscreen_monitor (layout);
|
||
if (monitor)
|
||
gdk_x11_surface_fullscreen_on_monitor (surface, monitor);
|
||
else
|
||
gdk_x11_surface_fullscreen (surface);
|
||
}
|
||
else
|
||
gdk_x11_surface_unfullscreen (surface);
|
||
|
||
if (surface->destroyed)
|
||
return TRUE;
|
||
|
||
was_mapped = GDK_SURFACE_IS_MAPPED (surface);
|
||
|
||
if (!was_mapped)
|
||
gdk_synthesize_surface_state (surface, GDK_SURFACE_STATE_WITHDRAWN, 0);
|
||
|
||
gdk_x11_surface_show (surface, was_mapped);
|
||
|
||
if (!was_mapped)
|
||
gdk_surface_invalidate_rect (surface, NULL);
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
static gboolean
|
||
gdk_x11_toplevel_minimize (GdkToplevel *toplevel)
|
||
{
|
||
gdk_x11_surface_minimize (GDK_SURFACE (toplevel));
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
static gboolean
|
||
gdk_x11_toplevel_lower (GdkToplevel *toplevel)
|
||
{
|
||
gdk_x11_surface_lower (GDK_SURFACE (toplevel));
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
static void
|
||
gdk_x11_toplevel_focus (GdkToplevel *toplevel,
|
||
guint32 timestamp)
|
||
{
|
||
gdk_x11_surface_focus (GDK_SURFACE (toplevel), timestamp);
|
||
}
|
||
|
||
static gboolean
|
||
gdk_x11_toplevel_show_window_menu (GdkToplevel *toplevel,
|
||
GdkEvent *event)
|
||
{
|
||
return gdk_x11_surface_show_window_menu (GDK_SURFACE (toplevel), event);
|
||
}
|
||
|
||
static gboolean
|
||
gdk_x11_toplevel_supports_edge_constraints (GdkToplevel *toplevel)
|
||
{
|
||
return gdk_x11_surface_supports_edge_constraints (GDK_SURFACE (toplevel));
|
||
}
|
||
|
||
static void
|
||
gdk_x11_toplevel_inhibit_system_shortcuts (GdkToplevel *toplevel,
|
||
GdkEvent *gdk_event)
|
||
{
|
||
GdkSurface *surface = GDK_SURFACE (toplevel);
|
||
GdkSeat *gdk_seat;
|
||
GdkGrabStatus status;
|
||
|
||
if (surface->shortcuts_inhibited)
|
||
return; /* Already inhibited */
|
||
|
||
if (!(surface->state & GDK_SURFACE_STATE_FOCUSED))
|
||
return;
|
||
|
||
gdk_seat = gdk_surface_get_seat_from_event (surface, gdk_event);
|
||
|
||
if (!(gdk_seat_get_capabilities (gdk_seat) & GDK_SEAT_CAPABILITY_KEYBOARD))
|
||
return;
|
||
|
||
status = gdk_seat_grab (gdk_seat, surface, GDK_SEAT_CAPABILITY_KEYBOARD,
|
||
TRUE, NULL, gdk_event, NULL, NULL);
|
||
|
||
if (status != GDK_GRAB_SUCCESS)
|
||
return;
|
||
|
||
surface->shortcuts_inhibited = TRUE;
|
||
surface->current_shortcuts_inhibited_seat = gdk_seat;
|
||
g_object_notify (G_OBJECT (toplevel), "shortcuts-inhibited");
|
||
}
|
||
|
||
static void
|
||
gdk_x11_toplevel_restore_system_shortcuts (GdkToplevel *toplevel)
|
||
{
|
||
GdkSurface *surface = GDK_SURFACE (toplevel);
|
||
GdkSeat *gdk_seat;
|
||
|
||
if (!surface->shortcuts_inhibited)
|
||
return; /* Not inhibited */
|
||
|
||
gdk_seat = surface->current_shortcuts_inhibited_seat;
|
||
gdk_seat_ungrab (gdk_seat);
|
||
surface->current_shortcuts_inhibited_seat = NULL;
|
||
|
||
surface->shortcuts_inhibited = FALSE;
|
||
g_object_notify (G_OBJECT (toplevel), "shortcuts-inhibited");
|
||
}
|
||
|
||
static void
|
||
gdk_x11_toplevel_state_callback (GdkSurface *surface)
|
||
{
|
||
if (surface->state & GDK_SURFACE_STATE_FOCUSED)
|
||
return;
|
||
|
||
if (surface->shortcuts_inhibited)
|
||
gdk_x11_toplevel_restore_system_shortcuts (GDK_TOPLEVEL (surface));
|
||
}
|
||
|
||
static gboolean
|
||
gdk_x11_toplevel_event_callback (GdkSurface *surface,
|
||
GdkEvent *gdk_event)
|
||
{
|
||
GdkSeat *gdk_seat;
|
||
|
||
if (!surface->shortcuts_inhibited)
|
||
return FALSE;
|
||
|
||
if (gdk_event_get_event_type (gdk_event) != GDK_GRAB_BROKEN)
|
||
return FALSE;
|
||
|
||
gdk_seat = gdk_surface_get_seat_from_event (surface, gdk_event);
|
||
if (gdk_seat != surface->current_shortcuts_inhibited_seat)
|
||
return FALSE;
|
||
|
||
surface->current_shortcuts_inhibited_seat = NULL;
|
||
surface->shortcuts_inhibited = FALSE;
|
||
g_object_notify (G_OBJECT (surface), "shortcuts-inhibited");
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
static void
|
||
gdk_x11_toplevel_iface_init (GdkToplevelInterface *iface)
|
||
{
|
||
iface->present = gdk_x11_toplevel_present;
|
||
iface->minimize = gdk_x11_toplevel_minimize;
|
||
iface->lower = gdk_x11_toplevel_lower;
|
||
iface->focus = gdk_x11_toplevel_focus;
|
||
iface->show_window_menu = gdk_x11_toplevel_show_window_menu;
|
||
iface->supports_edge_constraints = gdk_x11_toplevel_supports_edge_constraints;
|
||
iface->inhibit_system_shortcuts = gdk_x11_toplevel_inhibit_system_shortcuts;
|
||
iface->restore_system_shortcuts = gdk_x11_toplevel_restore_system_shortcuts;
|
||
iface->begin_resize = gdk_x11_toplevel_begin_resize;
|
||
iface->begin_move = gdk_x11_toplevel_begin_move;
|
||
}
|
||
|
||
typedef struct {
|
||
GdkX11Surface parent_instance;
|
||
} GdkX11DragSurface;
|
||
|
||
typedef struct {
|
||
GdkX11SurfaceClass parent_class;
|
||
} GdkX11DragSurfaceClass;
|
||
|
||
static void gdk_x11_drag_surface_iface_init (GdkDragSurfaceInterface *iface);
|
||
|
||
G_DEFINE_TYPE_WITH_CODE (GdkX11DragSurface, gdk_x11_drag_surface, GDK_TYPE_X11_SURFACE,
|
||
G_IMPLEMENT_INTERFACE (GDK_TYPE_DRAG_SURFACE,
|
||
gdk_x11_drag_surface_iface_init))
|
||
|
||
static void
|
||
gdk_x11_drag_surface_init (GdkX11DragSurface *surface)
|
||
{
|
||
}
|
||
|
||
static void
|
||
gdk_x11_drag_surface_class_init (GdkX11DragSurfaceClass *class)
|
||
{
|
||
}
|
||
|
||
static gboolean
|
||
gdk_x11_drag_surface_present (GdkDragSurface *drag_surface,
|
||
int width,
|
||
int height)
|
||
{
|
||
GdkSurface *surface = GDK_SURFACE (drag_surface);
|
||
|
||
gdk_x11_surface_toplevel_resize (surface, width, height);
|
||
gdk_synthesize_surface_state (surface, GDK_SURFACE_STATE_WITHDRAWN, 0);
|
||
gdk_x11_surface_show (surface, FALSE);
|
||
gdk_surface_invalidate_rect (surface, NULL);
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
static void
|
||
gdk_x11_drag_surface_iface_init (GdkDragSurfaceInterface *iface)
|
||
{
|
||
iface->present = gdk_x11_drag_surface_present;
|
||
}
|