+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ event.c for the Openbox window manager
+ 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 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 "framerender.h"
#include "focus.h"
#include "moveresize.h"
+#include "group.h"
#include "stacking.h"
#include "extensions.h"
-#include "event.h"
#include <X11/Xlib.h>
#include <X11/keysym.h>
#include <X11/ICE/ICElib.h>
#endif
+typedef struct
+{
+ gboolean ignored;
+} ObEventData;
+
static void event_process(const XEvent *e, gpointer data);
static void event_handle_root(XEvent *e);
static void 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 gboolean focus_delay_func(gpointer data);
static void focus_delay_client_dest(gpointer data);
+static gboolean menu_hide_delay_func(gpointer data);
+
#define INVALID_FOCUSIN(e) ((e)->xfocus.detail == NotifyInferior || \
(e)->xfocus.detail == NotifyAncestor || \
(e)->xfocus.detail > NotifyNonlinearVirtual)
};
static int mask_table_size;
-static ObClient *focus_delay_client;
+static guint ignore_enter_focus = 0;
+
+static gboolean menu_can_hide;
#ifdef USE_SM
static void ice_handler(int fd, gpointer conn)
{
if (reconfig) return;
+#ifdef USE_SM
+ IceRemoveConnectionWatch(ice_watch, NULL);
+#endif
+
client_remove_destructor(focus_delay_client_dest);
XFreeModifiermap(modmap);
}
static gboolean event_ignore(XEvent *e, ObClient *client)
{
switch(e->type) {
+ case EnterNotify:
+ case LeaveNotify:
+ if (e->xcrossing.detail == NotifyInferior)
+ return TRUE;
+ break;
case FocusIn:
/* NotifyAncestor is not ignored in FocusIn like it is in FocusOut
because of RevertToPointerRoot. If the focus ends up reverting to
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);
+ 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.
}
}
- /* 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, NULL);
- /* 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;
+ {
+ 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;
+ }
}
}
}
focus_fallback(OB_FOCUS_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
- ob_debug("%sNotify mode %d detail %d on %lx IGNORED\n",
- (e->type == EnterNotify ? "Enter" : "Leave"),
- e->xcrossing.mode,
- e->xcrossing.detail, client?client->window:0);
-#endif
- return TRUE;
- }
-#ifdef DEBUG_FOCUS
- ob_debug("%sNotify mode %d detail %d on %lx\n",
- (e->type == EnterNotify ? "Enter" : "Leave"),
- e->xcrossing.mode,
- e->xcrossing.detail, client?client->window:0);
-#endif
- break;
+ break;
}
return FALSE;
}
static void event_process(const XEvent *ec, gpointer data)
{
Window window;
+ 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 ((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;
+ 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_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 (client)
+ if (group)
+ event_handle_group(group, e);
+ else if (client)
event_handle_client(client, e);
else if (dockapp)
event_handle_dockapp(dockapp, e);
{
if (menu_frame_visible)
event_handle_menu(e);
- else if (moveresize_in_progress)
- moveresize_event(e);
else {
- ObFrameContext context;
+ if (!keyboard_process_interactive_grab(e, &client)) {
+ if (moveresize_in_progress) {
+ moveresize_event(e);
- context = frame_context(client, e->xany.window);
+ /* 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,
+ G_USEC_PER_SEC / 4,
+ menu_hide_delay_func,
+ NULL, NULL);
- if (!keyboard_process_interactive_grab(e, &client, &context)) {
if (e->type == ButtonPress || e->type == ButtonRelease ||
e->type == MotionNotify)
- mouse_event(client, context, e);
+ mouse_event(client, e);
else if (e->type == KeyPress)
- keyboard_event(client, e);
+ /* 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);
}
}
}
switch(e->type) {
case SelectionClear:
ob_debug("Another WM has requested to replace us. Exiting.\n");
- ob_exit();
+ ob_exit(0);
break;
case ClientMessage:
}
}
+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) {
+ ob_main_loop_timeout_remove(ob_main_loop, focus_delay_func);
+ ob_main_loop_timeout_add(ob_main_loop,
+ config_focus_delay,
+ focus_delay_func,
+ client, NULL);
+ } else
+ focus_delay_func(client);
+ }
+}
+
static void event_handle_client(ObClient *client, XEvent *e)
{
XEvent ce;
/* 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)) {
- switch (frame_context(client, e->xbutton.window)) {
+ 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);
frame_adjust_state(client->frame);
break;
case OB_FRAME_CONTEXT_FRAME:
- /* XXX if doing a 'reconfigure' make sure you kill this timer,
- maybe all timers.. */
- if (config_focus_delay && client == focus_delay_client) {
+ /*
+ if (config_focus_follow && config_focus_delay)
ob_main_loop_timeout_remove_data(ob_main_loop,
focus_delay_func,
- focus_delay_client);
- focus_delay_client = NULL;
- }
+ client);
+ */
+ break;
default:
break;
}
break;
case EnterNotify:
+ {
+ 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:
frame_adjust_state(client->frame);
break;
case OB_FRAME_CONTEXT_FRAME:
- if (client_normal(client)) {
- if (ob_state() == OB_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) {
+ if (e->xcrossing.mode == NotifyGrab ||
+ e->xcrossing.mode == NotifyUngrab)
+ {
#ifdef DEBUG_FOCUS
- ob_debug("EnterNotify on %lx, focusing window\n",
- client->window);
+ ob_debug("%sNotify mode %d detail %d on %lx IGNORED\n",
+ (e->type == EnterNotify ? "Enter" : "Leave"),
+ e->xcrossing.mode,
+ e->xcrossing.detail, client?client->window:0);
#endif
- if (config_focus_delay) {
- ob_main_loop_timeout_add(ob_main_loop,
- config_focus_delay,
- focus_delay_func,
- client, NULL);
- focus_delay_client = client;
- } else
- client_focus(client);
- }
+ } else {
+#ifdef DEBUG_FOCUS
+ ob_debug("%sNotify mode %d detail %d on %lx, "
+ "focusing window\n",
+ (e->type == EnterNotify ? "Enter" : "Leave"),
+ e->xcrossing.mode,
+ e->xcrossing.detail, client?client->window:0);
+#endif
+ if (!nofocus && config_focus_follow)
+ event_enter_client(client);
}
break;
default:
break;
}
break;
+ }
case ConfigureRequest:
/* compress these */
while (XCheckTypedWindowEvent(ob_display, client->window,
msgtype == prop_atoms.kwm_win_icon) {
client_update_icons(client);
}
+ else if (msgtype == prop_atoms.sm_client_id) {
+ client_update_sm_client_id(client);
+ }
default:
;
#ifdef SHAPE
switch (ev->type) {
case ButtonRelease:
- if (!(f = menu_frame_under(ev->xbutton.x_root,
- ev->xbutton.y_root)))
+ if ((e = menu_entry_frame_under(ev->xbutton.x_root,
+ ev->xbutton.y_root)))
+ menu_entry_frame_execute(e, ev->xbutton.state);
+ else if (menu_can_hide)
menu_frame_hide_all();
- else {
- if ((e = menu_entry_frame_under(ev->xbutton.x_root,
- ev->xbutton.y_root)))
- menu_entry_frame_execute(e, ev->xbutton.state);
- }
break;
case MotionNotify:
if ((f = menu_frame_under(ev->xmotion.x_root,
ev->xmotion.y_root)))
menu_frame_select(f, e);
}
+ {
+ ObMenuFrame *a;
+
+ a = find_active_menu();
+ if (a && a != f &&
+ a->selected->entry->type != OB_MENU_ENTRY_TYPE_SUBMENU)
+ {
+ menu_frame_select(a, NULL);
+ }
+ }
break;
case KeyPress:
if (ev->xkey.keycode == ob_keycode(OB_KEY_ESCAPE))
}
}
+static gboolean menu_hide_delay_func(gpointer data)
+{
+ menu_can_hide = TRUE;
+ return FALSE; /* no repeat */
+}
+
static gboolean focus_delay_func(gpointer data)
{
- client_focus(focus_delay_client);
+ ObClient *c = data;
+
+ client_focus(c);
+ if (config_focus_raise)
+ stacking_raise(CLIENT_AS_WINDOW(c));
return FALSE; /* no repeat */
}
static void focus_delay_client_dest(gpointer data)
{
ObClient *c = data;
- if (c == focus_delay_client) {
- ob_main_loop_timeout_remove_data(ob_main_loop, focus_delay_func,
- focus_delay_client);
- focus_delay_client = NULL;
+
+ ob_main_loop_timeout_remove_data(ob_main_loop, focus_delay_func, c);
+}
+
+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)) {
+ saved = g_slist_append(saved, e);
+ ++ignore_enter_focus;
+ } 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);
}