2 #define I3__FILE__ "handlers.c"
18 #include <xcb/randr.h>
19 #include <X11/XKBlib.h>
20 #define SN_API_NOT_YET_FROZEN 1
21 #include <libsn/sn-monitor.h>
43 event->added = time(NULL);
54 time_t now = time(NULL);
56 if ((now - event->
added) > 5) {
61 }
else event =
SLIST_NEXT(event, ignore_events);
97 ELOG(
"ERROR: No such screen\n");
101 if (output->
con == NULL) {
102 ELOG(
"ERROR: The screen is not recognized by i3 (no container associated)\n");
128 DLOG(
"enter_notify for %08x, mode = %d, detail %d, serial %d\n",
129 event->event, event->mode, event->detail, event->sequence);
130 DLOG(
"coordinates %d, %d\n", event->event_x, event->event_y);
131 if (event->mode != XCB_NOTIFY_MODE_NORMAL) {
132 DLOG(
"This was not a normal notify, ignoring\n");
138 DLOG(
"Event ignored\n");
142 bool enter_child =
false;
151 DLOG(
"Getting screen at %d x %d\n", event->root_x, event->root_y);
157 DLOG(
"Ignoring, this is a dock client\n");
167 LOG(
"using child %p / %s instead!\n", child, child->
name);
174 if (client->workspace != c_ws && client->workspace->output == c_ws->output) {
178 DLOG(
"enter_notify for a client on a different workspace but the same screen, ignoring\n");
212 if (event->child != 0)
217 DLOG(
"MotionNotify for an unknown container, checking if it crosses screen boundaries.\n");
252 if (event->request != XCB_MAPPING_KEYBOARD &&
253 event->request != XCB_MAPPING_MODIFIER)
256 DLOG(
"Received mapping_notify for keyboard or modifier mapping, re-grabbing keys\n");
257 xcb_refresh_keyboard_mapping(
keysyms, event);
273 xcb_get_window_attributes_cookie_t cookie;
275 cookie = xcb_get_window_attributes_unchecked(
conn, event->window);
277 DLOG(
"window = 0x%08x, serial is %d.\n", event->window, event->sequence);
296 DLOG(
"window 0x%08x wants to be at %dx%d with %dx%d\n",
297 event->window, event->x, event->y, event->width, event->height);
302 DLOG(
"Configure request for unmanaged window, can do that.\n");
307 #define COPY_MASK_MEMBER(mask_member, event_member) do { \
308 if (event->value_mask & mask_member) { \
309 mask |= mask_member; \
310 values[c++] = event->event_member; \
322 xcb_configure_window(
conn, event->window, mask, values);
328 DLOG(
"Configure request!\n");
348 bsr.
y += deco_height;
349 bsr.
height -= deco_height;
354 DLOG(
"This is a scratchpad container, ignoring ConfigureRequest\n");
360 if (event->value_mask & XCB_CONFIG_WINDOW_X) {
361 newrect.
x =
event->x + (-1) * bsr.
x;
362 DLOG(
"proposed x = %d, new x is %d\n", event->x, newrect.
x);
364 if (event->value_mask & XCB_CONFIG_WINDOW_Y) {
365 newrect.
y =
event->y + (-1) * bsr.
y;
366 DLOG(
"proposed y = %d, new y is %d\n", event->y, newrect.
y);
368 if (event->value_mask & XCB_CONFIG_WINDOW_WIDTH) {
369 newrect.
width =
event->width + (-1) * bsr.
width;
371 DLOG(
"proposed width = %d, new width is %d (x11 border %d)\n",
374 if (event->value_mask & XCB_CONFIG_WINDOW_HEIGHT) {
377 DLOG(
"proposed height = %d, new height is %d (x11 border %d)\n",
381 DLOG(
"Container is a floating leaf node, will do that.\n");
388 DLOG(
"Dock window, only height reconfiguration allowed\n");
389 if (event->value_mask & XCB_CONFIG_WINDOW_HEIGHT) {
390 DLOG(
"Height given, changing\n");
408 int handle_configure_event(
void *prophs, xcb_connection_t *
conn, xcb_configure_notify_event_t *event) {
409 DLOG(
"configure_event, sequence %d\n", event->sequence);
424 DLOG(
"RandR screen change\n");
428 xcb_get_geometry_cookie_t cookie = xcb_get_geometry(
conn,
root);
429 xcb_get_geometry_reply_t *reply = xcb_get_geometry_reply(
conn, cookie, NULL);
431 ELOG(
"Could not get geometry of the root window, exiting\n");
434 DLOG(
"root geometry reply: (%d, %d) %d x %d\n", reply->x, reply->y, reply->width, reply->height);
443 ipc_send_event(
"output", I3_IPC_EVENT_OUTPUT,
"{\"change\":\"unspecified\"}");
454 DLOG(
"UnmapNotify for 0x%08x (received from 0x%08x), serial %d\n", event->window, event->event, event->sequence);
455 xcb_get_input_focus_cookie_t cookie;
462 LOG(
"Not a managed window, ignoring UnmapNotify event\n");
469 cookie = xcb_get_input_focus(
conn);
470 DLOG(
"ignore_unmap = %d for frame of container %p\n", con->
ignore_unmap, con);
475 cookie = xcb_get_input_focus(
conn);
508 free(xcb_get_input_focus_reply(
conn, cookie, NULL));
521 DLOG(
"destroy notify for 0x%08x, 0x%08x\n", event->event, event->window);
523 xcb_unmap_notify_event_t unmap;
524 unmap.sequence =
event->sequence;
525 unmap.event =
event->event;
526 unmap.window =
event->window;
536 xcb_window_t window, xcb_atom_t atom, xcb_get_property_reply_t *prop) {
554 xcb_window_t window, xcb_atom_t atom, xcb_get_property_reply_t *prop) {
571 xcb_window_t window, xcb_atom_t atom, xcb_get_property_reply_t *prop) {
586 static int handle_windowclass_change(
void *data, xcb_connection_t *
conn, uint8_t
state,
587 xcb_window_t window, xcb_atom_t atom, xcb_get_property_reply_t *prop) {
605 DLOG(
"window = %08x\n", event->window);
608 LOG(
"expose event for unknown window, ignoring\n");
617 event->x, event->y, event->x, event->y,
618 event->width, event->height);
631 if (sn_xcb_display_process_event(
sndisplay, (xcb_generic_event_t*)event))
634 LOG(
"ClientMessage for window 0x%08x\n", event->window);
635 if (event->type == A__NET_WM_STATE) {
636 if (event->format != 32 ||
637 (event->data.data32[1] != A__NET_WM_STATE_FULLSCREEN &&
638 event->data.data32[1] != A__NET_WM_STATE_DEMANDS_ATTENTION)) {
639 DLOG(
"Unknown atom in clientmessage of type %d\n", event->data.data32[1]);
645 DLOG(
"Could not get window for client message\n");
649 if (event->data.data32[1] == A__NET_WM_STATE_FULLSCREEN) {
657 DLOG(
"toggling fullscreen\n");
660 }
else if (event->data.data32[1] == A__NET_WM_STATE_DEMANDS_ATTENTION) {
671 }
else if (event->type == A__NET_ACTIVE_WINDOW) {
672 DLOG(
"_NET_ACTIVE_WINDOW: Window 0x%08x should be activated\n", event->window);
675 DLOG(
"Could not get window for client message\n");
681 DLOG(
"Workspace not visible, ignoring _NET_ACTIVE_WINDOW\n");
686 DLOG(
"Workspace is internal, ignoring _NET_ACTIVE_WINDOW\n");
695 }
else if (event->type == A_I3_SYNC) {
696 xcb_window_t window =
event->data.data32[0];
697 uint32_t rnd =
event->data.data32[1];
698 DLOG(
"[i3 sync protocol] Sending random value %d back to X11 window 0x%08x\n", rnd, window);
701 xcb_client_message_event_t *ev = reply;
703 ev->response_type = XCB_CLIENT_MESSAGE;
705 ev->type = A_I3_SYNC;
707 ev->data.data32[0] = window;
708 ev->data.data32[1] = rnd;
710 xcb_send_event(conn,
false, window, XCB_EVENT_MASK_NO_EVENT, (
char*)ev);
713 }
else if (event->type == A__NET_REQUEST_FRAME_EXTENTS) {
726 DLOG(
"_NET_REQUEST_FRAME_EXTENTS for window 0x%08x\n", event->window);
737 XCB_PROP_MODE_REPLACE,
739 A__NET_FRAME_EXTENTS,
744 DLOG(
"unhandled clientmessage\n");
750 int handle_window_type(
void *data, xcb_connection_t *conn, uint8_t state, xcb_window_t window,
751 xcb_atom_t atom, xcb_get_property_reply_t *property) {
754 ELOG(
"_NET_WM_WINDOW_TYPE changed, this is not yet implemented.\n");
767 xcb_atom_t name, xcb_get_property_reply_t *reply) {
770 DLOG(
"Received WM_NORMAL_HINTS for unknown client\n");
774 xcb_size_hints_t size_hints;
786 DLOG(
"Minimum size: %d (width) x %d (height)\n", size_hints.min_width, size_hints.min_height);
789 bool changed =
false;
791 if (size_hints.width_inc > 0 && size_hints.width_inc < 0xFFFF)
796 if (size_hints.height_inc > 0 && size_hints.height_inc < 0xFFFF)
803 DLOG(
"resize increments changed\n");
806 int base_width = 0, base_height = 0;
812 base_width = size_hints.base_width;
813 base_height = size_hints.base_height;
816 base_width = size_hints.min_width;
817 base_height = size_hints.min_height;
824 DLOG(
"client's base_height changed to %d\n", base_height);
825 DLOG(
"client's base_width changed to %d\n", base_width);
831 (size_hints.min_aspect_num <= 0) ||
832 (size_hints.min_aspect_den <= 0)) {
833 goto render_and_return;
840 double min_aspect = (double)size_hints.min_aspect_num / size_hints.min_aspect_den;
841 double max_aspect = (
double)size_hints.max_aspect_num / size_hints.min_aspect_den;
843 DLOG(
"Aspect ratio set: minimum %f, maximum %f\n", min_aspect, max_aspect);
844 DLOG(
"width = %f, height = %f\n", width, height);
847 if (max_aspect <= 0 || min_aspect <= 0 || height == 0 || (width / height) <= 0)
848 goto render_and_return;
851 double aspect_ratio = 0.0;
852 if ((width / height) < min_aspect) {
853 aspect_ratio = min_aspect;
854 }
else if ((width / height) > max_aspect) {
855 aspect_ratio = max_aspect;
856 }
else goto render_and_return;
858 if (fabs(con->
aspect_ratio - aspect_ratio) > DBL_EPSILON) {
874 static bool handle_hints(
void *data, xcb_connection_t *conn, uint8_t state, xcb_window_t window,
875 xcb_atom_t name, xcb_get_property_reply_t *reply) {
878 DLOG(
"Received WM_HINTS for unknown client\n");
900 xcb_atom_t name, xcb_get_property_reply_t *prop) {
904 DLOG(
"No such window\n");
909 prop = xcb_get_property_reply(conn, xcb_get_property_unchecked(conn,
926 xcb_atom_t name, xcb_get_property_reply_t *prop) {
932 prop = xcb_get_property_reply(conn, xcb_get_property_unchecked(conn,
950 DLOG(
"focus change in, for window 0x%08x\n", event->event);
954 DLOG(
"That is con %p / %s\n", con, con->
name);
956 if (event->mode == XCB_NOTIFY_MODE_GRAB ||
957 event->mode == XCB_NOTIFY_MODE_UNGRAB) {
958 DLOG(
"FocusIn event for grab/ungrab, ignoring\n");
962 if (event->detail == XCB_NOTIFY_DETAIL_POINTER) {
963 DLOG(
"notify detail is pointer, ignoring this event\n");
968 DLOG(
"focus matches the currently focused window, not doing anything\n");
974 DLOG(
"This is a dock client, not focusing.\n");
978 DLOG(
"focus is different, updating decorations\n");
996 typedef bool (*
cb_property_handler_t)(
void *data, xcb_connection_t *c, uint8_t
state, xcb_window_t window, xcb_atom_t atom, xcb_get_property_reply_t *property);
1013 #define NUM_HANDLERS (sizeof(property_handlers) / sizeof(struct property_handler_t))
1024 property_handlers[0].
atom = A__NET_WM_NAME;
1028 property_handlers[4].
atom = A_WM_CLIENT_LEADER;
1030 property_handlers[6].
atom = A_WM_WINDOW_ROLE;
1035 xcb_get_property_reply_t *propr = NULL;
1038 if (property_handlers[c].atom != atom)
1041 handler = &property_handlers[c];
1045 if (handler == NULL) {
1050 if (state != XCB_PROPERTY_DELETE) {
1051 xcb_get_property_cookie_t cookie = xcb_get_property(conn, 0, window, atom, XCB_GET_PROPERTY_TYPE_ANY, 0, handler->
long_len);
1052 propr = xcb_get_property_reply(conn, cookie, 0);
1056 if (!handler->
cb(NULL, conn, state, window, atom, propr))
1067 type ==
randr_base + XCB_RANDR_SCREEN_CHANGE_NOTIFY) {
1074 case XCB_KEY_RELEASE:
1078 case XCB_BUTTON_PRESS:
1082 case XCB_MAP_REQUEST:
1086 case XCB_UNMAP_NOTIFY:
1090 case XCB_DESTROY_NOTIFY:
1098 case XCB_MOTION_NOTIFY:
1103 case XCB_ENTER_NOTIFY:
1110 case XCB_CLIENT_MESSAGE:
1115 case XCB_CONFIGURE_REQUEST:
1120 case XCB_MAPPING_NOTIFY:
1128 case XCB_PROPERTY_NOTIFY: {
1129 xcb_property_notify_event_t *e = (xcb_property_notify_event_t*)event;