+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ event.c for the Openbox window manager
+ Copyright (c) 2006 Mikael Magnusson
+ Copyright (c) 2003-2007 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#include "event.h"
+#include "debug.h"
+#include "window.h"
#include "openbox.h"
+#include "dock.h"
#include "client.h"
#include "xerror.h"
#include "prop.h"
#include "screen.h"
#include "frame.h"
#include "menu.h"
+#include "menuframe.h"
+#include "keyboard.h"
+#include "mouse.h"
+#include "mainloop.h"
#include "framerender.h"
#include "focus.h"
#include "moveresize.h"
+#include "group.h"
#include "stacking.h"
#include "extensions.h"
-#include "timer.h"
-#include "dispatch.h"
+#include "translate.h"
#include <X11/Xlib.h>
#include <X11/keysym.h>
#include <X11/Xatom.h>
+#include <glib.h>
+
#ifdef HAVE_SYS_SELECT_H
# include <sys/select.h>
#endif
+#ifdef HAVE_SIGNAL_H
+# include <signal.h>
+#endif
+#ifdef HAVE_UNISTD_H
+# include <unistd.h> /* for usleep() */
+#endif
+#ifdef XKB
+# include <X11/XKBlib.h>
+#endif
+
+#ifdef USE_SM
+#include <X11/ICE/ICElib.h>
+#endif
-static void event_process(XEvent *e);
+typedef struct
+{
+ gboolean ignored;
+} ObEventData;
+
+typedef struct
+{
+ ObClient *client;
+ Time time;
+} ObFocusDelayData;
+
+static void event_process(const XEvent *e, gpointer data);
static void event_handle_root(XEvent *e);
-static void event_handle_client(Client *c, XEvent *e);
-static void event_handle_menu(Menu *menu, XEvent *e);
+static gboolean event_handle_menu_keyboard(XEvent *e);
+static gboolean event_handle_menu(XEvent *e);
+static void event_handle_dock(ObDock *s, XEvent *e);
+static void event_handle_dockapp(ObDockApp *app, XEvent *e);
+static void event_handle_client(ObClient *c, XEvent *e);
+static void event_handle_group(ObGroup *g, XEvent *e);
+
+static void focus_delay_dest(gpointer data);
+static gboolean focus_delay_cmp(gconstpointer d1, gconstpointer d2);
+static gboolean focus_delay_func(gpointer data);
+static void focus_delay_client_dest(ObClient *client, gpointer data);
-#define INVALID_FOCUSIN(e) ((e)->xfocus.detail == NotifyInferior || \
- (e)->xfocus.detail > NotifyNonlinearVirtual)
-#define INVALID_FOCUSOUT(e) ((e)->xfocus.mode == NotifyGrab || \
- (e)->xfocus.detail == NotifyInferior || \
- (e)->xfocus.detail == NotifyAncestor || \
- (e)->xfocus.detail > NotifyNonlinearVirtual)
+static gboolean menu_hide_delay_func(gpointer data);
-Time event_lasttime = 0;
+/* The time for the current event being processed */
+Time event_curtime = CurrentTime;
/*! The value of the mask for the NumLock modifier */
-unsigned int NumLockMask;
+guint NumLockMask;
/*! The value of the mask for the ScrollLock modifier */
-unsigned int ScrollLockMask;
+guint ScrollLockMask;
/*! The key codes for the modifier keys */
static XModifierKeymap *modmap;
/*! Table of the constant modifier masks */
-static const int mask_table[] = {
+static const gint mask_table[] = {
ShiftMask, LockMask, ControlMask, Mod1Mask,
Mod2Mask, Mod3Mask, Mod4Mask, Mod5Mask
};
-static int mask_table_size;
+static gint mask_table_size;
+
+static guint ignore_enter_focus = 0;
-void event_startup()
+static gboolean menu_can_hide;
+
+#ifdef USE_SM
+static void ice_handler(gint fd, gpointer conn)
+{
+ Bool b;
+ IceProcessMessages(conn, NULL, &b);
+}
+
+static void ice_watch(IceConn conn, IcePointer data, Bool opening,
+ IcePointer *watch_data)
{
+ static gint fd = -1;
+
+ if (opening) {
+ fd = IceConnectionNumber(conn);
+ ob_main_loop_fd_add(ob_main_loop, fd, ice_handler, conn, NULL);
+ } else {
+ ob_main_loop_fd_remove(ob_main_loop, fd);
+ fd = -1;
+ }
+}
+#endif
+
+void event_startup(gboolean reconfig)
+{
+ if (reconfig) return;
+
mask_table_size = sizeof(mask_table) / sizeof(mask_table[0]);
/* get lock masks that are defined by the display (not constant) */
modmap = XGetModifierMapping(ob_display);
g_assert(modmap);
if (modmap && modmap->max_keypermod > 0) {
- size_t cnt;
- const size_t size = mask_table_size * modmap->max_keypermod;
- /* get the values of the keyboard lock modifiers
- Note: Caps lock is not retrieved the same way as Scroll and Num
- lock since it doesn't need to be. */
- const KeyCode num_lock = XKeysymToKeycode(ob_display, XK_Num_Lock);
- const KeyCode scroll_lock = XKeysymToKeycode(ob_display,
- XK_Scroll_Lock);
-
- for (cnt = 0; cnt < size; ++cnt) {
- if (! modmap->modifiermap[cnt]) continue;
-
- if (num_lock == modmap->modifiermap[cnt])
- NumLockMask = mask_table[cnt / modmap->max_keypermod];
- if (scroll_lock == modmap->modifiermap[cnt])
- ScrollLockMask = mask_table[cnt / modmap->max_keypermod];
- }
+ size_t cnt;
+ const size_t size = mask_table_size * modmap->max_keypermod;
+ /* get the values of the keyboard lock modifiers
+ Note: Caps lock is not retrieved the same way as Scroll and Num
+ lock since it doesn't need to be. */
+ const KeyCode num_lock = XKeysymToKeycode(ob_display, XK_Num_Lock);
+ const KeyCode scroll_lock = XKeysymToKeycode(ob_display,
+ XK_Scroll_Lock);
+
+ for (cnt = 0; cnt < size; ++cnt) {
+ if (! modmap->modifiermap[cnt]) continue;
+
+ if (num_lock == modmap->modifiermap[cnt])
+ NumLockMask = mask_table[cnt / modmap->max_keypermod];
+ if (scroll_lock == modmap->modifiermap[cnt])
+ ScrollLockMask = mask_table[cnt / modmap->max_keypermod];
+ }
}
-}
-void event_shutdown()
-{
- XFreeModifiermap(modmap);
+ ob_main_loop_x_add(ob_main_loop, event_process, NULL, NULL);
+
+#ifdef USE_SM
+ IceAddConnectionWatch(ice_watch, NULL);
+#endif
+
+ client_add_destructor(focus_delay_client_dest, NULL);
}
-void event_loop()
+void event_shutdown(gboolean reconfig)
{
- fd_set selset;
- XEvent e;
- int x_fd;
- struct timeval *wait;
- gboolean had_event = FALSE;
+ if (reconfig) return;
- while (TRUE) {
- /*
- There are slightly different event retrieval semantics here for
- local (or high bandwidth) versus remote (or low bandwidth)
- connections to the display/Xserver.
- */
- if (ob_remote) {
- if (!XPending(ob_display))
- break;
- } else {
- /*
- This XSync allows for far more compression of events, which
- makes things like Motion events perform far far better. Since
- it also means network traffic for every event instead of every
- X events (where X is the number retrieved at a time), it
- probably should not be used for setups where Openbox is
- running on a remote/low bandwidth display/Xserver.
- */
- XSync(ob_display, FALSE);
- if (!XEventsQueued(ob_display, QueuedAlready))
- break;
- }
- XNextEvent(ob_display, &e);
-
- event_process(&e);
- had_event = TRUE;
- }
+#ifdef USE_SM
+ IceRemoveConnectionWatch(ice_watch, NULL);
+#endif
- if (!had_event) {
- timer_dispatch((GTimeVal**)&wait);
- x_fd = ConnectionNumber(ob_display);
- FD_ZERO(&selset);
- FD_SET(x_fd, &selset);
- select(x_fd + 1, &selset, NULL, NULL, wait);
- }
+ client_remove_destructor(focus_delay_client_dest);
+ XFreeModifiermap(modmap);
}
static Window event_get_window(XEvent *e)
/* pick a window */
switch (e->type) {
+ case SelectionClear:
+ window = RootWindow(ob_display, ob_screen);
+ break;
case MapRequest:
- window = e->xmap.window;
- break;
+ window = e->xmap.window;
+ break;
case UnmapNotify:
- window = e->xunmap.window;
- break;
+ window = e->xunmap.window;
+ break;
case DestroyNotify:
- window = e->xdestroywindow.window;
- break;
+ window = e->xdestroywindow.window;
+ break;
case ConfigureRequest:
- window = e->xconfigurerequest.window;
- break;
+ window = e->xconfigurerequest.window;
+ break;
+ case ConfigureNotify:
+ window = e->xconfigure.window;
+ break;
default:
#ifdef XKB
- if (extensions_xkb && e->type == extensions_xkb_event_basep) {
- switch (((XkbAnyEvent*)&e)->xkb_type) {
- case XkbBellNotify:
- window = ((XkbBellNotifyEvent*)&e)->window;
- default:
- window = None;
- }
+ if (extensions_xkb && e->type == extensions_xkb_event_basep) {
+ switch (((XkbAnyEvent*)e)->xkb_type) {
+ case XkbBellNotify:
+ window = ((XkbBellNotifyEvent*)e)->window;
+ default:
+ window = None;
+ }
+ } else
+#endif
+#ifdef SYNC
+ if (extensions_sync &&
+ e->type == extensions_sync_event_basep + XSyncAlarmNotify)
+ {
+ window = None;
} else
#endif
window = e->xany.window;
return window;
}
-static void event_set_lasttime(XEvent *e)
+static void event_set_curtime(XEvent *e)
{
+ Time t = CurrentTime;
+
/* grab the lasttime and hack up the state */
switch (e->type) {
case ButtonPress:
case ButtonRelease:
- event_lasttime = e->xbutton.time;
- break;
+ t = e->xbutton.time;
+ break;
case KeyPress:
- event_lasttime = e->xkey.time;
- break;
+ t = e->xkey.time;
+ break;
case KeyRelease:
- event_lasttime = e->xkey.time;
- break;
+ t = e->xkey.time;
+ break;
case MotionNotify:
- event_lasttime = e->xmotion.time;
- break;
+ t = e->xmotion.time;
+ break;
case PropertyNotify:
- event_lasttime = e->xproperty.time;
- break;
+ t = e->xproperty.time;
+ break;
case EnterNotify:
case LeaveNotify:
- event_lasttime = e->xcrossing.time;
- break;
+ t = e->xcrossing.time;
+ break;
default:
- event_lasttime = CurrentTime;
+#ifdef SYNC
+ if (extensions_sync &&
+ e->type == extensions_sync_event_basep + XSyncAlarmNotify)
+ {
+ t = ((XSyncAlarmNotifyEvent*)e)->time;
+ }
+#endif
+ /* if more event types are anticipated, get their timestamp
+ explicitly */
break;
}
+
+ event_curtime = t;
}
#define STRIP_MODS(s) \
- s &= ~(LockMask | NumLockMask | ScrollLockMask), \
- /* kill off the Button1Mask etc, only want the modifiers */ \
- s &= (ControlMask | ShiftMask | Mod1Mask | \
+ s &= ~(LockMask | NumLockMask | ScrollLockMask), \
+ /* kill off the Button1Mask etc, only want the modifiers */ \
+ s &= (ControlMask | ShiftMask | Mod1Mask | \
Mod2Mask | Mod3Mask | Mod4Mask | Mod5Mask) \
static void event_hack_mods(XEvent *e)
{
+#ifdef XKB
+ XkbStateRec xkb_state;
+#endif
KeyCode *kp;
- int i, k;
+ gint i, k;
switch (e->type) {
case ButtonPress:
case ButtonRelease:
STRIP_MODS(e->xbutton.state);
- break;
+ break;
case KeyPress:
STRIP_MODS(e->xkey.state);
- break;
+ break;
case KeyRelease:
STRIP_MODS(e->xkey.state);
- /* remove from the state the mask of the modifier being released, if
- it is a modifier key being released (this is a little ugly..) */
- kp = modmap->modifiermap;
- for (i = 0; i < mask_table_size; ++i) {
- for (k = 0; k < modmap->max_keypermod; ++k) {
- if (*kp == e->xkey.keycode) { /* found the keycode */
- /* remove the mask for it */
- e->xkey.state &= ~mask_table[i];
- /* cause the first loop to break; */
- i = mask_table_size;
- break; /* get outta here! */
- }
- ++kp;
- }
- }
- break;
+ /* remove from the state the mask of the modifier being released, if
+ it is a modifier key being released (this is a little ugly..) */
+#ifdef XKB
+ if (XkbGetState(ob_display, XkbUseCoreKbd, &xkb_state) == Success) {
+ e->xkey.state = xkb_state.compat_state;
+ break;
+ }
+#endif
+ kp = modmap->modifiermap;
+ for (i = 0; i < mask_table_size; ++i) {
+ for (k = 0; k < modmap->max_keypermod; ++k) {
+ if (*kp == e->xkey.keycode) { /* found the keycode */
+ /* remove the mask for it */
+ e->xkey.state &= ~mask_table[i];
+ /* cause the first loop to break; */
+ i = mask_table_size;
+ break; /* get outta here! */
+ }
+ ++kp;
+ }
+ }
+ break;
case MotionNotify:
STRIP_MODS(e->xmotion.state);
- /* compress events */
+ /* compress events */
{
XEvent ce;
while (XCheckTypedWindowEvent(ob_display, e->xmotion.window,
e->xmotion.x_root = ce.xmotion.x_root;
e->xmotion.y_root = ce.xmotion.y_root;
}
- }
- break;
+ }
+ break;
}
}
-static gboolean event_ignore(XEvent *e, Client *client)
+static gboolean wanted_focusevent(XEvent *e)
{
- switch(e->type) {
- case FocusIn:
- /* NotifyAncestor is not ignored in FocusIn like it is in FocusOut
- because of RevertToPointerRoot. If the focus ends up reverting to
- pointer root on a workspace change, then the FocusIn event that we
- want will be of type NotifyAncestor. This situation does not occur
- for FocusOut, so it is safely ignored there.
- */
- if (INVALID_FOCUSIN(e) ||
- client == NULL) {
-#ifdef DEBUG_FOCUS
- g_message("FocusIn on %lx mode %d detail %d IGNORED", e->xfocus.window,
- e->xfocus.mode, e->xfocus.detail);
-#endif
- /* says a client was not found for the event (or a valid FocusIn
- event was not found.
- */
- e->xfocus.window = None;
- return TRUE;
+ gint mode = e->xfocus.mode;
+ gint detail = e->xfocus.detail;
+ Window win = e->xany.window;
+
+ if (e->type == FocusIn) {
+
+ /* These are ones we never want.. */
+
+ /* This means focus was given by a keyboard/mouse grab. */
+ if (mode == NotifyGrab)
+ return FALSE;
+ /* This means focus was given back from a keyboard/mouse grab. */
+ if (mode == NotifyUngrab)
+ return FALSE;
+
+ /* These are the ones we want.. */
+
+ if (win == RootWindow(ob_display, ob_screen)) {
+ /* This means focus reverted off of a client */
+ if (detail == NotifyPointerRoot || detail == NotifyDetailNone ||
+ detail == NotifyInferior)
+ return TRUE;
+ else
+ return FALSE;
}
-#ifdef DEBUG_FOCUS
- g_message("FocusIn on %lx mode %d detail %d", e->xfocus.window,
- e->xfocus.mode, e->xfocus.detail);
-#endif
- break;
- case FocusOut:
- if (INVALID_FOCUSOUT(e)) {
-#ifdef DEBUG_FOCUS
- g_message("FocusOut on %lx mode %d detail %d IGNORED",
- e->xfocus.window, e->xfocus.mode, e->xfocus.detail);
-#endif
+ /* This means focus moved from the root window to a client */
+ if (detail == NotifyVirtual)
+ return TRUE;
+ /* This means focus moved from one client to another */
+ if (detail == NotifyNonlinearVirtual)
return TRUE;
- }
-#ifdef DEBUG_FOCUS
- g_message("FocusOut on %lx mode %d detail %d",
- e->xfocus.window, e->xfocus.mode, e->xfocus.detail);
-#endif
+ /* Otherwise.. */
+ return FALSE;
+ } else {
+ g_assert(e->type == FocusOut);
- /* Try process a FocusIn first, and if a legit one isn't found, then
- do the fallback shiznit. */
- {
- XEvent fe;
- gboolean fallback = TRUE;
-
- while (TRUE) {
- if (!XCheckTypedEvent(ob_display, FocusOut, &fe))
- if (!XCheckTypedEvent(ob_display, FocusIn, &fe))
- break;
- if (fe.type == FocusOut) {
-#ifdef DEBUG_FOCUS
- g_message("found pending FocusOut");
-#endif
- if (!INVALID_FOCUSOUT(&fe)) {
- /* if there is a VALID FocusOut still coming, don't
- fallback focus yet, we'll deal with it then */
- XPutBackEvent(ob_display, &fe);
- fallback = FALSE;
- break;
- }
- } else {
-#ifdef DEBUG_FOCUS
- g_message("found pending FocusIn");
-#endif
- /* once all the FocusOut's have been dealt with, if there
- is a FocusIn still left and it is valid, then use it */
- event_process(&fe);
- /* secret magic way of event_process telling us that no
- client was found for the FocusIn event. ^_^ */
- if (fe.xfocus.window != None) {
- fallback = FALSE;
- break;
- }
- }
- }
- if (fallback) {
-#ifdef DEBUG_FOCUS
- g_message("no valid FocusIn and no FocusOut events found, "
- "falling back");
-#endif
- focus_fallback(Fallback_NoFocus);
- }
- }
- break;
- case EnterNotify:
- case LeaveNotify:
- /* NotifyUngrab occurs when a mouse button is released and the event is
- caused, like when lowering a window */
- /* NotifyVirtual occurs when ungrabbing the pointer */
- if (e->xcrossing.mode == NotifyGrab ||
- e->xcrossing.detail == NotifyInferior ||
- (e->xcrossing.mode == NotifyUngrab &&
- e->xcrossing.detail == NotifyVirtual)) {
-#ifdef DEBUG_FOCUS
- g_message("%sNotify mode %d detail %d on %lx IGNORED",
- (e->type == EnterNotify ? "Enter" : "Leave"),
- e->xcrossing.mode,
- e->xcrossing.detail, client?client->window:0);
-#endif
+
+ /* These are ones we never want.. */
+
+ /* This means focus was taken by a keyboard/mouse grab. */
+ if (mode == NotifyGrab)
+ return FALSE;
+
+ /* Focus left the root window revertedto state */
+ if (win == RootWindow(ob_display, ob_screen))
+ return FALSE;
+
+ /* These are the ones we want.. */
+
+ /* This means focus moved from a client to the root window */
+ if (detail == NotifyVirtual)
return TRUE;
- }
-#ifdef DEBUG_FOCUS
- g_message("%sNotify mode %d detail %d on %lx",
- (e->type == EnterNotify ? "Enter" : "Leave"),
- e->xcrossing.mode,
- e->xcrossing.detail, client?client->window:0);
-#endif
- break;
+ /* This means focus moved from one client to another */
+ if (detail == NotifyNonlinearVirtual)
+ return TRUE;
+ /* This means focus had moved to our frame window and now moved off */
+ if (detail == NotifyNonlinear)
+ return TRUE;
+
+ /* Otherwise.. */
+ return FALSE;
+ }
+}
+
+static Bool look_for_focusin(Display *d, XEvent *e, XPointer arg)
+{
+ return e->type == FocusIn && wanted_focusevent(e);
+}
+
+static gboolean event_ignore(XEvent *e, ObClient *client)
+{
+ switch(e->type) {
+ case FocusIn:
+ if (!wanted_focusevent(e))
+ return TRUE;
+ break;
+ case FocusOut:
+ if (!wanted_focusevent(e))
+ return TRUE;
+ break;
}
return FALSE;
}
-static void event_process(XEvent *e)
+static void event_process(const XEvent *ec, gpointer data)
{
Window window;
- Client *client;
- Menu *menu = NULL;
+ ObGroup *group = NULL;
+ ObClient *client = NULL;
+ ObDock *dock = NULL;
+ ObDockApp *dockapp = NULL;
+ ObWindow *obwin = NULL;
+ XEvent ee, *e;
+ ObEventData *ed = data;
+
+ /* make a copy we can mangle */
+ ee = *ec;
+ e = ⅇ
window = event_get_window(e);
- if (!(client = g_hash_table_lookup(client_map, &window)))
- menu = g_hash_table_lookup(menu_map, &window);
- event_set_lasttime(e);
+ if (!(e->type == PropertyNotify &&
+ (group = g_hash_table_lookup(group_map, &window))))
+ if ((obwin = g_hash_table_lookup(window_map, &window))) {
+ switch (obwin->type) {
+ case Window_Dock:
+ dock = WINDOW_AS_DOCK(obwin);
+ break;
+ case Window_DockApp:
+ dockapp = WINDOW_AS_DOCKAPP(obwin);
+ break;
+ case Window_Client:
+ client = WINDOW_AS_CLIENT(obwin);
+ break;
+ case Window_Menu:
+ case Window_Internal:
+ /* not to be used for events */
+ g_assert_not_reached();
+ break;
+ }
+ }
+
+ event_set_curtime(e);
event_hack_mods(e);
- if (event_ignore(e, client))
+ if (event_ignore(e, client)) {
+ if (ed)
+ ed->ignored = TRUE;
return;
+ } else if (ed)
+ ed->ignored = FALSE;
/* deal with it in the kernel */
- if (menu) {
- event_handle_menu(menu, e);
- return;
- } else if (client)
- event_handle_client(client, e);
- else if (window == ob_root)
- event_handle_root(e);
+
+ if (menu_frame_visible &&
+ (e->type == EnterNotify || e->type == LeaveNotify))
+ {
+ /* crossing events for menu */
+ event_handle_menu(e);
+ } else if (e->type == FocusIn) {
+ if (client && client != focus_client) {
+ frame_adjust_focus(client->frame, TRUE);
+ focus_set_client(client);
+ client_calc_layer(client);
+ }
+ } else if (e->type == FocusOut) {
+ gboolean nomove = FALSE;
+ XEvent ce;
+
+ ob_debug_type(OB_DEBUG_FOCUS, "FocusOut Event\n");
+
+ /* Look for the followup FocusIn */
+ if (!XCheckIfEvent(ob_display, &ce, look_for_focusin, NULL)) {
+ /* There is no FocusIn, this means focus went to a window that
+ is not being managed, or a window on another screen. */
+ ob_debug_type(OB_DEBUG_FOCUS, "Focus went to a black hole !\n");
+ /* nothing is focused */
+ focus_set_client(NULL);
+ } else if (ce.xany.window == e->xany.window) {
+ ob_debug_type(OB_DEBUG_FOCUS, "Focus didn't go anywhere\n");
+ /* If focus didn't actually move anywhere, there is nothing to do*/
+ nomove = TRUE;
+ } else if (ce.xfocus.detail == NotifyPointerRoot ||
+ ce.xfocus.detail == NotifyDetailNone ||
+ ce.xfocus.detail == NotifyInferior) {
+ ob_debug_type(OB_DEBUG_FOCUS, "Focus went to root\n");
+ /* Focus has been reverted to the root window or nothing
+ FocusOut events come after UnmapNotify, so we don't need to
+ worry about focusing an invalid window
+ */
+ focus_fallback(TRUE);
+ } else {
+ /* Focus did move, so process the FocusIn event */
+ ObEventData ed = { .ignored = FALSE };
+ event_process(&ce, &ed);
+ if (ed.ignored) {
+ /* The FocusIn was ignored, this means it was on a window
+ that isn't a client. */
+ ob_debug_type(OB_DEBUG_FOCUS,
+ "Focus went to an unmanaged window 0x%x !\n",
+ ce.xfocus.window);
+ focus_fallback(TRUE);
+ }
+ }
+
+ if (client && !nomove) {
+ frame_adjust_focus(client->frame, FALSE);
+ /* focus_set_client has already been called for sure */
+ client_calc_layer(client);
+ }
+ } else if (group)
+ event_handle_group(group, e);
+ else if (client)
+ event_handle_client(client, e);
+ else if (dockapp)
+ event_handle_dockapp(dockapp, e);
+ else if (dock)
+ event_handle_dock(dock, e);
+ else if (window == RootWindow(ob_display, ob_screen))
+ event_handle_root(e);
else if (e->type == MapRequest)
- client_manage(window);
+ client_manage(window);
else if (e->type == ConfigureRequest) {
- /* unhandled configure requests must be used to configure the
- window directly */
- XWindowChanges xwc;
-
- xwc.x = e->xconfigurerequest.x;
- xwc.y = e->xconfigurerequest.y;
- xwc.width = e->xconfigurerequest.width;
- xwc.height = e->xconfigurerequest.height;
- xwc.border_width = e->xconfigurerequest.border_width;
- xwc.sibling = e->xconfigurerequest.above;
- xwc.stack_mode = e->xconfigurerequest.detail;
+ /* unhandled configure requests must be used to configure the
+ window directly */
+ XWindowChanges xwc;
+
+ xwc.x = e->xconfigurerequest.x;
+ xwc.y = e->xconfigurerequest.y;
+ xwc.width = e->xconfigurerequest.width;
+ xwc.height = e->xconfigurerequest.height;
+ xwc.border_width = e->xconfigurerequest.border_width;
+ xwc.sibling = e->xconfigurerequest.above;
+ xwc.stack_mode = e->xconfigurerequest.detail;
- /* we are not to be held responsible if someone sends us an
- invalid request! */
- xerror_set_ignore(TRUE);
- XConfigureWindow(ob_display, window,
- e->xconfigurerequest.value_mask, &xwc);
- xerror_set_ignore(FALSE);
+ /* we are not to be held responsible if someone sends us an
+ invalid request! */
+ xerror_set_ignore(TRUE);
+ XConfigureWindow(ob_display, window,
+ e->xconfigurerequest.value_mask, &xwc);
+ xerror_set_ignore(FALSE);
}
-
- if (moveresize_in_progress)
- if (e->type == MotionNotify || e->type == ButtonRelease ||
- e->type == ButtonPress ||
- e->type == KeyPress || e->type == KeyRelease) {
+#ifdef SYNC
+ else if (extensions_sync &&
+ e->type == extensions_sync_event_basep + XSyncAlarmNotify)
+ {
+ XSyncAlarmNotifyEvent *se = (XSyncAlarmNotifyEvent*)e;
+ if (se->alarm == moveresize_alarm && moveresize_in_progress)
moveresize_event(e);
- return; /* no dispatch! */
- }
+ }
+#endif
/* user input (action-bound) events */
- /*
if (e->type == ButtonPress || e->type == ButtonRelease ||
- e->type == MotionNotify)
- mouse_event(e, client);
- else if (e->type == KeyPress || e->type == KeyRelease)
- ;
- */
+ e->type == MotionNotify || e->type == KeyPress ||
+ e->type == KeyRelease)
+ {
+ gboolean useevent = TRUE;
- /* dispatch the event to registered handlers */
- dispatch_x(e, client);
+ if (menu_frame_visible) {
+ if (event_handle_menu(e))
+ /* don't use the event if the menu used it, but if the menu
+ didn't use it and it's a keypress that is bound, it will
+ close the menu and be used */
+ useevent = FALSE;
+ }
+
+ if (useevent) {
+ if (!keyboard_process_interactive_grab(e, &client)) {
+ if (moveresize_in_progress) {
+ moveresize_event(e);
+
+ /* make further actions work on the client being
+ moved/resized */
+ client = moveresize_client;
+ }
+
+ menu_can_hide = FALSE;
+ ob_main_loop_timeout_add(ob_main_loop,
+ config_menu_hide_delay * 1000,
+ menu_hide_delay_func,
+ NULL, g_direct_equal, NULL);
+
+ if (e->type == ButtonPress || e->type == ButtonRelease ||
+ e->type == MotionNotify) {
+ mouse_event(client, e);
+ } else if (e->type == KeyPress) {
+ keyboard_event((focus_cycle_target ? focus_cycle_target :
+ (client ? client : focus_client)), e);
+ }
+ }
+ }
+ }
+ /* if something happens and it's not from an XEvent, then we don't know
+ the time */
+ event_curtime = CurrentTime;
}
static void event_handle_root(XEvent *e)
Atom msgtype;
switch(e->type) {
+ case SelectionClear:
+ ob_debug("Another WM has requested to replace us. Exiting.\n");
+ ob_exit_replace();
+ break;
+
case ClientMessage:
- if (e->xclient.format != 32) break;
-
- msgtype = e->xclient.message_type;
- if (msgtype == prop_atoms.net_current_desktop) {
- unsigned int d = e->xclient.data.l[0];
- if (d < screen_num_desktops)
- screen_set_desktop(d);
- } else if (msgtype == prop_atoms.net_number_of_desktops) {
- unsigned int d = e->xclient.data.l[0];
- if (d > 0)
- screen_set_num_desktops(d);
- } else if (msgtype == prop_atoms.net_showing_desktop) {
- screen_show_desktop(e->xclient.data.l[0] != 0);
- }
- break;
+ if (e->xclient.format != 32) break;
+
+ msgtype = e->xclient.message_type;
+ if (msgtype == prop_atoms.net_current_desktop) {
+ guint d = e->xclient.data.l[0];
+ if (d < screen_num_desktops) {
+ event_curtime = e->xclient.data.l[1];
+ ob_debug("SWITCH DESKTOP TIME: %d\n", event_curtime);
+ screen_set_desktop(d);
+ }
+ } else if (msgtype == prop_atoms.net_number_of_desktops) {
+ guint d = e->xclient.data.l[0];
+ if (d > 0)
+ screen_set_num_desktops(d);
+ } else if (msgtype == prop_atoms.net_showing_desktop) {
+ screen_show_desktop(e->xclient.data.l[0] != 0);
+ } else if (msgtype == prop_atoms.ob_control) {
+ if (e->xclient.data.l[0] == 1)
+ ob_reconfigure();
+ else if (e->xclient.data.l[0] == 2)
+ ob_restart();
+ }
+ break;
case PropertyNotify:
- if (e->xproperty.atom == prop_atoms.net_desktop_names)
- screen_update_desktop_names();
- else if (e->xproperty.atom == prop_atoms.net_desktop_layout)
- screen_update_layout();
- break;
+ if (e->xproperty.atom == prop_atoms.net_desktop_names)
+ screen_update_desktop_names();
+ else if (e->xproperty.atom == prop_atoms.net_desktop_layout)
+ screen_update_layout();
+ break;
+ case ConfigureNotify:
+#ifdef XRANDR
+ XRRUpdateConfiguration(e);
+#endif
+ screen_resize();
+ break;
+ default:
+ ;
+ }
+}
+
+static void event_handle_group(ObGroup *group, XEvent *e)
+{
+ GSList *it;
+
+ g_assert(e->type == PropertyNotify);
+
+ for (it = group->members; it; it = g_slist_next(it))
+ event_handle_client(it->data, e);
+}
+
+void event_enter_client(ObClient *client)
+{
+ g_assert(config_focus_follow);
+
+ if (client_normal(client) && client_can_focus(client)) {
+ if (config_focus_delay) {
+ ObFocusDelayData *data;
+
+ ob_main_loop_timeout_remove(ob_main_loop, focus_delay_func);
+
+ data = g_new(ObFocusDelayData, 1);
+ data->client = client;
+ data->time = event_curtime;
+
+ ob_main_loop_timeout_add(ob_main_loop,
+ config_focus_delay,
+ focus_delay_func,
+ data, focus_delay_cmp, focus_delay_dest);
+ } else {
+ ObFocusDelayData data;
+ data.client = client;
+ data.time = event_curtime;
+ focus_delay_func(&data);
+ }
}
}
-static void event_handle_client(Client *client, XEvent *e)
+static void event_handle_client(ObClient *client, XEvent *e)
{
XEvent ce;
Atom msgtype;
- int i=0;
+ gint i=0;
+ ObFrameContext con;
switch (e->type) {
case ButtonPress:
case ButtonRelease:
- switch (frame_context(client, e->xbutton.window)) {
- case Context_Maximize:
- client->frame->max_press = (e->type == ButtonPress);
- framerender_frame(client->frame);
+ /* Wheel buttons don't draw because they are an instant click, so it
+ is a waste of resources to go drawing it. */
+ if (!(e->xbutton.button == 4 || e->xbutton.button == 5)) {
+ con = frame_context(client, e->xbutton.window);
+ con = mouse_button_frame_context(con, e->xbutton.button);
+ switch (con) {
+ case OB_FRAME_CONTEXT_MAXIMIZE:
+ client->frame->max_press = (e->type == ButtonPress);
+ framerender_frame(client->frame);
+ break;
+ case OB_FRAME_CONTEXT_CLOSE:
+ client->frame->close_press = (e->type == ButtonPress);
+ framerender_frame(client->frame);
+ break;
+ case OB_FRAME_CONTEXT_ICONIFY:
+ client->frame->iconify_press = (e->type == ButtonPress);
+ framerender_frame(client->frame);
+ break;
+ case OB_FRAME_CONTEXT_ALLDESKTOPS:
+ client->frame->desk_press = (e->type == ButtonPress);
+ framerender_frame(client->frame);
+ break;
+ case OB_FRAME_CONTEXT_SHADE:
+ client->frame->shade_press = (e->type == ButtonPress);
+ framerender_frame(client->frame);
+ break;
+ default:
+ /* nothing changes with clicks for any other contexts */
+ break;
+ }
+ }
+ break;
+ case LeaveNotify:
+ con = frame_context(client, e->xcrossing.window);
+ switch (con) {
+ case OB_FRAME_CONTEXT_MAXIMIZE:
+ client->frame->max_hover = FALSE;
+ frame_adjust_state(client->frame);
+ break;
+ case OB_FRAME_CONTEXT_ALLDESKTOPS:
+ client->frame->desk_hover = FALSE;
+ frame_adjust_state(client->frame);
+ break;
+ case OB_FRAME_CONTEXT_SHADE:
+ client->frame->shade_hover = FALSE;
+ frame_adjust_state(client->frame);
break;
- case Context_Close:
- client->frame->close_press = (e->type == ButtonPress);
- framerender_frame(client->frame);
+ case OB_FRAME_CONTEXT_ICONIFY:
+ client->frame->iconify_hover = FALSE;
+ frame_adjust_state(client->frame);
break;
- case Context_Iconify:
- client->frame->iconify_press = (e->type == ButtonPress);
- framerender_frame(client->frame);
+ case OB_FRAME_CONTEXT_CLOSE:
+ client->frame->close_hover = FALSE;
+ frame_adjust_state(client->frame);
break;
- case Context_AllDesktops:
- client->frame->desk_press = (e->type == ButtonPress);
- framerender_frame(client->frame);
- break;
- case Context_Shade:
- client->frame->shade_press = (e->type == ButtonPress);
- framerender_frame(client->frame);
+ case OB_FRAME_CONTEXT_FRAME:
+ ob_debug_type(OB_DEBUG_FOCUS,
+ "%sNotify mode %d detail %d on %lx\n",
+ (e->type == EnterNotify ? "Enter" : "Leave"),
+ e->xcrossing.mode,
+ e->xcrossing.detail, (client?client->window:0));
+ if (keyboard_interactively_grabbed())
+ break;
+ if (config_focus_follow && config_focus_delay &&
+ /* leave inferior events can happen when the mouse goes onto
+ the window's border and then into the window before the
+ delay is up */
+ e->xcrossing.detail != NotifyInferior)
+ {
+ ob_main_loop_timeout_remove_data(ob_main_loop,
+ focus_delay_func,
+ client, FALSE);
+ }
break;
default:
- /* nothing changes with clicks for any other contexts */
break;
}
break;
- case FocusIn:
- focus_set_client(client);
- case FocusOut:
-#ifdef DEBUG_FOCUS
- g_message("Focus%s on client for %lx", (e->type==FocusIn?"In":"Out"),
- client->window);
-#endif
- /* focus state can affect the stacking layer */
- client_calc_layer(client);
- frame_adjust_focus(client->frame);
- break;
case EnterNotify:
- if (client_normal(client)) {
- if (ob_state == State_Starting) {
- /* move it to the top of the focus order */
- guint desktop = client->desktop;
- if (desktop == DESKTOP_ALL) desktop = screen_desktop;
- focus_order[desktop] = g_list_remove(focus_order[desktop],
- client);
- focus_order[desktop] = g_list_prepend(focus_order[desktop],
- client);
- } else if (config_focus_follow) {
-#ifdef DEBUG_FOCUS
- g_message("EnterNotify on %lx, focusing window",
- client->window);
-#endif
- client_focus(client);
+ {
+ gboolean nofocus = FALSE;
+
+ if (ignore_enter_focus) {
+ ignore_enter_focus--;
+ nofocus = TRUE;
+ }
+
+ con = frame_context(client, e->xcrossing.window);
+ switch (con) {
+ case OB_FRAME_CONTEXT_MAXIMIZE:
+ client->frame->max_hover = TRUE;
+ frame_adjust_state(client->frame);
+ break;
+ case OB_FRAME_CONTEXT_ALLDESKTOPS:
+ client->frame->desk_hover = TRUE;
+ frame_adjust_state(client->frame);
+ break;
+ case OB_FRAME_CONTEXT_SHADE:
+ client->frame->shade_hover = TRUE;
+ frame_adjust_state(client->frame);
+ break;
+ case OB_FRAME_CONTEXT_ICONIFY:
+ client->frame->iconify_hover = TRUE;
+ frame_adjust_state(client->frame);
+ break;
+ case OB_FRAME_CONTEXT_CLOSE:
+ client->frame->close_hover = TRUE;
+ frame_adjust_state(client->frame);
+ break;
+ case OB_FRAME_CONTEXT_FRAME:
+ if (keyboard_interactively_grabbed())
+ break;
+ if (e->xcrossing.mode == NotifyGrab ||
+ e->xcrossing.mode == NotifyUngrab ||
+ /*ignore enters when we're already in the window */
+ e->xcrossing.detail == NotifyInferior)
+ {
+ ob_debug_type(OB_DEBUG_FOCUS,
+ "%sNotify mode %d detail %d on %lx IGNORED\n",
+ (e->type == EnterNotify ? "Enter" : "Leave"),
+ e->xcrossing.mode,
+ e->xcrossing.detail, client?client->window:0);
+ } else {
+ ob_debug_type(OB_DEBUG_FOCUS,
+ "%sNotify mode %d detail %d on %lx, "
+ "focusing window: %d\n",
+ (e->type == EnterNotify ? "Enter" : "Leave"),
+ e->xcrossing.mode,
+ e->xcrossing.detail, (client?client->window:0),
+ !nofocus);
+ if (!nofocus && config_focus_follow)
+ event_enter_client(client);
}
+ break;
+ default:
+ break;
}
break;
+ }
case ConfigureRequest:
- /* compress these */
- while (XCheckTypedWindowEvent(ob_display, client->window,
- ConfigureRequest, &ce)) {
+ /* compress these */
+ while (XCheckTypedWindowEvent(ob_display, client->window,
+ ConfigureRequest, &ce)) {
++i;
- /* XXX if this causes bad things.. we can compress config req's
- with the same mask. */
- e->xconfigurerequest.value_mask |=
- ce.xconfigurerequest.value_mask;
- if (ce.xconfigurerequest.value_mask & CWX)
- e->xconfigurerequest.x = ce.xconfigurerequest.x;
- if (ce.xconfigurerequest.value_mask & CWY)
- e->xconfigurerequest.y = ce.xconfigurerequest.y;
- if (ce.xconfigurerequest.value_mask & CWWidth)
- e->xconfigurerequest.width = ce.xconfigurerequest.width;
- if (ce.xconfigurerequest.value_mask & CWHeight)
- e->xconfigurerequest.height = ce.xconfigurerequest.height;
- if (ce.xconfigurerequest.value_mask & CWBorderWidth)
- e->xconfigurerequest.border_width =
- ce.xconfigurerequest.border_width;
- if (ce.xconfigurerequest.value_mask & CWStackMode)
- e->xconfigurerequest.detail = ce.xconfigurerequest.detail;
- }
-
- /* if we are iconic (or shaded (fvwm does this)) ignore the event */
- if (client->iconic || client->shaded) return;
-
- if (e->xconfigurerequest.value_mask & CWBorderWidth)
- client->border_width = e->xconfigurerequest.border_width;
-
- /* resize, then move, as specified in the EWMH section 7.7 */
- if (e->xconfigurerequest.value_mask & (CWWidth | CWHeight |
- CWX | CWY)) {
- int x, y, w, h;
- Corner corner;
-
- x = (e->xconfigurerequest.value_mask & CWX) ?
- e->xconfigurerequest.x : client->area.x;
- y = (e->xconfigurerequest.value_mask & CWY) ?
- e->xconfigurerequest.y : client->area.y;
- w = (e->xconfigurerequest.value_mask & CWWidth) ?
- e->xconfigurerequest.width : client->area.width;
- h = (e->xconfigurerequest.value_mask & CWHeight) ?
- e->xconfigurerequest.height : client->area.height;
-
- switch (client->gravity) {
- case NorthEastGravity:
- case EastGravity:
- corner = Corner_TopRight;
- break;
- case SouthWestGravity:
- case SouthGravity:
- corner = Corner_BottomLeft;
- break;
- case SouthEastGravity:
- corner = Corner_BottomRight;
- break;
- default: /* NorthWest, Static, etc */
- corner = Corner_TopLeft;
- }
-
- client_configure(client, corner, x, y, w, h, FALSE, FALSE);
- }
-
- if (e->xconfigurerequest.value_mask & CWStackMode) {
- switch (e->xconfigurerequest.detail) {
- case Below:
- case BottomIf:
- stacking_lower(client);
- break;
-
- case Above:
- case TopIf:
- default:
- stacking_raise(client);
- break;
- }
- }
- break;
+ /* XXX if this causes bad things.. we can compress config req's
+ with the same mask. */
+ e->xconfigurerequest.value_mask |=
+ ce.xconfigurerequest.value_mask;
+ if (ce.xconfigurerequest.value_mask & CWX)
+ e->xconfigurerequest.x = ce.xconfigurerequest.x;
+ if (ce.xconfigurerequest.value_mask & CWY)
+ e->xconfigurerequest.y = ce.xconfigurerequest.y;
+ if (ce.xconfigurerequest.value_mask & CWWidth)
+ e->xconfigurerequest.width = ce.xconfigurerequest.width;
+ if (ce.xconfigurerequest.value_mask & CWHeight)
+ e->xconfigurerequest.height = ce.xconfigurerequest.height;
+ if (ce.xconfigurerequest.value_mask & CWBorderWidth)
+ e->xconfigurerequest.border_width =
+ ce.xconfigurerequest.border_width;
+ if (ce.xconfigurerequest.value_mask & CWStackMode)
+ e->xconfigurerequest.detail = ce.xconfigurerequest.detail;
+ }
+
+ /* if we are iconic (or shaded (fvwm does this)) ignore the event */
+ if (client->iconic || client->shaded) return;
+
+ /* resize, then move, as specified in the EWMH section 7.7 */
+ if (e->xconfigurerequest.value_mask & (CWWidth | CWHeight |
+ CWX | CWY |
+ CWBorderWidth)) {
+ gint x, y, w, h;
+
+ if (e->xconfigurerequest.value_mask & CWBorderWidth)
+ client->border_width = e->xconfigurerequest.border_width;
+
+ x = (e->xconfigurerequest.value_mask & CWX) ?
+ e->xconfigurerequest.x : client->area.x;
+ y = (e->xconfigurerequest.value_mask & CWY) ?
+ e->xconfigurerequest.y : client->area.y;
+ w = (e->xconfigurerequest.value_mask & CWWidth) ?
+ e->xconfigurerequest.width : client->area.width;
+ h = (e->xconfigurerequest.value_mask & CWHeight) ?
+ e->xconfigurerequest.height : client->area.height;
+
+ ob_debug("ConfigureRequest x %d %d y %d %d\n",
+ e->xconfigurerequest.value_mask & CWX, x,
+ e->xconfigurerequest.value_mask & CWY, y);
+
+ client_find_onscreen(client, &x, &y, w, h, FALSE);
+ client_configure_full(client, x, y, w, h, FALSE, TRUE, TRUE);
+ }
+
+ if (e->xconfigurerequest.value_mask & CWStackMode) {
+ switch (e->xconfigurerequest.detail) {
+ case Below:
+ case BottomIf:
+ /* Apps are so rude. And this is totally disconnected from
+ activation/focus. Bleh. */
+ /*client_lower(client);*/
+ break;
+
+ case Above:
+ case TopIf:
+ default:
+ /* Apps are so rude. And this is totally disconnected from
+ activation/focus. Bleh. */
+ /*client_raise(client);*/
+ break;
+ }
+ }
+ break;
case UnmapNotify:
- if (client->ignore_unmaps) {
- client->ignore_unmaps--;
- break;
- }
- client_unmanage(client);
- break;
+ if (client->ignore_unmaps) {
+ client->ignore_unmaps--;
+ break;
+ }
+ ob_debug("UnmapNotify for window 0x%x eventwin 0x%x sendevent %d "
+ "ignores left %d\n",
+ client->window, e->xunmap.event, e->xunmap.from_configure,
+ client->ignore_unmaps);
+ client_unmanage(client);
+ break;
case DestroyNotify:
- client_unmanage(client);
- break;
+ ob_debug("DestroyNotify for window 0x%x\n", client->window);
+ client_unmanage(client);
+ break;
case ReparentNotify:
- /* this is when the client is first taken captive in the frame */
- if (e->xreparent.parent == client->frame->plate) break;
-
- /*
- This event is quite rare and is usually handled in unmapHandler.
- However, if the window is unmapped when the reparent event occurs,
- the window manager never sees it because an unmap event is not sent
- to an already unmapped window.
- */
-
- /* we don't want the reparent event, put it back on the stack for the
- X server to deal with after we unmanage the window */
- XPutBackEvent(ob_display, e);
+ /* this is when the client is first taken captive in the frame */
+ if (e->xreparent.parent == client->frame->plate) break;
+
+ /*
+ This event is quite rare and is usually handled in unmapHandler.
+ However, if the window is unmapped when the reparent event occurs,
+ the window manager never sees it because an unmap event is not sent
+ to an already unmapped window.
+ */
+
+ /* we don't want the reparent event, put it back on the stack for the
+ X server to deal with after we unmanage the window */
+ XPutBackEvent(ob_display, e);
- client_unmanage(client);
- break;
+ ob_debug("ReparentNotify for window 0x%x\n", client->window);
+ client_unmanage(client);
+ break;
case MapRequest:
- g_message("MapRequest for 0x%lx", client->window);
+ ob_debug("MapRequest for 0x%lx\n", client->window);
if (!client->iconic) break; /* this normally doesn't happen, but if it
- does, we don't want it! */
- if (screen_showing_desktop)
- screen_show_desktop(FALSE);
- client_iconify(client, FALSE, TRUE);
- if (!client->frame->visible)
- /* if its not visible still, then don't mess with it */
- break;
- if (client->shaded)
- client_shade(client, FALSE);
- client_focus(client);
- stacking_raise(client);
- break;
+ does, we don't want it!
+ it can happen now when the window is on
+ another desktop, but we still don't
+ want it! */
+ client_activate(client, FALSE, TRUE);
+ break;
case ClientMessage:
- /* validate cuz we query stuff off the client here */
- if (!client_validate(client)) break;
-
- if (e->xclient.format != 32) return;
-
- msgtype = e->xclient.message_type;
- if (msgtype == prop_atoms.wm_change_state) {
- /* compress changes into a single change */
- while (XCheckTypedWindowEvent(ob_display, e->type,
- client->window, &ce)) {
- /* XXX: it would be nice to compress ALL messages of a
- type, not just messages in a row without other
- message types between. */
- if (ce.xclient.message_type != msgtype) {
- XPutBackEvent(ob_display, &ce);
- break;
- }
- e->xclient = ce.xclient;
- }
- client_set_wm_state(client, e->xclient.data.l[0]);
- } else if (msgtype == prop_atoms.net_wm_desktop) {
- /* compress changes into a single change */
- while (XCheckTypedWindowEvent(ob_display, e->type,
- client->window, &ce)) {
- /* XXX: it would be nice to compress ALL messages of a
- type, not just messages in a row without other
- message types between. */
- if (ce.xclient.message_type != msgtype) {
- XPutBackEvent(ob_display, &ce);
- break;
- }
- e->xclient = ce.xclient;
- }
+ /* validate cuz we query stuff off the client here */
+ if (!client_validate(client)) break;
+
+ if (e->xclient.format != 32) return;
+
+ msgtype = e->xclient.message_type;
+ if (msgtype == prop_atoms.wm_change_state) {
+ /* compress changes into a single change */
+ while (XCheckTypedWindowEvent(ob_display, client->window,
+ e->type, &ce)) {
+ /* XXX: it would be nice to compress ALL messages of a
+ type, not just messages in a row without other
+ message types between. */
+ if (ce.xclient.message_type != msgtype) {
+ XPutBackEvent(ob_display, &ce);
+ break;
+ }
+ e->xclient = ce.xclient;
+ }
+ client_set_wm_state(client, e->xclient.data.l[0]);
+ } else if (msgtype == prop_atoms.net_wm_desktop) {
+ /* compress changes into a single change */
+ while (XCheckTypedWindowEvent(ob_display, client->window,
+ e->type, &ce)) {
+ /* XXX: it would be nice to compress ALL messages of a
+ type, not just messages in a row without other
+ message types between. */
+ if (ce.xclient.message_type != msgtype) {
+ XPutBackEvent(ob_display, &ce);
+ break;
+ }
+ e->xclient = ce.xclient;
+ }
if ((unsigned)e->xclient.data.l[0] < screen_num_desktops ||
(unsigned)e->xclient.data.l[0] == DESKTOP_ALL)
client_set_desktop(client, (unsigned)e->xclient.data.l[0],
FALSE);
- } else if (msgtype == prop_atoms.net_wm_state) {
- /* can't compress these */
- g_message("net_wm_state %s %ld %ld for 0x%lx",
- (e->xclient.data.l[0] == 0 ? "Remove" :
- e->xclient.data.l[0] == 1 ? "Add" :
- e->xclient.data.l[0] == 2 ? "Toggle" : "INVALID"),
- e->xclient.data.l[1], e->xclient.data.l[2],
- client->window);
- client_set_state(client, e->xclient.data.l[0],
- e->xclient.data.l[1], e->xclient.data.l[2]);
- } else if (msgtype == prop_atoms.net_close_window) {
- g_message("net_close_window for 0x%lx", client->window);
- client_close(client);
- } else if (msgtype == prop_atoms.net_active_window) {
- g_message("net_active_window for 0x%lx", client->window);
- if (screen_showing_desktop)
- screen_show_desktop(FALSE);
- if (client->iconic)
- client_iconify(client, FALSE, TRUE);
- else if (!client->frame->visible)
- /* if its not visible for other reasons, then don't mess
- with it */
- break;
- if (client->shaded)
- client_shade(client, FALSE);
- client_focus(client);
- stacking_raise(client);
- } else if (msgtype == prop_atoms.net_wm_moveresize) {
- g_message("net_wm_moveresize for 0x%lx", client->window);
+ } else if (msgtype == prop_atoms.net_wm_state) {
+ /* can't compress these */
+ ob_debug("net_wm_state %s %ld %ld for 0x%lx\n",
+ (e->xclient.data.l[0] == 0 ? "Remove" :
+ e->xclient.data.l[0] == 1 ? "Add" :
+ e->xclient.data.l[0] == 2 ? "Toggle" : "INVALID"),
+ e->xclient.data.l[1], e->xclient.data.l[2],
+ client->window);
+ client_set_state(client, e->xclient.data.l[0],
+ e->xclient.data.l[1], e->xclient.data.l[2]);
+ } else if (msgtype == prop_atoms.net_close_window) {
+ ob_debug("net_close_window for 0x%lx\n", client->window);
+ client_close(client);
+ } else if (msgtype == prop_atoms.net_active_window) {
+ ob_debug("net_active_window for 0x%lx source=%s\n",
+ client->window,
+ (e->xclient.data.l[0] == 0 ? "unknown" :
+ (e->xclient.data.l[0] == 1 ? "application" :
+ (e->xclient.data.l[0] == 2 ? "user" : "INVALID"))));
+ /* XXX make use of data.l[2] ! */
+ event_curtime = e->xclient.data.l[1];
+ client_activate(client, FALSE,
+ (e->xclient.data.l[0] == 0 ||
+ e->xclient.data.l[0] == 2));
+ } else if (msgtype == prop_atoms.net_wm_moveresize) {
+ ob_debug("net_wm_moveresize for 0x%lx direction %d\n",
+ client->window, e->xclient.data.l[2]);
if ((Atom)e->xclient.data.l[2] ==
prop_atoms.net_wm_moveresize_size_topleft ||
(Atom)e->xclient.data.l[2] ==
e->xclient.data.l[1], e->xclient.data.l[3],
e->xclient.data.l[2]);
}
+ else if ((Atom)e->xclient.data.l[2] ==
+ prop_atoms.net_wm_moveresize_cancel)
+ moveresize_end(TRUE);
} else if (msgtype == prop_atoms.net_moveresize_window) {
- int oldg = client->gravity;
- int tmpg, x, y, w, h;
+ gint grav, x, y, w, h;
if (e->xclient.data.l[0] & 0xff)
- tmpg = e->xclient.data.l[0] & 0xff;
- else
- tmpg = oldg;
+ grav = e->xclient.data.l[0] & 0xff;
+ else
+ grav = client->gravity;
if (e->xclient.data.l[0] & 1 << 8)
x = e->xclient.data.l[1];
if (e->xclient.data.l[0] & 1 << 10)
w = e->xclient.data.l[3];
else
- w = client->area.y;
+ w = client->area.width;
if (e->xclient.data.l[0] & 1 << 11)
h = e->xclient.data.l[4];
else
- h = client->area.y;
- client->gravity = tmpg;
- client_configure(client, Corner_TopLeft, x, y, w, h, TRUE, TRUE);
- client->gravity = oldg;
+ h = client->area.height;
+
+ ob_debug("MOVERESIZE x %d %d y %d %d\n",
+ e->xclient.data.l[0] & 1 << 8, x,
+ e->xclient.data.l[0] & 1 << 9, y);
+ client_convert_gravity(client, grav, &x, &y, w, h);
+ client_find_onscreen(client, &x, &y, w, h, FALSE);
+ client_configure(client, x, y, w, h, FALSE, TRUE);
}
- break;
+ break;
case PropertyNotify:
- /* validate cuz we query stuff off the client here */
- if (!client_validate(client)) break;
+ /* validate cuz we query stuff off the client here */
+ if (!client_validate(client)) break;
- /* compress changes to a single property into a single change */
- while (XCheckTypedWindowEvent(ob_display, e->type,
- client->window, &ce)) {
- /* XXX: it would be nice to compress ALL changes to a property,
- not just changes in a row without other props between. */
- if (ce.xproperty.atom != e->xproperty.atom) {
- XPutBackEvent(ob_display, &ce);
- break;
- }
- }
-
- msgtype = e->xproperty.atom;
- if (msgtype == XA_WM_NORMAL_HINTS) {
- client_update_normal_hints(client);
- /* normal hints can make a window non-resizable */
- client_setup_decor_and_functions(client);
- }
- else if (msgtype == XA_WM_HINTS)
- client_update_wmhints(client);
- else if (msgtype == XA_WM_TRANSIENT_FOR) {
- client_update_transient_for(client);
- client_get_type(client);
- /* type may have changed, so update the layer */
- client_calc_layer(client);
- client_setup_decor_and_functions(client);
- }
- else if (msgtype == prop_atoms.net_wm_name ||
- msgtype == prop_atoms.wm_name)
- client_update_title(client);
- else if (msgtype == prop_atoms.net_wm_icon_name ||
- msgtype == prop_atoms.wm_icon_name)
- client_update_icon_title(client);
- else if (msgtype == prop_atoms.wm_class)
- client_update_class(client);
- else if (msgtype == prop_atoms.wm_protocols) {
- client_update_protocols(client);
- client_setup_decor_and_functions(client);
- }
- else if (msgtype == prop_atoms.net_wm_strut)
- client_update_strut(client);
- else if (msgtype == prop_atoms.net_wm_icon)
- client_update_icons(client);
- else if (msgtype == prop_atoms.kwm_win_icon)
- client_update_kwm_icon(client);
+ /* compress changes to a single property into a single change */
+ while (XCheckTypedWindowEvent(ob_display, client->window,
+ e->type, &ce)) {
+ Atom a, b;
+
+ /* XXX: it would be nice to compress ALL changes to a property,
+ not just changes in a row without other props between. */
+
+ a = ce.xproperty.atom;
+ b = e->xproperty.atom;
+
+ if (a == b)
+ continue;
+ if ((a == prop_atoms.net_wm_name ||
+ a == prop_atoms.wm_name ||
+ a == prop_atoms.net_wm_icon_name ||
+ a == prop_atoms.wm_icon_name)
+ &&
+ (b == prop_atoms.net_wm_name ||
+ b == prop_atoms.wm_name ||
+ b == prop_atoms.net_wm_icon_name ||
+ b == prop_atoms.wm_icon_name)) {
+ continue;
+ }
+ if (a == prop_atoms.net_wm_icon &&
+ b == prop_atoms.net_wm_icon)
+ continue;
+
+ XPutBackEvent(ob_display, &ce);
+ break;
+ }
+
+ msgtype = e->xproperty.atom;
+ if (msgtype == XA_WM_NORMAL_HINTS) {
+ client_update_normal_hints(client);
+ /* normal hints can make a window non-resizable */
+ client_setup_decor_and_functions(client);
+ } else if (msgtype == XA_WM_HINTS) {
+ client_update_wmhints(client);
+ } else if (msgtype == XA_WM_TRANSIENT_FOR) {
+ client_update_transient_for(client);
+ client_get_type(client);
+ /* type may have changed, so update the layer */
+ client_calc_layer(client);
+ client_setup_decor_and_functions(client);
+ } else if (msgtype == prop_atoms.net_wm_name ||
+ msgtype == prop_atoms.wm_name ||
+ msgtype == prop_atoms.net_wm_icon_name ||
+ msgtype == prop_atoms.wm_icon_name) {
+ client_update_title(client);
+ } else if (msgtype == prop_atoms.wm_class) {
+ client_update_class(client);
+ } else if (msgtype == prop_atoms.wm_protocols) {
+ client_update_protocols(client);
+ client_setup_decor_and_functions(client);
+ }
+ else if (msgtype == prop_atoms.net_wm_strut) {
+ client_update_strut(client);
+ }
+ else if (msgtype == prop_atoms.net_wm_icon) {
+ client_update_icons(client);
+ }
+ else if (msgtype == prop_atoms.net_wm_user_time) {
+ client_update_user_time(client);
+ }
+#ifdef SYNC
+ else if (msgtype == prop_atoms.net_wm_sync_request_counter) {
+ client_update_sync_request_counter(client);
+ }
+#endif
+ else if (msgtype == prop_atoms.sm_client_id) {
+ client_update_sm_client_id(client);
+ }
+ case ColormapNotify:
+ client_update_colormap(client, e->xcolormap.colormap);
+ break;
default:
;
#ifdef SHAPE
}
}
-static void event_handle_menu(Menu *menu, XEvent *e)
+static void event_handle_dock(ObDock *s, XEvent *e)
{
- MenuEntry *entry;
-
- g_message("EVENT %d", e->type);
switch (e->type) {
case ButtonPress:
- if (e->xbutton.button == 3)
- menu_hide(menu);
+ if (e->xbutton.button == 1)
+ stacking_raise(DOCK_AS_WINDOW(s));
+ else if (e->xbutton.button == 2)
+ stacking_lower(DOCK_AS_WINDOW(s));
break;
- case ButtonRelease:
- if (!menu->shown) break;
-
-/* grab_pointer_window(FALSE, None, menu->frame);*/
-
- entry = menu_find_entry(menu, e->xbutton.window);
- if (entry) {
- int junk;
- Window wjunk;
- guint ujunk, b, w, h;
- XGetGeometry(ob_display, e->xbutton.window,
- &wjunk, &junk, &junk, &w, &h, &b, &ujunk);
- if (e->xbutton.x >= (signed)-b &&
- e->xbutton.y >= (signed)-b &&
- e->xbutton.x < (signed)(w+b) &&
- e->xbutton.y < (signed)(h+b)) {
- menu_entry_fire(entry);
+ case EnterNotify:
+ dock_hide(FALSE);
+ break;
+ case LeaveNotify:
+ dock_hide(TRUE);
+ break;
+ }
+}
+
+static void event_handle_dockapp(ObDockApp *app, XEvent *e)
+{
+ switch (e->type) {
+ case MotionNotify:
+ dock_app_drag(app, &e->xmotion);
+ break;
+ case UnmapNotify:
+ if (app->ignore_unmaps) {
+ app->ignore_unmaps--;
+ break;
+ }
+ dock_remove(app, TRUE);
+ break;
+ case DestroyNotify:
+ dock_remove(app, FALSE);
+ break;
+ case ReparentNotify:
+ dock_remove(app, FALSE);
+ break;
+ case ConfigureNotify:
+ dock_app_configure(app, e->xconfigure.width, e->xconfigure.height);
+ break;
+ }
+}
+
+static ObMenuFrame* find_active_menu()
+{
+ GList *it;
+ ObMenuFrame *ret = NULL;
+
+ for (it = menu_frame_visible; it; it = g_list_next(it)) {
+ ret = it->data;
+ if (ret->selected)
+ break;
+ ret = NULL;
+ }
+ return ret;
+}
+
+static ObMenuFrame* find_active_or_last_menu()
+{
+ ObMenuFrame *ret = NULL;
+
+ ret = find_active_menu();
+ if (!ret && menu_frame_visible)
+ ret = menu_frame_visible->data;
+ return ret;
+}
+
+static gboolean event_handle_menu_keyboard(XEvent *ev)
+{
+ guint keycode, state;
+ gunichar unikey;
+ ObMenuFrame *frame;
+ gboolean ret = TRUE;
+
+ keycode = ev->xkey.keycode;
+ state = ev->xkey.state;
+ unikey = translate_unichar(keycode);
+
+ frame = find_active_or_last_menu();
+ if (frame == NULL)
+ ret = FALSE;
+
+ else if (keycode == ob_keycode(OB_KEY_ESCAPE) && state == 0) {
+ /* Escape closes the active menu */
+ menu_frame_hide(frame);
+ }
+
+ else if (keycode == ob_keycode(OB_KEY_RETURN) && (state == 0 ||
+ state == ControlMask))
+ {
+ /* Enter runs the active item or goes into the submenu.
+ Control-Enter runs it without closing the menu. */
+ if (frame->child)
+ menu_frame_select_next(frame->child);
+ else
+ menu_entry_frame_execute(frame->selected, state, ev->xkey.time);
+ }
+
+ else if (keycode == ob_keycode(OB_KEY_LEFT) && ev->xkey.state == 0) {
+ /* Left goes to the parent menu */
+ menu_frame_select(frame, NULL, TRUE);
+ }
+
+ else if (keycode == ob_keycode(OB_KEY_RIGHT) && ev->xkey.state == 0) {
+ /* Right goes to the selected submenu */
+ if (frame->child) menu_frame_select_next(frame->child);
+ }
+
+ else if (keycode == ob_keycode(OB_KEY_UP) && state == 0) {
+ menu_frame_select_previous(frame);
+ }
+
+ else if (keycode == ob_keycode(OB_KEY_DOWN) && state == 0) {
+ menu_frame_select_next(frame);
+ }
+
+ /* keyboard accelerator shortcuts. */
+ else if (ev->xkey.state == 0 &&
+ /* was it a valid key? */
+ unikey != 0 &&
+ /* don't bother if the menu is empty. */
+ frame->entries)
+ {
+ GList *start;
+ GList *it;
+ ObMenuEntryFrame *found = NULL;
+ guint num_found = 0;
+
+ /* start after the selected one */
+ start = frame->entries;
+ if (frame->selected) {
+ for (it = start; frame->selected != it->data; it = g_list_next(it))
+ g_assert(it != NULL); /* nothing was selected? */
+ /* next with wraparound */
+ start = g_list_next(it);
+ if (start == NULL) start = frame->entries;
+ }
+
+ it = start;
+ do {
+ ObMenuEntryFrame *e = it->data;
+ gunichar entrykey = 0;
+
+ if (e->entry->type == OB_MENU_ENTRY_TYPE_NORMAL &&
+ e->entry->data.normal.enabled)
+ entrykey = e->entry->data.normal.shortcut;
+ else if (e->entry->type == OB_MENU_ENTRY_TYPE_SUBMENU)
+ entrykey = e->entry->data.submenu.submenu->shortcut;
+
+ if (unikey == entrykey) {
+ if (found == NULL) found = e;
+ ++num_found;
+ }
+
+ /* next with wraparound */
+ it = g_list_next(it);
+ if (it == NULL) it = frame->entries;
+ } while (it != start);
+
+ if (found) {
+ if (found->entry->type == OB_MENU_ENTRY_TYPE_NORMAL &&
+ num_found == 1)
+ {
+ menu_frame_select(frame, found, TRUE);
+ usleep(50000);
+ menu_entry_frame_execute(found, state, ev->xkey.time);
+ } else {
+ menu_frame_select(frame, found, TRUE);
+ if (num_found == 1)
+ menu_frame_select_next(frame->child);
}
+ } else
+ ret = FALSE;
+ }
+ else
+ ret = FALSE;
+
+ return ret;
+}
+
+static gboolean event_handle_menu(XEvent *ev)
+{
+ ObMenuFrame *f;
+ ObMenuEntryFrame *e;
+ gboolean ret = TRUE;
+
+ switch (ev->type) {
+ case ButtonRelease:
+ if ((ev->xbutton.button < 4 || ev->xbutton.button > 5)
+ && menu_can_hide)
+ {
+ if ((e = menu_entry_frame_under(ev->xbutton.x_root,
+ ev->xbutton.y_root)))
+ menu_entry_frame_execute(e, ev->xbutton.state,
+ ev->xbutton.time);
+ else
+ menu_frame_hide_all();
}
break;
case EnterNotify:
+ if ((e = g_hash_table_lookup(menu_frame_map, &ev->xcrossing.window))) {
+ if (e->ignore_enters)
+ --e->ignore_enters;
+ else
+ menu_frame_select(e->frame, e, FALSE);
+ }
+ break;
case LeaveNotify:
- g_message("enter/leave");
- entry = menu_find_entry(menu, e->xcrossing.window);
- if (entry) {
- entry->hilite = e->type == EnterNotify;
- menu_entry_render(entry);
+ if ((e = g_hash_table_lookup(menu_frame_map, &ev->xcrossing.window)) &&
+ (f = find_active_menu()) && f->selected == e &&
+ e->entry->type != OB_MENU_ENTRY_TYPE_SUBMENU)
+ {
+ menu_frame_select(e->frame, NULL, FALSE);
}
+ case MotionNotify:
+ if ((e = menu_entry_frame_under(ev->xmotion.x_root,
+ ev->xmotion.y_root)))
+ menu_frame_select(e->frame, e, FALSE);
+ break;
+ case KeyPress:
+ ret = event_handle_menu_keyboard(ev);
break;
}
+ return ret;
+}
+
+static gboolean menu_hide_delay_func(gpointer data)
+{
+ menu_can_hide = TRUE;
+ return FALSE; /* no repeat */
+}
+
+static void focus_delay_dest(gpointer data)
+{
+ g_free(data);
+}
+
+static gboolean focus_delay_cmp(gconstpointer d1, gconstpointer d2)
+{
+ const ObFocusDelayData *f1 = d1;
+ return f1->client == d2;
+}
+
+static gboolean focus_delay_func(gpointer data)
+{
+ ObFocusDelayData *d = data;
+ Time old = event_curtime;
+
+ event_curtime = d->time;
+ if (focus_client != d->client) {
+ if (client_focus(d->client) && config_focus_raise)
+ client_raise(d->client);
+ }
+ event_curtime = old;
+ return FALSE; /* no repeat */
+}
+
+static void focus_delay_client_dest(ObClient *client, gpointer data)
+{
+ ob_main_loop_timeout_remove_data(ob_main_loop, focus_delay_func,
+ client, FALSE);
+}
+
+void event_halt_focus_delay()
+{
+ ob_main_loop_timeout_remove(ob_main_loop, focus_delay_func);
+}
+
+void event_ignore_queued_enters()
+{
+ GSList *saved = NULL, *it;
+ XEvent *e;
+
+ XSync(ob_display, FALSE);
+
+ /* count the events */
+ while (TRUE) {
+ e = g_new(XEvent, 1);
+ if (XCheckTypedEvent(ob_display, EnterNotify, e)) {
+ ObWindow *win;
+
+ win = g_hash_table_lookup(window_map, &e->xany.window);
+ if (win && WINDOW_IS_CLIENT(win))
+ ++ignore_enter_focus;
+
+ saved = g_slist_append(saved, e);
+ } else {
+ g_free(e);
+ break;
+ }
+ }
+ /* put the events back */
+ for (it = saved; it; it = g_slist_next(it)) {
+ XPutBackEvent(ob_display, it->data);
+ g_free(it->data);
+ }
+ g_slist_free(saved);
+}
+
+gboolean event_time_after(Time t1, Time t2)
+{
+ g_assert(t1 != CurrentTime);
+ g_assert(t2 != CurrentTime);
+
+ /*
+ Timestamp values wrap around (after about 49.7 days). The server, given
+ its current time is represented by timestamp T, always interprets
+ timestamps from clients by treating half of the timestamp space as being
+ later in time than T.
+ - http://tronche.com/gui/x/xlib/input/pointer-grabbing.html
+ */
+
+ /* TIME_HALF is half of the number space of a Time type variable */
+#define TIME_HALF (Time)(1 << (sizeof(Time)*8-1))
+
+ if (t2 >= TIME_HALF)
+ /* t2 is in the second half so t1 might wrap around and be smaller than
+ t2 */
+ return t1 >= t2 || t1 < (t2 + TIME_HALF);
+ else
+ /* t2 is in the first half so t1 has to come after it */
+ return t1 >= t2 && t1 < (t2 + TIME_HALF);
}