#include #define CLEANMASK(m) ((m & ~0x80)) // Definitions for modifier keys #define SUPER XCB_MOD_MASK_4 #define ALT XCB_MOD_MASK_1 #define CTRL XCB_MOD_MASK_CONTROL #define SHIFT XCB_MOD_MASK_SHIFT // Change this to change the modifier key you want to use #define MOD SUPER // Standard headers #include #include #include #include // XCB header #include #include // Global variables // Connection to X server xcb_connection_t *conn; // Screen of the X server xcb_screen_t *scr; // Currently focused window xcb_window_t focuswin; // Geometry structure for rectangles and positions typedef struct Geometry { int16_t x; int16_t y; uint16_t width; uint16_t height; } Geometry; // Event structure to represent various X events typedef struct Event { int32_t type; uint32_t window; int8_t override_redirect; uint32_t btn; int32_t x; int32_t y; uint32_t height; uint32_t width; uint32_t state; int8_t is_root; } Event; // Cleanup function to close the X connection on exit void cleanup(void) { if (conn != NULL) xcb_disconnect(conn); } // Keybind function to setup a key grab if the keycode is clicked along with the // MOD key void add_keybind(int key) { xcb_grab_key(conn, 0, scr->root, MOD, key, XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC); } // Mousebind function to setup a mouse button grab if the button is clicked // along with the MOD key void add_mousebind(int button) { xcb_grab_button(conn, 0, scr->root, XCB_EVENT_MASK_BUTTON_PRESS | XCB_EVENT_MASK_BUTTON_RELEASE, XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC, scr->root, XCB_NONE, button, MOD); } // Deploy function to initialize the X connection, set up event masks, and // prepare the window manager int deploy(void) { int mask; // Start X connection and return -1 if it fails if (xcb_connection_has_error(conn = xcb_connect(NULL, NULL))) return -1; // Define screen and focus window scr = xcb_setup_roots_iterator(xcb_get_setup(conn)).data; focuswin = scr->root; // Setuup event masks mask = XCB_CW_EVENT_MASK; uint32_t values[] = {XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT | XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_PROPERTY_CHANGE}; xcb_change_window_attributes_checked(conn, scr->root, mask, values); xcb_flush(conn); return 0; } // Free a geometry structure void free_geometry(Geometry *g) { free(g); } // Set input focus to a window void focus(xcb_window_t win) { xcb_set_input_focus(conn, XCB_INPUT_FOCUS_POINTER_ROOT, win, XCB_CURRENT_TIME); focuswin = win; } // Get the currently focused window xcb_window_t get_focus(void) { return focuswin; } // Subscribe to events on a window void subscribe(xcb_window_t win) { uint32_t values[2]; values[0] = XCB_EVENT_MASK_ENTER_WINDOW; values[1] = XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY; xcb_change_window_attributes(conn, win, XCB_CW_EVENT_MASK, values); } // Kill a window void kill(xcb_window_t window) { xcb_kill_client(conn, window); xcb_flush(conn); } // Show a window void show(xcb_window_t window) { xcb_map_window(conn, window); xcb_flush(conn); } // Hide a window void hide(xcb_window_t window) { xcb_unmap_window(conn, window); xcb_flush(conn); } // Bring a window to the top of the stack void send_to_top(xcb_window_t win) { uint32_t values[1] = {XCB_STACK_MODE_ABOVE}; xcb_configure_window(conn, win, XCB_CONFIG_WINDOW_STACK_MODE, values); xcb_flush(conn); } // Get the geometry of a window Geometry get_geometry(xcb_window_t win) { xcb_get_geometry_reply_t *geom = xcb_get_geometry_reply(conn, xcb_get_geometry(conn, win), NULL); Geometry g = {geom->x, geom->y, geom->width, geom->height}; free(geom); return g; } // Get the current pointer position Geometry get_pointer(void) { xcb_query_pointer_reply_t *geom; geom = xcb_query_pointer_reply(conn, xcb_query_pointer(conn, scr->root), 0); Geometry g = {geom->root_x, geom->root_y, 0, 0}; free(geom); return g; } // Get the screen geometry Geometry get_screen(void) { return (Geometry){0, 0, scr->width_in_pixels, scr->height_in_pixels}; } // Warp the pointer to a specific position in a window // Can be used with get_geometry to warp to a specific position in a window (eg. // the center) void warp_pointer(xcb_window_t win, int x, int y) { xcb_warp_pointer(conn, XCB_NONE, win, 0, 0, 0, 0, x, y); xcb_flush(conn); } // Move a window to a specific position void move_window(xcb_window_t win, int x, int y) { uint32_t values[2] = {x, y}; xcb_configure_window(conn, win, XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y, values); xcb_flush(conn); } // Resize a window to specific dimensions void resize_window(xcb_window_t win, int width, int height) { uint32_t values[2] = {width, height}; xcb_configure_window( conn, win, XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT, values); xcb_flush(conn); } xcb_size_hints_t get_wm_n_hints(xcb_window_t win) { xcb_size_hints_t hints; xcb_get_property_cookie_t cookie = xcb_icccm_get_wm_normal_hints(conn, win); xcb_icccm_get_wm_normal_hints_reply(conn, cookie, &hints, NULL); return hints; } xcb_icccm_wm_hints_t get_wm_hints(xcb_window_t win) { xcb_get_property_cookie_t cookie = xcb_icccm_get_wm_hints(conn, win); xcb_icccm_wm_hints_t hints; xcb_icccm_get_wm_hints_reply(conn, cookie, &hints, NULL); return hints; } char *get_wm_name(xcb_window_t win) { xcb_get_property_cookie_t cookie = xcb_icccm_get_wm_name(conn, win); xcb_icccm_get_text_property_reply_t reply; xcb_icccm_get_wm_name_reply(conn, cookie, &reply, NULL); return reply.name; } uint8_t get_wm_transient_for(xcb_window_t win) { xcb_get_property_cookie_t cookie = xcb_icccm_get_wm_transient_for(conn, win); return xcb_icccm_get_wm_transient_for_reply(conn, cookie, &win, NULL); } void set_wm_state(xcb_window_t win, int state) { xcb_intern_atom_cookie_t cookie = xcb_intern_atom(conn, 0, strlen("WM_STATE"), "WM_STATE"); xcb_intern_atom_reply_t *reply = xcb_intern_atom_reply(conn, cookie, NULL); if (!reply) return; xcb_atom_t WM_STATE = reply->atom; free(reply); uint32_t data[2]; data[0] = state; data[1] = XCB_WINDOW_NONE; xcb_change_property(conn, XCB_PROP_MODE_REPLACE, win, WM_STATE, WM_STATE, 32, 2, data); xcb_flush(conn); } xcb_window_t draw_rectangle(int x, int y, int width, int height, uint32_t color) { // Get the screen const xcb_setup_t *setup = xcb_get_setup(conn); xcb_screen_iterator_t iter = xcb_setup_roots_iterator(setup); xcb_screen_t *screen = iter.data; // Create an override-redirect window xcb_window_t win = xcb_generate_id(conn); uint32_t mask = XCB_CW_BACK_PIXEL | XCB_CW_OVERRIDE_REDIRECT | XCB_CW_EVENT_MASK; uint32_t values[3]; values[0] = screen->black_pixel; // initial background pixel values[1] = 1; // override_redirect = true values[2] = XCB_EVENT_MASK_EXPOSURE; // we want exposure events xcb_create_window(conn, XCB_COPY_FROM_PARENT, // depth win, // window ID screen->root, // parent x, y, // x, y width, height, // width, height 0, // border width XCB_WINDOW_CLASS_INPUT_OUTPUT, screen->root_visual, mask, values); // Map the window (make it visible) xcb_map_window(conn, win); // Fill it with the solid color using a Graphics Context xcb_gcontext_t gc = xcb_generate_id(conn); uint32_t gc_values[] = {color, XCB_LINE_STYLE_SOLID}; xcb_create_gc(conn, gc, win, XCB_GC_FOREGROUND | XCB_GC_LINE_STYLE, gc_values); xcb_rectangle_t rect = {0, 0, width, height}; // relative to window xcb_poly_fill_rectangle(conn, win, gc, 1, &rect); // Flush the connection to send commands to the server xcb_flush(conn); return win; } void grab_pointer(xcb_window_t win) { xcb_grab_pointer(conn, 0, win, XCB_EVENT_MASK_BUTTON_RELEASE | XCB_EVENT_MASK_BUTTON_MOTION | XCB_EVENT_MASK_POINTER_MOTION_HINT, XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC, win, XCB_NONE, XCB_CURRENT_TIME); } void ungrab_pointer(void) { xcb_ungrab_pointer(conn, XCB_CURRENT_TIME); } // Wait for an event and return it as an Event structure // This function is blocking // The event is sent by value, so no need to free anything Event wait_for_event(void) { Event ret = {0}; xcb_generic_event_t *ev; ev = xcb_wait_for_event(conn); if (!ev) errx(1, "xcb connection broken"); switch (CLEANMASK(ev->response_type)) { case XCB_CREATE_NOTIFY: { xcb_create_notify_event_t *e; e = (xcb_create_notify_event_t *)ev; ret.type = 1; ret.window = e->window; ret.override_redirect = e->override_redirect; } break; case XCB_DESTROY_NOTIFY: { xcb_destroy_notify_event_t *e; e = (xcb_destroy_notify_event_t *)ev; ret.type = 2; ret.window = e->window; } break; case XCB_ENTER_NOTIFY: { xcb_enter_notify_event_t *e; e = (xcb_enter_notify_event_t *)ev; ret.type = 3; ret.window = e->event; } break; case XCB_MAP_NOTIFY: { xcb_map_notify_event_t *e; e = (xcb_map_notify_event_t *)ev; ret.type = 4; ret.window = e->window; ret.override_redirect = e->override_redirect; } break; case XCB_MAP_REQUEST: { xcb_map_request_event_t *e; e = (xcb_map_request_event_t *)ev; ret.type = 5; ret.window = e->window; } break; case XCB_BUTTON_PRESS: { xcb_button_press_event_t *e = (xcb_button_press_event_t *)ev; if (!e->child) break; ret.type = 6; ret.window = e->child; ret.is_root = e->child == scr->root; ret.x = e->event_x; ret.y = e->event_y; ret.btn = e->detail; } break; case XCB_MOTION_NOTIFY: { xcb_motion_notify_event_t *e = (xcb_motion_notify_event_t *)ev; ret.type = 7; ret.x = e->event_x; ret.y = e->event_y; ret.state = e->state; } break; case XCB_BUTTON_RELEASE: { xcb_button_release_event_t *e = (xcb_button_release_event_t *)ev; ret.type = 8; ret.x = e->event_x; ret.y = e->event_y; ret.btn = e->detail; } break; case XCB_KEY_PRESS: { xcb_key_press_event_t *e; e = (xcb_key_press_event_t *)ev; ret.type = 9; ret.window = e->child; ret.btn = e->detail; ret.state = e->state; } break; case XCB_KEY_RELEASE: { xcb_key_release_event_t *e; e = (xcb_key_release_event_t *)ev; ret.type = 10; ret.window = e->child; ret.btn = e->detail; ret.state = e->state; } break; case XCB_UNMAP_NOTIFY: { xcb_unmap_notify_event_t *e; e = (xcb_unmap_notify_event_t *)ev; ret.type = 11; ret.window = e->window; } break; case XCB_CONFIGURE_REQUEST: { xcb_configure_request_event_t *e = (xcb_configure_request_event_t *)ev; ret.type = 12; ret.window = e->window; ret.x = e->x; ret.y = e->y; ret.width = e->width; ret.height = e->height; } break; case XCB_RESIZE_REQUEST: { xcb_resize_request_event_t *e = (xcb_resize_request_event_t *)ev; ret.type = 13; ret.window = e->window; ret.width = e->width; ret.height = e->height; } break; } xcb_flush(conn); free(ev); return ret; }