#include "stacking.h"
#include "extensions.h"
#include "translate.h"
+#include "ping.h"
#include <X11/Xlib.h>
#include <X11/Xatom.h>
{
ObClient *client;
Time time;
+ gulong serial;
} ObFocusDelayData;
typedef struct
static void event_handle_dockapp(ObDockApp *app, XEvent *e);
static void event_handle_client(ObClient *c, XEvent *e);
static void event_handle_user_input(ObClient *client, XEvent *e);
-static gboolean is_enter_focus_event_ignored(XEvent *e);
+static gboolean is_enter_focus_event_ignored(gulong serial);
+static void event_ignore_enter_range(gulong start, gulong end);
static void focus_delay_dest(gpointer data);
static gboolean focus_delay_cmp(gconstpointer d1, gconstpointer d2);
Time event_curtime = CurrentTime;
Time event_last_user_time = CurrentTime;
+/*! The serial of the current X event */
+static gulong event_curserial;
static gboolean focus_left_screen = FALSE;
/*! A list of ObSerialRanges which are to be ignored for mouse enter events */
static GSList *ignore_serials = NULL;
static void event_hack_mods(XEvent *e)
{
+#ifdef XKB
+ XkbStateRec xkb_state;
+#endif
+
switch (e->type) {
case ButtonPress:
case ButtonRelease:
e->xkey.state = modkeys_only_modifier_masks(e->xkey.state);
break;
case KeyRelease:
- e->xkey.state = modkeys_only_modifier_masks(e->xkey.state);
- /* remove from the state the mask of the modifier key being released,
- if it is a modifier key being released that is */
- e->xkey.state &= ~modkeys_keycode_to_mask(e->xkey.keycode);
+#ifdef XKB
+ /* If XKB is present, then the modifiers are all strange from its
+ magic. Our X core protocol stuff won't work, so we use this to
+ find what the modifier state is instead. */
+ if (XkbGetState(ob_display, XkbUseCoreKbd, &xkb_state) == Success)
+ e->xkey.state =
+ modkeys_only_modifier_masks(xkb_state.compat_state);
+ else
+#endif
+ {
+ e->xkey.state = modkeys_only_modifier_masks(e->xkey.state);
+ /* remove from the state the mask of the modifier key being
+ released, if it is a modifier key being released that is */
+ e->xkey.state &= ~modkeys_keycode_to_mask(e->xkey.keycode);
+ }
break;
case MotionNotify:
e->xmotion.state = modkeys_only_modifier_masks(e->xmotion.state);
client = WINDOW_AS_CLIENT(obwin);
break;
case Window_Menu:
- case Window_Internal:
/* not to be used for events */
g_assert_not_reached();
break;
+ case Window_Internal:
+ /* we don't do anything with events directly on these windows */
+ break;
}
}
event_set_curtime(e);
+ event_curserial = e->xany.serial;
event_hack_mods(e);
if (event_ignore(e, client)) {
if (ed)
window with RevertToParent focus */
frame_adjust_focus(client->frame, FALSE);
/* focus_set_client(NULL) has already been called */
- client_calc_layer(client);
}
else if (e->xfocus.detail == NotifyPointerRoot ||
e->xfocus.detail == NotifyDetailNone ||
frame_adjust_focus(client->frame, FALSE);
/* focus_set_client(NULL) has already been called in this
section or by focus_fallback */
- client_calc_layer(client);
}
}
else if (client)
event_handle_root(e);
else if (e->type == MapRequest)
client_manage(window);
+ else if (e->type == MappingNotify) {
+ /* keyboard layout changes for modifier mapping changes. reload the
+ modifier map, and rebind all the key bindings as appropriate */
+ ob_debug("Kepboard map changed. Reloading keyboard bindings.\n");
+ modkeys_shutdown(TRUE);
+ modkeys_startup(TRUE);
+ keyboard_rebind();
+ }
else if (e->type == ClientMessage) {
/* This is for _NET_WM_REQUEST_FRAME_EXTENTS messages. They come for
windows that are not managed yet. */
}
#endif
- if (e->type == ButtonPress || e->type == ButtonRelease ||
- e->type == MotionNotify || e->type == KeyPress ||
- e->type == KeyRelease)
- {
- event_handle_user_input(client, e);
+ if (e->type == ButtonPress || e->type == ButtonRelease) {
+ /* If the button press was on some non-root window, or was physically
+ on the root window, then process it */
+ if (window != RootWindow(ob_display, ob_screen) ||
+ e->xbutton.subwindow == None)
+ {
+ event_handle_user_input(client, e);
+ }
+ /* Otherwise only process it if it was physically on an openbox
+ internal window */
+ else {
+ ObWindow *w;
+
+ if ((w = g_hash_table_lookup(window_map, &e->xbutton.subwindow)) &&
+ WINDOW_IS_INTERNAL(w))
+ {
+ event_handle_user_input(client, e);
+ }
+ }
}
+ else if (e->type == KeyPress || e->type == KeyRelease ||
+ e->type == MotionNotify)
+ event_handle_user_input(client, e);
/* if something happens and it's not from an XEvent, then we don't know
the time */
event_curtime = CurrentTime;
+ event_curserial = 0;
}
static void event_handle_root(XEvent *e)
ob_reconfigure();
else if (e->xclient.data.l[0] == 2)
ob_restart();
+ else if (e->xclient.data.l[0] == 3)
+ ob_exit(0);
+ } else if (msgtype == prop_atoms.wm_protocols) {
+ if ((Atom)e->xclient.data.l[0] == prop_atoms.net_wm_ping)
+ ping_got_pong(e->xclient.data.l[1]);
}
break;
case PropertyNotify:
{
g_assert(config_focus_follow);
+ if (is_enter_focus_event_ignored(event_curserial)) {
+ ob_debug_type(OB_DEBUG_FOCUS, "Ignoring enter event with serial %lu\n"
+ "on client 0x%x", event_curserial, client->window);
+ return;
+ }
+
if (client_enter_focusable(client) && client_can_focus(client)) {
if (config_focus_delay) {
ObFocusDelayData *data;
data = g_new(ObFocusDelayData, 1);
data->client = client;
data->time = event_curtime;
+ data->serial = event_curserial;
ob_main_loop_timeout_add(ob_main_loop,
config_focus_delay * 1000,
ObFocusDelayData data;
data.client = client;
data.time = event_curtime;
+ data.serial = event_curserial;
focus_delay_func(&data);
}
}
if (e->xcrossing.mode == NotifyGrab ||
e->xcrossing.mode == NotifyUngrab ||
/*ignore enters when we're already in the window */
- e->xcrossing.detail == NotifyInferior ||
- is_enter_focus_event_ignored(e))
+ e->xcrossing.detail == NotifyInferior)
{
ob_debug_type(OB_DEBUG_FOCUS,
- "%sNotify mode %d detail %d on %lx IGNORED\n",
+ "%sNotify mode %d detail %d serial %lu on %lx "
+ "IGNORED\n",
(e->type == EnterNotify ? "Enter" : "Leave"),
e->xcrossing.mode,
- e->xcrossing.detail, client?client->window:0);
+ e->xcrossing.detail,
+ e->xcrossing.serial,
+ client?client->window:0);
}
else {
ob_debug_type(OB_DEBUG_FOCUS,
- "%sNotify mode %d detail %d on %lx, "
+ "%sNotify mode %d detail %d serial %lu on %lx, "
"focusing window\n",
(e->type == EnterNotify ? "Enter" : "Leave"),
e->xcrossing.mode,
- e->xcrossing.detail, (client?client->window:0));
+ e->xcrossing.detail,
+ e->xcrossing.serial,
+ (client?client->window:0));
if (config_focus_follow)
event_enter_client(client);
}
ob_debug_type(OB_DEBUG_APP_BUGS,
"_NET_ACTIVE_WINDOW message for window %s is "
"missing source indication\n");
- client_activate(client, FALSE, TRUE, TRUE,
+ client_activate(client, TRUE, TRUE, TRUE,
(e->xclient.data.l[0] == 0 ||
e->xclient.data.l[0] == 2));
} else if (msgtype == prop_atoms.net_wm_moveresize) {
}
}
-static ObMenuFrame* find_active_menu()
+static ObMenuFrame* find_active_menu(void)
{
GList *it;
ObMenuFrame *ret = NULL;
return ret;
}
-static ObMenuFrame* find_active_or_last_menu()
+static ObMenuFrame* find_active_or_last_menu(void)
{
ObMenuFrame *ret = NULL;
/* Allow control while going thru the menu */
else if (ev->type == KeyPress && (state & ~ControlMask) == 0) {
+ frame->got_press = TRUE;
+
if (keycode == ob_keycode(OB_KEY_ESCAPE)) {
menu_frame_hide_all();
ret = TRUE;
Allow ControlMask only, and don't bother if the menu is empty */
else if (ev->type == KeyRelease && (state & ~ControlMask) == 0 &&
- frame->entries)
+ frame->entries && frame->got_press)
{
if (keycode == ob_keycode(OB_KEY_RETURN)) {
/* Enter runs the active item or goes into the submenu.
if (menu_frame_visible || moveresize_in_progress) return FALSE;
event_curtime = d->time;
- if (focus_client != d->client) {
- if (client_focus(d->client) && config_focus_raise)
- stacking_raise(CLIENT_AS_WINDOW(d->client));
- }
+ event_curserial = d->serial;
+ if (client_focus(d->client) && config_focus_raise)
+ stacking_raise(CLIENT_AS_WINDOW(d->client));
event_curtime = old;
return FALSE; /* no repeat */
}
client, FALSE);
}
-void event_halt_focus_delay()
+void event_halt_focus_delay(void)
{
- /* ignore all enter events up till now */
- event_end_ignore_all_enters(1);
+ /* ignore all enter events up till the event which caused this to occur */
+ if (event_curserial) event_ignore_enter_range(1, event_curserial);
ob_main_loop_timeout_remove(ob_main_loop, focus_delay_func);
}
-gulong event_start_ignore_all_enters()
+gulong event_start_ignore_all_enters(void)
{
- XSync(ob_display, FALSE);
- return LastKnownRequestProcessed(ob_display);
+ return NextRequest(ob_display);
}
-void event_end_ignore_all_enters(gulong start)
+static void event_ignore_enter_range(gulong start, gulong end)
{
ObSerialRange *r;
g_assert(start != 0);
- XSync(ob_display, FALSE);
+ g_assert(end != 0);
r = g_new(ObSerialRange, 1);
r->start = start;
- r->end = LastKnownRequestProcessed(ob_display);
+ r->end = end;
ignore_serials = g_slist_prepend(ignore_serials, r);
+ ob_debug_type(OB_DEBUG_FOCUS, "ignoring enters from %lu until %lu\n",
+ r->start, r->end);
+
/* increment the serial so we don't ignore events we weren't meant to */
- XSync(ob_display, FALSE);
+ PROP_ERASE(screen_support_win, motif_wm_hints);
}
-static gboolean is_enter_focus_event_ignored(XEvent *e)
+void event_end_ignore_all_enters(gulong start)
{
- GSList *it, *next;
+ /* Use (NextRequest-1) so that we ignore up to the current serial only.
+ Inside event_ignore_enter_range, we increment the serial by one, but if
+ we ignore that serial too, then any enter events generated by mouse
+ movement will be ignored until we create some further network traffic.
+ Instead ignore up to NextRequest-1, then when we increment the serial,
+ we will be *past* the range of ignored serials */
+ event_ignore_enter_range(start, NextRequest(ob_display)-1);
+}
- g_assert(e->type == EnterNotify &&
- !(e->xcrossing.mode == NotifyGrab ||
- e->xcrossing.mode == NotifyUngrab ||
- e->xcrossing.detail == NotifyInferior));
+static gboolean is_enter_focus_event_ignored(gulong serial)
+{
+ GSList *it, *next;
for (it = ignore_serials; it; it = next) {
ObSerialRange *r = it->data;
next = g_slist_next(it);
- if ((glong)(e->xany.serial - r->end) > 0) {
+ if ((glong)(serial - r->end) > 0) {
/* past the end */
ignore_serials = g_slist_delete_link(ignore_serials, it);
g_free(r);
}
- else if ((glong)(e->xany.serial - r->start) >= 0)
+ else if ((glong)(serial - r->start) >= 0)
return TRUE;
}
return FALSE;
}
-void event_cancel_all_key_grabs()
+void event_cancel_all_key_grabs(void)
{
if (actions_interactive_act_running()) {
actions_interactive_cancel_act();
}
else
ungrab_passive_key();
+
+ XSync(ob_display, FALSE);
}
gboolean event_time_after(Time t1, Time t2)
return t1 >= t2 && t1 < (t2 + TIME_HALF);
}
-Time event_get_server_time()
+Time event_get_server_time(void)
{
/* Generate a timestamp */
XEvent event;