X-Git-Url: https://git.lizzy.rs/?a=blobdiff_plain;f=events.c;h=b368eee9519425587dac4ec61a00d0f100488c18;hb=a7821774c50aefa8c758b964814818b4634b77ce;hp=9b118b5d88e66371e7940523be2c2c2ffb663b85;hpb=5630b3c73fca162b9cf94a4f9ee883e28b9284b8;p=bspwm.git diff --git a/events.c b/events.c index 9b118b5..b368eee 100644 --- a/events.c +++ b/events.c @@ -1,60 +1,469 @@ -#include -#include -#include -#include -#include "helpers.h" -#include "types.h" +/* Copyright (c) 2012, Bastien Dejean + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include #include "bspwm.h" -#include "utils.h" -#include "events.h" +#include "ewmh.h" +#include "monitor.h" +#include "query.h" +#include "settings.h" +#include "subscribe.h" #include "tree.h" +#include "window.h" +#include "pointer.h" +#include "events.h" void handle_event(xcb_generic_event_t *evt) { - switch (XCB_EVENT_RESPONSE_TYPE(evt)) { - case XCB_MAP_REQUEST: - PUTS("received a map request\n"); - map_request(evt); - break; - case XCB_CONFIGURE_REQUEST: - PUTS("received a configure request\n"); - break; - case XCB_UNGRAB_KEY: - /* PUTS("ungrab key received"); */ - break; - case XCB_KEY_PRESS: - PUTS("keypress received"); - break; - case XCB_KEY_RELEASE: - PUTS("keyrelease received"); - break; - case XCB_BUTTON_PRESS: - case XCB_BUTTON_RELEASE: - PUTS("button event"); - break; - default: - PRINTF("received event %i\n", XCB_EVENT_RESPONSE_TYPE(evt)); - } + uint8_t resp_type = XCB_EVENT_RESPONSE_TYPE(evt); + switch (resp_type) { + case XCB_MAP_REQUEST: + map_request(evt); + break; + case XCB_DESTROY_NOTIFY: + destroy_notify(evt); + break; + case XCB_UNMAP_NOTIFY: + unmap_notify(evt); + break; + case XCB_CLIENT_MESSAGE: + client_message(evt); + break; + case XCB_CONFIGURE_REQUEST: + configure_request(evt); + break; + case XCB_PROPERTY_NOTIFY: + property_notify(evt); + break; + case XCB_ENTER_NOTIFY: + enter_notify(evt); + break; + case XCB_BUTTON_PRESS: + button_press(evt); + break; + case XCB_FOCUS_IN: + focus_in(evt); + break; + case 0: + process_error(evt); + break; + default: + if (randr && resp_type == randr_base + XCB_RANDR_SCREEN_CHANGE_NOTIFY) { + update_monitors(); + } + break; + } } void map_request(xcb_generic_event_t *evt) { - xcb_map_request_event_t *e = (xcb_map_request_event_t *) evt; - xcb_get_window_attributes_reply_t *wa; - xcb_window_t win = e->window; - wa = xcb_get_window_attributes_reply(dpy, xcb_get_window_attributes(dpy, win), NULL); - - if ((wa != NULL && wa->override_redirect) || is_managed(win)) - return; - - free(wa); - bool takes_focus = true; - client_t *c = make_client(win); - num_clients++; - node_t *birth = make_node(); - birth->client = c; - insert_node(desk, birth); - if (takes_focus) - focus_node(desk, birth); - xcb_map_window(dpy, c->window); + xcb_map_request_event_t *e = (xcb_map_request_event_t *) evt; + + schedule_window(e->window); +} + +void configure_request(xcb_generic_event_t *evt) +{ + xcb_configure_request_event_t *e = (xcb_configure_request_event_t *) evt; + + coordinates_t loc; + bool is_managed = locate_window(e->window, &loc); + client_t *c = (is_managed ? loc.node->client : NULL); + uint16_t width, height; + + if (!is_managed) { + uint16_t mask = 0; + uint32_t values[7]; + unsigned short i = 0; + + if (e->value_mask & XCB_CONFIG_WINDOW_X) { + mask |= XCB_CONFIG_WINDOW_X; + values[i++] = e->x; + } + + if (e->value_mask & XCB_CONFIG_WINDOW_Y) { + mask |= XCB_CONFIG_WINDOW_Y; + values[i++] = e->y; + } + + if (e->value_mask & XCB_CONFIG_WINDOW_WIDTH) { + mask |= XCB_CONFIG_WINDOW_WIDTH; + values[i++] = e->width; + } + + if (e->value_mask & XCB_CONFIG_WINDOW_HEIGHT) { + mask |= XCB_CONFIG_WINDOW_HEIGHT; + values[i++] = e->height; + } + + if (e->value_mask & XCB_CONFIG_WINDOW_BORDER_WIDTH) { + mask |= XCB_CONFIG_WINDOW_BORDER_WIDTH; + values[i++] = e->border_width; + } + + if (e->value_mask & XCB_CONFIG_WINDOW_SIBLING) { + mask |= XCB_CONFIG_WINDOW_SIBLING; + values[i++] = e->sibling; + } + + if (e->value_mask & XCB_CONFIG_WINDOW_STACK_MODE) { + mask |= XCB_CONFIG_WINDOW_STACK_MODE; + values[i++] = e->stack_mode; + } + + xcb_configure_window(dpy, e->window, mask, values); + + } else if (IS_FLOATING(c)) { + width = c->floating_rectangle.width; + height = c->floating_rectangle.height; + + if (e->value_mask & XCB_CONFIG_WINDOW_X) { + c->floating_rectangle.x = e->x; + } + + if (e->value_mask & XCB_CONFIG_WINDOW_Y) { + c->floating_rectangle.y = e->y; + } + + if (e->value_mask & XCB_CONFIG_WINDOW_WIDTH) { + width = e->width; + } + + if (e->value_mask & XCB_CONFIG_WINDOW_HEIGHT) { + height = e->height; + } + + apply_size_hints(c, &width, &height); + c->floating_rectangle.width = width; + c->floating_rectangle.height = height; + xcb_rectangle_t r = c->floating_rectangle; + + if (focus_follows_pointer) { + listen_enter_notify(loc.desktop->root, false); + } + + window_move_resize(e->window, r.x, r.y, r.width, r.height); + + if (focus_follows_pointer) { + listen_enter_notify(loc.desktop->root, true); + } + + put_status(SBSC_MASK_NODE_GEOMETRY, "node_geometry 0x%08X 0x%08X 0x%08X %ux%u+%i+%i\n", loc.monitor->id, loc.desktop->id, e->window, r.width, r.height, r.x, r.y); + + monitor_t *m = monitor_from_client(c); + if (m != loc.monitor) { + transfer_node(loc.monitor, loc.desktop, loc.node, m, m->desk, m->desk->focus); + } + } else { + if (c->state == STATE_PSEUDO_TILED) { + width = c->floating_rectangle.width; + height = c->floating_rectangle.height; + if (e->value_mask & XCB_CONFIG_WINDOW_WIDTH) { + width = e->width; + } + if (e->value_mask & XCB_CONFIG_WINDOW_HEIGHT) { + height = e->height; + } + apply_size_hints(c, &width, &height); + if (width != c->floating_rectangle.width || height != c->floating_rectangle.height) { + c->floating_rectangle.width = width; + c->floating_rectangle.height = height; + arrange(loc.monitor, loc.desktop); + } + } + + + xcb_configure_notify_event_t evt; + unsigned int bw = c->border_width; + + xcb_rectangle_t r = IS_FULLSCREEN(c) ? loc.monitor->rectangle : c->tiled_rectangle; + + evt.response_type = XCB_CONFIGURE_NOTIFY; + evt.event = e->window; + evt.window = e->window; + evt.above_sibling = XCB_NONE; + evt.x = r.x; + evt.y = r.y; + evt.width = r.width; + evt.height = r.height; + evt.border_width = bw; + evt.override_redirect = false; + + xcb_send_event(dpy, false, e->window, XCB_EVENT_MASK_STRUCTURE_NOTIFY, (const char *) &evt); + } +} + +void destroy_notify(xcb_generic_event_t *evt) +{ + xcb_destroy_notify_event_t *e = (xcb_destroy_notify_event_t *) evt; + + unmanage_window(e->window); +} + +void unmap_notify(xcb_generic_event_t *evt) +{ + xcb_unmap_notify_event_t *e = (xcb_unmap_notify_event_t *) evt; + + unmanage_window(e->window); +} + +void property_notify(xcb_generic_event_t *evt) +{ + xcb_property_notify_event_t *e = (xcb_property_notify_event_t *) evt; + + if (e->atom != XCB_ATOM_WM_HINTS && e->atom != XCB_ATOM_WM_NORMAL_HINTS) { + return; + } + + coordinates_t loc; + if (!locate_window(e->window, &loc)) { + return; + } + + if (e->atom == XCB_ATOM_WM_HINTS) { + xcb_icccm_wm_hints_t hints; + if (xcb_icccm_get_wm_hints_reply(dpy, xcb_icccm_get_wm_hints(dpy, e->window), &hints, NULL) == 1 && + (hints.flags & XCB_ICCCM_WM_HINT_X_URGENCY)) + set_urgent(loc.monitor, loc.desktop, loc.node, xcb_icccm_wm_hints_get_urgency(&hints)); + } else if (e->atom == XCB_ATOM_WM_NORMAL_HINTS) { + client_t *c = loc.node->client; + if (xcb_icccm_get_wm_normal_hints_reply(dpy, xcb_icccm_get_wm_normal_hints(dpy, e->window), &c->size_hints, NULL) == 1) { + arrange(loc.monitor, loc.desktop); + } + } +} + +void client_message(xcb_generic_event_t *evt) +{ + xcb_client_message_event_t *e = (xcb_client_message_event_t *) evt; + + if (e->type == ewmh->_NET_CURRENT_DESKTOP) { + coordinates_t loc; + if (ewmh_locate_desktop(e->data.data32[0], &loc)) { + focus_node(loc.monitor, loc.desktop, loc.desktop->focus); + } + return; + } + + coordinates_t loc; + if (!locate_window(e->window, &loc)) { + return; + } + + if (e->type == ewmh->_NET_WM_STATE) { + handle_state(loc.monitor, loc.desktop, loc.node, e->data.data32[1], e->data.data32[0]); + handle_state(loc.monitor, loc.desktop, loc.node, e->data.data32[2], e->data.data32[0]); + } else if (e->type == ewmh->_NET_ACTIVE_WINDOW) { + if ((ignore_ewmh_focus && e->data.data32[0] == XCB_EWMH_CLIENT_SOURCE_TYPE_NORMAL) || + loc.node == mon->desk->focus) { + return; + } + focus_node(loc.monitor, loc.desktop, loc.node); + } else if (e->type == ewmh->_NET_WM_DESKTOP) { + coordinates_t dloc; + if (ewmh_locate_desktop(e->data.data32[0], &dloc)) { + transfer_node(loc.monitor, loc.desktop, loc.node, dloc.monitor, dloc.desktop, dloc.desktop->focus); + } + } else if (e->type == ewmh->_NET_CLOSE_WINDOW) { + close_node(loc.node); + } +} + +void focus_in(xcb_generic_event_t *evt) +{ + xcb_focus_in_event_t *e = (xcb_focus_in_event_t *) evt; + + if (e->mode == XCB_NOTIFY_MODE_GRAB || e->mode == XCB_NOTIFY_MODE_UNGRAB + || e->detail == XCB_NOTIFY_DETAIL_POINTER || e->detail == XCB_NOTIFY_DETAIL_POINTER_ROOT + || e->detail == XCB_NOTIFY_DETAIL_NONE) { + return; + } + + if (mon->desk->focus != NULL && e->event == mon->desk->focus->id) { + return; + } + + coordinates_t loc; + if (locate_window(e->event, &loc)) { + // prevent input focus stealing + update_input_focus(); + } +} + +void button_press(xcb_generic_event_t *evt) +{ + xcb_button_press_event_t *e = (xcb_button_press_event_t *) evt; + bool replay = false; + switch (e->detail) { + case XCB_BUTTON_INDEX_1: + if (click_to_focus && cleaned_mask(e->state) == XCB_NONE) { + replay = true; + bool pff = pointer_follows_focus; + bool pfm = pointer_follows_monitor; + pointer_follows_focus = false; + pointer_follows_monitor = false; + grab_pointer(ACTION_FOCUS); + pointer_follows_focus = pff; + pointer_follows_monitor = pfm; + } else { + grab_pointer(pointer_actions[0]); + } + break; + case XCB_BUTTON_INDEX_2: + grab_pointer(pointer_actions[1]); + break; + case XCB_BUTTON_INDEX_3: + grab_pointer(pointer_actions[2]); + break; + } + xcb_allow_events(dpy, replay ? XCB_ALLOW_REPLAY_POINTER : XCB_ALLOW_SYNC_POINTER, e->time); + xcb_flush(dpy); +} + +void enter_notify(xcb_generic_event_t *evt) +{ + xcb_enter_notify_event_t *e = (xcb_enter_notify_event_t *) evt; + xcb_window_t win = e->event; + + if (e->mode != XCB_NOTIFY_MODE_NORMAL || e->detail == XCB_NOTIFY_DETAIL_INFERIOR) { + return; + } + + xcb_point_t pt = {e->root_x, e->root_y}; + monitor_t *m = monitor_from_point(pt); + + if (m == NULL) { + return; + } + + bool pff = pointer_follows_focus; + bool pfm = pointer_follows_monitor; + pointer_follows_focus = false; + pointer_follows_monitor = false; + auto_raise = false; + + desktop_t *d = m->desk; + node_t *n = NULL; + + for (n = first_extrema(d->root); n != NULL; n = next_leaf(n, d->root)) { + if (n->id == win || (n->presel != NULL && n->presel->feedback == win)) { + break; + } + } + + if (n != mon->desk->focus) { + if (n != NULL) { + focus_node(m, d, n); + } else if (m != mon) { + focus_node(m, m->desk, m->desk->focus); + } + } + + pointer_follows_focus = pff; + pointer_follows_monitor = pfm; + auto_raise = true; +} + +void handle_state(monitor_t *m, desktop_t *d, node_t *n, xcb_atom_t state, unsigned int action) +{ + if (state == ewmh->_NET_WM_STATE_FULLSCREEN) { + if (action == XCB_EWMH_WM_STATE_ADD) { + set_state(m, d, n, STATE_FULLSCREEN); + } else if (action == XCB_EWMH_WM_STATE_REMOVE) { + if (n->client->state == STATE_FULLSCREEN) { + set_state(m, d, n, n->client->last_state); + } + } else if (action == XCB_EWMH_WM_STATE_TOGGLE) { + set_state(m, d, n, IS_FULLSCREEN(n->client) ? n->client->last_state : STATE_FULLSCREEN); + } + arrange(m, d); + } else if (state == ewmh->_NET_WM_STATE_BELOW) { + if (action == XCB_EWMH_WM_STATE_ADD) { + set_layer(m, d, n, LAYER_BELOW); + } else if (action == XCB_EWMH_WM_STATE_REMOVE) { + if (n->client->layer == LAYER_BELOW) { + set_layer(m, d, n, n->client->last_layer); + } + } else if (action == XCB_EWMH_WM_STATE_TOGGLE) { + set_layer(m, d, n, n->client->layer == LAYER_BELOW ? n->client->last_layer : LAYER_BELOW); + } + } else if (state == ewmh->_NET_WM_STATE_ABOVE) { + if (action == XCB_EWMH_WM_STATE_ADD) { + set_layer(m, d, n, LAYER_ABOVE); + } else if (action == XCB_EWMH_WM_STATE_REMOVE) { + if (n->client->layer == LAYER_ABOVE) { + set_layer(m, d, n, n->client->last_layer); + } + } else if (action == XCB_EWMH_WM_STATE_TOGGLE) { + set_layer(m, d, n, n->client->layer == LAYER_ABOVE ? n->client->last_layer : LAYER_ABOVE); + } + } else if (state == ewmh->_NET_WM_STATE_HIDDEN) { + if (action == XCB_EWMH_WM_STATE_ADD) { + set_hidden(m, d, n, true); + } else if (action == XCB_EWMH_WM_STATE_REMOVE) { + set_hidden(m, d, n, false); + } else if (action == XCB_EWMH_WM_STATE_TOGGLE) { + set_hidden(m, d, n, !n->hidden); + } + } else if (state == ewmh->_NET_WM_STATE_STICKY) { + if (action == XCB_EWMH_WM_STATE_ADD) { + set_sticky(m, d, n, true); + } else if (action == XCB_EWMH_WM_STATE_REMOVE) { + set_sticky(m, d, n, false); + } else if (action == XCB_EWMH_WM_STATE_TOGGLE) { + set_sticky(m, d, n, !n->sticky); + } + } else if (state == ewmh->_NET_WM_STATE_DEMANDS_ATTENTION) { + if (action == XCB_EWMH_WM_STATE_ADD) { + set_urgent(m, d, n, true); + } else if (action == XCB_EWMH_WM_STATE_REMOVE) { + set_urgent(m, d, n, false); + } else if (action == XCB_EWMH_WM_STATE_TOGGLE) { + set_urgent(m, d, n, !n->client->urgent); + } +#define HANDLE_WM_STATE(s) \ + } else if (state == ewmh->_NET_WM_STATE_##s) { \ + if (action == XCB_EWMH_WM_STATE_ADD) { \ + n->client->wm_flags |= WM_FLAG_##s; \ + } else if (action == XCB_EWMH_WM_STATE_REMOVE) { \ + n->client->wm_flags &= ~WM_FLAG_##s; \ + } else if (action == XCB_EWMH_WM_STATE_TOGGLE) { \ + n->client->wm_flags ^= WM_FLAG_##s; \ + } \ + ewmh_wm_state_update(n); + HANDLE_WM_STATE(MODAL) + HANDLE_WM_STATE(MAXIMIZED_VERT) + HANDLE_WM_STATE(MAXIMIZED_HORZ) + HANDLE_WM_STATE(SHADED) + HANDLE_WM_STATE(SKIP_TASKBAR) + HANDLE_WM_STATE(SKIP_PAGER) + } +#undef HANDLE_WM_STATE +} + +void process_error(xcb_generic_event_t *evt) +{ + xcb_request_error_t *e = (xcb_request_error_t *) evt; + warn("Failed request: %s, %s: 0x%08X.\n", xcb_event_get_request_label(e->major_opcode), xcb_event_get_error_label(e->error_code), e->bad_value); }