/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
-event.c for the Openbox window manager
-Copyright (c) 2003 Ben Jansens
+ event.c for the Openbox window manager
+ Copyright (c) 2004 Mikael Magnusson
+ Copyright (c) 2003 Ben 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 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.
+ 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.
+ See the COPYING file for a copy of the GNU General Public License.
*/
#include "event.h"
#ifdef HAVE_SIGNAL_H
# include <signal.h>
#endif
+#ifdef XKB
+# include <X11/XKBlib.h>
+#endif
#ifdef USE_SM
#include <X11/ICE/ICElib.h>
#endif
+typedef struct
+{
+ gboolean ignored;
+} ObEventData;
+
static void event_process(const XEvent *e, gpointer data);
-static void event_done(gpointer data);
static void event_client_dest(ObClient *client, gpointer data);
static void event_handle_root(XEvent *e);
static void event_handle_menu(XEvent *e);
static gboolean menu_hide_delay_func(gpointer data);
+#define INVALID_FOCUSIN(e) ((e)->xfocus.detail == NotifyInferior || \
+ (e)->xfocus.detail == NotifyAncestor || \
+ (e)->xfocus.detail > NotifyNonlinearVirtual)
+#define INVALID_FOCUSOUT(e) ((e)->xfocus.mode == NotifyGrab || \
+ (e)->xfocus.detail == NotifyInferior || \
+ (e)->xfocus.detail == NotifyAncestor || \
+ (e)->xfocus.detail > NotifyNonlinearVirtual)
+
Time event_lasttime = 0;
/*! 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;
static gboolean menu_can_hide;
-static ObClient *focus_in, *focus_out;
-
#ifdef USE_SM
-static void ice_handler(int fd, gpointer conn)
+static void ice_handler(gint fd, gpointer conn)
{
Bool b;
IceProcessMessages(conn, NULL, &b);
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])
}
}
- ob_main_loop_x_add(ob_main_loop, event_process, event_done, NULL, NULL);
+ ob_main_loop_x_add(ob_main_loop, event_process, NULL, NULL);
#ifdef USE_SM
IceAddConnectionWatch(ice_watch, NULL);
#endif
client_remove_destructor(focus_delay_client_dest);
+ client_remove_destructor(event_client_dest);
XFreeModifiermap(modmap);
}
}
#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:
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..) */
+#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) {
return TRUE;
break;
case FocusIn:
- if (e->xfocus.detail > NotifyNonlinearVirtual)
+ /* 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
+ ob_debug("FocusIn on %lx mode %d detail %d IGNORED\n",
+ 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;
+ }
+
+#ifdef DEBUG_FOCUS
+ ob_debug("FocusIn on %lx mode %d detail %d\n", e->xfocus.window,
+ e->xfocus.mode, e->xfocus.detail);
+#endif
break;
case FocusOut:
- if (e->xfocus.detail > NotifyNonlinearVirtual)
- return TRUE;
- if (e->xfocus.detail == NotifyInferior ||
- e->xfocus.mode == NotifyGrab)
+ if (INVALID_FOCUSOUT(e)) {
+#ifdef DEBUG_FOCUS
+ ob_debug("FocusOut on %lx mode %d detail %d IGNORED\n",
+ e->xfocus.window, e->xfocus.mode, e->xfocus.detail);
+#endif
return TRUE;
- break;
- }
- return FALSE;
-}
-
-static void event_client_dest(ObClient *client, gpointer data)
-{
- if (client == focus_in)
- focus_in = NULL;
- if (client == focus_out)
- focus_out = NULL;
-}
-
-static void event_done(gpointer data)
-{
- static ObClient *last = NULL;
-
- if (focus_in) {
- if (focus_in != focus_client) {
- focus_set_client(focus_in);
- frame_adjust_focus(focus_in->frame, TRUE);
- client_calc_layer(focus_in);
}
- }
- if (focus_out) {
- if (focus_out == focus_client)
- focus_set_client(NULL);
- frame_adjust_focus(focus_out->frame, FALSE);
- client_calc_layer(focus_out);
- }
- if (focus_client != last) {
- if (!focus_client)
- focus_fallback(OB_FOCUS_FALLBACK_NOFOCUS);
- last = focus_client;
+#ifdef DEBUG_FOCUS
+ ob_debug("FocusOut on %lx mode %d detail %d\n",
+ e->xfocus.window, e->xfocus.mode, e->xfocus.detail);
+#endif
+ {
+ XEvent fe;
+ gboolean fallback = TRUE;
+
+ while (TRUE) {
+ if (!XCheckTypedWindowEvent(ob_display, e->xfocus.window,
+ FocusOut, &fe))
+ if (!XCheckTypedEvent(ob_display, FocusIn, &fe))
+ break;
+ if (fe.type == FocusOut) {
+#ifdef DEBUG_FOCUS
+ ob_debug("found pending FocusOut\n");
+#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
+ ob_debug("found pending FocusIn\n");
+#endif
+ /* is the focused window getting a FocusOut/In back to
+ itself?
+ */
+ if (fe.xfocus.window == e->xfocus.window &&
+ !event_ignore(&fe, client)) {
+ /*
+ if focus_client is not set, then we can't do
+ this. we need the FocusIn. This happens in the
+ case when the set_focus_client(NULL) in the
+ focus_fallback function fires and then
+ focus_fallback picks the currently focused
+ window (such as on a SendToDesktop-esque action.
+ */
+ if (focus_client) {
+#ifdef DEBUG_FOCUS
+ ob_debug("focused window got an Out/In back to "
+ "itself IGNORED both\n");
+#endif
+ return TRUE;
+ } else {
+ event_process(&fe, NULL);
+#ifdef DEBUG_FOCUS
+ ob_debug("focused window got an Out/In back to "
+ "itself but focus_client was null "
+ "IGNORED just the Out\n");
+#endif
+ return TRUE;
+ }
+ }
+
+ {
+ ObEventData d;
+
+ /* 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, &d);
+ if (!d.ignored) {
+#ifdef DEBUG_FOCUS
+ ob_debug("FocusIn was OK, so don't fallback\n");
+#endif
+ fallback = FALSE;
+ break;
+ }
+ }
+ }
+ }
+ if (fallback) {
+#ifdef DEBUG_FOCUS
+ ob_debug("no valid FocusIn and no FocusOut events found, "
+ "falling back\n");
+#endif
+ focus_fallback(OB_FOCUS_FALLBACK_NOFOCUS);
+ }
+ }
+ break;
}
-
- focus_in = focus_out = NULL;
+ return FALSE;
}
static void event_process(const XEvent *ec, gpointer data)
ObDockApp *dockapp = NULL;
ObWindow *obwin = NULL;
XEvent ee, *e;
+ ObEventData *ed = data;
/* make a copy we can mangle */
ee = *ec;
event_set_lasttime(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 (group)
/* 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;
menu_can_hide = FALSE;
ob_main_loop_timeout_add(ob_main_loop,
- G_USEC_PER_SEC / 4,
+ config_menu_hide_delay * 1000,
menu_hide_delay_func,
NULL, NULL);
e->type == MotionNotify)
mouse_event(client, e);
else if (e->type == KeyPress)
- /* when in the middle of a focus cycling action, this
- causes the window which appears to be focused to be
- the one on which the actions will be executed */
- keyboard_event((focus_cycle_target ?
- focus_cycle_target : client), e);
+ keyboard_event((focus_cycle_target ? focus_cycle_target :
+ (focus_hilite ? focus_hilite : client)),
+ e);
}
}
}
msgtype = e->xclient.message_type;
if (msgtype == prop_atoms.net_current_desktop) {
- unsigned int d = e->xclient.data.l[0];
+ guint 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];
+ guint d = e->xclient.data.l[0];
if (d > 0)
screen_set_num_desktops(d);
} else if (msgtype == prop_atoms.net_showing_desktop) {
break;
default:
;
-#ifdef VIDMODE
- if (extensions_vidmode && e->type == extensions_vidmode_event_basep) {
- ob_debug("VIDMODE EVENT\n");
- }
-#endif
}
}
{
XEvent ce;
Atom msgtype;
- int i=0;
+ gint i=0;
ObFrameContext con;
switch (e->type) {
e->xfocus.window, client->window,
e->xfocus.mode, e->xfocus.detail);
#endif
- focus_in = client;
- if (focus_out == client)
- focus_out = NULL;
+ if (client != focus_client) {
+ focus_set_client(client);
+ frame_adjust_focus(client->frame, TRUE);
+ client_calc_layer(client);
+ }
break;
case FocusOut:
#ifdef DEBUG_FOCUS
e->xfocus.window, client->window,
e->xfocus.mode, e->xfocus.detail);
#endif
- if (focus_in == client)
- focus_in = NULL;
- if (client == focus_client)
- focus_out = client;
+ focus_hilite = NULL;
+ frame_adjust_focus(client->frame, FALSE);
+ client_calc_layer(client);
break;
case LeaveNotify:
con = frame_context(client, e->xcrossing.window);
frame_adjust_state(client->frame);
break;
case OB_FRAME_CONTEXT_FRAME:
- /*
- if (config_focus_follow && config_focus_delay)
- ob_main_loop_timeout_remove_data(ob_main_loop,
- focus_delay_func,
- client);
- */
+ if (config_focus_follow && config_focus_delay)
+ ob_main_loop_timeout_remove_data(ob_main_loop,
+ focus_delay_func,
+ client, TRUE);
break;
default:
break;
if (e->xconfigurerequest.value_mask & (CWWidth | CWHeight |
CWX | CWY |
CWBorderWidth)) {
- int x, y, w, h;
+ gint x, y, w, h;
ObCorner corner;
if (e->xconfigurerequest.value_mask & CWBorderWidth)
e->xconfigurerequest.height : client->area.height;
{
- int newx = x;
- int newy = y;
- int fw = w +
- client->frame->size.left + client->frame->size.right;
- int fh = h +
- client->frame->size.top + client->frame->size.bottom;
+ gint newx = x;
+ gint newy = y;
+ gint fw = w +
+ client->frame->size.left + client->frame->size.right;
+ gint fh = h +
+ client->frame->size.top + client->frame->size.bottom;
client_find_onscreen(client, &newx, &newy, fw, fh,
client_normal(client));
if (e->xconfigurerequest.value_mask & CWX)
if (e->xconfigurerequest.value_mask & CWY)
y = newy;
}
-
+
switch (client->gravity) {
case NorthEastGravity:
case EastGravity:
case MapRequest:
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! */
+ 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);
break;
case ClientMessage:
e->xclient.data.l[2]);
}
} else if (msgtype == prop_atoms.net_moveresize_window) {
- int oldg = client->gravity;
- int tmpg, x, y, w, h;
+ gint oldg = client->gravity;
+ gint tmpg, x, y, w, h;
if (e->xclient.data.l[0] & 0xff)
tmpg = e->xclient.data.l[0] & 0xff;
client->gravity = tmpg;
{
- int newx = x;
- int newy = y;
- int fw = w +
- client->frame->size.left + client->frame->size.right;
- int fh = h +
- client->frame->size.top + client->frame->size.bottom;
+ gint newx = x;
+ gint newy = y;
+ gint fw = w +
+ client->frame->size.left + client->frame->size.right;
+ gint fh = h +
+ client->frame->size.top + client->frame->size.bottom;
client_find_onscreen(client, &newx, &newy, fw, fh,
client_normal(client));
if (e->xclient.data.l[0] & 1 << 8)
if (e->xclient.data.l[0] & 1 << 9)
y = newy;
}
-
+
client_configure(client, OB_CORNER_TOPLEFT,
x, y, w, h, FALSE, TRUE);
{
switch (e->type) {
case ButtonPress:
- stacking_raise(DOCK_AS_WINDOW(s));
+ if (e->xbutton.button == 1)
+ stacking_raise(DOCK_AS_WINDOW(s), FALSE);
+ else if (e->xbutton.button == 2)
+ stacking_lower(DOCK_AS_WINDOW(s), FALSE);
break;
case EnterNotify:
dock_hide(FALSE);
static void focus_delay_client_dest(ObClient *client, gpointer data)
{
- ob_main_loop_timeout_remove_data(ob_main_loop, focus_delay_func, client);
+ ob_main_loop_timeout_remove_data(ob_main_loop, focus_delay_func, client, TRUE);
+}
+
+static void event_client_dest(ObClient *client, gpointer data)
+{
+ if (client == focus_hilite)
+ focus_hilite = NULL;
+}
+
+void event_halt_focus_delay()
+{
+ ob_main_loop_timeout_remove(ob_main_loop, focus_delay_func);
}
void event_ignore_queued_enters()