i3
src/xcb.c
Go to the documentation of this file.
00001 /*
00002  * vim:ts=4:sw=4:expandtab
00003  *
00004  * i3 - an improved dynamic tiling window manager
00005  * © 2009-2011 Michael Stapelberg and contributors (see also: LICENSE)
00006  *
00007  * xcb.c: Helper functions for easier usage of XCB
00008  *
00009  */
00010 #include "all.h"
00011 
00012 unsigned int xcb_numlock_mask;
00013 
00014 /*
00015  * Convenience wrapper around xcb_create_window which takes care of depth, generating an ID and checking
00016  * for errors.
00017  *
00018  */
00019 xcb_window_t create_window(xcb_connection_t *conn, Rect dims, uint16_t window_class,
00020         enum xcursor_cursor_t cursor, bool map, uint32_t mask, uint32_t *values) {
00021     xcb_window_t result = xcb_generate_id(conn);
00022 
00023     /* If the window class is XCB_WINDOW_CLASS_INPUT_ONLY, depth has to be 0 */
00024     uint16_t depth = (window_class == XCB_WINDOW_CLASS_INPUT_ONLY ? 0 : XCB_COPY_FROM_PARENT);
00025 
00026     xcb_create_window(conn,
00027             depth,
00028             result, /* the window id */
00029             root, /* parent == root */
00030             dims.x, dims.y, dims.width, dims.height, /* dimensions */
00031             0, /* border = 0, we draw our own */
00032             window_class,
00033             XCB_WINDOW_CLASS_COPY_FROM_PARENT, /* copy visual from parent */
00034             mask,
00035             values);
00036 
00037     /* Set the cursor */
00038     if (xcursor_supported) {
00039         mask = XCB_CW_CURSOR;
00040         values[0] = xcursor_get_cursor(cursor);
00041         xcb_change_window_attributes(conn, result, mask, values);
00042     } else {
00043         xcb_cursor_t cursor_id = xcb_generate_id(conn);
00044         i3Font cursor_font = load_font("cursor", false);
00045         int xcb_cursor = xcursor_get_xcb_cursor(cursor);
00046         xcb_create_glyph_cursor(conn, cursor_id, cursor_font.id, cursor_font.id,
00047                 xcb_cursor, xcb_cursor + 1, 0, 0, 0, 65535, 65535, 65535);
00048         xcb_change_window_attributes(conn, result, XCB_CW_CURSOR, &cursor_id);
00049         xcb_free_cursor(conn, cursor_id);
00050     }
00051 
00052     /* Map the window (= make it visible) */
00053     if (map)
00054         xcb_map_window(conn, result);
00055 
00056     return result;
00057 }
00058 
00059 /*
00060  * Draws a line from x,y to to_x,to_y using the given color
00061  *
00062  */
00063 void xcb_draw_line(xcb_connection_t *conn, xcb_drawable_t drawable, xcb_gcontext_t gc,
00064                    uint32_t colorpixel, uint32_t x, uint32_t y, uint32_t to_x, uint32_t to_y) {
00065     xcb_change_gc(conn, gc, XCB_GC_FOREGROUND, (uint32_t[]){ colorpixel });
00066     xcb_poly_line(conn, XCB_COORD_MODE_ORIGIN, drawable, gc, 2,
00067                   (xcb_point_t[]) { {x, y}, {to_x, to_y} });
00068 }
00069 
00070 /*
00071  * Draws a rectangle from x,y with width,height using the given color
00072  *
00073  */
00074 void xcb_draw_rect(xcb_connection_t *conn, xcb_drawable_t drawable, xcb_gcontext_t gc,
00075                    uint32_t colorpixel, uint32_t x, uint32_t y, uint32_t width, uint32_t height) {
00076     xcb_change_gc(conn, gc, XCB_GC_FOREGROUND, (uint32_t[]){ colorpixel });
00077     xcb_rectangle_t rect = {x, y, width, height};
00078     xcb_poly_fill_rectangle(conn, drawable, gc, 1, &rect);
00079 }
00080 
00081 /*
00082  * Generates a configure_notify_event with absolute coordinates (relative to the X root
00083  * window, not to the client’s frame) for the given client.
00084  *
00085  */
00086 void fake_absolute_configure_notify(Con *con) {
00087     xcb_rectangle_t absolute;
00088     if (con->window == NULL)
00089         return;
00090 
00091     absolute.x = con->rect.x + con->window_rect.x;
00092     absolute.y = con->rect.y + con->window_rect.y;
00093     absolute.width = con->window_rect.width;
00094     absolute.height = con->window_rect.height;
00095 
00096     DLOG("fake rect = (%d, %d, %d, %d)\n", absolute.x, absolute.y, absolute.width, absolute.height);
00097 
00098     fake_configure_notify(conn, absolute, con->window->id, con->border_width);
00099 }
00100 
00101 /*
00102  * Sends the WM_TAKE_FOCUS ClientMessage to the given window
00103  *
00104  */
00105 void send_take_focus(xcb_window_t window) {
00106     /* Every X11 event is 32 bytes long. Therefore, XCB will copy 32 bytes.
00107      * In order to properly initialize these bytes, we allocate 32 bytes even
00108      * though we only need less for an xcb_configure_notify_event_t */
00109     void *event = scalloc(32);
00110     xcb_client_message_event_t *ev = event;
00111 
00112     ev->response_type = XCB_CLIENT_MESSAGE;
00113     ev->window = window;
00114     ev->type = A_WM_PROTOCOLS;
00115     ev->format = 32;
00116     ev->data.data32[0] = A_WM_TAKE_FOCUS;
00117     ev->data.data32[1] = XCB_CURRENT_TIME;
00118 
00119     DLOG("Sending WM_TAKE_FOCUS to the client\n");
00120     xcb_send_event(conn, false, window, XCB_EVENT_MASK_NO_EVENT, (char*)ev);
00121     free(event);
00122 }
00123 
00124 /*
00125  * Raises the given window (typically client->frame) above all other windows
00126  *
00127  */
00128 void xcb_raise_window(xcb_connection_t *conn, xcb_window_t window) {
00129     uint32_t values[] = { XCB_STACK_MODE_ABOVE };
00130     xcb_configure_window(conn, window, XCB_CONFIG_WINDOW_STACK_MODE, values);
00131 }
00132 
00133 /*
00134  * Query the width of the given text (16-bit characters, UCS) with given real
00135  * length (amount of glyphs) using the given font.
00136  *
00137  */
00138 int predict_text_width(char *text, int length) {
00139     xcb_query_text_extents_cookie_t cookie;
00140     xcb_query_text_extents_reply_t *reply;
00141     xcb_generic_error_t *error;
00142     int width;
00143 
00144     cookie = xcb_query_text_extents(conn, config.font.id, length, (xcb_char2b_t*)text);
00145     if ((reply = xcb_query_text_extents_reply(conn, cookie, &error)) == NULL) {
00146         ELOG("Could not get text extents (X error code %d)\n",
00147              error->error_code);
00148         /* We return the rather safe guess of 7 pixels, because a
00149          * rendering error is better than a crash. Plus, the user will
00150          * see the error in his log. */
00151         return 7;
00152     }
00153 
00154     width = reply->overall_width;
00155     free(reply);
00156     return width;
00157 }
00158 
00159 /*
00160  * Configures the given window to have the size/position specified by given rect
00161  *
00162  */
00163 void xcb_set_window_rect(xcb_connection_t *conn, xcb_window_t window, Rect r) {
00164     xcb_void_cookie_t cookie;
00165     cookie = xcb_configure_window(conn, window,
00166                          XCB_CONFIG_WINDOW_X |
00167                          XCB_CONFIG_WINDOW_Y |
00168                          XCB_CONFIG_WINDOW_WIDTH |
00169                          XCB_CONFIG_WINDOW_HEIGHT,
00170                          &(r.x));
00171     /* ignore events which are generated because we configured a window */
00172     add_ignore_event(cookie.sequence, -1);
00173 }
00174 
00175 /*
00176  * Returns true if the given reply contains the given atom.
00177  *
00178  */
00179 bool xcb_reply_contains_atom(xcb_get_property_reply_t *prop, xcb_atom_t atom) {
00180     if (prop == NULL || xcb_get_property_value_length(prop) == 0)
00181         return false;
00182 
00183     xcb_atom_t *atoms;
00184     if ((atoms = xcb_get_property_value(prop)) == NULL)
00185         return false;
00186 
00187     for (int i = 0; i < xcb_get_property_value_length(prop) / (prop->format / 8); i++)
00188         if (atoms[i] == atom)
00189             return true;
00190 
00191     return false;
00192 
00193 }
00194 
00199 void xcb_warp_pointer_rect(xcb_connection_t *conn, Rect *rect) {
00200     int mid_x = rect->x + (rect->width / 2);
00201     int mid_y = rect->y + (rect->height / 2);
00202 
00203     LOG("warp pointer to: %d %d\n", mid_x, mid_y);
00204     xcb_warp_pointer(conn, XCB_NONE, root, 0, 0, 0, 0, mid_x, mid_y);
00205 }
00206 
00207 /*
00208  * Set the cursor of the root window to the given cursor id.
00209  * This function should only be used if xcursor_supported == false.
00210  * Otherwise, use xcursor_set_root_cursor().
00211  *
00212  */
00213 void xcb_set_root_cursor(int cursor) {
00214     xcb_cursor_t cursor_id = xcb_generate_id(conn);
00215     i3Font cursor_font = load_font("cursor", false);
00216     int xcb_cursor = xcursor_get_xcb_cursor(cursor);
00217     xcb_create_glyph_cursor(conn, cursor_id, cursor_font.id, cursor_font.id,
00218             xcb_cursor, xcb_cursor + 1, 0, 0, 0, 65535, 65535, 65535);
00219     xcb_change_window_attributes(conn, root, XCB_CW_CURSOR, &cursor_id);
00220     xcb_free_cursor(conn, cursor_id);
00221     xcb_flush(conn);
00222 }