#include "stacking.h"
#include "ping.h"
#include "obt/display.h"
+#include "obt/xqueue.h"
#include "obt/prop.h"
#include "obt/keyboard.h"
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);
+static void unfocus_delay_dest(gpointer data);
static gboolean focus_delay_func(gpointer data);
static gboolean unfocus_delay_func(gpointer data);
static void focus_delay_client_dest(ObClient *client, gpointer data);
-Time event_curtime = CurrentTime;
Time event_last_user_time = CurrentTime;
-/*! The serial of the current X event */
+/*! The time of the current X event (if it had a timestamp) */
+static Time event_curtime = CurrentTime;
+/*! The source time that started the current X event (user-provided, so not
+ to be trusted) */
+static Time event_sourcetime = CurrentTime;
+
+/*! The serial of the current X event */
static gulong event_curserial;
static gboolean focus_left_screen = FALSE;
+static gboolean waiting_for_focusin = FALSE;
/*! A list of ObSerialRanges which are to be ignored for mouse enter events */
static GSList *ignore_serials = NULL;
+static guint focus_delay_timeout_id = 0;
+static ObClient *focus_delay_timeout_client = NULL;
+static guint unfocus_delay_timeout_id = 0;
+static ObClient *unfocus_delay_timeout_client = NULL;
#ifdef USE_SM
-static void ice_handler(gint fd, gpointer conn)
+static gboolean ice_handler(GIOChannel *source, GIOCondition cond,
+ gpointer conn)
{
Bool b;
IceProcessMessages(conn, NULL, &b);
+ return TRUE; /* don't remove the event source */
}
static void ice_watch(IceConn conn, IcePointer data, Bool opening,
IcePointer *watch_data)
{
- static gint fd = -1;
+ static guint id = 0;
if (opening) {
- fd = IceConnectionNumber(conn);
- obt_main_loop_fd_add(ob_main_loop, fd, ice_handler, conn, NULL);
- } else {
- obt_main_loop_fd_remove(ob_main_loop, fd);
- fd = -1;
+ GIOChannel *ch;
+
+ ch = g_io_channel_unix_new(IceConnectionNumber(conn));
+ id = g_io_add_watch(ch, G_IO_IN, ice_handler, conn);
+ g_io_channel_unref(ch);
+ } else if (id) {
+ g_source_remove(id);
+ id = 0;
}
}
#endif
{
if (reconfig) return;
- obt_main_loop_x_add(ob_main_loop, event_process, NULL, NULL);
+ xqueue_add_callback(event_process, NULL);
#ifdef USE_SM
IceAddConnectionWatch(ice_watch, NULL);
#endif
client_add_destroy_notify(focus_delay_client_dest, NULL);
-
- /* get an initial time for event_curtime (mapping the initial windows needs
- a timestamp) */
- event_curtime = event_get_server_time();
}
void event_shutdown(gboolean reconfig)
return window;
}
-static inline Time event_time(const XEvent *e)
+static inline Time event_get_timestamp(const XEvent *e)
{
Time t = CurrentTime;
static void event_set_curtime(XEvent *e)
{
- Time t = event_time(e);
-
- if (t == CurrentTime) {
- /* Some events don't come with timestamps :(
- ...but we want the time anyways. */
- if (e->type == MapRequest)
- t = event_get_server_time();
- }
+ Time t = event_get_timestamp(e);
/* watch that if we get an event earlier than the last specified user_time,
which can happen if the clock goes backwards, we erase the last
specified user_time */
if (t && event_last_user_time && event_time_after(event_last_user_time, t))
- event_last_user_time = CurrentTime;
+ event_reset_user_time();
+ event_sourcetime = CurrentTime;
event_curtime = t;
}
/* compress events */
{
XEvent ce;
- while (XCheckTypedWindowEvent(obt_display, e->xmotion.window,
- e->type, &ce)) {
+ ObtXQueueWindowType wt;
+
+ wt.window = e->xmotion.window;
+ wt.type = MotionNotify;
+ while (xqueue_remove_local(&ce, xqueue_match_window_type, &wt)) {
e->xmotion.x = ce.xmotion.x;
e->xmotion.y = ce.xmotion.y;
e->xmotion.x_root = ce.xmotion.x_root;
}
}
-static Bool event_look_for_focusin(Display *d, XEvent *e, XPointer arg)
+static gboolean event_look_for_focusin(XEvent *e, gpointer data)
{
return e->type == FocusIn && wanted_focusevent(e, FALSE);
}
-static Bool event_look_for_focusin_client(Display *d, XEvent *e, XPointer arg)
+static gboolean event_look_for_focusin_client(XEvent *e, gpointer data)
{
return e->type == FocusIn && wanted_focusevent(e, TRUE);
}
}
-static gboolean event_ignore(XEvent *e, ObClient *client)
-{
- switch(e->type) {
- case FocusIn:
- print_focusevent(e);
- if (!wanted_focusevent(e, FALSE))
- return TRUE;
- break;
- case FocusOut:
- print_focusevent(e);
- if (!wanted_focusevent(e, FALSE))
- return TRUE;
- break;
- }
- return FALSE;
-}
-
static void event_process(const XEvent *ec, gpointer data)
{
XEvent ee, *e;
- ObEventData *ed = data;
-
Window window;
ObClient *client = NULL;
ObDock *dock = NULL;
event_set_curtime(e);
event_curserial = e->xany.serial;
event_hack_mods(e);
- if (event_ignore(e, client)) {
- if (ed)
- ed->ignored = TRUE;
- return;
- } else if (ed)
- ed->ignored = FALSE;
/* deal with it in the kernel */
if (e->type == FocusIn) {
- if (client &&
- e->xfocus.detail == NotifyInferior)
- {
- ob_debug_type(OB_DEBUG_FOCUS,
- "Focus went to the frame window");
+ print_focusevent(e);
+ if (!wanted_focusevent(e, FALSE)) {
+ if (waiting_for_focusin) {
+ /* We were waiting for this FocusIn, since we got a FocusOut
+ earlier, but it went to a window that isn't a client. */
+ ob_debug_type(OB_DEBUG_FOCUS,
+ "Focus went to an unmanaged window 0x%x !",
+ e->xfocus.window);
+ focus_fallback(TRUE, config_focus_under_mouse, TRUE, TRUE);
+ }
+ }
+ else if (client && e->xfocus.detail == NotifyInferior) {
+ ob_debug_type(OB_DEBUG_FOCUS, "Focus went to the frame window");
focus_left_screen = FALSE;
e->xfocus.detail == NotifyInferior ||
e->xfocus.detail == NotifyNonlinear)
{
- XEvent ce;
-
ob_debug_type(OB_DEBUG_FOCUS,
"Focus went to root or pointer root/none");
But if the other focus in is something like PointerRoot then we
still want to fall back.
*/
- if (XCheckIfEvent(obt_display, &ce, event_look_for_focusin_client,
- NULL))
- {
- XPutBackEvent(obt_display, &ce);
+ if (xqueue_exists_local(event_look_for_focusin_client, NULL)) {
ob_debug_type(OB_DEBUG_FOCUS,
" but another FocusIn is coming");
} else {
client_calc_layer(client);
client_bring_helper_windows(client);
}
- } else if (e->type == FocusOut) {
- XEvent ce;
+ waiting_for_focusin = FALSE;
+ } else if (e->type == FocusOut) {
+ print_focusevent(e);
+ if (!wanted_focusevent(e, FALSE))
+ ; /* skip this one */
/* Look for the followup FocusIn */
- if (!XCheckIfEvent(obt_display, &ce, event_look_for_focusin, NULL)) {
+ else if (!xqueue_exists_local(event_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. */
Window win, root;
/* nothing is focused */
focus_set_client(NULL);
} else {
- /* Focus moved, 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 !",
- ce.xfocus.window);
- focus_fallback(TRUE, config_focus_under_mouse, TRUE, TRUE);
- }
+ /* Focus moved, so mark that we are waiting to process that
+ FocusIn */
+ waiting_for_focusin = TRUE;
+
+ /* nothing is focused right now, but will be again shortly */
+ focus_set_client(NULL);
}
- if (client && client != focus_client) {
+ if (client && client != focus_client)
frame_adjust_focus(client->frame, FALSE);
- /* focus_set_client(NULL) has already been called in this
- section or by focus_fallback */
- }
}
else if (client)
event_handle_client(client, e);
if (e->type == ButtonPress || e->type == ButtonRelease) {
ObWindow *w;
static guint pressed = 0;
- static Window pressed_win = None;
+
+ event_sourcetime = event_curtime;
/* If the button press was on some non-root window, or was physically
on the root window... */
/* ...or it if it was physically on an openbox
internal window... */
((w = window_find(e->xbutton.subwindow)) &&
- WINDOW_IS_INTERNAL(w)))
+ (WINDOW_IS_INTERNAL(w) || WINDOW_IS_DOCK(w))))
/* ...then process the event, otherwise ignore it */
{
used = event_handle_user_input(client, e);
- if (e->type == ButtonPress) {
+ if (prompt && !used)
+ used = event_handle_prompt(prompt, e);
+
+ if (e->type == ButtonPress)
pressed = e->xbutton.button;
- pressed_win = e->xbutton.subwindow;
- }
}
}
else if (e->type == KeyPress || e->type == KeyRelease ||
e->type == MotionNotify)
+ {
+ event_sourcetime = event_curtime;
+
used = event_handle_user_input(client, e);
- if (prompt && !used)
- used = event_handle_prompt(prompt, e);
+ if (prompt && !used)
+ used = event_handle_prompt(prompt, e);
+ }
+
+ /* show any debug prompts that are queued */
+ ob_debug_show_prompts();
/* if something happens and it's not from an XEvent, then we don't know
- the time */
- event_curtime = CurrentTime;
+ the time, so clear it here until the next event is handled */
+ event_curtime = event_sourcetime = CurrentTime;
event_curserial = 0;
}
if (msgtype == OBT_PROP_ATOM(NET_CURRENT_DESKTOP)) {
guint d = e->xclient.data.l[0];
if (d < screen_num_desktops) {
- event_curtime = e->xclient.data.l[1];
- if (event_curtime == 0)
+ if (e->xclient.data.l[1] == 0)
ob_debug_type(OB_DEBUG_APP_BUGS,
"_NET_CURRENT_DESKTOP message is missing "
"a timestamp");
+ else
+ event_sourcetime = e->xclient.data.l[1];
screen_set_desktop(d, TRUE);
}
} else if (msgtype == OBT_PROP_ATOM(NET_NUMBER_OF_DESKTOPS)) {
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"
+ ob_debug_type(OB_DEBUG_FOCUS, "Ignoring enter event with serial %lu "
"on client 0x%x", event_curserial, client->window);
return;
}
+ ob_debug_type(OB_DEBUG_FOCUS, "using enter event with serial %lu "
+ "on client 0x%x", event_curserial, client->window);
+
if (client_enter_focusable(client) && client_can_focus(client)) {
if (config_focus_delay) {
ObFocusDelayData *data;
- obt_main_loop_timeout_remove(ob_main_loop, focus_delay_func);
+ if (focus_delay_timeout_id)
+ g_source_remove(focus_delay_timeout_id);
data = g_slice_new(ObFocusDelayData);
data->client = client;
- data->time = event_curtime;
+ data->time = event_time();
data->serial = event_curserial;
- obt_main_loop_timeout_add(ob_main_loop,
- config_focus_delay * 1000,
- focus_delay_func,
- data, focus_delay_cmp, focus_delay_dest);
+ focus_delay_timeout_id = g_timeout_add_full(G_PRIORITY_DEFAULT,
+ config_focus_delay,
+ focus_delay_func,
+ data,
+ focus_delay_dest);
+ focus_delay_timeout_client = client;
} else {
ObFocusDelayData data;
data.client = client;
- data.time = event_curtime;
+ data.time = event_time();
data.serial = event_curserial;
focus_delay_func(&data);
}
if (config_focus_delay) {
ObFocusDelayData *data;
- obt_main_loop_timeout_remove(ob_main_loop, unfocus_delay_func);
+ if (unfocus_delay_timeout_id)
+ g_source_remove(unfocus_delay_timeout_id);
data = g_slice_new(ObFocusDelayData);
data->client = client;
- data->time = event_curtime;
+ data->time = event_time();
data->serial = event_curserial;
- obt_main_loop_timeout_add(ob_main_loop,
- config_focus_delay * 1000,
- unfocus_delay_func,
- data, focus_delay_cmp, focus_delay_dest);
+ unfocus_delay_timeout_id = g_timeout_add_full(G_PRIORITY_DEFAULT,
+ config_focus_delay,
+ unfocus_delay_func,
+ data,
+ unfocus_delay_dest);
+ unfocus_delay_timeout_client = client;
} else {
ObFocusDelayData data;
data.client = client;
- data.time = event_curtime;
+ data.time = event_time();
data.serial = event_curserial;
unfocus_delay_func(&data);
}
}
}
-static void compress_client_message_event(XEvent *e, XEvent *ce, Window window,
- Atom msgtype)
+static gboolean more_client_message_event(Window window, Atom msgtype)
{
- /* compress changes into a single change */
- while (XCheckTypedWindowEvent(obt_display, 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(obt_display, ce);
- break;
+ ObtXQueueWindowMessage wm;
+ wm.window = window;
+ wm.message = msgtype;
+ return xqueue_exists_local(xqueue_match_window_message, &wm);
+}
+
+struct ObSkipPropertyChange {
+ Window window;
+ Atom prop;
+};
+
+static gboolean skip_property_change(XEvent *e, gpointer data)
+{
+ const struct ObSkipPropertyChange s = *(struct ObSkipPropertyChange*)data;
+
+ if (e->type == PropertyNotify && e->xproperty.window == s.window) {
+ const Atom a = e->xproperty.atom;
+ const Atom b = s.prop;
+
+ /* these are all updated together */
+ if ((a == OBT_PROP_ATOM(NET_WM_NAME) ||
+ a == OBT_PROP_ATOM(WM_NAME) ||
+ a == OBT_PROP_ATOM(NET_WM_ICON_NAME) ||
+ a == OBT_PROP_ATOM(WM_ICON_NAME))
+ &&
+ (b == OBT_PROP_ATOM(NET_WM_NAME) ||
+ b == OBT_PROP_ATOM(WM_NAME) ||
+ b == OBT_PROP_ATOM(NET_WM_ICON_NAME) ||
+ b == OBT_PROP_ATOM(WM_ICON_NAME)))
+ {
+ return TRUE;
}
- e->xclient = ce->xclient;
+ else if (a == b && a == OBT_PROP_ATOM(NET_WM_ICON))
+ return TRUE;
}
+ return FALSE;
}
static void event_handle_client(ObClient *client, XEvent *e)
{
- XEvent ce;
Atom msgtype;
ObFrameContext con;
gboolean *but;
delay is up */
e->xcrossing.detail != NotifyInferior)
{
- if (config_focus_delay)
- obt_main_loop_timeout_remove_data(ob_main_loop,
- focus_delay_func,
- client, FALSE);
+ if (config_focus_delay && focus_delay_timeout_id)
+ g_source_remove(focus_delay_timeout_id);
if (config_unfocus_leave)
event_leave_client(client);
}
if (grab_on_keyboard())
break;
if (e->xcrossing.mode == NotifyGrab ||
- e->xcrossing.mode == NotifyUngrab ||
+ (e->xcrossing.mode == NotifyUngrab &&
+ /* ungrab enters are used when _under_ mouse is being used */
+ !(config_focus_follow && config_focus_under_mouse)) ||
/*ignore enters when we're already in the window */
e->xcrossing.detail == NotifyInferior)
{
e->xcrossing.serial,
(client?client->window:0));
if (config_focus_follow) {
- if (config_focus_delay)
- obt_main_loop_timeout_remove_data(ob_main_loop,
- unfocus_delay_func,
- client, FALSE);
+ if (config_focus_delay && unfocus_delay_timeout_id)
+ g_source_remove(unfocus_delay_timeout_id);
event_enter_client(client);
}
}
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(obt_display, e);
-
ob_debug("ReparentNotify for window 0x%x", client->window);
client_unmanage(client);
break;
msgtype = e->xclient.message_type;
if (msgtype == OBT_PROP_ATOM(WM_CHANGE_STATE)) {
- compress_client_message_event(e, &ce, client->window, msgtype);
- client_set_wm_state(client, e->xclient.data.l[0]);
+ if (!more_client_message_event(client->window, msgtype))
+ client_set_wm_state(client, e->xclient.data.l[0]);
} else if (msgtype == OBT_PROP_ATOM(NET_WM_DESKTOP)) {
- compress_client_message_event(e, &ce, client->window, msgtype);
- if ((unsigned)e->xclient.data.l[0] < screen_num_desktops ||
- (unsigned)e->xclient.data.l[0] == DESKTOP_ALL)
+ if (!more_client_message_event(client->window, msgtype) &&
+ ((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, FALSE);
+ }
} else if (msgtype == OBT_PROP_ATOM(NET_WM_STATE)) {
gulong ignore_start;
so do not use this timestamp in event_curtime, as this would
be used in XSetInputFocus.
*/
- /*event_curtime = e->xclient.data.l[1];*/
+ event_sourcetime = e->xclient.data.l[1];
if (e->xclient.data.l[1] == 0)
ob_debug_type(OB_DEBUG_APP_BUGS,
"_NET_ACTIVE_WINDOW message for window %s is"
" missing a timestamp", client->title);
-
- event_curtime = event_get_server_time();
} else
ob_debug_type(OB_DEBUG_APP_BUGS,
"_NET_ACTIVE_WINDOW message for window %s is "
"missing source indication", client->title);
- client_activate(client, FALSE, FALSE, TRUE, TRUE,
- (e->xclient.data.l[0] == 0 ||
- e->xclient.data.l[0] == 2));
+ /* TODO(danakj) This should use
+ (e->xclient.data.l[0] == 0 ||
+ e->xclient.data.l[0] == 2)
+ to determine if a user requested the activation, however GTK+
+ applications seem unable to make this distinction ever
+ (including panels such as xfce4-panel and gnome-panel).
+ So we are left just assuming all activations are from the user.
+ */
+ client_activate(client, FALSE, FALSE, TRUE, TRUE, TRUE);
} else if (msgtype == OBT_PROP_ATOM(NET_WM_MOVERESIZE)) {
ob_debug("net_wm_moveresize for 0x%lx direction %d",
client->window, e->xclient.data.l[2]);
}
else if ((Atom)e->xclient.data.l[2] ==
OBT_PROP_ATOM(NET_WM_MOVERESIZE_CANCEL))
- moveresize_end(TRUE);
+ if (moveresize_client)
+ moveresize_end(TRUE);
} else if (msgtype == OBT_PROP_ATOM(NET_MOVERESIZE_WINDOW)) {
gint ograv, x, y, w, h;
/* 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(obt_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 == OBT_PROP_ATOM(NET_WM_NAME) ||
- a == OBT_PROP_ATOM(WM_NAME) ||
- a == OBT_PROP_ATOM(NET_WM_ICON_NAME) ||
- a == OBT_PROP_ATOM(WM_ICON_NAME))
- &&
- (b == OBT_PROP_ATOM(NET_WM_NAME) ||
- b == OBT_PROP_ATOM(WM_NAME) ||
- b == OBT_PROP_ATOM(NET_WM_ICON_NAME) ||
- b == OBT_PROP_ATOM(WM_ICON_NAME))) {
- continue;
- }
- if (a == OBT_PROP_ATOM(NET_WM_ICON) &&
- b == OBT_PROP_ATOM(NET_WM_ICON))
- continue;
+ msgtype = e->xproperty.atom;
- XPutBackEvent(obt_display, &ce);
- break;
+ /* ignore changes to some properties if there is another change
+ coming in the queue */
+ {
+ struct ObSkipPropertyChange s;
+ s.window = client->window;
+ s.prop = msgtype;
+ if (xqueue_exists_local(skip_property_change, &s))
+ break;
}
msgtype = e->xproperty.atom;
client_update_title(client);
} else if (msgtype == OBT_PROP_ATOM(WM_PROTOCOLS)) {
client_update_protocols(client);
- client_setup_decor_and_functions(client, TRUE);
}
else if (msgtype == OBT_PROP_ATOM(NET_WM_STRUT) ||
msgtype == OBT_PROP_ATOM(NET_WM_STRUT_PARTIAL)) {
event_last_user_time = t;
}
}
+ else if (msgtype == OBT_PROP_ATOM(NET_WM_WINDOW_OPACITY)) {
+ client_update_opacity(client);
+ }
#ifdef SYNC
else if (msgtype == OBT_PROP_ATOM(NET_WM_SYNC_REQUEST_COUNTER)) {
+ /* if they are resizing right now this would cause weird behaviour.
+ if one day a user reports clients stop resizing, then handle
+ this better by resetting a new XSync alarm and stuff on the
+ new counter, but I expect it will never happen */
+ if (moveresize_client == client)
+ moveresize_end(FALSE);
client_update_sync_request_counter(client);
}
#endif
client->shaped = ((XShapeEvent*)e)->shaped;
kind = ShapeBounding;
break;
+#ifdef ShapeInput
case ShapeInput:
client->shaped_input = ((XShapeEvent*)e)->shaped;
kind = ShapeInput;
break;
+#endif
default:
g_assert_not_reached();
}
static void event_handle_dock(ObDock *s, XEvent *e)
{
switch (e->type) {
- case ButtonPress:
- 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 EnterNotify:
dock_hide(FALSE);
break;
if (ev->type == ButtonRelease || ev->type == ButtonPress) {
ObMenuEntryFrame *e;
- if (menu_hide_delay_reached() &&
- (ev->xbutton.button < 4 || ev->xbutton.button > 5))
+ if ((ev->xbutton.button < 4 || ev->xbutton.button > 5) &&
+ ((ev->type == ButtonRelease && menu_hide_delay_reached()) ||
+ ev->type == ButtonPress))
{
if ((e = menu_entry_frame_under(ev->xbutton.x_root,
ev->xbutton.y_root)))
if (ev->type == ButtonRelease)
menu_entry_frame_execute(e, ev->xbutton.state);
}
- else if (ev->type == ButtonRelease)
+ else
menu_frame_hide_all();
}
ret = TRUE;
}
- else if (ev->type == MotionNotify) {
- ObMenuFrame *f;
- ObMenuEntryFrame *e;
-
- if ((e = menu_entry_frame_under(ev->xmotion.x_root,
- ev->xmotion.y_root)))
- if (!(f = find_active_menu()) ||
- f == e->frame ||
- f->parent == e->frame ||
- f->child == e->frame)
- menu_frame_select(e->frame, e, FALSE);
- }
else if (ev->type == KeyPress || ev->type == KeyRelease) {
guint mods;
ObMenuFrame *frame;
ret = TRUE;
}
- else if (sym == XK_Right) {
- /* Right goes to the selected submenu */
- if (frame->selected->entry->type == OB_MENU_ENTRY_TYPE_SUBMENU)
- {
- /* make sure it is visible */
- menu_frame_select(frame, frame->selected, TRUE);
- menu_frame_select_next(frame->child);
+ else if (sym == XK_Right || sym == XK_Return || sym == XK_KP_Enter)
+ {
+ /* Right and enter goes to the selected submenu.
+ Enter executes instead if it's not on a submenu. */
+
+ if (frame->selected) {
+ const ObMenuEntryType t = frame->selected->entry->type;
+
+ if (t == OB_MENU_ENTRY_TYPE_SUBMENU) {
+ /* make sure it is visible */
+ menu_frame_select(frame, frame->selected, TRUE);
+ /* move focus to the child menu */
+ menu_frame_select_next(frame->child);
+ }
+ else if (sym != XK_Right) {
+ frame->press_doexec = TRUE;
+ }
}
ret = TRUE;
}
ret = TRUE;
}
- else if (sym == XK_Return || sym == XK_KP_Enter) {
- frame->press_doexec = TRUE;
- ret = TRUE;
- }
-
/* keyboard accelerator shortcuts. (if it was a valid key) */
else if (frame->entries &&
(unikey =
if (found) {
menu_frame_select(frame, found, TRUE);
- if (num_found == 1)
- frame->press_doexec = TRUE;
+ if (num_found == 1) {
+ if (found->entry->type == OB_MENU_ENTRY_TYPE_SUBMENU) {
+ /* move focus to the child menu */
+ menu_frame_select_next(frame->child);
+ }
+ else {
+ frame->press_doexec = TRUE;
+ }
+ }
ret = TRUE;
}
}
frame->got_press &&
frame->press_doexec)
{
- if (frame->child)
- menu_frame_select_next(frame->child);
- else if (frame->selected)
+ if (frame->selected)
menu_entry_frame_execute(frame->selected, ev->xkey.state);
}
}
return ret;
}
-static Bool event_look_for_menu_enter(Display *d, XEvent *ev, XPointer arg)
+static gboolean event_look_for_menu_enter(XEvent *ev, gpointer data)
{
- ObMenuFrame *f = (ObMenuFrame*)arg;
+ const ObMenuFrame *f = (ObMenuFrame*)data;
ObMenuEntryFrame *e;
return ev->type == EnterNotify &&
(e = g_hash_table_lookup(menu_frame_map, &ev->xcrossing.window)) &&
- !e->ignore_enters && e->frame == f;
+ e->frame == f && !e->ignore_enters;
}
static void event_handle_menu(ObMenuFrame *frame, XEvent *ev)
ObMenuEntryFrame *e;
switch (ev->type) {
+ case MotionNotify:
+ /* We need to catch MotionNotify in addition to EnterNotify because
+ it is possible for the menu to be opened under the mouse cursor, and
+ moving the mouse should select the item. */
+ if ((e = g_hash_table_lookup(menu_frame_map, &ev->xmotion.window))) {
+ if (e->ignore_enters)
+ --e->ignore_enters;
+ else if (!(f = find_active_menu()) ||
+ f == e->frame ||
+ f->parent == e->frame ||
+ f->child == e->frame)
+ menu_frame_select(e->frame, e, FALSE);
+ }
+ break;
case EnterNotify:
if ((e = g_hash_table_lookup(menu_frame_map, &ev->xcrossing.window))) {
if (e->ignore_enters)
}
break;
case LeaveNotify:
- /*ignore leaves when we're already in the window */
+ /* ignore leaves when we're already in the window */
if (ev->xcrossing.detail == NotifyInferior)
break;
- if ((e = g_hash_table_lookup(menu_frame_map, &ev->xcrossing.window)))
- {
- XEvent ce;
-
+ if ((e = g_hash_table_lookup(menu_frame_map, &ev->xcrossing.window))) {
/* check if an EnterNotify event is coming, and if not, then select
nothing in the menu */
- if (XCheckIfEvent(obt_display, &ce, event_look_for_menu_enter,
- (XPointer)e->frame))
- XPutBackEvent(obt_display, &ce);
- else
+ if (!xqueue_exists_local(event_look_for_menu_enter, e->frame))
menu_frame_select(e->frame, NULL, FALSE);
}
break;
static void focus_delay_dest(gpointer data)
{
g_slice_free(ObFocusDelayData, data);
+ focus_delay_timeout_id = 0;
+ focus_delay_timeout_client = NULL;
}
-static gboolean focus_delay_cmp(gconstpointer d1, gconstpointer d2)
+static void unfocus_delay_dest(gpointer data)
{
- const ObFocusDelayData *f1 = d1;
- return f1->client == d2;
+ g_slice_free(ObFocusDelayData, data);
+ unfocus_delay_timeout_id = 0;
+ unfocus_delay_timeout_client = NULL;
}
static gboolean focus_delay_func(gpointer data)
{
ObFocusDelayData *d = data;
- Time old = event_curtime;
+ Time old = event_curtime; /* save the curtime */
event_curtime = d->time;
event_curserial = d->serial;
static gboolean unfocus_delay_func(gpointer data)
{
ObFocusDelayData *d = data;
- Time old = event_curtime;
+ Time old = event_curtime; /* save the curtime */
event_curtime = d->time;
event_curserial = d->serial;
static void focus_delay_client_dest(ObClient *client, gpointer data)
{
- obt_main_loop_timeout_remove_data(ob_main_loop, focus_delay_func,
- client, FALSE);
- obt_main_loop_timeout_remove_data(ob_main_loop, unfocus_delay_func,
- client, FALSE);
+ if (focus_delay_timeout_client == client && focus_delay_timeout_id)
+ g_source_remove(focus_delay_timeout_id);
+ if (unfocus_delay_timeout_client == client && unfocus_delay_timeout_id)
+ g_source_remove(unfocus_delay_timeout_id);
}
void event_halt_focus_delay(void)
{
/* ignore all enter events up till the event which caused this to occur */
if (event_curserial) event_ignore_enter_range(1, event_curserial);
- obt_main_loop_timeout_remove(ob_main_loop, focus_delay_func);
- obt_main_loop_timeout_remove(ob_main_loop, unfocus_delay_func);
+ if (focus_delay_timeout_id) g_source_remove(focus_delay_timeout_id);
+ if (unfocus_delay_timeout_id) g_source_remove(unfocus_delay_timeout_id);
}
gulong event_start_ignore_all_enters(void)
return t1 >= t2 && t1 < (t2 + TIME_HALF);
}
-Bool find_timestamp(Display *d, XEvent *e, XPointer a)
+gboolean find_timestamp(XEvent *e, gpointer data)
{
- const Time t = event_time(e);
- return t != CurrentTime;
+ const Time t = event_get_timestamp(e);
+ if (t && t >= event_curtime) {
+ event_curtime = t;
+ return TRUE;
+ }
+ else
+ return FALSE;
}
-Time event_get_server_time(void)
+static Time next_time(void)
{
- XEvent event;
+ /* Some events don't come with timestamps :(
+ ...but we can get one anyways >:) */
/* Generate a timestamp so there is guaranteed at least one in the queue
eventually */
8, PropModeAppend, NULL, 0);
/* Grab the first timestamp available */
- XPeekIfEvent(obt_display, &event, find_timestamp, NULL);
+ xqueue_exists(find_timestamp, NULL);
- return event.xproperty.time;
+ /*g_assert(event_curtime != CurrentTime);*/
+
+ /* Save the time so we don't have to do this again for this event */
+ return event_curtime;
+}
+
+Time event_time(void)
+{
+ if (event_curtime) return event_curtime;
+
+ return next_time();
+}
+
+Time event_source_time(void)
+{
+ return event_sourcetime;
+}
+
+void event_reset_time(void)
+{
+ next_time();
+}
+
+void event_update_user_time(void)
+{
+ event_last_user_time = event_time();
+}
+
+void event_reset_user_time(void)
+{
+ event_last_user_time = CurrentTime;
}