* vim:ts=4:sw=4:expandtab
*
* i3 - an improved dynamic tiling window manager
- * © 2009-2010 Michael Stapelberg and contributors (see also: LICENSE)
+ * © 2009-2011 Michael Stapelberg and contributors (see also: LICENSE)
+ *
+ * handlers.c: Small handlers for various events (keypresses, focus changes,
+ * …).
*
*/
-#include <time.h>
+#include "all.h"
+#include <time.h>
#include <xcb/randr.h>
-
#include <X11/XKBlib.h>
-
-#include "all.h"
+#define SN_API_NOT_YET_FROZEN 1
+#include <libsn/sn-monitor.h>
int randr_base = -1;
*
*/
static int handle_key_press(xcb_key_press_event_t *event) {
+
+ last_timestamp = event->time;
+
DLOG("Keypress %d, state raw = %d\n", event->detail, event->state);
/* Remove the numlock bit, all other bits are modifiers we can bind to */
/* Focus the output on which the user moved his cursor */
Con *old_focused = focused;
- con_focus(con_descend_focused(output_get_content(output->con)));
+ Con *next = con_descend_focused(output_get_content(output->con));
+ /* Since we are switching outputs, this *must* be a different workspace, so
+ * call workspace_show() */
+ workspace_show(con_get_workspace(next));
+ con_focus(next);
/* If the focus changed, we re-render to get updated decorations */
if (old_focused != focused)
static int handle_enter_notify(xcb_enter_notify_event_t *event) {
Con *con;
+ last_timestamp = event->time;
+
DLOG("enter_notify for %08x, mode = %d, detail %d, serial %d\n",
event->event, event->mode, event->detail, event->sequence);
DLOG("coordinates %d, %d\n", event->event_x, event->event_y);
if (config.disable_focus_follows_mouse)
return 1;
+ /* Get the currently focused workspace to check if the focus change also
+ * involves changing workspaces. If so, we need to call workspace_show() to
+ * correctly update state and send the IPC event. */
+ Con *ws = con_get_workspace(con);
+ if (ws != con_get_workspace(focused))
+ workspace_show(ws);
+
con_focus(con_descend_focused(con));
tree_render();
*
*/
static int handle_motion_notify(xcb_motion_notify_event_t *event) {
+
+ last_timestamp = event->time;
+
/* Skip events where the pointer was over a child window, we are only
* interested in events on the root window. */
if (event->child != 0)
DLOG("Received mapping_notify for keyboard or modifier mapping, re-grabbing keys\n");
xcb_refresh_keyboard_mapping(keysyms, event);
- xcb_get_numlock_mask(conn);
+ xcb_numlock_mask = aio_get_mod_mask_for(XCB_NUM_LOCK, keysyms);
ungrab_all_keys(conn);
translate_keysyms();
bsr.height -= deco_height;
}
Con *floatingcon = con->parent;
- DLOG("Container is a floating leaf node, will do that.\n");
+
+ Rect newrect = floatingcon->rect;
+
if (event->value_mask & XCB_CONFIG_WINDOW_X) {
- floatingcon->rect.x = event->x + (-1) * bsr.x;
- DLOG("proposed x = %d, new x is %d\n", event->x, floatingcon->rect.x);
+ newrect.x = event->x + (-1) * bsr.x;
+ DLOG("proposed x = %d, new x is %d\n", event->x, newrect.x);
}
if (event->value_mask & XCB_CONFIG_WINDOW_Y) {
- floatingcon->rect.y = event->y + (-1) * bsr.y;
- DLOG("proposed y = %d, new y is %d\n", event->y, floatingcon->rect.y);
+ newrect.y = event->y + (-1) * bsr.y;
+ DLOG("proposed y = %d, new y is %d\n", event->y, newrect.y);
}
if (event->value_mask & XCB_CONFIG_WINDOW_WIDTH) {
- floatingcon->rect.width = event->width + (-1) * bsr.width;
- floatingcon->rect.width += con->border_width * 2;
- DLOG("proposed width = %d, new width is %d (x11 border %d)\n", event->width, floatingcon->rect.width, con->border_width);
+ newrect.width = event->width + (-1) * bsr.width;
+ newrect.width += con->border_width * 2;
+ DLOG("proposed width = %d, new width is %d (x11 border %d)\n",
+ event->width, newrect.width, con->border_width);
}
if (event->value_mask & XCB_CONFIG_WINDOW_HEIGHT) {
- floatingcon->rect.height = event->height + (-1) * bsr.height;
- floatingcon->rect.height += con->border_width * 2;
- DLOG("proposed height = %d, new height is %d (x11 border %d)\n", event->height, floatingcon->rect.height, con->border_width);
+ newrect.height = event->height + (-1) * bsr.height;
+ newrect.height += con->border_width * 2;
+ DLOG("proposed height = %d, new height is %d (x11 border %d)\n",
+ event->height, newrect.height, con->border_width);
}
- floating_maybe_reassign_ws(floatingcon);
- tree_render();
+
+ DLOG("Container is a floating leaf node, will do that.\n");
+ floating_reposition(floatingcon, newrect);
+ return 1;
}
/* Dock windows can be reconfigured in their height */
* now, so we better clean up before.
*
*/
-static int handle_unmap_notify_event(xcb_unmap_notify_event_t *event) {
- // XXX: this is commented out because in src/x.c we disable EnterNotify events
- /* we need to ignore EnterNotify events which will be generated because a
- * different window is visible now */
- //add_ignore_event(event->sequence, XCB_ENTER_NOTIFY);
-
+static void handle_unmap_notify_event(xcb_unmap_notify_event_t *event) {
DLOG("UnmapNotify for 0x%08x (received from 0x%08x), serial %d\n", event->window, event->event, event->sequence);
Con *con = con_by_window_id(event->window);
if (con == NULL) {
con = con_by_frame_id(event->window);
if (con == NULL) {
LOG("Not a managed window, ignoring UnmapNotify event\n");
- return 1;
+ return;
}
+
if (con->ignore_unmap > 0)
con->ignore_unmap--;
DLOG("ignore_unmap = %d for frame of container %p\n", con->ignore_unmap, con);
- return 1;
+ goto ignore_end;
}
if (con->ignore_unmap > 0) {
DLOG("ignore_unmap = %d, dec\n", con->ignore_unmap);
con->ignore_unmap--;
- return 1;
+ goto ignore_end;
}
tree_close(con, DONT_KILL_WINDOW, false, false);
tree_render();
x_push_changes(croot);
- return 1;
-
-#if 0
- if (client == NULL) {
- DLOG("not a managed window. Ignoring.\n");
-
- /* This was most likely the destroyed frame of a client which is
- * currently being unmapped, so we add this sequence (again!) to
- * the ignore list (enter_notify events will get sent for both,
- * the child and its frame). */
- add_ignore_event(event->sequence);
-
- return 0;
- }
-#endif
-
-
-#if 0
- /* Let’s see how many clients there are left on the workspace to delete it if it’s empty */
- bool workspace_empty = SLIST_EMPTY(&(client->workspace->focus_stack));
- bool workspace_focused = (c_ws == client->workspace);
- Client *to_focus = (!workspace_empty ? SLIST_FIRST(&(client->workspace->focus_stack)) : NULL);
-
- /* If this workspace is currently visible, we don’t delete it */
- if (workspace_is_visible(client->workspace))
- workspace_empty = false;
-
- if (workspace_empty) {
- client->workspace->output = NULL;
- ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, "{\"change\":\"empty\"}");
- }
-
- /* Remove the urgency flag if set */
- client->urgent = false;
- workspace_update_urgent_flag(client->workspace);
-
- render_layout(conn);
-#endif
- return 1;
+ignore_end:
+ /* If the client (as opposed to i3) destroyed or unmapped a window, an
+ * EnterNotify event will follow (indistinguishable from an EnterNotify
+ * event caused by moving your mouse), causing i3 to set focus to whichever
+ * window is now visible.
+ *
+ * In a complex stacked or tabbed layout (take two v-split containers in a
+ * tabbed container), when the bottom window in tab2 is closed, the bottom
+ * window of tab1 is visible instead. X11 will thus send an EnterNotify
+ * event for the bottom window of tab1, while the focus should be set to
+ * the remaining window of tab2.
+ *
+ * Therefore, we ignore all EnterNotify events which have the same sequence
+ * as an UnmapNotify event. */
+ add_ignore_event(event->sequence, XCB_ENTER_NOTIFY);
}
/*
return true;
}
+/*
+ * Called when a window changes its WM_WINDOW_ROLE.
+ *
+ */
+static bool handle_windowrole_change(void *data, xcb_connection_t *conn, uint8_t state,
+ xcb_window_t window, xcb_atom_t atom, xcb_get_property_reply_t *prop) {
+ Con *con;
+ if ((con = con_by_window_id(window)) == NULL || con->window == NULL)
+ return false;
+
+ window_update_role(con->window, prop, false);
+
+ return true;
+}
+
#if 0
/*
* Updates the client’s WM_CLASS property
* Handle client messages (EWMH)
*
*/
-static int handle_client_message(xcb_client_message_event_t *event) {
+static void handle_client_message(xcb_client_message_event_t *event) {
+ /* If this is a startup notification ClientMessage, the library will handle
+ * it and call our monitor_event() callback. */
+ if (sn_xcb_display_process_event(sndisplay, (xcb_generic_event_t*)event))
+ return;
+
LOG("ClientMessage for window 0x%08x\n", event->window);
if (event->type == A__NET_WM_STATE) {
if (event->format != 32 || event->data.data32[1] != A__NET_WM_STATE_FULLSCREEN) {
DLOG("atom in clientmessage is %d, fullscreen is %d\n",
event->data.data32[1], A__NET_WM_STATE_FULLSCREEN);
DLOG("not about fullscreen atom\n");
- return 0;
+ return;
}
Con *con = con_by_window_id(event->window);
if (con == NULL) {
DLOG("Could not get window for client message\n");
- return 0;
+ return;
}
/* Check if the fullscreen state should be toggled */
tree_render();
x_push_changes(croot);
+ } else if (event->type == A_I3_SYNC) {
+ DLOG("i3 sync, yay\n");
+ xcb_window_t window = event->data.data32[0];
+ uint32_t rnd = event->data.data32[1];
+ DLOG("Sending random value %d back to X11 window 0x%08x\n", rnd, window);
+
+ void *reply = scalloc(32);
+ xcb_client_message_event_t *ev = reply;
+
+ ev->response_type = XCB_CLIENT_MESSAGE;
+ ev->window = window;
+ ev->type = A_I3_SYNC;
+ ev->format = 32;
+ ev->data.data32[0] = window;
+ ev->data.data32[1] = rnd;
+
+ xcb_send_event(conn, false, window, XCB_EVENT_MASK_NO_EVENT, (char*)ev);
+ xcb_flush(conn);
+ free(reply);
} else {
- ELOG("unhandled clientmessage\n");
- return 0;
+ DLOG("unhandled clientmessage\n");
+ return;
}
-
- return 1;
}
#if 0
xcb_icccm_wm_hints_t hints;
- if (reply != NULL) {
- if (!xcb_icccm_get_wm_hints_from_reply(&hints, reply))
+ if (reply == NULL)
+ if (!(reply = xcb_get_property_reply(conn, xcb_icccm_get_wm_hints(conn, window), NULL)))
return false;
- } else {
- if (!xcb_icccm_get_wm_hints_reply(conn, xcb_icccm_get_wm_hints_unchecked(conn, con->window->id), &hints, NULL))
- return false;
- }
+
+ if (!xcb_icccm_get_wm_hints_from_reply(&hints, reply))
+ return false;
if (!con->urgent && focused == con) {
DLOG("Ignoring urgency flag for current client\n");
- FREE(reply);
- return true;
+ goto end;
}
/* Update the flag on the client directly */
tree_render();
-#if 0
- /* If the workspace this client is on is not visible, we need to redraw
- * the workspace bar */
- if (!workspace_is_visible(client->workspace)) {
- Output *output = client->workspace->output;
- render_workspace(conn, output, output->current_workspace);
- xcb_flush(conn);
- }
-#endif
-
- FREE(reply);
+end:
+ if (con->window)
+ window_update_hints(con->window, reply);
+ else free(reply);
return true;
}
return 1;
}
+ /* Skip dock clients, they cannot get the i3 focus. */
+ if (con->parent->type == CT_DOCKAREA) {
+ DLOG("This is a dock client, not focusing.\n");
+ return 1;
+ }
+
DLOG("focus is different, updating decorations\n");
+
+ /* Get the currently focused workspace to check if the focus change also
+ * involves changing workspaces. If so, we need to call workspace_show() to
+ * correctly update state and send the IPC event. */
+ Con *ws = con_get_workspace(con);
+ if (ws != con_get_workspace(focused))
+ workspace_show(ws);
+
con_focus(con);
/* We update focused_id because we don’t need to set focus again */
focused_id = event->event;
{ 0, 128, handle_windowname_change_legacy },
{ 0, UINT_MAX, handle_normal_hints },
{ 0, UINT_MAX, handle_clientleader_change },
- { 0, UINT_MAX, handle_transient_for }
+ { 0, UINT_MAX, handle_transient_for },
+ { 0, 128, handle_windowrole_change }
};
#define NUM_HANDLERS (sizeof(property_handlers) / sizeof(struct property_handler_t))
*
*/
void property_handlers_init() {
+
+ sn_monitor_context_new(sndisplay, conn_screen, startup_monitor_event, NULL, NULL);
+
property_handlers[0].atom = A__NET_WM_NAME;
property_handlers[1].atom = XCB_ATOM_WM_HINTS;
property_handlers[2].atom = XCB_ATOM_WM_NAME;
property_handlers[3].atom = XCB_ATOM_WM_NORMAL_HINTS;
property_handlers[4].atom = A_WM_CLIENT_LEADER;
property_handlers[5].atom = XCB_ATOM_WM_TRANSIENT_FOR;
+ property_handlers[6].atom = A_WM_WINDOW_ROLE;
}
static void property_notify(uint8_t state, xcb_window_t window, xcb_atom_t atom) {
}
if (handler == NULL) {
- DLOG("Unhandled property notify for atom %d (0x%08x)\n", atom, atom);
+ //DLOG("Unhandled property notify for atom %d (0x%08x)\n", atom, atom);
return;
}
handle_focus_in((xcb_focus_in_event_t*)event);
break;
- case XCB_PROPERTY_NOTIFY:
- DLOG("Property notify\n");
+ case XCB_PROPERTY_NOTIFY: {
xcb_property_notify_event_t *e = (xcb_property_notify_event_t*)event;
+ last_timestamp = e->time;
property_notify(e->state, e->window, e->atom);
break;
+ }
default:
- DLOG("Unhandled event of type %d\n", type);
+ //DLOG("Unhandled event of type %d\n", type);
break;
}
}