diff options
author | Havoc Pennington <hp@redhat.com> | 2001-02-27 20:40:15 +0000 |
---|---|---|
committer | Havoc Pennington <hp@src.gnome.org> | 2001-02-27 20:40:15 +0000 |
commit | 75d79abf9801195a0241b02ed7974fafa8278f5c (patch) | |
tree | 750b8f34aca36f06e24ff6ec060353d3e785289f /gdk | |
parent | af03d3855dc4f6eefe713c69dce355de6f26d4ab (diff) | |
download | gtk+-75d79abf9801195a0241b02ed7974fafa8278f5c.tar.gz |
test the window state stuff
2001-02-26 Havoc Pennington <hp@redhat.com>
* gtk/testgtk.c: test the window state stuff
* gtk/gtkwindow.c (gtk_window_present): new function, makes a
window come to the user's attention as if it were just created
(gtk_window_iconify): new function
(gtk_window_deiconify): new function
(gtk_window_stick): new function
(gtk_window_unstick): new function
(gtk_window_maximize): new function
(gtk_window_unmaximize): new function
* gtk/gtkwidget.h, gtk/gtkwidget.c: add window_state_event signal
* gtk/gtkmain.c (gtk_main_do_event): handle GDK_WINDOW_STATE
* gdk/x11/gdkevents-x11.c: create window state events when
appropriate
(gdk_wmspec_supported): new function
* gdk/x11/gdkwindow-x11.c (gdk_window_iconify): handle
iconification before showing the window
(gdk_window_deiconify): new function
(gdk_window_stick): new function
(gdk_window_unstick): new function
(gdk_window_maximize): new function
(gdk_window_unmaximize): new function
* gdk/gdkwindow.c: store the window state in the window;
change to using the GDK_WINDOW_STATE_WITHDRAWN flag instead of
window->mapped.
(gdk_window_get_state): return the current window state
* gdk/gdkevents.c (gdk_event_get_time): handle GDK_WINDOW_STATE
(gdk_event_get_state): handle GDK_WINDOW_STATE
(gdk_synthesize_window_state): function to create the window state
events
* gdk/gdkevents.h (struct _GdkEventWindowState): new type of
event, for changes to "window state" such as maximized, sticky,
etc.
* gdk/x11/gdkwindow-x11.c (gdk_window_focus): new function,
focuses a window
* gdk/x11/gdkmain-x11.c (_gdk_wmspec_supported): new function,
finds out if we support a given WM spec hint
Diffstat (limited to 'gdk')
-rw-r--r-- | gdk/gdkevents.c | 55 | ||||
-rw-r--r-- | gdk/gdkevents.h | 24 | ||||
-rw-r--r-- | gdk/gdkinternals.h | 5 | ||||
-rw-r--r-- | gdk/gdkwindow.c | 34 | ||||
-rw-r--r-- | gdk/gdkwindow.h | 14 | ||||
-rw-r--r-- | gdk/x11/gdkevents-x11.c | 263 | ||||
-rw-r--r-- | gdk/x11/gdkgeometry-x11.c | 7 | ||||
-rw-r--r-- | gdk/x11/gdkmain-x11.c | 1 | ||||
-rw-r--r-- | gdk/x11/gdkwindow-x11.c | 413 | ||||
-rw-r--r-- | gdk/x11/gdkx.h | 3 |
10 files changed, 792 insertions, 27 deletions
diff --git a/gdk/gdkevents.c b/gdk/gdkevents.c index 8308bbeccd..203f7f4e24 100644 --- a/gdk/gdkevents.c +++ b/gdk/gdkevents.c @@ -454,6 +454,7 @@ gdk_event_get_time (GdkEvent *event) case GDK_EXPOSE: case GDK_MAP: case GDK_UNMAP: + case GDK_WINDOW_STATE: /* return current time */ break; } @@ -529,6 +530,7 @@ gdk_event_get_state (GdkEvent *event, case GDK_EXPOSE: case GDK_MAP: case GDK_UNMAP: + case GDK_WINDOW_STATE: /* no state field */ break; } @@ -803,3 +805,56 @@ gdk_event_button_generate (GdkEvent *event) button_number[0] = event->button.button; } } + + +void +gdk_synthesize_window_state (GdkWindow *window, + GdkWindowState unset_flags, + GdkWindowState set_flags) +{ + GdkEventWindowState temp_event; + GdkWindowState old; + + g_return_if_fail (window != NULL); + + temp_event.window = window; + temp_event.type = GDK_WINDOW_STATE; + temp_event.send_event = FALSE; + + old = ((GdkWindowObject*) temp_event.window)->state; + + temp_event.changed_mask = (unset_flags | set_flags) ^ old; + temp_event.new_window_state = old; + temp_event.new_window_state |= set_flags; + temp_event.new_window_state &= ~unset_flags; + + if (temp_event.new_window_state == old) + return; /* No actual work to do, nothing changed. */ + + /* Actually update the field in GdkWindow, this is sort of an odd + * place to do it, but seems like the safest since it ensures we expose no + * inconsistent state to the user. + */ + + ((GdkWindowObject*) window)->state = temp_event.new_window_state; + + /* We only really send the event to toplevels, since + * all the window states don't apply to non-toplevels. + * Non-toplevels do use the GDK_WINDOW_STATE_WITHDRAWN flag + * internally so we needed to update window->state. + */ + switch (((GdkWindowObject*) window)->window_type) + { + case GDK_WINDOW_TOPLEVEL: + case GDK_WINDOW_DIALOG: + case GDK_WINDOW_TEMP: /* ? */ + gdk_event_put ((GdkEvent*) &temp_event); + break; + + case GDK_WINDOW_FOREIGN: + case GDK_WINDOW_ROOT: + case GDK_WINDOW_CHILD: + break; + } +} + diff --git a/gdk/gdkevents.h b/gdk/gdkevents.h index f49b156ecd..ea2c25b3ab 100644 --- a/gdk/gdkevents.h +++ b/gdk/gdkevents.h @@ -28,8 +28,8 @@ typedef struct _GdkEventProperty GdkEventProperty; typedef struct _GdkEventSelection GdkEventSelection; typedef struct _GdkEventProximity GdkEventProximity; typedef struct _GdkEventClient GdkEventClient; - typedef struct _GdkEventDND GdkEventDND; +typedef struct _GdkEventWindowState GdkEventWindowState; typedef union _GdkEvent GdkEvent; @@ -110,7 +110,8 @@ typedef enum GDK_CLIENT_EVENT = 28, GDK_VISIBILITY_NOTIFY = 29, GDK_NO_EXPOSE = 30, - GDK_SCROLL = 31 + GDK_SCROLL = 31, + GDK_WINDOW_STATE = 32 } GdkEventType; /* Event masks. (Used to select what types of events a window @@ -193,6 +194,14 @@ typedef enum GDK_PROPERTY_DELETE } GdkPropertyState; +typedef enum +{ + GDK_WINDOW_STATE_WITHDRAWN = 1 << 0, + GDK_WINDOW_STATE_ICONIFIED = 1 << 1, + GDK_WINDOW_STATE_MAXIMIZED = 1 << 2, + GDK_WINDOW_STATE_STICKY = 1 << 3 +} GdkWindowState; + struct _GdkEventAny { GdkEventType type; @@ -366,6 +375,16 @@ struct _GdkEventClient } data; }; + +struct _GdkEventWindowState +{ + GdkEventType type; + GdkWindow *window; + gint8 send_event; + GdkWindowState changed_mask; + GdkWindowState new_window_state; +}; + /* Event types for DND */ struct _GdkEventDND { @@ -397,6 +416,7 @@ union _GdkEvent GdkEventProximity proximity; GdkEventClient client; GdkEventDND dnd; + GdkEventWindowState window_state; }; gboolean gdk_events_pending (void); diff --git a/gdk/gdkinternals.h b/gdk/gdkinternals.h index 2987f81863..5f96ffd7ed 100644 --- a/gdk/gdkinternals.h +++ b/gdk/gdkinternals.h @@ -117,6 +117,9 @@ void gdk_event_queue_remove_link (GList *node); void gdk_event_queue_append (GdkEvent *event); void gdk_event_button_generate (GdkEvent *event); +void gdk_synthesize_window_state (GdkWindow *window, + GdkWindowState unset_flags, + GdkWindowState set_flags); /************************************* * Interfaces used by windowing code * @@ -163,6 +166,8 @@ void _gdk_windowing_window_clear_area_e (GdkWindow *window, gint width, gint height); +#define GDK_WINDOW_IS_MAPPED(window) ((((GdkWindowObject*)window)->state & GDK_WINDOW_STATE_WITHDRAWN) == 0) + /* Called before processing updates for a window. This gives the windowing * layer a chance to save the region for later use in avoiding duplicate * exposes. The return value indicates whether the function has a saved diff --git a/gdk/gdkwindow.c b/gdk/gdkwindow.c index 6cd615333b..acead1b234 100644 --- a/gdk/gdkwindow.c +++ b/gdk/gdkwindow.c @@ -188,6 +188,8 @@ gdk_window_init (GdkWindowObject *window) window->window_type = GDK_WINDOW_CHILD; + window->state = GDK_WINDOW_STATE_WITHDRAWN; + window->impl = g_object_new (_gdk_window_impl_get_type (), NULL); } @@ -290,7 +292,7 @@ _gdk_window_destroy_hierarchy (GdkWindow *window, case GDK_WINDOW_FOREIGN: if (!GDK_WINDOW_DESTROYED (window)) { - private->mapped = FALSE; + private->state |= GDK_WINDOW_STATE_WITHDRAWN; private->destroyed = TRUE; _gdk_windowing_window_destroy (window, recursing, foreign_destroy); @@ -573,12 +575,9 @@ gdk_window_get_toplevels (void) gboolean gdk_window_is_visible (GdkWindow *window) { - GdkWindowObject *private = (GdkWindowObject *)window; - - g_return_val_if_fail (window != NULL, FALSE); g_return_val_if_fail (GDK_IS_WINDOW (window), FALSE); - return private->mapped; + return GDK_WINDOW_IS_MAPPED (window); } /************************************************************* @@ -605,7 +604,7 @@ gdk_window_is_viewable (GdkWindow *window) (private != (GdkWindowObject *)gdk_parent_root) && (GDK_WINDOW_TYPE (private) != GDK_WINDOW_FOREIGN)) { - if (!private->mapped) + if (!GDK_WINDOW_IS_MAPPED (window)) return FALSE; private = (GdkWindowObject *)private->parent; @@ -615,6 +614,25 @@ gdk_window_is_viewable (GdkWindow *window) } /** + * gdk_window_get_state: + * @window: a #GdkWindow + * + * Gets the bitwise OR of the currently active window state flags, + * from the #GdkWindowState enumeration. + * + * Return value: window state bitfield + **/ +GdkWindowState +gdk_window_get_state (GdkWindow *window) +{ + GdkWindowObject *private = (GdkWindowObject *)window; + + g_return_val_if_fail (GDK_IS_WINDOW (window), FALSE); + + return private->state; +} + +/** * gdk_window_begin_paint_rect: * @window: a #GdkWindow * @rectangle: rectangle you intend to draw to @@ -1871,7 +1889,7 @@ gdk_window_invalidate_rect (GdkWindow *window, if (GDK_WINDOW_DESTROYED (window)) return; - if (private->input_only || !private->mapped) + if (private->input_only || !GDK_WINDOW_IS_MAPPED (window)) return; if (!rect) @@ -1927,7 +1945,7 @@ gdk_window_invalidate_region (GdkWindow *window, if (GDK_WINDOW_DESTROYED (window)) return; - if (private->input_only || !private->mapped) + if (private->input_only || !GDK_WINDOW_IS_MAPPED (window)) return; visible_region = gdk_drawable_get_visible_region (window); diff --git a/gdk/gdkwindow.h b/gdk/gdkwindow.h index 435e5868dc..9976265edc 100644 --- a/gdk/gdkwindow.h +++ b/gdk/gdkwindow.h @@ -199,7 +199,9 @@ struct _GdkWindowObject guint8 window_type; guint8 depth; guint8 resize_count; - guint mapped : 1; + + GdkWindowState state; + guint guffaw_gravity : 1; guint input_only : 1; @@ -254,6 +256,8 @@ void gdk_window_clear_area_e (GdkWindow *window, gint height); void gdk_window_raise (GdkWindow *window); void gdk_window_lower (GdkWindow *window); +void gdk_window_focus (GdkWindow *window, + guint32 timestamp); void gdk_window_set_user_data (GdkWindow *window, gpointer user_data); void gdk_window_set_override_redirect (GdkWindow *window, @@ -305,6 +309,8 @@ void gdk_window_merge_child_shapes (GdkWindow *window); gboolean gdk_window_is_visible (GdkWindow *window); gboolean gdk_window_is_viewable (GdkWindow *window); +GdkWindowState gdk_window_get_state (GdkWindow *window); + /* Set static bit gravity on the parent, and static * window gravity on all children. */ @@ -393,7 +399,13 @@ gboolean gdk_window_get_decorations (GdkWindow *window, void gdk_window_set_functions (GdkWindow *window, GdkWMFunction functions); GList * gdk_window_get_toplevels (void); + void gdk_window_iconify (GdkWindow *window); +void gdk_window_deiconify (GdkWindow *window); +void gdk_window_stick (GdkWindow *window); +void gdk_window_unstick (GdkWindow *window); +void gdk_window_maximize (GdkWindow *window); +void gdk_window_unmaximize (GdkWindow *window); void gdk_window_register_dnd (GdkWindow *window); diff --git a/gdk/x11/gdkevents-x11.c b/gdk/x11/gdkevents-x11.c index a005e5a148..7056d9bc75 100644 --- a/gdk/x11/gdkevents-x11.c +++ b/gdk/x11/gdkevents-x11.c @@ -44,6 +44,8 @@ #include <X11/XKBlib.h> #endif +#include <X11/Xatom.h> + typedef struct _GdkIOClosure GdkIOClosure; typedef struct _GdkEventPrivate GdkEventPrivate; @@ -119,7 +121,9 @@ static GSourceFuncs event_funcs = { NULL }; -GPollFD event_poll_fd; +static GPollFD event_poll_fd; + +static Window wmspec_check_window = None; /********************************************* * Functions for maintaining the event queue * @@ -260,6 +264,128 @@ gdk_add_client_message_filter (GdkAtom message_type, client_filters = g_list_prepend (client_filters, filter); } +static GdkAtom wm_state_atom = 0; +static GdkAtom wm_desktop_atom = 0; + +static void +gdk_check_wm_state_changed (GdkWindow *window) +{ + Atom type; + gint format; + gulong nitems; + gulong bytes_after; + GdkAtom *atoms = NULL; + gulong i; + GdkAtom sticky_atom; + GdkAtom maxvert_atom; + GdkAtom maxhorz_atom; + gboolean found_sticky, found_maxvert, found_maxhorz; + GdkWindowState old_state; + + if (GDK_WINDOW_DESTROYED (window)) + return; + + if (wm_state_atom == 0) + wm_state_atom = gdk_atom_intern ("_NET_WM_STATE", FALSE); + + if (wm_desktop_atom == 0) + wm_desktop_atom = gdk_atom_intern ("_NET_WM_DESKTOP", FALSE); + + XGetWindowProperty (GDK_WINDOW_XDISPLAY (window), GDK_WINDOW_XID (window), + wm_state_atom, 0, G_MAXLONG, + False, XA_ATOM, &type, &format, &nitems, + &bytes_after, (guchar **)&atoms); + + if (type != None) + { + + sticky_atom = gdk_atom_intern ("_NET_WM_STATE_STICKY", FALSE); + maxvert_atom = gdk_atom_intern ("_NET_WM_STATE_MAXIMIZED_VERT", FALSE); + maxhorz_atom = gdk_atom_intern ("_NET_WM_STATE_MAXIMIZED_HORZ", FALSE); + + found_sticky = FALSE; + found_maxvert = FALSE; + found_maxhorz = FALSE; + + i = 0; + while (i < nitems) + { + if (atoms[i] == sticky_atom) + found_sticky = TRUE; + else if (atoms[i] == maxvert_atom) + found_maxvert = TRUE; + else if (atoms[i] == maxhorz_atom) + found_maxhorz = TRUE; + + ++i; + } + + XFree (atoms); + } + else + { + found_sticky = FALSE; + found_maxvert = FALSE; + found_maxhorz = FALSE; + } + + /* For found_sticky to remain TRUE, we have to also be on desktop + * 0xFFFFFFFF + */ + + if (found_sticky) + { + gulong *desktop; + + XGetWindowProperty (GDK_WINDOW_XDISPLAY (window), GDK_WINDOW_XID (window), + wm_desktop_atom, 0, G_MAXLONG, + False, XA_CARDINAL, &type, &format, &nitems, + &bytes_after, (guchar **)&desktop); + + if (type != None) + { + if (*desktop != 0xFFFFFFFF) + found_sticky = FALSE; + XFree (desktop); + } + } + + old_state = gdk_window_get_state (window); + + if (old_state & GDK_WINDOW_STATE_STICKY) + { + if (!found_sticky) + gdk_synthesize_window_state (window, + GDK_WINDOW_STATE_STICKY, + 0); + } + else + { + if (found_sticky) + gdk_synthesize_window_state (window, + 0, + GDK_WINDOW_STATE_STICKY); + } + + /* Our "maximized" means both vertical and horizontal; if only one, + * we don't expose that via GDK + */ + if (old_state & GDK_WINDOW_STATE_MAXIMIZED) + { + if (!(found_maxvert && found_maxhorz)) + gdk_synthesize_window_state (window, + GDK_WINDOW_STATE_MAXIMIZED, + 0); + } + else + { + if (found_maxvert && found_maxhorz) + gdk_synthesize_window_state (window, + 0, + GDK_WINDOW_STATE_MAXIMIZED); + } +} + static gint gdk_event_translate (GdkEvent *event, XEvent *xevent, @@ -306,6 +432,19 @@ gdk_event_translate (GdkEvent *event, if (window != NULL) gdk_window_ref (window); + + if (wmspec_check_window != None && + xevent->xany.window == wmspec_check_window) + { + if (xevent->type == DestroyNotify) + wmspec_check_window = None; + + /* Eat events on this window unless someone had wrapped + * it as a foreign window + */ + if (window == NULL) + return FALSE; + } event->any.window = window; event->any.send_event = xevent->xany.send_event ? TRUE : FALSE; @@ -948,7 +1087,17 @@ gdk_event_translate (GdkEvent *event, xevent->xmap.window)); event->any.type = GDK_UNMAP; - event->any.window = window; + event->any.window = window; + + /* If we are shown (not withdrawn) and get an unmap, it means we + * were iconified in the X sense. If we are withdrawn, and get + * an unmap, it means we hid the window ourselves, so we + * will have already flipped the iconified bit off. + */ + if (GDK_WINDOW_IS_MAPPED (window)) + gdk_synthesize_window_state (window, + 0, + GDK_WINDOW_STATE_ICONIFIED); if (gdk_xgrab_window == window_private) gdk_xgrab_window = NULL; @@ -962,6 +1111,12 @@ gdk_event_translate (GdkEvent *event, event->any.type = GDK_MAP; event->any.window = window; + + /* Unset iconified if it was set */ + if (((GdkWindowObject*)window)->state & GDK_WINDOW_STATE_ICONIFIED) + gdk_synthesize_window_state (window, + GDK_WINDOW_STATE_ICONIFIED, + 0); break; @@ -1064,6 +1219,19 @@ gdk_event_translate (GdkEvent *event, event->property.atom = xevent->xproperty.atom; event->property.time = xevent->xproperty.time; event->property.state = xevent->xproperty.state; + + if (wm_state_atom == 0) + wm_state_atom = gdk_atom_intern ("_NET_WM_STATE", FALSE); + + if (wm_desktop_atom == 0) + wm_desktop_atom = gdk_atom_intern ("_NET_WM_DESKTOP", FALSE); + + if (event->property.atom == wm_state_atom || + event->property.atom == wm_desktop_atom) + { + /* If window state changed, then synthesize those events. */ + gdk_check_wm_state_changed (event->property.window); + } break; @@ -1586,3 +1754,94 @@ gdk_x11_get_server_time (GdkWindow *window) return xevent.xproperty.time; } + +gboolean +gdk_wmspec_supported (GdkAtom property) +{ + static GdkAtom wmspec_check_atom = 0; + static GdkAtom wmspec_supported_atom = 0; + static GdkAtom *atoms = NULL; + static gulong n_atoms = 0; + Atom type; + gint format; + gulong nitems; + gulong bytes_after; + Window *xwindow; + gulong i; + + if (wmspec_check_window != None) + { + if (atoms == NULL) + return FALSE; + + i = 0; + while (i < n_atoms) + { + if (atoms[i] == property) + return TRUE; + + ++i; + } + + return FALSE; + } + + if (atoms) + XFree (atoms); + + atoms = NULL; + n_atoms = 0; + + /* This function is very slow on every call if you are not running a + * spec-supporting WM. For now not optimized, because it isn't in + * any critical code paths, but if you used it somewhere that had to + * be fast you want to avoid "GTK is slow with old WMs" complaints. + * Probably at that point the function should be changed to query + * _NET_SUPPORTING_WM_CHECK only once every 10 seconds or something. + */ + + if (wmspec_check_atom == 0) + wmspec_check_atom = gdk_atom_intern ("_NET_SUPPORTING_WM_CHECK", FALSE); + + if (wmspec_supported_atom == 0) + wmspec_supported_atom = gdk_atom_intern ("_NET_SUPPORTED", FALSE); + + XGetWindowProperty (gdk_display, gdk_root_window, + wmspec_check_atom, 0, G_MAXLONG, + False, XA_WINDOW, &type, &format, &nitems, + &bytes_after, (guchar **)&xwindow); + + if (type != XA_WINDOW) + return FALSE; + + gdk_error_trap_push (); + + /* Find out if this WM goes away, so we can reset everything. */ + XSelectInput (gdk_display, *xwindow, + StructureNotifyMask); + + gdk_flush (); + + if (gdk_error_trap_pop ()) + { + XFree (xwindow); + return FALSE; + } + + XGetWindowProperty (gdk_display, gdk_root_window, + wmspec_supported_atom, 0, G_MAXLONG, + False, XA_ATOM, &type, &format, &n_atoms, + &bytes_after, (guchar **)&atoms); + + if (type != XA_ATOM) + return FALSE; + + wmspec_check_window = *xwindow; + XFree (xwindow); + + /* since wmspec_check_window != None this isn't infinite. ;-) */ + return gdk_wmspec_supported (property); +} + + + diff --git a/gdk/x11/gdkgeometry-x11.c b/gdk/x11/gdkgeometry-x11.c index 6102dc1ce5..17b6f8b9d5 100644 --- a/gdk/x11/gdkgeometry-x11.c +++ b/gdk/x11/gdkgeometry-x11.c @@ -28,6 +28,7 @@ #include "gdkprivate-x11.h" #include "gdkx.h" #include "gdkregion.h" +#include "gdkinternals.h" typedef struct _GdkWindowQueueItem GdkWindowQueueItem; typedef struct _GdkWindowParentPos GdkWindowParentPos; @@ -332,7 +333,7 @@ _gdk_window_move_resize_child (GdkWindow *window, if (impl->position_info.no_bg) gdk_window_tmp_reset_bg (window); - if (!impl->position_info.mapped && new_info.mapped && obj->mapped) + if (!impl->position_info.mapped && new_info.mapped && GDK_WINDOW_IS_MAPPED (obj)) XMapWindow (GDK_WINDOW_XDISPLAY (window), GDK_WINDOW_XID (window)); impl->position_info = new_info; @@ -378,7 +379,7 @@ _gdk_window_move_resize_child (GdkWindow *window, if (impl->position_info.no_bg) gdk_window_tmp_reset_bg (window); - if (!impl->position_info.mapped && new_info.mapped && obj->mapped) + if (!impl->position_info.mapped && new_info.mapped && GDK_WINDOW_IS_MAPPED (obj)) XMapWindow (GDK_WINDOW_XDISPLAY (window), GDK_WINDOW_XID (window)); impl->position_info = new_info; @@ -651,7 +652,7 @@ gdk_window_postmove (GdkWindow *window, new_info.x, new_info.y, new_info.width, new_info.height); } - if (!impl->position_info.mapped && new_info.mapped && obj->mapped) + if (!impl->position_info.mapped && new_info.mapped && GDK_WINDOW_IS_MAPPED (obj)) XMapWindow (GDK_DRAWABLE_XDISPLAY (window), GDK_DRAWABLE_XID (window)); if (impl->position_info.no_bg) diff --git a/gdk/x11/gdkmain-x11.c b/gdk/x11/gdkmain-x11.c index 1f0097b944..1f395e390d 100644 --- a/gdk/x11/gdkmain-x11.c +++ b/gdk/x11/gdkmain-x11.c @@ -737,4 +737,3 @@ gdk_send_xevent (Window window, gboolean propagate, glong event_mask, return result && !gdk_error_code; } - diff --git a/gdk/x11/gdkwindow-x11.c b/gdk/x11/gdkwindow-x11.c index a68b2be56a..548bb587c4 100644 --- a/gdk/x11/gdkwindow-x11.c +++ b/gdk/x11/gdkwindow-x11.c @@ -635,7 +635,12 @@ gdk_window_foreign_new (GdkNativeWindow anid) impl->height = attrs.height; private->window_type = GDK_WINDOW_FOREIGN; private->destroyed = FALSE; - private->mapped = (attrs.map_state != IsUnmapped); + + if (attrs.map_state == IsUnmapped) + private->state = GDK_WINDOW_STATE_WITHDRAWN; + else + private->state = 0; + private->depth = attrs.depth; gdk_drawable_ref (window); @@ -709,6 +714,73 @@ gdk_window_destroy_notify (GdkWindow *window) gdk_drawable_unref (window); } +static void +set_initial_hints (GdkWindow *window) +{ + GdkWindowObject *private; + GdkAtom atoms[5]; + gint i; + + private = (GdkWindowObject*) window; + + if (private->state & GDK_WINDOW_STATE_ICONIFIED) + { + XWMHints *wm_hints; + + wm_hints = XGetWMHints (GDK_WINDOW_XDISPLAY (window), + GDK_WINDOW_XID (window)); + if (!wm_hints) + wm_hints = XAllocWMHints (); + + wm_hints->flags |= StateHint; + wm_hints->initial_state = IconicState; + + XSetWMHints (GDK_WINDOW_XDISPLAY (window), + GDK_WINDOW_XID (window), wm_hints); + XFree (wm_hints); + } + + /* 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 (private->state & GDK_WINDOW_STATE_MAXIMIZED) + { + atoms[i] = gdk_atom_intern ("_NET_WM_STATE_MAXIMIZED_VERT", FALSE); + ++i; + atoms[i] = gdk_atom_intern ("_NET_WM_STATE_MAXIMIZED_HORZ", FALSE); + ++i; + } + + if (private->state & GDK_WINDOW_STATE_STICKY) + { + atoms[i] = gdk_atom_intern ("_NET_WM_STATE_STICKY", FALSE); + ++i; + } + + if (i > 0) + { + XChangeProperty (GDK_WINDOW_XDISPLAY (window), + GDK_WINDOW_XID (window), + gdk_atom_intern ("_NET_WM_STATE", FALSE), + XA_ATOM, 32, PropModeReplace, + (guchar*) atoms, i); + } + + if (private->state & GDK_WINDOW_STATE_STICKY) + { + atoms[0] = 0xFFFFFFFF; + XChangeProperty (GDK_WINDOW_XDISPLAY (window), + GDK_WINDOW_XID (window), + gdk_atom_intern ("_NET_WM_DESKTOP", FALSE), + XA_CARDINAL, 32, PropModeReplace, + (guchar*) atoms, 1); + } +} + void gdk_window_show (GdkWindow *window) { @@ -719,13 +791,23 @@ gdk_window_show (GdkWindow *window) private = (GdkWindowObject*) window; if (!private->destroyed) { - private->mapped = TRUE; XRaiseWindow (GDK_WINDOW_XDISPLAY (window), GDK_WINDOW_XID (window)); + + if (!GDK_WINDOW_IS_MAPPED (window)) + { + set_initial_hints (window); + + gdk_synthesize_window_state (window, + GDK_WINDOW_STATE_WITHDRAWN, + 0); + } + + g_assert (GDK_WINDOW_IS_MAPPED (window)); if (GDK_WINDOW_IMPL_X11 (private->impl)->position_info.mapped) - XMapWindow (GDK_WINDOW_XDISPLAY (window), - GDK_WINDOW_XID (window)); + XMapWindow (GDK_WINDOW_XDISPLAY (window), + GDK_WINDOW_XID (window)); } } @@ -737,14 +819,36 @@ gdk_window_hide (GdkWindow *window) g_return_if_fail (window != NULL); private = (GdkWindowObject*) window; + + /* You can't simply unmap toplevel windows. */ + switch (private->window_type) + { + case GDK_WINDOW_TOPLEVEL: + case GDK_WINDOW_DIALOG: + case GDK_WINDOW_TEMP: /* ? */ + gdk_window_withdraw (window); + return; + break; + + case GDK_WINDOW_FOREIGN: + case GDK_WINDOW_ROOT: + case GDK_WINDOW_CHILD: + break; + } + if (!private->destroyed) { - private->mapped = FALSE; + if (GDK_WINDOW_IS_MAPPED (window)) + gdk_synthesize_window_state (window, + 0, + GDK_WINDOW_STATE_WITHDRAWN); + g_assert (!GDK_WINDOW_IS_MAPPED (window)); + _gdk_window_clear_update_area (window); XUnmapWindow (GDK_WINDOW_XDISPLAY (window), - GDK_WINDOW_XID (window)); + GDK_WINDOW_XID (window)); } } @@ -757,8 +861,17 @@ gdk_window_withdraw (GdkWindow *window) private = (GdkWindowObject*) window; if (!private->destroyed) - XWithdrawWindow (GDK_WINDOW_XDISPLAY (window), - GDK_WINDOW_XID (window), 0); + { + if (GDK_WINDOW_IS_MAPPED (window)) + gdk_synthesize_window_state (window, + 0, + GDK_WINDOW_STATE_WITHDRAWN); + + g_assert (!GDK_WINDOW_IS_MAPPED (window)); + + XWithdrawWindow (GDK_WINDOW_XDISPLAY (window), + GDK_WINDOW_XID (window), 0); + } } void @@ -945,6 +1058,41 @@ gdk_window_lower (GdkWindow *window) } void +gdk_window_focus (GdkWindow *window, + guint32 timestamp) +{ + g_return_if_fail (GDK_IS_WINDOW (window)); + + if (GDK_WINDOW_DESTROYED (window)) + return; + + if (gdk_wmspec_supported (gdk_atom_intern ("_NET_ACTIVE_WINDOW", FALSE))) + { + XEvent xev; + + xev.xclient.type = ClientMessage; + xev.xclient.serial = 0; + xev.xclient.send_event = True; + xev.xclient.window = GDK_WINDOW_XWINDOW (window); + xev.xclient.display = gdk_display; + xev.xclient.message_type = gdk_atom_intern ("_NET_ACTIVE_WINDOW", FALSE); + xev.xclient.format = 32; + xev.xclient.data.l[0] = 0; + + XSendEvent (gdk_display, gdk_root_window, False, + SubstructureRedirectMask | SubstructureNotifyMask, + &xev); + } + else + { + XSetInputFocus (GDK_DISPLAY (), + GDK_WINDOW_XWINDOW (window), + RevertToNone, + timestamp); + } +} + +void gdk_window_set_hints (GdkWindow *window, gint x, gint y, @@ -1807,12 +1955,43 @@ gdk_window_set_icon_name (GdkWindow *window, GUINT_TO_POINTER (TRUE)); set_text_property (window, gdk_atom_intern ("WM_ICON_NAME", FALSE), name); -} +} void gdk_window_iconify (GdkWindow *window) { Display *display; + GdkWindowObject *private; + + g_return_if_fail (window != NULL); + g_return_if_fail (GDK_IS_WINDOW (window)); + + if (GDK_WINDOW_DESTROYED (window)) + return; + + display = GDK_WINDOW_XDISPLAY (window); + + private = (GdkWindowObject*) window; + + if (GDK_WINDOW_IS_MAPPED (window)) + { + XIconifyWindow (display, GDK_WINDOW_XWINDOW (window), DefaultScreen (display)); + + } + else + { + /* Flip our client side flag, the real work happens on map. */ + gdk_synthesize_window_state (window, + 0, + GDK_WINDOW_STATE_ICONIFIED); + } +} + +void +gdk_window_deiconify (GdkWindow *window) +{ + Display *display; + GdkWindowObject *private; g_return_if_fail (window != NULL); g_return_if_fail (GDK_IS_WINDOW (window)); @@ -1821,7 +2000,221 @@ gdk_window_iconify (GdkWindow *window) return; display = GDK_WINDOW_XDISPLAY (window); - XIconifyWindow (display, GDK_WINDOW_XWINDOW (window), DefaultScreen (display)); + + private = (GdkWindowObject*) window; + + if (GDK_WINDOW_IS_MAPPED (window)) + { + gdk_window_show (window); + } + else + { + /* Flip our client side flag, the real work happens on map. */ + gdk_synthesize_window_state (window, + GDK_WINDOW_STATE_ICONIFIED, + 0); + } +} + +void +gdk_window_stick (GdkWindow *window) +{ + g_return_if_fail (GDK_IS_WINDOW (window)); + + if (GDK_WINDOW_DESTROYED (window)) + return; + + if (GDK_WINDOW_IS_MAPPED (window)) + { + /* "stick" means stick to all desktops _and_ do not scroll with the + * viewport. i.e. glue to the monitor glass in all cases. + */ + + XEvent xev; + + /* Request stick during viewport scroll */ + xev.xclient.type = ClientMessage; + xev.xclient.serial = 0; + xev.xclient.send_event = True; + xev.xclient.window = GDK_WINDOW_XWINDOW (window); + xev.xclient.display = gdk_display; + xev.xclient.message_type = gdk_atom_intern ("_NET_WM_STATE", FALSE); + xev.xclient.format = 32; + + xev.xclient.data.l[0] = gdk_atom_intern ("_NET_WM_STATE_ADD", FALSE); + xev.xclient.data.l[1] = gdk_atom_intern ("_NET_WM_STATE_STICKY", FALSE); + xev.xclient.data.l[2] = 0; + + XSendEvent (gdk_display, gdk_root_window, False, + SubstructureRedirectMask | SubstructureNotifyMask, + &xev); + + /* Request desktop 0xFFFFFFFF */ + xev.xclient.type = ClientMessage; + xev.xclient.serial = 0; + xev.xclient.send_event = True; + xev.xclient.window = GDK_WINDOW_XWINDOW (window); + xev.xclient.display = gdk_display; + xev.xclient.message_type = gdk_atom_intern ("_NET_WM_DESKTOP", FALSE); + xev.xclient.format = 32; + + xev.xclient.data.l[0] = 0xFFFFFFFF; + + XSendEvent (gdk_display, gdk_root_window, False, + SubstructureRedirectMask | SubstructureNotifyMask, + &xev); + } + else + { + /* Flip our client side flag, the real work happens on map. */ + gdk_synthesize_window_state (window, + 0, + GDK_WINDOW_STATE_STICKY); + } +} + +void +gdk_window_unstick (GdkWindow *window) +{ + g_return_if_fail (GDK_IS_WINDOW (window)); + + if (GDK_WINDOW_DESTROYED (window)) + return; + + if (GDK_WINDOW_IS_MAPPED (window)) + { + XEvent xev; + Atom type; + gint format; + gulong nitems; + gulong bytes_after; + gulong *current_desktop; + + /* Request unstick from viewport */ + xev.xclient.type = ClientMessage; + xev.xclient.serial = 0; + xev.xclient.send_event = True; + xev.xclient.window = GDK_WINDOW_XWINDOW (window); + xev.xclient.display = gdk_display; + xev.xclient.message_type = gdk_atom_intern ("_NET_WM_STATE", FALSE); + xev.xclient.format = 32; + + xev.xclient.data.l[0] = gdk_atom_intern ("_NET_WM_STATE_REMOVE", FALSE); + xev.xclient.data.l[1] = gdk_atom_intern ("_NET_WM_STATE_STICKY", FALSE); + xev.xclient.data.l[2] = 0; + + XSendEvent (gdk_display, gdk_root_window, False, + SubstructureRedirectMask | SubstructureNotifyMask, + &xev); + + /* Get current desktop, then set it; this is a race, but not + * one that matters much in practice. + */ + XGetWindowProperty (gdk_display, gdk_root_window, + gdk_atom_intern ("_NET_CURRENT_DESKTOP", FALSE), + 0, G_MAXLONG, + False, XA_CARDINAL, &type, &format, &nitems, + &bytes_after, (guchar **)¤t_desktop); + + if (type == XA_CARDINAL) + { + xev.xclient.type = ClientMessage; + xev.xclient.serial = 0; + xev.xclient.send_event = True; + xev.xclient.window = GDK_WINDOW_XWINDOW (window); + xev.xclient.display = gdk_display; + xev.xclient.message_type = gdk_atom_intern ("_NET_WM_DESKTOP", FALSE); + xev.xclient.format = 32; + + xev.xclient.data.l[0] = *current_desktop; + + XSendEvent (gdk_display, gdk_root_window, False, + SubstructureRedirectMask | SubstructureNotifyMask, + &xev); + + XFree (current_desktop); + } + } + else + { + /* Flip our client side flag, the real work happens on map. */ + gdk_synthesize_window_state (window, + GDK_WINDOW_STATE_STICKY, + 0); + + } +} + +void +gdk_window_maximize (GdkWindow *window) +{ + g_return_if_fail (GDK_IS_WINDOW (window)); + + if (GDK_WINDOW_DESTROYED (window)) + return; + + if (GDK_WINDOW_IS_MAPPED (window)) + { + XEvent xev; + + xev.xclient.type = ClientMessage; + xev.xclient.serial = 0; + xev.xclient.send_event = True; + xev.xclient.window = GDK_WINDOW_XWINDOW (window); + xev.xclient.display = gdk_display; + xev.xclient.message_type = gdk_atom_intern ("_NET_WM_STATE", FALSE); + xev.xclient.format = 32; + + xev.xclient.data.l[0] = gdk_atom_intern ("_NET_WM_STATE_ADD", FALSE); + xev.xclient.data.l[1] = gdk_atom_intern ("_NET_WM_STATE_MAXIMIZED_VERT", FALSE); + xev.xclient.data.l[2] = gdk_atom_intern ("_NET_WM_STATE_MAXIMIZED_HORZ", FALSE); + + XSendEvent (gdk_display, gdk_root_window, False, + SubstructureRedirectMask | SubstructureNotifyMask, + &xev); + } + else + { + gdk_synthesize_window_state (window, + 0, + GDK_WINDOW_STATE_MAXIMIZED); + } +} + +void +gdk_window_unmaximize (GdkWindow *window) +{ + g_return_if_fail (GDK_IS_WINDOW (window)); + + if (GDK_WINDOW_DESTROYED (window)) + return; + + if (GDK_WINDOW_IS_MAPPED (window)) + { + XEvent xev; + + xev.xclient.type = ClientMessage; + xev.xclient.serial = 0; + xev.xclient.send_event = True; + xev.xclient.window = GDK_WINDOW_XWINDOW (window); + xev.xclient.display = gdk_display; + xev.xclient.message_type = gdk_atom_intern ("_NET_WM_STATE", FALSE); + xev.xclient.format = 32; + + xev.xclient.data.l[0] = gdk_atom_intern ("_NET_WM_STATE_REMOVE", FALSE); + xev.xclient.data.l[1] = gdk_atom_intern ("_NET_WM_STATE_MAXIMIZED_VERT", FALSE); + xev.xclient.data.l[2] = gdk_atom_intern ("_NET_WM_STATE_MAXIMIZED_HORZ", FALSE); + + XSendEvent (gdk_display, gdk_root_window, False, + SubstructureRedirectMask | SubstructureNotifyMask, + &xev); + } + else + { + gdk_synthesize_window_state (window, + GDK_WINDOW_STATE_MAXIMIZED, + 0); + } } void diff --git a/gdk/x11/gdkx.h b/gdk/x11/gdkx.h index 336bd35f31..628d32206f 100644 --- a/gdk/x11/gdkx.h +++ b/gdk/x11/gdkx.h @@ -185,6 +185,9 @@ gpointer gdk_xid_table_lookup (XID xid); guint32 gdk_x11_get_server_time (GdkWindow *window); +/* returns TRUE if we support the given WM spec feature */ +gboolean gdk_wmspec_supported (GdkAtom property); + #define gdk_window_lookup(xid) ((GdkWindow*) gdk_xid_table_lookup (xid)) #define gdk_pixmap_lookup(xid) ((GdkPixmap*) gdk_xid_table_lookup (xid)) #define gdk_font_lookup(xid) ((GdkFont*) gdk_xid_table_lookup (xid)) |