X-Git-Url: http://pileus.org/git/?a=blobdiff_plain;f=gdk%2Fx11%2Fgdkevents-x11.c;h=e32fe2182b29e0ed00d7a3a1e77b9078ba9fc2d2;hb=9902a67094c3f1d8d7f3e5ede4242ed51cc9a4f9;hp=87ed39295295bfcb93c9c16dea20f6c9a9f85be2;hpb=445e90fa82b1ccccde303754725b0248bb07b622;p=~andy%2Fgtk diff --git a/gdk/x11/gdkevents-x11.c b/gdk/x11/gdkevents-x11.c index 87ed39295..e32fe2182 100644 --- a/gdk/x11/gdkevents-x11.c +++ b/gdk/x11/gdkevents-x11.c @@ -28,9 +28,13 @@ #include "gdkprivate-x11.h" #include "gdkinternals.h" #include "gdkx.h" +#include "gdkscreen-x11.h" +#include "gdkdisplay-x11.h" #include "gdkkeysyms.h" +#include "xsettings-client.h" + #if HAVE_CONFIG_H # include # if STDC_HEADERS @@ -47,21 +51,13 @@ #include typedef struct _GdkIOClosure GdkIOClosure; -typedef struct _GdkEventPrivate GdkEventPrivate; +typedef struct _GdkDisplaySource GdkDisplaySource; #define DOUBLE_CLICK_TIME 250 #define TRIPLE_CLICK_TIME 500 #define DOUBLE_CLICK_DIST 5 #define TRIPLE_CLICK_DIST 5 -typedef enum -{ - /* Following flag is set for events on the event queue during - * translation and cleared afterwards. - */ - GDK_EVENT_PENDING = 1 << 0 -} GdkEventFlags; - struct _GdkIOClosure { GdkInputFunction function; @@ -70,10 +66,12 @@ struct _GdkIOClosure gpointer data; }; -struct _GdkEventPrivate +struct _GdkDisplaySource { - GdkEvent event; - guint flags; + GSource source; + + GdkDisplay *display; + GPollFD event_poll_fd; }; /* @@ -83,14 +81,10 @@ struct _GdkEventPrivate static gint gdk_event_apply_filters (XEvent *xevent, GdkEvent *event, GList *filters); -static gint gdk_event_translate (GdkEvent *event, - XEvent *xevent, - gboolean return_exposes); -#if 0 -static Bool gdk_event_get_type (Display *display, - XEvent *xevent, - XPointer arg); -#endif +static gboolean gdk_event_translate (GdkDisplay *display, + GdkEvent *event, + XEvent *xevent, + gboolean return_exposes); static gboolean gdk_event_prepare (GSource *source, gint *timeout); @@ -99,20 +93,26 @@ static gboolean gdk_event_dispatch (GSource *source, GSourceFunc callback, gpointer user_data); -GdkFilterReturn gdk_wm_protocols_filter (GdkXEvent *xev, - GdkEvent *event, - gpointer data); +static GdkFilterReturn gdk_wm_protocols_filter (GdkXEvent *xev, + GdkEvent *event, + gpointer data); + +static GSource *gdk_display_source_new (GdkDisplay *display); +static gboolean gdk_check_xpending (GdkDisplay *display); + +static void gdk_xsettings_watch_cb (Window window, + Bool is_start, + long mask, + void *cb_data); +static void gdk_xsettings_notify_cb (const char *name, + XSettingsAction action, + XSettingsSetting *setting, + void *data); /* Private variable declarations */ -static int connection_number = 0; /* The file descriptor number of our - * connection to the X server. This - * is used so that we may determine - * when events are pending by using - * the "select" system call. - */ -static GList *client_filters; /* Filters for client messages */ +static GList *display_sources; static GSourceFuncs event_funcs = { gdk_event_prepare, @@ -121,82 +121,123 @@ static GSourceFuncs event_funcs = { NULL }; -static GPollFD event_poll_fd; +static GSource * +gdk_display_source_new (GdkDisplay *display) +{ + GSource *source = g_source_new (&event_funcs, sizeof (GdkDisplaySource)); + GdkDisplaySource *display_source = (GdkDisplaySource *)source; + + display_source->display = display; + + return source; +} -static Window wmspec_check_window = None; +static gboolean +gdk_check_xpending (GdkDisplay *display) +{ + return XPending (GDK_DISPLAY_XDISPLAY (display)); +} /********************************************* * Functions for maintaining the event queue * *********************************************/ +void +_gdk_x11_events_init_screen (GdkScreen *screen) +{ + GdkScreenX11 *screen_x11 = GDK_SCREEN_X11 (screen); + + /* Keep a flag to avoid extra notifies that we don't need + */ + screen_x11->xsettings_in_init = TRUE; + screen_x11->xsettings_client = xsettings_client_new (screen_x11->xdisplay, + screen_x11->screen_num, + gdk_xsettings_notify_cb, + gdk_xsettings_watch_cb, + screen); + screen_x11->xsettings_in_init = FALSE; +} + +void +_gdk_x11_events_uninit_screen (GdkScreen *screen) +{ + GdkScreenX11 *screen_x11 = GDK_SCREEN_X11 (screen); + + xsettings_client_destroy (screen_x11->xsettings_client); + screen_x11->xsettings_client = NULL; +} + void -gdk_events_init (void) +_gdk_events_init (GdkDisplay *display) { GSource *source; + GdkDisplaySource *display_source; + GdkDisplayX11 *display_x11 = GDK_DISPLAY_X11 (display); - connection_number = ConnectionNumber (gdk_display); - GDK_NOTE (MISC, - g_message ("connection number: %d", connection_number)); + int connection_number = ConnectionNumber (display_x11->xdisplay); + GDK_NOTE (MISC, g_message ("connection number: %d", connection_number)); - source = g_source_new (&event_funcs, sizeof (GSource)); + source = display_x11->event_source = gdk_display_source_new (display); + display_source = (GdkDisplaySource*) source; g_source_set_priority (source, GDK_PRIORITY_EVENTS); - event_poll_fd.fd = connection_number; - event_poll_fd.events = G_IO_IN; + display_source->event_poll_fd.fd = connection_number; + display_source->event_poll_fd.events = G_IO_IN; - g_source_add_poll (source, &event_poll_fd); + g_source_add_poll (source, &display_source->event_poll_fd); g_source_set_can_recurse (source, TRUE); g_source_attach (source, NULL); - gdk_add_client_message_filter (gdk_wm_protocols, - gdk_wm_protocols_filter, NULL); + display_sources = g_list_prepend (display_sources,display_source); + + gdk_display_add_client_message_filter ( + display, + gdk_atom_intern ("WM_PROTOCOLS", FALSE), + gdk_wm_protocols_filter, + NULL); } -/* - *-------------------------------------------------------------- - * gdk_events_pending - * - * Returns if events are pending on the queue. - * - * Arguments: - * - * Results: - * Returns TRUE if events are pending - * - * Side effects: - * - *-------------------------------------------------------------- - */ +/** + * gdk_events_pending: + * + * Checks if any events are ready to be processed for any display. + * + * Return value: %TRUE if any events are pending. + **/ gboolean gdk_events_pending (void) { - return (gdk_event_queue_find_first() || XPending (gdk_display)); -} + GList *tmp_list; -/* - *-------------------------------------------------------------- - * gdk_event_get_graphics_expose - * - * Waits for a GraphicsExpose or NoExpose event - * - * Arguments: - * - * Results: - * For GraphicsExpose events, returns a pointer to the event - * converted into a GdkEvent Otherwise, returns NULL. - * - * Side effects: - * - *-------------------------------------------------------------- */ + for (tmp_list = display_sources; tmp_list; tmp_list = tmp_list->next) + { + GdkDisplaySource *tmp_source = tmp_list->data; + GdkDisplay *display = tmp_source->display; + + if (_gdk_event_queue_find_first (display)) + return TRUE; + } + + for (tmp_list = display_sources; tmp_list; tmp_list = tmp_list->next) + { + GdkDisplaySource *tmp_source = tmp_list->data; + GdkDisplay *display = tmp_source->display; + + if (gdk_check_xpending (display)) + return TRUE; + } + + return FALSE; +} static Bool graphics_expose_predicate (Display *display, XEvent *xevent, XPointer arg) { - if (xevent->xany.window == GDK_DRAWABLE_XID (arg) && + if (xevent->xany.window == GDK_DRAWABLE_XID ((GdkDrawable *)arg) && (xevent->xany.type == GraphicsExpose || xevent->xany.type == NoExpose)) return True; @@ -204,6 +245,17 @@ graphics_expose_predicate (Display *display, return False; } +/** + * gdk_event_get_graphics_expose: + * @window: the #GdkWindow to wait for the events for. + * + * Waits for a GraphicsExpose or NoExpose event from the X server. + * This is used in the #GtkText and #GtkCList widgets in GTK+ to make sure any + * GraphicsExpose events are handled before the widget is scrolled. + * + * Return value: a #GdkEventExpose if a GraphicsExpose was received, or %NULL if a + * NoExpose event was received. + **/ GdkEvent* gdk_event_get_graphics_expose (GdkWindow *window) { @@ -212,13 +264,15 @@ gdk_event_get_graphics_expose (GdkWindow *window) g_return_val_if_fail (window != NULL, NULL); - XIfEvent (gdk_display, &xevent, graphics_expose_predicate, (XPointer) window); + XIfEvent (GDK_WINDOW_XDISPLAY (window), &xevent, + graphics_expose_predicate, (XPointer) window); if (xevent.xany.type == GraphicsExpose) { - event = gdk_event_new (); + event = gdk_event_new (GDK_NOTHING); - if (gdk_event_translate (event, &xevent, TRUE)) + if (gdk_event_translate (GDK_WINDOW_DISPLAY (window), event, + &xevent, TRUE)) return event; else gdk_event_free (event); @@ -250,22 +304,57 @@ gdk_event_apply_filters (XEvent *xevent, return GDK_FILTER_CONTINUE; } +/** + * gdk_display_add_client_message_filter: + * @display: a #GdkDisplay for which this message filter applies + * @message_type: the type of ClientMessage events to receive. + * This will be checked against the @message_type field + * of the XClientMessage event struct. + * @func: the function to call to process the event. + * @data: user data to pass to @func. + * + * Adds a filter to be called when X ClientMessage events are received. + * + * Since: 2.2 + **/ void -gdk_add_client_message_filter (GdkAtom message_type, - GdkFilterFunc func, - gpointer data) +gdk_display_add_client_message_filter (GdkDisplay *display, + GdkAtom message_type, + GdkFilterFunc func, + gpointer data) { - GdkClientFilter *filter = g_new (GdkClientFilter, 1); + GdkClientFilter *filter; + g_return_if_fail (GDK_IS_DISPLAY (display)); + filter = g_new (GdkClientFilter, 1); filter->type = message_type; filter->function = func; filter->data = data; - client_filters = g_list_prepend (client_filters, filter); + GDK_DISPLAY_X11(display)->client_filters = + g_list_prepend (GDK_DISPLAY_X11 (display)->client_filters, + filter); } -static GdkAtom wm_state_atom = 0; -static GdkAtom wm_desktop_atom = 0; +/** + * gdk_add_client_message_filter: + * @message_type: the type of ClientMessage events to receive. This will be + * checked against the message_type field of the + * XClientMessage event struct. + * @func: the function to call to process the event. + * @data: user data to pass to @func. + * + * Adds a filter to the default display to be called when X ClientMessage events + * are received. See gdk_display_add_client_message_filter(). + **/ +void +gdk_add_client_message_filter (GdkAtom message_type, + GdkFilterFunc func, + gpointer data) +{ + gdk_display_add_client_message_filter (gdk_display_get_default (), + message_type, func, data); +} static void gdk_check_wm_state_changed (GdkWindow *window) @@ -274,39 +363,33 @@ gdk_check_wm_state_changed (GdkWindow *window) gint format; gulong nitems; gulong bytes_after; - GdkAtom *atoms = NULL; + Atom *atoms = NULL; gulong i; - GdkAtom sticky_atom; - GdkAtom maxvert_atom; - GdkAtom maxhorz_atom; - gboolean found_sticky, found_maxvert, found_maxhorz; + gboolean found_sticky, found_maxvert, found_maxhorz, found_fullscreen; GdkWindowState old_state; + GdkDisplay *display = GDK_WINDOW_DISPLAY (window); - if (GDK_WINDOW_DESTROYED (window)) + if (GDK_WINDOW_DESTROYED (window) || + gdk_window_get_window_type (window) != GDK_WINDOW_TOPLEVEL) 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); + found_sticky = FALSE; + found_maxvert = FALSE; + found_maxhorz = FALSE; + found_fullscreen = FALSE; XGetWindowProperty (GDK_WINDOW_XDISPLAY (window), GDK_WINDOW_XID (window), - wm_state_atom, 0, G_MAXLONG, - False, XA_ATOM, &type, &format, &nitems, + gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_STATE"), + 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; - + Atom sticky_atom = gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_STATE_STICKY"); + Atom maxvert_atom = gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_STATE_MAXIMIZED_VERT"); + Atom maxhorz_atom = gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_STATE_MAXIMIZED_HORZ"); + Atom fullscreen_atom = gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_STATE_FULLSCREEN"); + i = 0; while (i < nitems) { @@ -316,18 +399,14 @@ gdk_check_wm_state_changed (GdkWindow *window) found_maxvert = TRUE; else if (atoms[i] == maxhorz_atom) found_maxhorz = TRUE; - + else if (atoms[i] == fullscreen_atom) + found_fullscreen = 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 @@ -337,9 +416,12 @@ gdk_check_wm_state_changed (GdkWindow *window) { gulong *desktop; - XGetWindowProperty (GDK_WINDOW_XDISPLAY (window), GDK_WINDOW_XID (window), - wm_desktop_atom, 0, G_MAXLONG, - False, XA_CARDINAL, &type, &format, &nitems, + XGetWindowProperty (GDK_WINDOW_XDISPLAY (window), + GDK_WINDOW_XID (window), + gdk_x11_get_xatom_by_name_for_display + (display, "_NET_WM_DESKTOP"), + 0, G_MAXLONG, False, XA_CARDINAL, &type, + &format, &nitems, &bytes_after, (guchar **)&desktop); if (type != None) @@ -367,6 +449,21 @@ gdk_check_wm_state_changed (GdkWindow *window) GDK_WINDOW_STATE_STICKY); } + if (old_state & GDK_WINDOW_STATE_FULLSCREEN) + { + if (!found_fullscreen) + gdk_synthesize_window_state (window, + GDK_WINDOW_STATE_FULLSCREEN, + 0); + } + else + { + if (found_fullscreen) + gdk_synthesize_window_state (window, + 0, + GDK_WINDOW_STATE_FULLSCREEN); + } + /* Our "maximized" means both vertical and horizontal; if only one, * we don't expose that via GDK */ @@ -386,34 +483,246 @@ gdk_check_wm_state_changed (GdkWindow *window) } } -static gint -gdk_event_translate (GdkEvent *event, - XEvent *xevent, - gboolean return_exposes) +#define HAS_FOCUS(window_impl) \ + ((window_impl)->has_focus || (window_impl)->has_pointer_focus) + +static void +generate_focus_event (GdkWindow *window, + gboolean in) +{ + GdkEvent event; + + event.type = GDK_FOCUS_CHANGE; + event.focus_change.window = window; + event.focus_change.send_event = FALSE; + event.focus_change.in = in; + + gdk_event_put (&event); +} + +static void +set_screen_from_root (GdkDisplay *display, + GdkEvent *event, + Window xrootwin) +{ + GdkScreen *screen; + + screen = _gdk_x11_display_screen_for_xrootwin (display, xrootwin); + g_assert (screen); + + gdk_event_set_screen (event, screen); +} + +static void +translate_key_event (GdkDisplay *display, + GdkEvent *event, + XEvent *xevent) +{ + GdkKeymap *keymap = gdk_keymap_get_for_display (display); + gunichar c = 0; + guchar buf[7]; + + event->key.type = xevent->xany.type == KeyPress ? GDK_KEY_PRESS : GDK_KEY_RELEASE; + event->key.time = xevent->xkey.time; + + event->key.state = (GdkModifierType) xevent->xkey.state; + event->key.group = _gdk_x11_get_group_for_state (display, xevent->xkey.state); + event->key.hardware_keycode = xevent->xkey.keycode; + + event->key.keyval = GDK_VoidSymbol; + + gdk_keymap_translate_keyboard_state (keymap, + event->key.hardware_keycode, + event->key.state, + event->key.group, + &event->key.keyval, + NULL, NULL, NULL); + + /* Fill in event->string crudely, since various programs + * depend on it. + */ + event->key.string = NULL; + + if (event->key.keyval != GDK_VoidSymbol) + c = gdk_keyval_to_unicode (event->key.keyval); + + if (c) + { + gsize bytes_written; + gint len; + + /* Apply the control key - Taken from Xlib + */ + if (event->key.state & GDK_CONTROL_MASK) + { + if ((c >= '@' && c < '\177') || c == ' ') c &= 0x1F; + else if (c == '2') + { + event->key.string = g_memdup ("\0\0", 2); + event->key.length = 1; + buf[0] = '\0'; + goto out; + } + else if (c >= '3' && c <= '7') c -= ('3' - '\033'); + else if (c == '8') c = '\177'; + else if (c == '/') c = '_' & 0x1F; + } + + len = g_unichar_to_utf8 (c, buf); + buf[len] = '\0'; + + event->key.string = g_locale_from_utf8 (buf, len, + NULL, &bytes_written, + NULL); + if (event->key.string) + event->key.length = bytes_written; + } + else if (event->key.keyval == GDK_Escape) + { + event->key.length = 1; + event->key.string = g_strdup ("\033"); + } + else if (event->key.keyval == GDK_Return || + event->key.keyval == GDK_KP_Enter) + { + event->key.length = 1; + event->key.string = g_strdup ("\r"); + } + + if (!event->key.string) + { + event->key.length = 0; + event->key.string = g_strdup (""); + } + + out: +#ifdef G_ENABLE_DEBUG + if (_gdk_debug_flags & GDK_DEBUG_EVENTS) + { + g_message ("%s:\t\twindow: %ld key: %12s %d", + event->type == GDK_KEY_PRESS ? "key press " : "key release", + xevent->xkey.window, + event->key.keyval ? gdk_keyval_name (event->key.keyval) : "(none)", + event->key.keyval); + + if (event->key.length > 0) + g_message ("\t\tlength: %4d string: \"%s\"", + event->key.length, buf); + } +#endif /* G_ENABLE_DEBUG */ +} + +/* Return the window this has to do with, if any, rather + * than the frame or root window that was selecting + * for substructure + */ +static Window +get_real_window (XEvent *event) +{ + switch (event->type) + { + case CreateNotify: + return event->xcreatewindow.window; + + case DestroyNotify: + return event->xdestroywindow.window; + + case UnmapNotify: + return event->xunmap.window; + + case MapNotify: + return event->xmap.window; + + case MapRequest: + return event->xmaprequest.window; + + case ReparentNotify: + return event->xreparent.window; + + case ConfigureNotify: + return event->xconfigure.window; + + case ConfigureRequest: + return event->xconfigurerequest.window; + + case GravityNotify: + return event->xgravity.window; + + case CirculateNotify: + return event->xcirculate.window; + + case CirculateRequest: + return event->xcirculaterequest.window; + + default: + return event->xany.window; + } +} + +#ifdef G_ENABLE_DEBUG +static const char notify_modes[][18] = { + "NotifyNormal", + "NotifyGrab", + "NotifyUngrab", + "NotifyWhileGrabbed" +}; + +static const char notify_details[][22] = { + "NotifyAncestor", + "NotifyVirtual", + "NotifyInferior", + "NotifyNonlinear", + "NotifyNonlinearVirtual", + "NotifyPointer", + "NotifyPointerRoot", + "NotifyDetailNone" +}; +#endif + +static gboolean +gdk_event_translate (GdkDisplay *display, + GdkEvent *event, + XEvent *xevent, + gboolean return_exposes) { GdkWindow *window; GdkWindowObject *window_private; - static XComposeStatus compose; - KeySym keysym; - int charcount; -#ifdef USE_XIM - static gchar* buf = NULL; - static gint buf_len= 0; -#else - char buf[16]; -#endif + GdkWindow *filter_window; + GdkWindowImplX11 *window_impl = NULL; gint return_val; gint xoffset, yoffset; + GdkScreen *screen = NULL; + GdkScreenX11 *screen_x11 = NULL; + GdkDisplayX11 *display_x11 = GDK_DISPLAY_X11 (display); + Window xwindow; return_val = FALSE; - - /* Find the GdkWindow that this event occurred in. - * - * We handle events with window=None - * specially - they are generated by XFree86's XInput under - * some circumstances. - */ + + /* init these, since the done: block uses them */ + window = NULL; + window_private = NULL; + event->any.window = NULL; + + if (_gdk_default_filters) + { + /* Apply global filters */ + GdkFilterReturn result; + result = gdk_event_apply_filters (xevent, event, + _gdk_default_filters); + + if (result != GDK_FILTER_CONTINUE) + { + return_val = (result == GDK_FILTER_TRANSLATE) ? TRUE : FALSE; + goto done; + } + } + + /* We handle events with window=None + * specially - they are generated by XFree86's XInput under + * some circumstances. This handling for obvious reasons + * goes before we bother to lookup the event window. + */ if (xevent->xany.window == None) { @@ -424,88 +733,130 @@ gdk_event_translate (GdkEvent *event, else return_val = FALSE; } + + /* Find the GdkWindow that this event relates to. + * Basically this means substructure events + * are reported same as structure events + */ + xwindow = get_real_window (xevent); - window = gdk_window_lookup (xevent->xany.window); - /* FIXME: window might be a GdkPixmap!!! */ - + window = gdk_window_lookup_for_display (display, xwindow); window_private = (GdkWindowObject *) window; - - if (window != NULL) - gdk_window_ref (window); - if (wmspec_check_window != None && - xevent->xany.window == wmspec_check_window) + /* We always run the filters for the window where the event + * is delivered, not the window that it relates to + */ + if (xevent->xany.window == xwindow) + filter_window = window; + else + filter_window = gdk_window_lookup_for_display (display, xevent->xany.window); + + if (window) { - if (xevent->type == DestroyNotify) - wmspec_check_window = None; - - /* Eat events on this window unless someone had wrapped - * it as a foreign window + screen = GDK_WINDOW_SCREEN (window); + screen_x11 = GDK_SCREEN_X11 (screen); + } + + if (window != NULL) + { + /* Window may be a pixmap, so check its type. + * (This check is probably too expensive unless + * GLib short-circuits an exact type match, + * which has been proposed) */ - if (window == NULL) - return FALSE; + if (GDK_IS_WINDOW (window)) + { + window_impl = GDK_WINDOW_IMPL_X11 (window_private->impl); + + /* Move key events on focus window to the real toplevel, and + * filter out all other events on focus window + */ + if (xwindow == window_impl->focus_window) + { + switch (xevent->type) + { + case KeyPress: + case KeyRelease: + xwindow = GDK_WINDOW_XID (window); + xevent->xany.window = xwindow; + break; + default: + return FALSE; + } + } + } + + g_object_ref (window); } - + event->any.window = window; event->any.send_event = xevent->xany.send_event ? TRUE : FALSE; if (window_private && GDK_WINDOW_DESTROYED (window)) { if (xevent->type != DestroyNotify) - return FALSE; - } - else - { - /* Check for filters for this window - */ - GdkFilterReturn result; - result = gdk_event_apply_filters (xevent, event, - window_private - ?window_private->filters - :gdk_default_filters); - - if (result != GDK_FILTER_CONTINUE) { - return_val = (result == GDK_FILTER_TRANSLATE) ? TRUE : FALSE; + return_val = FALSE; goto done; } } - -#ifdef USE_XIM - if (window == NULL && gdk_xim_window && xevent->type == KeyPress && - !GDK_WINDOW_DESTROYED (gdk_xim_window)) + else if (filter_window) { - /* - * If user presses a key in Preedit or Status window, keypress event - * is sometimes sent to these windows. These windows are not managed - * by GDK, so we redirect KeyPress event to xim_window. - * - * If someone want to use the window whitch is not managed by GDK - * and want to get KeyPress event, he/she must register the filter - * function to gdk_default_filters to intercept the event. - */ - + /* Apply per-window filters */ + GdkWindowObject *filter_private = (GdkWindowObject *) filter_window; GdkFilterReturn result; - window = gdk_xim_window; - window_private = (GdkWindowObject *) window; - gdk_window_ref (window); - event->any.window = window; - - GDK_NOTE (XIM, - g_message ("KeyPress event is redirected to xim_window: %#lx", - xevent->xany.window)); + if (filter_private->filters) + { + g_object_ref (filter_window); + + result = gdk_event_apply_filters (xevent, event, + filter_private->filters); + + g_object_unref (filter_window); + + if (result != GDK_FILTER_CONTINUE) + { + return_val = (result == GDK_FILTER_TRANSLATE) ? TRUE : FALSE; + goto done; + } + } + } + + if (screen_x11 && screen_x11->wmspec_check_window != None && + xwindow == screen_x11->wmspec_check_window) + { + if (xevent->type == DestroyNotify) + { + screen_x11->wmspec_check_window = None; + g_free (screen_x11->window_manager_name); + screen_x11->window_manager_name = g_strdup ("unknown"); - result = gdk_event_apply_filters (xevent, event, - window_private->filters); - if (result != GDK_FILTER_CONTINUE) + /* careful, reentrancy */ + _gdk_x11_screen_window_manager_changed (GDK_SCREEN (screen_x11)); + } + + /* Eat events on this window unless someone had wrapped + * it as a foreign window + */ + if (window == NULL) { - return_val = (result == GDK_FILTER_TRANSLATE) ? TRUE : FALSE; + return_val = FALSE; goto done; } } -#endif + if (window && + (xevent->xany.type == MotionNotify || + xevent->xany.type == ButtonRelease)) + { + if (_gdk_moveresize_handle_event (xevent)) + { + return_val = FALSE; + goto done; + } + } + /* We do a "manual" conversion of the XEvent to a * GdkEvent. The structures are mostly the same so * the conversion is fairly straightforward. We also @@ -528,134 +879,42 @@ gdk_event_translate (GdkEvent *event, switch (xevent->type) { case KeyPress: - /* Lookup the string corresponding to the given keysym. - */ - -#ifdef USE_XIM - if (buf_len == 0) - { - buf_len = 128; - buf = g_new (gchar, buf_len); - } - keysym = GDK_VoidSymbol; - - if (gdk_xim_ic && gdk_xim_ic->xic) - { - Status status; - - /* Clear keyval. Depending on status, may not be set */ - charcount = XmbLookupString(gdk_xim_ic->xic, - &xevent->xkey, buf, buf_len-1, - &keysym, &status); - if (status == XBufferOverflow) - { /* retry */ - /* alloc adequate size of buffer */ - GDK_NOTE (XIM, - g_message("XIM: overflow (required %i)", charcount)); - - while (buf_len <= charcount) - buf_len *= 2; - buf = (gchar *) g_realloc (buf, buf_len); - - charcount = XmbLookupString (gdk_xim_ic->xic, - &xevent->xkey, buf, buf_len-1, - &keysym, &status); - } - if (status == XLookupNone) - { - return_val = FALSE; - break; - } - } - else - charcount = XLookupString (&xevent->xkey, buf, buf_len, - &keysym, &compose); -#else - charcount = XLookupString (&xevent->xkey, buf, 16, - &keysym, &compose); -#endif - event->key.keyval = keysym; - event->key.hardware_keycode = xevent->xkey.keycode; - - if (charcount > 0 && buf[charcount-1] == '\0') - charcount --; - else - buf[charcount] = '\0'; - -#ifdef G_ENABLE_DEBUG - if (gdk_debug_flags & GDK_DEBUG_EVENTS) - { - g_message ("key press:\twindow: %ld key: %12s %d", - xevent->xkey.window, - event->key.keyval ? XKeysymToString (event->key.keyval) : "(none)", - event->key.keyval); - if (charcount > 0) - g_message ("\t\tlength: %4d string: \"%s\"", - charcount, buf); - } -#endif /* G_ENABLE_DEBUG */ - - /* bits 13 and 14 in the "state" field are the keyboard group */ -#define KEYBOARD_GROUP_SHIFT 13 -#define KEYBOARD_GROUP_MASK ((1 << 13) | (1 << 14)) - - event->key.type = GDK_KEY_PRESS; - event->key.window = window; - event->key.time = xevent->xkey.time; - event->key.state = (GdkModifierType) xevent->xkey.state; - event->key.string = g_strdup (buf); - event->key.length = charcount; - - event->key.group = (xevent->xkey.state & KEYBOARD_GROUP_MASK) >> KEYBOARD_GROUP_SHIFT; - + if (window_private == NULL) + { + return_val = FALSE; + break; + } + translate_key_event (display, event, xevent); break; - - case KeyRelease: - /* Lookup the string corresponding to the given keysym. - */ + case KeyRelease: + if (window_private == NULL) + { + return_val = FALSE; + break; + } + /* Emulate detectable auto-repeat by checking to see * if the next event is a key press with the same * keycode and timestamp, and if so, ignoring the event. */ - if (!_gdk_have_xkb_autorepeat && XPending (gdk_display)) + if (!display_x11->have_xkb_autorepeat && XPending (xevent->xkey.display)) { XEvent next_event; - XPeekEvent (gdk_display, &next_event); + XPeekEvent (xevent->xkey.display, &next_event); if (next_event.type == KeyPress && next_event.xkey.keycode == xevent->xkey.keycode && next_event.xkey.time == xevent->xkey.time) - break; - } - -#ifdef USE_XIM - if (buf_len == 0) - { - buf_len = 128; - buf = g_new (gchar, buf_len); + { + return_val = FALSE; + break; + } } -#endif - keysym = GDK_VoidSymbol; - charcount = XLookupString (&xevent->xkey, buf, 16, - &keysym, &compose); - event->key.keyval = keysym; - - GDK_NOTE (EVENTS, - g_message ("key release:\t\twindow: %ld key: %12s %d", - xevent->xkey.window, - XKeysymToString (event->key.keyval), - event->key.keyval)); - - event->key.type = GDK_KEY_RELEASE; - event->key.window = window; - event->key.time = xevent->xkey.time; - event->key.state = (GdkModifierType) xevent->xkey.state; - event->key.length = 0; - event->key.string = NULL; - + + translate_key_event (display, event, xevent); break; case ButtonPress: @@ -665,9 +924,9 @@ gdk_event_translate (GdkEvent *event, xevent->xbutton.x, xevent->xbutton.y, xevent->xbutton.button)); - if (window_private && - (window_private->extension_events != 0) && - gdk_input_ignore_core) + if (window_private == NULL || + ((window_private->extension_events != 0) && + display_x11->input_ignore_core)) { return_val = FALSE; break; @@ -675,22 +934,37 @@ gdk_event_translate (GdkEvent *event, /* If we get a ButtonPress event where the button is 4 or 5, it's a Scroll event */ - if (xevent->xbutton.button == 4 || xevent->xbutton.button == 5) - { + switch (xevent->xbutton.button) + { + case 4: /* up */ + case 5: /* down */ + case 6: /* left */ + case 7: /* right */ event->scroll.type = GDK_SCROLL; - event->scroll.direction = (xevent->xbutton.button == 4) ? - GDK_SCROLL_UP : GDK_SCROLL_DOWN; + + if (xevent->xbutton.button == 4) + event->scroll.direction = GDK_SCROLL_UP; + else if (xevent->xbutton.button == 5) + event->scroll.direction = GDK_SCROLL_DOWN; + else if (xevent->xbutton.button == 6) + event->scroll.direction = GDK_SCROLL_LEFT; + else + event->scroll.direction = GDK_SCROLL_RIGHT; + event->scroll.window = window; - event->scroll.time = xevent->xbutton.x; + event->scroll.time = xevent->xbutton.time; event->scroll.x = xevent->xbutton.x + xoffset; event->scroll.y = xevent->xbutton.y + yoffset; event->scroll.x_root = (gfloat)xevent->xbutton.x_root; event->scroll.y_root = (gfloat)xevent->xbutton.y_root; event->scroll.state = (GdkModifierType) xevent->xbutton.state; - event->scroll.device = gdk_core_pointer; - } - else - { + event->scroll.device = display->core_pointer; + + set_screen_from_root (display, event, xevent->xbutton.root); + + break; + + default: event->button.type = GDK_BUTTON_PRESS; event->button.window = window; event->button.time = xevent->xbutton.time; @@ -701,9 +975,12 @@ gdk_event_translate (GdkEvent *event, event->button.axes = NULL; event->button.state = (GdkModifierType) xevent->xbutton.state; event->button.button = xevent->xbutton.button; - event->button.device = gdk_core_pointer; + event->button.device = display->core_pointer; - gdk_event_button_generate (event); + set_screen_from_root (display, event, xevent->xbutton.root); + + _gdk_event_button_generate (display, event); + break; } break; @@ -715,16 +992,17 @@ gdk_event_translate (GdkEvent *event, xevent->xbutton.x, xevent->xbutton.y, xevent->xbutton.button)); - if (window_private && - (window_private->extension_events != 0) && - gdk_input_ignore_core) + if (window_private == NULL || + ((window_private->extension_events != 0) && + display_x11->input_ignore_core)) { return_val = FALSE; break; } /* We treat button presses as scroll wheel events, so ignore the release */ - if (xevent->xbutton.button == 4 || xevent->xbutton.button == 5) + if (xevent->xbutton.button == 4 || xevent->xbutton.button == 5 || + xevent->xbutton.button == 6 || xevent->xbutton.button ==7) { return_val = FALSE; break; @@ -740,7 +1018,9 @@ gdk_event_translate (GdkEvent *event, event->button.axes = NULL; event->button.state = (GdkModifierType) xevent->xbutton.state; event->button.button = xevent->xbutton.button; - event->button.device = gdk_core_pointer; + event->button.device = display->core_pointer; + + set_screen_from_root (display, event, xevent->xbutton.root); break; @@ -751,9 +1031,9 @@ gdk_event_translate (GdkEvent *event, xevent->xmotion.x, xevent->xmotion.y, (xevent->xmotion.is_hint) ? "true" : "false")); - if (window_private && - (window_private->extension_events != 0) && - gdk_input_ignore_core) + if (window_private == NULL || + ((window_private->extension_events != 0) && + display_x11->input_ignore_core)) { return_val = FALSE; break; @@ -769,7 +1049,9 @@ gdk_event_translate (GdkEvent *event, event->motion.axes = NULL; event->motion.state = (GdkModifierType) xevent->xmotion.state; event->motion.is_hint = xevent->xmotion.is_hint; - event->motion.device = gdk_core_pointer; + event->motion.device = display->core_pointer; + + set_screen_from_root (display, event, xevent->xmotion.root); break; @@ -779,7 +1061,27 @@ gdk_event_translate (GdkEvent *event, xevent->xcrossing.window, xevent->xcrossing.detail, xevent->xcrossing.subwindow)); + + if (window_private == NULL) + { + return_val = FALSE; + break; + } + /* Handle focusing (in the case where no window manager is running */ + if (window && + GDK_WINDOW_TYPE (window) != GDK_WINDOW_CHILD && + xevent->xcrossing.detail != NotifyInferior && + xevent->xcrossing.focus && !window_impl->has_focus_window) + { + gboolean had_focus = HAS_FOCUS (window_impl); + + window_impl->has_pointer_focus = TRUE; + + if (HAS_FOCUS (window_impl) != had_focus) + generate_focus_event (window, TRUE); + } + /* Tell XInput stuff about it if appropriate */ if (window_private && !GDK_WINDOW_DESTROYED (window) && @@ -793,7 +1095,7 @@ gdk_event_translate (GdkEvent *event, * lookup the corresponding GdkWindow. */ if (xevent->xcrossing.subwindow != None) - event->crossing.subwindow = gdk_window_lookup (xevent->xcrossing.subwindow); + event->crossing.subwindow = gdk_window_lookup_for_display (display, xevent->xcrossing.subwindow); else event->crossing.subwindow = NULL; @@ -803,6 +1105,8 @@ gdk_event_translate (GdkEvent *event, event->crossing.x_root = xevent->xcrossing.x_root; event->crossing.y_root = xevent->xcrossing.y_root; + set_screen_from_root (display, event, xevent->xcrossing.root); + /* Translate the crossing mode into Gdk terms. */ switch (xevent->xcrossing.mode) @@ -852,7 +1156,27 @@ gdk_event_translate (GdkEvent *event, g_message ("leave notify:\t\twindow: %ld detail: %d subwin: %ld", xevent->xcrossing.window, xevent->xcrossing.detail, xevent->xcrossing.subwindow)); + + if (window_private == NULL) + { + return_val = FALSE; + break; + } + /* Handle focusing (in the case where no window manager is running */ + if (window && + GDK_WINDOW_TYPE (window) != GDK_WINDOW_CHILD && + xevent->xcrossing.detail != NotifyInferior && + xevent->xcrossing.focus && !window_impl->has_focus_window) + { + gboolean had_focus = HAS_FOCUS (window_impl); + + window_impl->has_pointer_focus = FALSE; + + if (HAS_FOCUS (window_impl) != had_focus) + generate_focus_event (window, FALSE); + } + event->crossing.type = GDK_LEAVE_NOTIFY; event->crossing.window = window; @@ -860,7 +1184,7 @@ gdk_event_translate (GdkEvent *event, * lookup the corresponding GdkWindow. */ if (xevent->xcrossing.subwindow != None) - event->crossing.subwindow = gdk_window_lookup (xevent->xcrossing.subwindow); + event->crossing.subwindow = gdk_window_lookup_for_display (display, xevent->xcrossing.subwindow); else event->crossing.subwindow = NULL; @@ -870,6 +1194,8 @@ gdk_event_translate (GdkEvent *event, event->crossing.x_root = xevent->xcrossing.x_root; event->crossing.y_root = xevent->xcrossing.y_root; + set_screen_from_root (display, event, xevent->xcrossing.root); + /* Translate the crossing mode into Gdk terms. */ switch (xevent->xcrossing.mode) @@ -914,38 +1240,97 @@ gdk_event_translate (GdkEvent *event, break; - case FocusIn: - case FocusOut: /* We only care about focus events that indicate that _this_ * window (not a ancestor or child) got or lost the focus */ - switch (xevent->xfocus.detail) + case FocusIn: + GDK_NOTE (EVENTS, + g_message ("focus in:\t\twindow: %ld, detail: %s, mode: %s", + xevent->xfocus.window, + notify_details[xevent->xfocus.detail], + notify_modes[xevent->xfocus.mode])); + + if (window && GDK_WINDOW_TYPE (window) != GDK_WINDOW_CHILD) { - case NotifyAncestor: - case NotifyInferior: - case NotifyNonlinear: - GDK_NOTE (EVENTS, - g_message ("focus %s:\t\twindow: %ld", - (xevent->xany.type == FocusIn) ? "in" : "out", - xevent->xfocus.window)); + gboolean had_focus = HAS_FOCUS (window_impl); + switch (xevent->xfocus.detail) + { + case NotifyAncestor: + case NotifyNonlinear: + case NotifyVirtual: + case NotifyNonlinearVirtual: + window_impl->has_focus_window = TRUE; + /* We pretend that the focus moves to the grab + * window, so we pay attention to NotifyGrab + * NotifyUngrab, and ignore NotifyWhileGrabbed + */ + if (xevent->xfocus.mode != NotifyWhileGrabbed) + window_impl->has_focus = TRUE; + break; + case NotifyPointer: + /* The X server sends NotifyPointer/NotifyGrab, + * but the pointer focus is ignored while a + * grab is in effect + */ + if (xevent->xfocus.mode != NotifyGrab) + window_impl->has_pointer_focus = TRUE; + break; + case NotifyInferior: + case NotifyPointerRoot: + case NotifyDetailNone: + break; + } + + if (HAS_FOCUS (window_impl) != had_focus) + generate_focus_event (window, TRUE); + } + break; + case FocusOut: + GDK_NOTE (EVENTS, + g_message ("focus out:\t\twindow: %ld, detail: %s, mode: %s", + xevent->xfocus.window, + notify_details[xevent->xfocus.detail], + notify_modes[xevent->xfocus.mode])); + + if (window && GDK_WINDOW_TYPE (window) != GDK_WINDOW_CHILD) + { + gboolean had_focus = HAS_FOCUS (window_impl); + + switch (xevent->xfocus.detail) + { + case NotifyAncestor: + case NotifyNonlinear: + case NotifyVirtual: + case NotifyNonlinearVirtual: + window_impl->has_focus_window = FALSE; + if (xevent->xfocus.mode != NotifyWhileGrabbed) + window_impl->has_focus = FALSE; + break; + case NotifyPointer: + if (xevent->xfocus.mode != NotifyUngrab) + window_impl->has_pointer_focus = FALSE; + break; + case NotifyInferior: + case NotifyPointerRoot: + case NotifyDetailNone: + break; + } + + if (HAS_FOCUS (window_impl) != had_focus) + generate_focus_event (window, FALSE); + } + break; + +#if 0 /* gdk_keyboard_grab() causes following events. These events confuse * the XIM focus, so ignore them. */ if (xevent->xfocus.mode == NotifyGrab || xevent->xfocus.mode == NotifyUngrab) break; - - event->focus_change.type = GDK_FOCUS_CHANGE; - event->focus_change.window = window; - event->focus_change.in = (xevent->xany.type == FocusIn); +#endif - break; - default: - return_val = FALSE; - } - break; - case KeymapNotify: GDK_NOTE (EVENTS, g_message ("keymap notify")); @@ -961,6 +1346,13 @@ gdk_event_translate (GdkEvent *event, xevent->xexpose.x, xevent->xexpose.y, xevent->xexpose.width, xevent->xexpose.height, event->any.send_event ? " (send)" : "")); + + if (window_private == NULL) + { + return_val = FALSE; + break; + } + { GdkRectangle expose_rect; @@ -973,6 +1365,7 @@ gdk_event_translate (GdkEvent *event, { event->expose.type = GDK_EXPOSE; event->expose.area = expose_rect; + event->expose.region = gdk_region_rectangle (&expose_rect); event->expose.window = window; event->expose.count = xevent->xexpose.count; @@ -981,7 +1374,6 @@ gdk_event_translate (GdkEvent *event, else { _gdk_window_process_expose (window, xevent->xexpose.serial, &expose_rect); - return_val = FALSE; } @@ -997,7 +1389,13 @@ gdk_event_translate (GdkEvent *event, GDK_NOTE (EVENTS, g_message ("graphics expose:\tdrawable: %ld", xevent->xgraphicsexpose.drawable)); - + + if (window_private == NULL) + { + return_val = FALSE; + break; + } + expose_rect.x = xevent->xgraphicsexpose.x + xoffset; expose_rect.y = xevent->xgraphicsexpose.y + yoffset; expose_rect.width = xevent->xgraphicsexpose.width; @@ -1007,6 +1405,7 @@ gdk_event_translate (GdkEvent *event, { event->expose.type = GDK_EXPOSE; event->expose.area = expose_rect; + event->expose.region = gdk_region_rectangle (&expose_rect); event->expose.window = window; event->expose.count = xevent->xgraphicsexpose.count; @@ -1034,7 +1433,7 @@ gdk_event_translate (GdkEvent *event, case VisibilityNotify: #ifdef G_ENABLE_DEBUG - if (gdk_debug_flags & GDK_DEBUG_EVENTS) + if (_gdk_debug_flags & GDK_DEBUG_EVENTS) switch (xevent->xvisibility.state) { case VisibilityFullyObscured: @@ -1052,6 +1451,12 @@ gdk_event_translate (GdkEvent *event, } #endif /* G_ENABLE_DEBUG */ + if (window_private == NULL) + { + return_val = FALSE; + break; + } + event->visibility.type = GDK_VISIBILITY_NOTIFY; event->visibility.window = window; @@ -1090,14 +1495,21 @@ gdk_event_translate (GdkEvent *event, GDK_NOTE (EVENTS, g_message ("destroy notify:\twindow: %ld", xevent->xdestroywindow.window)); + + /* Ignore DestroyNotify from SubstructureNotifyMask */ + if (xevent->xdestroywindow.window == xevent->xdestroywindow.event) + { + event->any.type = GDK_DESTROY; + event->any.window = window; + + return_val = window_private && !GDK_WINDOW_DESTROYED (window); + + if (window && GDK_WINDOW_XID (window) != screen_x11->xroot_window) + gdk_window_destroy_notify (window); + } + else + return_val = FALSE; - event->any.type = GDK_DESTROY; - event->any.window = window; - - return_val = window_private && !GDK_WINDOW_DESTROYED (window); - - if (window && GDK_WINDOW_XID (window) != GDK_ROOT_WINDOW()) - gdk_window_destroy_notify (window); break; case UnmapNotify: @@ -1113,13 +1525,15 @@ gdk_event_translate (GdkEvent *event, * 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; + if (window) + { + if (GDK_WINDOW_IS_MAPPED (window)) + gdk_synthesize_window_state (window, + 0, + GDK_WINDOW_STATE_ICONIFIED); + + _gdk_xgrab_check_unmap (window, xevent->xany.serial); + } break; @@ -1132,7 +1546,7 @@ gdk_event_translate (GdkEvent *event, event->any.window = window; /* Unset iconified if it was set */ - if (((GdkWindowObject*)window)->state & GDK_WINDOW_STATE_ICONIFIED) + if (window && (((GdkWindowObject*)window)->state & GDK_WINDOW_STATE_ICONIFIED)) gdk_synthesize_window_state (window, GDK_WINDOW_STATE_ICONIFIED, 0); @@ -1167,13 +1581,22 @@ gdk_event_translate (GdkEvent *event, ? " (discarding)" : GDK_WINDOW_TYPE (window) == GDK_WINDOW_CHILD ? " (discarding child)" + : xevent->xconfigure.event != xevent->xconfigure.window + ? " (discarding substructure)" : "")); + if (window && GDK_WINDOW_TYPE (window) == GDK_WINDOW_ROOT) + _gdk_x11_screen_size_changed (screen, xevent); + if (window && + xevent->xconfigure.event == xevent->xconfigure.window && !GDK_WINDOW_DESTROYED (window) && (window_private->extension_events != 0)) _gdk_input_configure_event (&xevent->xconfigure, window); - if (!window || GDK_WINDOW_TYPE (window) == GDK_WINDOW_CHILD) + if (!window || + xevent->xconfigure.event != xevent->xconfigure.window || + GDK_WINDOW_TYPE (window) == GDK_WINDOW_CHILD || + GDK_WINDOW_TYPE (window) == GDK_WINDOW_ROOT) return_val = FALSE; else { @@ -1182,8 +1605,7 @@ gdk_event_translate (GdkEvent *event, event->configure.width = xevent->xconfigure.width; event->configure.height = xevent->xconfigure.height; - if (!xevent->xconfigure.x && - !xevent->xconfigure.y && + if (!xevent->xconfigure.send_event && !GDK_WINDOW_DESTROYED (window)) { gint tx = 0; @@ -1193,7 +1615,7 @@ gdk_event_translate (GdkEvent *event, gdk_error_trap_push (); if (XTranslateCoordinates (GDK_DRAWABLE_XDISPLAY (window), GDK_DRAWABLE_XID (window), - gdk_root_window, + screen_x11->xroot_window, 0, 0, &tx, &ty, &child_window)) @@ -1216,42 +1638,49 @@ gdk_event_translate (GdkEvent *event, window_private->y = event->configure.y; GDK_WINDOW_IMPL_X11 (window_private->impl)->width = xevent->xconfigure.width; GDK_WINDOW_IMPL_X11 (window_private->impl)->height = xevent->xconfigure.height; - if (window_private->resize_count > 1) - window_private->resize_count -= 1; + if (window_private->resize_count >= 1) + { + window_private->resize_count -= 1; + + if (window_private->resize_count == 0) + _gdk_moveresize_configure_done (display, window); + } } break; case PropertyNotify: GDK_NOTE (EVENTS, - gchar *atom = gdk_atom_name (xevent->xproperty.atom); g_message ("property notify:\twindow: %ld, atom(%ld): %s%s%s", xevent->xproperty.window, xevent->xproperty.atom, - atom ? "\"" : "", - atom ? atom : "unknown", - atom ? "\"" : ""); - g_free (atom); - ); - - event->property.type = GDK_PROPERTY_NOTIFY; - event->property.window = window; - 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) + "\"", + gdk_x11_get_xatom_name_for_display (display, xevent->xproperty.atom), + "\"")); + + if (window_private == NULL) + { + return_val = FALSE; + break; + } + + if (xevent->xproperty.atom == gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_STATE") || + xevent->xproperty.atom == gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_DESKTOP")) { /* If window state changed, then synthesize those events. */ - gdk_check_wm_state_changed (event->property.window); + gdk_check_wm_state_changed (window); } + if (window_private->event_mask & GDK_PROPERTY_CHANGE_MASK) + { + event->property.type = GDK_PROPERTY_NOTIFY; + event->property.window = window; + event->property.atom = gdk_x11_xatom_to_atom_for_display (display, xevent->xproperty.atom); + event->property.time = xevent->xproperty.time; + event->property.state = xevent->xproperty.state; + } + else + return_val = FALSE; + break; case SelectionClear: @@ -1263,7 +1692,7 @@ gdk_event_translate (GdkEvent *event, { event->selection.type = GDK_SELECTION_CLEAR; event->selection.window = window; - event->selection.selection = xevent->xselectionclear.selection; + event->selection.selection = gdk_x11_xatom_to_atom_for_display (display, xevent->xselectionclear.selection); event->selection.time = xevent->xselectionclear.time; } else @@ -1278,9 +1707,9 @@ gdk_event_translate (GdkEvent *event, event->selection.type = GDK_SELECTION_REQUEST; event->selection.window = window; - event->selection.selection = xevent->xselectionrequest.selection; - event->selection.target = xevent->xselectionrequest.target; - event->selection.property = xevent->xselectionrequest.property; + event->selection.selection = gdk_x11_xatom_to_atom_for_display (display, xevent->xselectionrequest.selection); + event->selection.target = gdk_x11_xatom_to_atom_for_display (display, xevent->xselectionrequest.target); + event->selection.property = gdk_x11_xatom_to_atom_for_display (display, xevent->xselectionrequest.property); event->selection.requestor = xevent->xselectionrequest.requestor; event->selection.time = xevent->xselectionrequest.time; @@ -1294,9 +1723,9 @@ gdk_event_translate (GdkEvent *event, event->selection.type = GDK_SELECTION_NOTIFY; event->selection.window = window; - event->selection.selection = xevent->xselection.selection; - event->selection.target = xevent->xselection.target; - event->selection.property = xevent->xselection.property; + event->selection.selection = gdk_x11_xatom_to_atom_for_display (display, xevent->xselection.selection); + event->selection.target = gdk_x11_xatom_to_atom_for_display (display, xevent->xselection.target); + event->selection.property = gdk_x11_xatom_to_atom_for_display (display, xevent->xselection.property); event->selection.time = xevent->xselection.time; break; @@ -1314,16 +1743,17 @@ gdk_event_translate (GdkEvent *event, { GList *tmp_list; GdkFilterReturn result = GDK_FILTER_CONTINUE; + GdkAtom message_type = gdk_x11_xatom_to_atom_for_display (display, xevent->xclient.message_type); GDK_NOTE (EVENTS, g_message ("client message:\twindow: %ld", xevent->xclient.window)); - tmp_list = client_filters; + tmp_list = display_x11->client_filters; while (tmp_list) { GdkClientFilter *filter = tmp_list->data; - if (filter->type == xevent->xclient.message_type) + if (filter->type == message_type) { result = (*filter->function) (xevent, event, filter->data); break; @@ -1342,13 +1772,21 @@ gdk_event_translate (GdkEvent *event, break; case GDK_FILTER_CONTINUE: /* Send unknown ClientMessage's on to Gtk for it to use */ - event->client.type = GDK_CLIENT_EVENT; - event->client.window = window; - event->client.message_type = xevent->xclient.message_type; - event->client.data_format = xevent->xclient.format; - memcpy(&event->client.data, &xevent->xclient.data, - sizeof(event->client.data)); - } + if (window_private == NULL) + { + return_val = FALSE; + } + else + { + event->client.type = GDK_CLIENT_EVENT; + event->client.window = window; + event->client.message_type = message_type; + event->client.data_format = xevent->xclient.format; + memcpy(&event->client.data, &xevent->xclient.data, + sizeof(event->client.data)); + } + break; + } } break; @@ -1360,39 +1798,54 @@ gdk_event_translate (GdkEvent *event, /* Let XLib know that there is a new keyboard mapping. */ XRefreshKeyboardMapping (&xevent->xmapping); - ++_gdk_keymap_serial; + _gdk_keymap_keys_changed (display); return_val = FALSE; break; -#ifdef HAVE_XKB - case XkbMapNotify: - ++_gdk_keymap_serial; - return_val = FALSE; - break; -#endif - default: - /* something else - (e.g., a Xinput event) */ - - if (window_private && - !GDK_WINDOW_DESTROYED (window_private) && - (window_private->extension_events != 0)) - return_val = _gdk_input_other_event(event, xevent, window); +#ifdef HAVE_XKB + if (xevent->type == display_x11->xkb_event_type) + { + XkbEvent *xkb_event = (XkbEvent *)xevent; + + switch (xkb_event->any.xkb_type) + { + case XkbMapNotify: + _gdk_keymap_keys_changed (display); + + return_val = FALSE; + break; + + case XkbStateNotify: + _gdk_keymap_state_changed (display); + break; + } + } else - return_val = FALSE; - - break; +#endif + { + /* something else - (e.g., a Xinput event) */ + + if (window_private && + !GDK_WINDOW_DESTROYED (window_private) && + (window_private->extension_events != 0)) + return_val = _gdk_input_other_event(event, xevent, window); + else + return_val = FALSE; + + break; + } } done: if (return_val) { if (event->any.window) - gdk_window_ref (event->any.window); + g_object_ref (event->any.window); if (((event->any.type == GDK_ENTER_NOTIFY) || (event->any.type == GDK_LEAVE_NOTIFY)) && (event->crossing.subwindow != NULL)) - gdk_window_ref (event->crossing.subwindow); + g_object_ref (event->crossing.subwindow); } else { @@ -1402,19 +1855,21 @@ gdk_event_translate (GdkEvent *event, } if (window) - gdk_window_unref (window); + g_object_unref (window); return return_val; } -GdkFilterReturn +static GdkFilterReturn gdk_wm_protocols_filter (GdkXEvent *xev, GdkEvent *event, gpointer data) { XEvent *xevent = (XEvent *)xev; + GdkWindow *win = event->any.window; + GdkDisplay *display = GDK_WINDOW_DISPLAY (win); - if ((Atom) xevent->xclient.data.l[0] == gdk_wm_delete_window) + if ((Atom) xevent->xclient.data.l[0] == gdk_x11_get_xatom_by_name_for_display (display, "WM_DELETE_WINDOW")) { /* The delete window request specifies a window * to delete. We don't actually destroy the @@ -1432,68 +1887,47 @@ gdk_wm_protocols_filter (GdkXEvent *xev, return GDK_FILTER_TRANSLATE; } - else if ((Atom) xevent->xclient.data.l[0] == gdk_wm_take_focus) + else if ((Atom) xevent->xclient.data.l[0] == gdk_x11_get_xatom_by_name_for_display (display, "WM_TAKE_FOCUS")) { + GdkWindow *win = event->any.window; + Window focus_win = GDK_WINDOW_IMPL_X11(((GdkWindowObject *)win)->impl)->focus_window; + + /* There is no way of knowing reliably whether we are viewable so we need + * to trap errors so we don't cause a BadMatch. + */ + gdk_error_trap_push (); + XSetInputFocus (GDK_WINDOW_XDISPLAY (win), + focus_win, + RevertToParent, + xevent->xclient.data.l[1]); + XSync (GDK_WINDOW_XDISPLAY (win), False); + gdk_error_trap_pop (); } - else if ((Atom) xevent->xclient.data.l[0] == gdk_atom_intern ("_NET_WM_PING", FALSE)) + else if ((Atom) xevent->xclient.data.l[0] == gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_PING")) { XEvent xev = *xevent; - xev.xclient.window = gdk_root_window; - XSendEvent (gdk_display, gdk_root_window, False, SubstructureRedirectMask | SubstructureNotifyMask, &xev); + xev.xclient.window = GDK_WINDOW_XROOTWIN (win); + XSendEvent (GDK_WINDOW_XDISPLAY (win), + xev.xclient.window, + False, + SubstructureRedirectMask | SubstructureNotifyMask, &xev); } return GDK_FILTER_REMOVE; } -#if 0 -static Bool -gdk_event_get_type (Display *display, - XEvent *xevent, - XPointer arg) -{ - GdkEvent event; - GdkPredicate *pred; - - if (gdk_event_translate (&event, xevent, FALSE)) - { - pred = (GdkPredicate*) arg; - return (* pred->func) (&event, pred->data); - } - - return FALSE; -} -#endif - void -gdk_events_queue (void) +_gdk_events_queue (GdkDisplay *display) { GList *node; GdkEvent *event; XEvent xevent; + Display *xdisplay = GDK_DISPLAY_XDISPLAY (display); - while (!gdk_event_queue_find_first() && XPending (gdk_display)) + while (!_gdk_event_queue_find_first(display) && XPending (xdisplay)) { -#ifdef USE_XIM - Window w = None; - - XNextEvent (gdk_display, &xevent); - if (gdk_xim_window) - switch (xevent.type) - { - case KeyPress: - case KeyRelease: - case ButtonPress: - case ButtonRelease: - w = GDK_WINDOW_XWINDOW (gdk_xim_window); - break; - } - - if (XFilterEvent (&xevent, w)) - continue; -#else - XNextEvent (gdk_display, &xevent); -#endif + XNextEvent (xdisplay, &xevent); switch (xevent.type) { @@ -1505,24 +1939,22 @@ gdk_events_queue (void) continue; } - event = gdk_event_new (); + event = gdk_event_new (GDK_NOTHING); - event->any.type = GDK_NOTHING; event->any.window = NULL; event->any.send_event = xevent.xany.send_event ? TRUE : FALSE; ((GdkEventPrivate *)event)->flags |= GDK_EVENT_PENDING; - gdk_event_queue_append (event); - node = gdk_queued_tail; + node = _gdk_event_queue_append (display, event); - if (gdk_event_translate (event, &xevent, FALSE)) + if (gdk_event_translate (display, event, &xevent, FALSE)) { ((GdkEventPrivate *)event)->flags &= ~GDK_EVENT_PENDING; } else { - gdk_event_queue_remove_link (node); + _gdk_event_queue_remove_link (display, node); g_list_free_1 (node); gdk_event_free (event); } @@ -1533,28 +1965,31 @@ static gboolean gdk_event_prepare (GSource *source, gint *timeout) { + GdkDisplay *display = ((GdkDisplaySource*)source)->display; gboolean retval; GDK_THREADS_ENTER (); *timeout = -1; - - retval = (gdk_event_queue_find_first () != NULL) || XPending (gdk_display); - + retval = (_gdk_event_queue_find_first (display) != NULL || + gdk_check_xpending (display)); + GDK_THREADS_LEAVE (); return retval; } static gboolean -gdk_event_check (GSource *source) +gdk_event_check (GSource *source) { + GdkDisplaySource *display_source = (GdkDisplaySource*)source; gboolean retval; - + GDK_THREADS_ENTER (); - if (event_poll_fd.revents & G_IO_IN) - retval = (gdk_event_queue_find_first () != NULL) || XPending (gdk_display); + if (display_source->event_poll_fd.revents & G_IO_IN) + retval = (_gdk_event_queue_find_first (display_source->display) != NULL || + gdk_check_xpending (display_source->display)); else retval = FALSE; @@ -1568,17 +2003,18 @@ gdk_event_dispatch (GSource *source, GSourceFunc callback, gpointer user_data) { + GdkDisplay *display = ((GdkDisplaySource*)source)->display; GdkEvent *event; GDK_THREADS_ENTER (); - gdk_events_queue(); - event = gdk_event_unqueue(); + _gdk_events_queue (display); + event = _gdk_event_unqueue (display); if (event) { - if (gdk_event_func) - (*gdk_event_func) (event, gdk_event_data); + if (_gdk_event_func) + (*_gdk_event_func) (event, _gdk_event_data); gdk_event_free (event); } @@ -1588,58 +2024,69 @@ gdk_event_dispatch (GSource *source, return TRUE; } -/* Sends a ClientMessage to all toplevel client windows */ +/** + * gdk_event_send_client_message_for_display: + * @display: the #GdkDisplay for the window where the message is to be sent. + * @event: the #GdkEvent to send, which should be a #GdkEventClient. + * @winid: the window to send the X ClientMessage event to. + * + * Sends an X ClientMessage event to a given window. + * + * This could be used for communicating between different applications, + * though the amount of data is limited to 20 bytes. + * + * Returns: non-zero on success. + * + * Since: 2.2 + */ gboolean -gdk_event_send_client_message (GdkEvent *event, guint32 xid) +gdk_event_send_client_message_for_display (GdkDisplay *display, + GdkEvent *event, + GdkNativeWindow winid) { XEvent sev; g_return_val_if_fail(event != NULL, FALSE); - + /* Set up our event to send, with the exception of its target window */ sev.xclient.type = ClientMessage; - sev.xclient.display = gdk_display; + sev.xclient.display = GDK_DISPLAY_XDISPLAY (display); sev.xclient.format = event->client.data_format; - sev.xclient.window = xid; - memcpy(&sev.xclient.data, &event->client.data, sizeof(sev.xclient.data)); - sev.xclient.message_type = event->client.message_type; + sev.xclient.window = winid; + memcpy(&sev.xclient.data, &event->client.data, sizeof (sev.xclient.data)); + sev.xclient.message_type = gdk_x11_atom_to_xatom_for_display (display, event->client.message_type); - return gdk_send_xevent (xid, False, NoEventMask, &sev); + return _gdk_send_xevent (display, winid, False, NoEventMask, &sev); } + + /* Sends a ClientMessage to all toplevel client windows */ gboolean -gdk_event_send_client_message_to_all_recurse (XEvent *xev, - guint32 xid, - guint level) +gdk_event_send_client_message_to_all_recurse (GdkDisplay *display, + XEvent *xev, + guint32 xid, + guint level) { - static GdkAtom wm_state_atom = GDK_NONE; Atom type = None; int format; unsigned long nitems, after; unsigned char *data; Window *ret_children, ret_root, ret_parent; unsigned int ret_nchildren; - gint old_warnings = gdk_error_warnings; gboolean send = FALSE; gboolean found = FALSE; + gboolean result = FALSE; int i; - - if (!wm_state_atom) - wm_state_atom = gdk_atom_intern ("WM_STATE", FALSE); - - gdk_error_warnings = FALSE; - gdk_error_code = 0; - XGetWindowProperty (gdk_display, xid, wm_state_atom, 0, 0, False, AnyPropertyType, - &type, &format, &nitems, &after, &data); - - if (gdk_error_code) - { - gdk_error_warnings = old_warnings; - - return FALSE; - } - + + gdk_error_trap_push (); + + if (XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display), xid, + gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_STATE"), + 0, 0, False, AnyPropertyType, + &type, &format, &nitems, &after, &data) != Success) + goto out; + if (type) { send = TRUE; @@ -1648,17 +2095,13 @@ gdk_event_send_client_message_to_all_recurse (XEvent *xev, else { /* OK, we're all set, now let's find some windows to send this to */ - if (XQueryTree (gdk_display, xid, &ret_root, &ret_parent, - &ret_children, &ret_nchildren) != True || - gdk_error_code) - { - gdk_error_warnings = old_warnings; - - return FALSE; - } + if (!XQueryTree (GDK_DISPLAY_XDISPLAY (display), xid, + &ret_root, &ret_parent, + &ret_children, &ret_nchildren)) + goto out; for(i = 0; i < ret_nchildren; i++) - if (gdk_event_send_client_message_to_all_recurse (xev, ret_children[i], level + 1)) + if (gdk_event_send_client_message_to_all_recurse (display, xev, ret_children[i], level + 1)) found = TRUE; XFree (ret_children); @@ -1667,32 +2110,56 @@ gdk_event_send_client_message_to_all_recurse (XEvent *xev, if (send || (!found && (level == 1))) { xev->xclient.window = xid; - gdk_send_xevent (xid, False, NoEventMask, xev); + _gdk_send_xevent (display, xid, False, NoEventMask, xev); } - gdk_error_warnings = old_warnings; + result = send || found; + + out: + gdk_error_trap_pop (); - return (send || found); + return result; } +/** + * gdk_screen_broadcast_client_message: + * @screen: the #GdkScreen where the event will be broadcasted. + * @event: the #GdkEvent. + * + * Sends an X ClientMessage event to all toplevel windows on @screen. + * + * Toplevel windows are determined by checking for the WM_STATE property, + * as described in the Inter-Client Communication Conventions Manual (ICCCM). + * If no windows are found with the WM_STATE property set, the message is + * sent to all children of the root window. + * + * Since: 2.2 + */ + void -gdk_event_send_clientmessage_toall (GdkEvent *event) +gdk_screen_broadcast_client_message (GdkScreen *screen, + GdkEvent *event) { XEvent sev; - gint old_warnings = gdk_error_warnings; + GdkWindow *root_window; - g_return_if_fail(event != NULL); + g_return_if_fail (event != NULL); + + root_window = gdk_screen_get_root_window (screen); /* Set up our event to send, with the exception of its target window */ sev.xclient.type = ClientMessage; - sev.xclient.display = gdk_display; + sev.xclient.display = GDK_WINDOW_XDISPLAY (root_window); sev.xclient.format = event->client.data_format; - memcpy(&sev.xclient.data, &event->client.data, sizeof(sev.xclient.data)); - sev.xclient.message_type = event->client.message_type; - - gdk_event_send_client_message_to_all_recurse(&sev, gdk_root_window, 0); - - gdk_error_warnings = old_warnings; + memcpy(&sev.xclient.data, &event->client.data, sizeof (sev.xclient.data)); + sev.xclient.message_type = + gdk_x11_atom_to_xatom_for_display (GDK_WINDOW_DISPLAY (root_window), + event->client.message_type); + + gdk_event_send_client_message_to_all_recurse (gdk_screen_get_display (screen), + &sev, + GDK_WINDOW_XID (root_window), + 0); } /* @@ -1716,21 +2183,27 @@ gdk_event_send_clientmessage_toall (GdkEvent *event) void gdk_flush (void) { - XSync (gdk_display, False); + GSList *tmp_list = _gdk_displays; + + while (tmp_list) + { + XSync (GDK_DISPLAY_XDISPLAY (tmp_list->data), False); + tmp_list = tmp_list->next; + } } -static GdkAtom timestamp_prop_atom = 0; - 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 == timestamp_prop_atom) + xevent->xproperty.atom == gdk_x11_get_xatom_by_name_for_display + (gdk_display, "GDK_TIMESTAMP_PROP")) return True; return False; @@ -1753,18 +2226,19 @@ gdk_x11_get_server_time (GdkWindow *window) Window xwindow; guchar c = 'a'; XEvent xevent; + Atom timestamp_prop_atom; g_return_val_if_fail (GDK_IS_WINDOW (window), 0); g_return_val_if_fail (!GDK_WINDOW_DESTROYED (window), 0); - if (!timestamp_prop_atom) - timestamp_prop_atom = gdk_atom_intern ("GDK_TIMESTAMP_PROP", FALSE); - xdisplay = GDK_WINDOW_XDISPLAY (window); xwindow = GDK_WINDOW_XWINDOW (window); + timestamp_prop_atom = + gdk_x11_get_xatom_by_name_for_display (GDK_WINDOW_DISPLAY (window), + "GDK_TIMESTAMP_PROP"); - XChangeProperty (xdisplay, xwindow, - timestamp_prop_atom, timestamp_prop_atom, + XChangeProperty (xdisplay, xwindow, timestamp_prop_atom, + timestamp_prop_atom, 8, PropModeReplace, &c, 1); XIfEvent (xdisplay, &xevent, @@ -1773,94 +2247,458 @@ gdk_x11_get_server_time (GdkWindow *window) return xevent.xproperty.time; } - -gboolean -gdk_net_wm_supports (GdkAtom property) +static void +fetch_net_wm_check_window (GdkScreen *screen) { - static GdkAtom wmspec_check_atom = 0; - static GdkAtom wmspec_supported_atom = 0; - static GdkAtom *atoms = NULL; - static gulong n_atoms = 0; + GdkScreenX11 *screen_x11; + GdkDisplay *display; Atom type; gint format; - gulong nitems; + gulong n_items; gulong bytes_after; Window *xwindow; - gulong i; + + /* 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. + */ + + screen_x11 = GDK_SCREEN_X11 (screen); + display = screen_x11->display; + + if (screen_x11->wmspec_check_window != None) + return; /* already have it */ + + XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display), screen_x11->xroot_window, + gdk_x11_get_xatom_by_name_for_display (display, "_NET_SUPPORTING_WM_CHECK"), + 0, G_MAXLONG, False, XA_WINDOW, &type, &format, + &n_items, &bytes_after, (guchar **) & xwindow); + + if (type != XA_WINDOW) + return; + + gdk_error_trap_push (); + + /* Find out if this WM goes away, so we can reset everything. */ + XSelectInput (screen_x11->xdisplay, *xwindow, StructureNotifyMask); + + screen_x11->wmspec_check_window = *xwindow; + XFree (xwindow); + + screen_x11->need_refetch_net_supported = TRUE; + screen_x11->need_refetch_wm_name = TRUE; + + /* Careful, reentrancy */ + _gdk_x11_screen_window_manager_changed (GDK_SCREEN (screen_x11)); +} + +/** + * gdk_x11_screen_get_window_manager_name: + * @screen: a #GdkScreen + * + * Returns the name of the window manager for @screen. + * + * Return value: the name of the window manager screen @screen, or + * "unknown" if the window manager is unknown. The string is owned by GDK + * and should not be freed. + * + * Since: 2.2 + **/ +const char* +gdk_x11_screen_get_window_manager_name (GdkScreen *screen) +{ + GdkScreenX11 *screen_x11; + + screen_x11 = GDK_SCREEN_X11 (screen); + + fetch_net_wm_check_window (screen); - if (wmspec_check_window != None) + if (screen_x11->need_refetch_wm_name) { - if (atoms == NULL) - return FALSE; + /* Get the name of the window manager */ + screen_x11->need_refetch_wm_name = FALSE; - i = 0; - while (i < n_atoms) + g_free (screen_x11->window_manager_name); + screen_x11->window_manager_name = g_strdup ("unknown"); + + if (screen_x11->wmspec_check_window != None) { - if (atoms[i] == property) - return TRUE; + Atom type; + gint format; + gulong n_items; + gulong bytes_after; + guchar *name; - ++i; + name = NULL; + + XGetWindowProperty (GDK_DISPLAY_XDISPLAY (screen_x11->display), + screen_x11->wmspec_check_window, + gdk_x11_get_xatom_by_name_for_display (screen_x11->display, + "_NET_WM_NAME"), + 0, G_MAXLONG, False, + gdk_x11_get_xatom_by_name_for_display (screen_x11->display, + "UTF8_STRING"), + &type, &format, + &n_items, &bytes_after, + (guchar **)&name); + + gdk_display_sync (screen_x11->display); + + gdk_error_trap_pop (); + + if (name != NULL) + { + g_free (screen_x11->window_manager_name); + screen_x11->window_manager_name = g_strdup (name); + XFree (name); + } } + } + + return GDK_SCREEN_X11 (screen)->window_manager_name; +} - return FALSE; +typedef struct _NetWmSupportedAtoms NetWmSupportedAtoms; + +struct _NetWmSupportedAtoms +{ + Atom *atoms; + gulong n_atoms; +}; + +/** + * gdk_x11_screen_supports_net_wm_hint: + * @screen: the relevant #GdkScreen. + * @property: a property atom. + * + * This function is specific to the X11 backend of GDK, and indicates + * whether the window manager supports a certain hint from the + * Extended Window Manager Hints Specification. You can find this + * specification on + * http://www.freedesktop.org. + * + * When using this function, keep in mind that the window manager + * can change over time; so you shouldn't use this function in + * a way that impacts persistent application state. A common bug + * is that your application can start up before the window manager + * does when the user logs in, and before the window manager starts + * gdk_x11_screen_supports_net_wm_hint() will return %FALSE for every property. + * You can monitor the window_manager_changed signal on #GdkScreen to detect + * a window manager change. + * + * Return value: %TRUE if the window manager supports @property + * + * Since: 2.2 + **/ +gboolean +gdk_x11_screen_supports_net_wm_hint (GdkScreen *screen, + GdkAtom property) +{ + gulong i; + GdkScreenX11 *screen_x11; + NetWmSupportedAtoms *supported_atoms; + GdkDisplay *display; + + g_return_val_if_fail (GDK_IS_SCREEN (screen), FALSE); + + screen_x11 = GDK_SCREEN_X11 (screen); + display = screen_x11->display; + + supported_atoms = g_object_get_data (G_OBJECT (screen), "gdk-net-wm-supported-atoms"); + if (!supported_atoms) + { + supported_atoms = g_new0 (NetWmSupportedAtoms, 1); + g_object_set_data (G_OBJECT (screen), "gdk-net-wm-supported-atoms", supported_atoms); } - if (atoms) - XFree (atoms); + fetch_net_wm_check_window (screen); - atoms = NULL; - n_atoms = 0; + if (screen_x11->wmspec_check_window == None) + return FALSE; - /* 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 (screen_x11->need_refetch_net_supported) + { + /* WM has changed since we last got the supported list, + * refetch it. + */ + Atom type; + gint format; + gulong bytes_after; + + screen_x11->need_refetch_net_supported = FALSE; + + if (supported_atoms->atoms) + XFree (supported_atoms->atoms); + + supported_atoms->atoms = NULL; + supported_atoms->n_atoms = 0; + + XGetWindowProperty (GDK_DISPLAY_XDISPLAY (display), screen_x11->xroot_window, + gdk_x11_get_xatom_by_name_for_display (display, "_NET_SUPPORTED"), + 0, G_MAXLONG, False, XA_ATOM, &type, &format, + &supported_atoms->n_atoms, &bytes_after, + (guchar **)&supported_atoms->atoms); + + if (type != XA_ATOM) + return FALSE; + } + + if (supported_atoms->atoms == NULL) + return FALSE; - if (wmspec_check_atom == 0) - wmspec_check_atom = gdk_atom_intern ("_NET_SUPPORTING_WM_CHECK", FALSE); + i = 0; + while (i < supported_atoms->n_atoms) + { + if (supported_atoms->atoms[i] == gdk_x11_atom_to_xatom_for_display (display, property)) + return TRUE; - if (wmspec_supported_atom == 0) - wmspec_supported_atom = gdk_atom_intern ("_NET_SUPPORTED", FALSE); + ++i; + } - XGetWindowProperty (gdk_display, gdk_root_window, - wmspec_check_atom, 0, G_MAXLONG, - False, XA_WINDOW, &type, &format, &nitems, - &bytes_after, (guchar **)&xwindow); + return FALSE; +} - if (type != XA_WINDOW) - return FALSE; +/** + * gdk_net_wm_supports: + * @property: a property atom. + * + * This function is specific to the X11 backend of GDK, and indicates + * whether the window manager for the default screen supports a certain + * hint from the Extended Window Manager Hints Specification. See + * gdk_x11_screen_supports_net_wm_hint() for complete details. + * + * Return value: %TRUE if the window manager supports @property + **/ +gboolean +gdk_net_wm_supports (GdkAtom property) +{ + return gdk_x11_screen_supports_net_wm_hint (gdk_screen_get_default (), property); +} - gdk_error_trap_push (); +static struct +{ + const char *xsettings_name; + const char *gdk_name; +} settings_map[] = { + { "Net/DoubleClickTime", "gtk-double-click-time" }, + { "Net/DndDragThreshold", "gtk-dnd-drag-threshold" }, + { "Gtk/CanChangeAccels", "gtk-can-change-accels" }, + { "Gtk/ColorPalette", "gtk-color-palette" }, + { "Gtk/FontName", "gtk-font-name" }, + { "Gtk/IconSizes", "gtk-icon-sizes" }, + { "Gtk/KeyThemeName", "gtk-key-theme-name" }, + { "Gtk/ToolbarStyle", "gtk-toolbar-style" }, + { "Gtk/ToolbarIconSize", "gtk-toolbar-icon-size" }, + { "Gtk/IMPreeditStyle", "gtk-im-preedit-style" }, + { "Gtk/IMStatusStyle", "gtk-im-status-style" }, + { "Net/CursorBlink", "gtk-cursor-blink" }, + { "Net/CursorBlinkTime", "gtk-cursor-blink-time" }, + { "Net/ThemeName", "gtk-theme-name" } +}; + +static void +gdk_xsettings_notify_cb (const char *name, + XSettingsAction action, + XSettingsSetting *setting, + void *data) +{ + GdkEvent new_event; + GdkScreen *screen = data; + GdkScreenX11 *screen_x11 = data; + int i; + + if (screen_x11->xsettings_in_init) + return; + + new_event.type = GDK_SETTING; + new_event.setting.window = gdk_screen_get_root_window (screen); + new_event.setting.send_event = FALSE; + new_event.setting.name = NULL; + + for (i = 0; i < G_N_ELEMENTS (settings_map) ; i++) + if (strcmp (settings_map[i].xsettings_name, name) == 0) + { + new_event.setting.name = (char *)settings_map[i].gdk_name; + break; + } - /* Find out if this WM goes away, so we can reset everything. */ - XSelectInput (gdk_display, *xwindow, - StructureNotifyMask); - gdk_flush (); + if (!new_event.setting.name) + return; - if (gdk_error_trap_pop ()) + switch (action) + { + case XSETTINGS_ACTION_NEW: + new_event.setting.action = GDK_SETTING_ACTION_NEW; + break; + case XSETTINGS_ACTION_CHANGED: + new_event.setting.action = GDK_SETTING_ACTION_CHANGED; + break; + case XSETTINGS_ACTION_DELETED: + new_event.setting.action = GDK_SETTING_ACTION_DELETED; + break; + } + + gdk_event_put (&new_event); +} + +static gboolean +check_transform (const gchar *xsettings_name, + GType src_type, + GType dest_type) +{ + if (!g_value_type_transformable (src_type, dest_type)) { - XFree (xwindow); + g_warning ("Cannot tranform xsetting %s of type %s to type %s\n", + xsettings_name, + g_type_name (src_type), + g_type_name (dest_type)); return FALSE; } + else + return TRUE; +} - XGetWindowProperty (gdk_display, gdk_root_window, - wmspec_supported_atom, 0, G_MAXLONG, - False, XA_ATOM, &type, &format, &n_atoms, - &bytes_after, (guchar **)&atoms); +/** + * gdk_screen_get_setting: + * @screen: the #GdkScreen where the setting is located + * @name: the name of the setting + * @value: location to store the value of the setting + * + * Retrieves a desktop-wide setting such as double-click time + * for the #GdkScreen @screen. + * + * FIXME needs a list of valid settings here, or a link to + * more information. + * + * Returns: %TRUE if the setting existed and a value was stored + * in @value, %FALSE otherwise. + * + * Since: 2.2 + **/ +gboolean +gdk_screen_get_setting (GdkScreen *screen, + const gchar *name, + GValue *value) +{ + + const char *xsettings_name = NULL; + XSettingsResult result; + XSettingsSetting *setting; + GdkScreenX11 *screen_x11; + gboolean success = FALSE; + gint i; + GValue tmp_val = { 0, }; - if (type != XA_ATOM) + g_return_val_if_fail (GDK_IS_SCREEN (screen), FALSE); + + screen_x11 = GDK_SCREEN_X11 (screen); + + for (i = 0; i < G_N_ELEMENTS (settings_map) ; i++) + if (strcmp (settings_map[i].gdk_name, name) == 0) + { + xsettings_name = settings_map[i].xsettings_name; + break; + } + + if (!xsettings_name) + return FALSE; + + result = xsettings_client_get_setting (screen_x11->xsettings_client, + xsettings_name, &setting); + if (result != XSETTINGS_SUCCESS) return FALSE; + + switch (setting->type) + { + case XSETTINGS_TYPE_INT: + if (check_transform (xsettings_name, G_TYPE_INT, G_VALUE_TYPE (value))) + { + g_value_init (&tmp_val, G_TYPE_INT); + g_value_set_int (&tmp_val, setting->data.v_int); + g_value_transform (&tmp_val, value); + + success = TRUE; + } + break; + case XSETTINGS_TYPE_STRING: + if (check_transform (xsettings_name, G_TYPE_STRING, G_VALUE_TYPE (value))) + { + g_value_init (&tmp_val, G_TYPE_STRING); + g_value_set_string (&tmp_val, setting->data.v_string); + g_value_transform (&tmp_val, value); + + success = TRUE; + } + break; + case XSETTINGS_TYPE_COLOR: + if (!check_transform (xsettings_name, GDK_TYPE_COLOR, G_VALUE_TYPE (value))) + { + GdkColor color; + + g_value_init (&tmp_val, GDK_TYPE_COLOR); + + color.pixel = 0; + color.red = setting->data.v_color.red; + color.green = setting->data.v_color.green; + color.blue = setting->data.v_color.blue; + + g_value_set_boxed (&tmp_val, &color); + + g_value_transform (&tmp_val, value); + + success = TRUE; + } + break; + } - wmspec_check_window = *xwindow; - XFree (xwindow); + g_value_unset (&tmp_val); + + xsettings_setting_free (setting); + + return success; +} + +static GdkFilterReturn +gdk_xsettings_client_event_filter (GdkXEvent *xevent, + GdkEvent *event, + gpointer data) +{ + GdkScreenX11 *screen = data; - /* since wmspec_check_window != None this isn't infinite. ;-) */ - return gdk_net_wm_supports (property); + if (xsettings_client_process_event (screen->xsettings_client, (XEvent *)xevent)) + return GDK_FILTER_REMOVE; + else + return GDK_FILTER_CONTINUE; } +static void +gdk_xsettings_watch_cb (Window window, + Bool is_start, + long mask, + void *cb_data) +{ + GdkWindow *gdkwin; + GdkScreen *screen = cb_data; + gdkwin = gdk_window_lookup_for_display (gdk_screen_get_display (screen), window); + if (is_start) + { + if (!gdkwin) + gdkwin = gdk_window_foreign_new_for_display (gdk_screen_get_display (screen), window); + else + g_object_ref (gdkwin); + + gdk_window_add_filter (gdkwin, gdk_xsettings_client_event_filter, screen); + } + else + { + g_assert (gdkwin); + gdk_window_remove_filter (gdkwin, gdk_xsettings_client_event_filter, screen); + g_object_unref (gdkwin); + } +}