X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=src%2Fhandlers.c;h=8c3bb48665626a24966efe90d6a20e330ef8fff7;hb=87c855f85121df98909636def91583d121aed8ae;hp=a1f90ba626389a017a764334c480e63cdf4bccca;hpb=51728bab77210a1050f11df0f0e27b3b88dc6674;p=i3%2Fi3 diff --git a/src/handlers.c b/src/handlers.c index a1f90ba6..8c3bb486 100644 --- a/src/handlers.c +++ b/src/handlers.c @@ -1,3 +1,5 @@ +#undef I3__FILE__ +#define I3__FILE__ "handlers.c" /* * vim:ts=4:sw=4:expandtab * @@ -11,6 +13,8 @@ #include "all.h" #include +#include +#include #include #include #define SN_API_NOT_YET_FROZEN 1 @@ -76,53 +80,6 @@ bool event_is_ignored(const int sequence, const int response_type) { return false; } - -/* - * There was a key press. We compare this key code with our bindings table and pass - * the bound action to parse_command(). - * - */ -static void 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 */ - uint16_t state_filtered = event->state & ~(xcb_numlock_mask | XCB_MOD_MASK_LOCK); - DLOG("(removed numlock, state = %d)\n", state_filtered); - /* Only use the lower 8 bits of the state (modifier masks) so that mouse - * button masks are filtered out */ - state_filtered &= 0xFF; - DLOG("(removed upper 8 bits, state = %d)\n", state_filtered); - - if (xkb_current_group == XkbGroup2Index) - state_filtered |= BIND_MODE_SWITCH; - - DLOG("(checked mode_switch, state %d)\n", state_filtered); - - /* Find the binding */ - Binding *bind = get_binding(state_filtered, event->detail); - - /* No match? Then the user has Mode_switch enabled but does not have a - * specific keybinding. Fall back to the default keybindings (without - * Mode_switch). Makes it much more convenient for users of a hybrid - * layout (like us, ru). */ - if (bind == NULL) { - state_filtered &= ~(BIND_MODE_SWITCH); - DLOG("no match, new state_filtered = %d\n", state_filtered); - if ((bind = get_binding(state_filtered, event->detail)) == NULL) { - ELOG("Could not lookup key binding (modifiers %d, keycode %d)\n", - state_filtered, event->detail); - return; - } - } - - char *json_result = parse_command(bind->command); - FREE(json_result); - return; -} - /* * Called with coordinates of an enter_notify event or motion_notify event * to check if the user crossed virtual screen boundaries and adjust the @@ -202,7 +159,7 @@ static void handle_enter_notify(xcb_enter_notify_event_t *event) { } /* see if the user entered the window on a certain window decoration */ - int layout = (enter_child ? con->parent->layout : con->layout); + layout_t layout = (enter_child ? con->parent->layout : con->layout); if (layout == L_DEFAULT) { Con *child; TAILQ_FOREACH(child, &(con->nodes_head), nodes) @@ -233,6 +190,7 @@ static void handle_enter_notify(xcb_enter_notify_event_t *event) { if (ws != con_get_workspace(focused)) workspace_show(ws); + focused_id = XCB_NONE; con_focus(con_descend_focused(con)); tree_render(); @@ -256,6 +214,7 @@ static void handle_motion_notify(xcb_motion_notify_event_t *event) { Con *con; if ((con = con_by_frame_id(event->event)) == NULL) { + DLOG("MotionNotify for an unknown container, checking if it crosses screen boundaries.\n"); check_crossing_screen_boundary(event->root_x, event->root_y); return; } @@ -367,9 +326,21 @@ static void handle_configure_request(xcb_configure_request_event_t *event) { } DLOG("Configure request!\n"); - if (con_is_floating(con) && con_is_leaf(con)) { + + Con *workspace = con_get_workspace(con), + *fullscreen = NULL; + + /* There might not be a corresponding workspace for dock cons, therefore we + * have to be careful here. */ + if (workspace) { + fullscreen = con_get_fullscreen_con(workspace, CF_OUTPUT); + if (!fullscreen) + fullscreen = con_get_fullscreen_con(workspace, CF_GLOBAL); + } + + if (fullscreen != con && con_is_floating(con) && con_is_leaf(con)) { /* find the height for the decorations */ - int deco_height = config.font.height + 5; + int deco_height = con->deco_rect.height; /* we actually need to apply the size/position changes to the *parent* * container */ Rect bsr = con_border_style_rect(con); @@ -379,6 +350,11 @@ static void handle_configure_request(xcb_configure_request_event_t *event) { } Con *floatingcon = con->parent; + if (strcmp(con_get_workspace(floatingcon)->name, "__i3_scratch") == 0) { + DLOG("This is a scratchpad container, ignoring ConfigureRequest\n"); + return; + } + Rect newrect = floatingcon->rect; if (event->value_mask & XCB_CONFIG_WINDOW_X) { @@ -447,8 +423,23 @@ int handle_configure_event(void *prophs, xcb_connection_t *conn, xcb_configure_n static void handle_screen_change(xcb_generic_event_t *e) { DLOG("RandR screen change\n"); + /* The geometry of the root window is used for “fullscreen global” and + * changes when new outputs are added. */ + xcb_get_geometry_cookie_t cookie = xcb_get_geometry(conn, root); + xcb_get_geometry_reply_t *reply = xcb_get_geometry_reply(conn, cookie, NULL); + if (reply == NULL) { + ELOG("Could not get geometry of the root window, exiting\n"); + exit(1); + } + DLOG("root geometry reply: (%d, %d) %d x %d\n", reply->x, reply->y, reply->width, reply->height); + + croot->rect.width = reply->width; + croot->rect.height = reply->height; + randr_query_outputs(); + scratchpad_fix_resolution(); + ipc_send_event("output", I3_IPC_EVENT_OUTPUT, "{\"change\":\"unspecified\"}"); return; @@ -537,6 +528,17 @@ static void handle_destroy_notify_event(xcb_destroy_notify_event_t *event) { handle_unmap_notify_event(&unmap); } +static bool window_name_changed(i3Window *window, char *old_name) { + if ((old_name == NULL) && (window->name == NULL)) + return false; + + /* Either the old or the new one is NULL, but not both. */ + if ((old_name == NULL) ^ (window->name == NULL)) + return true; + + return (strcmp(old_name, i3string_as_utf8(window->name)) != 0); +} + /* * Called when a window changes its title * @@ -547,10 +549,17 @@ static bool handle_windowname_change(void *data, xcb_connection_t *conn, uint8_t if ((con = con_by_window_id(window)) == NULL || con->window == NULL) return false; + char *old_name = (con->window->name != NULL ? sstrdup(i3string_as_utf8(con->window->name)) : NULL); + window_update_name(con->window, prop, false); x_push_changes(croot); + if (window_name_changed(con->window, old_name)) + ipc_send_window_event("title", con); + + FREE(old_name); + return true; } @@ -565,10 +574,17 @@ static bool handle_windowname_change_legacy(void *data, xcb_connection_t *conn, if ((con = con_by_window_id(window)) == NULL || con->window == NULL) return false; + char *old_name = (con->window->name != NULL ? sstrdup(i3string_as_utf8(con->window->name)) : NULL); + window_update_name_legacy(con->window, prop, false); x_push_changes(croot); + if (window_name_changed(con->window, old_name)) + ipc_send_window_event("title", con); + + FREE(old_name); + return true; } @@ -642,10 +658,10 @@ static void handle_client_message(xcb_client_message_event_t *event) { 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"); + if (event->format != 32 || + (event->data.data32[1] != A__NET_WM_STATE_FULLSCREEN && + event->data.data32[1] != A__NET_WM_STATE_DEMANDS_ATTENTION)) { + DLOG("Unknown atom in clientmessage of type %d\n", event->data.data32[1]); return; } @@ -655,24 +671,56 @@ static void handle_client_message(xcb_client_message_event_t *event) { return; } - /* Check if the fullscreen state should be toggled */ - if ((con->fullscreen_mode != CF_NONE && - (event->data.data32[0] == _NET_WM_STATE_REMOVE || - event->data.data32[0] == _NET_WM_STATE_TOGGLE)) || - (con->fullscreen_mode == CF_NONE && - (event->data.data32[0] == _NET_WM_STATE_ADD || - event->data.data32[0] == _NET_WM_STATE_TOGGLE))) { - DLOG("toggling fullscreen\n"); - con_toggle_fullscreen(con, CF_OUTPUT); + if (event->data.data32[1] == A__NET_WM_STATE_FULLSCREEN) { + /* Check if the fullscreen state should be toggled */ + if ((con->fullscreen_mode != CF_NONE && + (event->data.data32[0] == _NET_WM_STATE_REMOVE || + event->data.data32[0] == _NET_WM_STATE_TOGGLE)) || + (con->fullscreen_mode == CF_NONE && + (event->data.data32[0] == _NET_WM_STATE_ADD || + event->data.data32[0] == _NET_WM_STATE_TOGGLE))) { + DLOG("toggling fullscreen\n"); + con_toggle_fullscreen(con, CF_OUTPUT); + } + } else if (event->data.data32[1] == A__NET_WM_STATE_DEMANDS_ATTENTION) { + /* Check if the urgent flag must be set or not */ + if (event->data.data32[0] == _NET_WM_STATE_ADD) + con_set_urgency(con, true); + else if (event->data.data32[0] == _NET_WM_STATE_REMOVE) + con_set_urgency(con, false); + else if (event->data.data32[0] == _NET_WM_STATE_TOGGLE) + con_set_urgency(con, !con->urgent); } tree_render(); - x_push_changes(croot); + } else if (event->type == A__NET_ACTIVE_WINDOW) { + DLOG("_NET_ACTIVE_WINDOW: Window 0x%08x should be activated\n", event->window); + Con *con = con_by_window_id(event->window); + if (con == NULL) { + DLOG("Could not get window for client message\n"); + return; + } + + Con *ws = con_get_workspace(con); + if (!workspace_is_visible(ws)) { + DLOG("Workspace not visible, ignoring _NET_ACTIVE_WINDOW\n"); + return; + } + + if (con_is_internal(ws)) { + DLOG("Workspace is internal, ignoring _NET_ACTIVE_WINDOW\n"); + return; + } + + if (ws != con_get_workspace(focused)) + workspace_show(ws); + + con_focus(con); + tree_render(); } 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); + DLOG("[i3 sync protocol] Sending random value %d back to X11 window 0x%08x\n", rnd, window); void *reply = scalloc(32); xcb_client_message_event_t *ev = reply; @@ -687,6 +735,36 @@ static void handle_client_message(xcb_client_message_event_t *event) { xcb_send_event(conn, false, window, XCB_EVENT_MASK_NO_EVENT, (char*)ev); xcb_flush(conn); free(reply); + } else if (event->type == A__NET_REQUEST_FRAME_EXTENTS) { + /* + * A client can request an estimate for the frame size which the window + * manager will put around it before actually mapping its window. Java + * does this (as of openjdk-7). + * + * Note that the calculation below is not entirely accurate — once you + * set a different border type, it’s off. We _could_ request all the + * window properties (which have to be set up at this point according + * to EWMH), but that seems rather elaborate. The standard explicitly + * says the application must cope with an estimate that is not entirely + * accurate. + */ + DLOG("_NET_REQUEST_FRAME_EXTENTS for window 0x%08x\n", event->window); + + /* The reply data: approximate frame size */ + Rect r = { + config.default_border_width, /* left */ + config.default_border_width, /* right */ + config.font.height + 5, /* top */ + config.default_border_width /* bottom */ + }; + xcb_change_property( + conn, + XCB_PROP_MODE_REPLACE, + event->window, + A__NET_FRAME_EXTENTS, + XCB_ATOM_CARDINAL, 32, 4, + &r); + xcb_flush(conn); } else { DLOG("unhandled clientmessage\n"); return; @@ -795,22 +873,18 @@ static bool handle_normal_hints(void *data, xcb_connection_t *conn, uint8_t stat goto render_and_return; /* Check if we need to set proportional_* variables using the correct ratio */ + double aspect_ratio = 0.0; if ((width / height) < min_aspect) { - if (con->proportional_width != width || - con->proportional_height != (width / min_aspect)) { - con->proportional_width = width; - con->proportional_height = width / min_aspect; - changed = true; - } + aspect_ratio = min_aspect; } else if ((width / height) > max_aspect) { - if (con->proportional_width != width || - con->proportional_height != (width / max_aspect)) { - con->proportional_width = width; - con->proportional_height = width / max_aspect; - changed = true; - } + aspect_ratio = max_aspect; } else goto render_and_return; + if (fabs(con->aspect_ratio - aspect_ratio) > DBL_EPSILON) { + con->aspect_ratio = aspect_ratio; + changed = true; + } + render_and_return: if (changed) tree_render(); @@ -830,37 +904,13 @@ static bool handle_hints(void *data, xcb_connection_t *conn, uint8_t state, xcb_ return false; } - xcb_icccm_wm_hints_t hints; - + bool urgency_hint; if (reply == NULL) - if (!(reply = xcb_get_property_reply(conn, xcb_icccm_get_wm_hints(conn, window), 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"); - goto end; - } - - /* Update the flag on the client directly */ - con->urgent = (xcb_icccm_wm_hints_get_urgency(&hints) != 0); - //CLIENT_LOG(con); - LOG("Urgency flag changed to %d\n", con->urgent); - - Con *ws; - /* Set the urgency flag on the workspace, if a workspace could be found - * (for dock clients, that is not the case). */ - if ((ws = con_get_workspace(con)) != NULL) - workspace_update_urgent_flag(ws); - + reply = xcb_get_property_reply(conn, xcb_icccm_get_wm_hints(conn, window), NULL); + window_update_hints(con->window, reply, &urgency_hint); + con_set_urgency(con, urgency_hint); tree_render(); -end: - if (con->window) - window_update_hints(con->window, reply); - else free(reply); return true; } @@ -992,7 +1042,7 @@ static struct property_handler_t property_handlers[] = { * received from X11 * */ -void property_handlers_init() { +void property_handlers_init(void) { sn_monitor_context_new(sndisplay, conn_screen, startup_monitor_event, NULL, NULL); @@ -1009,7 +1059,7 @@ static void property_notify(uint8_t state, xcb_window_t window, xcb_atom_t atom) struct property_handler_t *handler = NULL; xcb_get_property_reply_t *propr = NULL; - for (int c = 0; c < sizeof(property_handlers) / sizeof(struct property_handler_t); c++) { + for (size_t c = 0; c < sizeof(property_handlers) / sizeof(struct property_handler_t); c++) { if (property_handlers[c].atom != atom) continue; @@ -1046,6 +1096,7 @@ void handle_event(int type, xcb_generic_event_t *event) { switch (type) { case XCB_KEY_PRESS: + case XCB_KEY_RELEASE: handle_key_press((xcb_key_press_event_t*)event); break; @@ -1080,7 +1131,7 @@ void handle_event(int type, xcb_generic_event_t *event) { /* Client message are sent to the root window. The only interesting * client message for us is _NET_WM_STATE, we honour - * _NET_WM_STATE_FULLSCREEN */ + * _NET_WM_STATE_FULLSCREEN and _NET_WM_STATE_DEMANDS_ATTENTION */ case XCB_CLIENT_MESSAGE: handle_client_message((xcb_client_message_event_t*)event); break;