#include #include #include #include #include #include #include #include #include #include #include #include #include "x11.h" #include "log.h" struct Window { xcb_screen_t *screen; xcb_connection_t *connection; xcb_xrm_database_t *database; xcb_drawable_t window; xcb_visualtype_t *visual_type; cairo_surface_t *surface; cairo_t *cr; double dpi; }; Window *window_create(void) { Window *win = g_malloc0(sizeof(Window)); int preferred_screen = 0; win->connection = xcb_connect(NULL, &preferred_screen); assert(win->connection != NULL && !xcb_connection_has_error(win->connection)); log_debug("Xcb established connection"); xcb_randr_query_version_cookie_t version_cookie = xcb_randr_query_version(win->connection, XCB_RANDR_MAJOR_VERSION, XCB_RANDR_MINOR_VERSION); xcb_generic_error_t *error; xcb_randr_query_version_reply_t *randr_version = xcb_randr_query_version_reply(win->connection, version_cookie, &error); log_debug("RandR version %d.%d", randr_version->major_version, randr_version->minor_version); assert(error == NULL || randr_version->major_version < 1); xcb_randr_select_input(win->connection, win->window, XCB_RANDR_NOTIFY_MASK_SCREEN_CHANGE | XCB_RANDR_NOTIFY_MASK_OUTPUT_CHANGE | XCB_RANDR_NOTIFY_MASK_OUTPUT_PROPERTY); win->database = xcb_xrm_database_from_default(win->connection); assert(win->database != NULL); char *dpi_value; if (xcb_xrm_resource_get_string(win->database, "Xft.dpi", "Xft.dpi", &dpi_value) >= 0) { log_debug("Xrm query '%s' found '%s'", "Xft.dpi", dpi_value); win->dpi = strtod(dpi_value, NULL); g_free(dpi_value); } else { log_debug("Xrm query '%s' not found", "Xft.dpi"); // TODO: Calculate automatically somehow win->dpi = 96.0; } xcb_screen_iterator_t iter = xcb_setup_roots_iterator(xcb_get_setup(win->connection)); log_debug("Default screen %d", preferred_screen); while (preferred_screen != 0 && iter.rem) { xcb_screen_next(&iter); preferred_screen--; } win->screen = iter.data; xcb_depth_iterator_t depth_iter = xcb_screen_allowed_depths_iterator(win->screen); while (depth_iter.rem) { xcb_visualtype_iterator_t visual_iter = xcb_depth_visuals_iterator(depth_iter.data); while (visual_iter.rem) { if (win->screen->root_visual == visual_iter.data->visual_id) { win->visual_type = visual_iter.data; goto found_visual; } xcb_visualtype_next(&visual_iter); } xcb_depth_next(&depth_iter); } found_visual: log_debug("Xcb visual type found (id %u)", win->visual_type->visual_id); const uint32_t value_mask = XCB_CW_BACK_PIXMAP | XCB_CW_BACK_PIXEL | XCB_CW_BORDER_PIXEL | XCB_CW_OVERRIDE_REDIRECT | XCB_CW_EVENT_MASK | XCB_CW_COLORMAP; const uint32_t event_mask = XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_KEY_PRESS | XCB_EVENT_MASK_FOCUS_CHANGE | XCB_EVENT_MASK_BUTTON_RELEASE | XCB_EVENT_MASK_BUTTON_PRESS; xcb_colormap_t colormap = xcb_generate_id(win->connection); xcb_create_colormap(win->connection, XCB_COLORMAP_ALLOC_NONE, colormap, win->screen->root, win->visual_type->visual_id); log_debug("X11 colormap created (id %u)", colormap); const uint32_t value_list[] = { XCB_NONE, // back pixmap 0x000000, // back pixel 0x000000, // border pixel true, // override redirect event_mask, // event mask colormap // colormap }; int y = 10, x = 10, width = 1000, height = 100; win->window = xcb_generate_id(win->connection); xcb_create_window(win->connection, XCB_COPY_FROM_PARENT, win->window, win->screen->root, y, x, width, height, 0, XCB_WINDOW_CLASS_INPUT_OUTPUT, win->screen->root_visual, value_mask, value_list); log_debug("X11 window created (id %u)", win->window); xcb_icccm_set_wm_name(win->connection, win->window, XCB_ATOM_STRING, 8, strlen("comet"), "comet"); xcb_map_window(win->connection, win->window); xcb_flush(win->connection); log_debug("Xcb initialized"); win->surface = cairo_xcb_surface_create(win->connection, win->window, win->visual_type, width, height); assert(cairo_surface_status(win->surface) == CAIRO_STATUS_SUCCESS); win->cr = cairo_create(win->surface); assert(cairo_status(win->cr) == CAIRO_STATUS_SUCCESS); log_debug("Cairo initialized"); return win; } cairo_t *window_get_context(Window *win) { return win->cr; } double window_get_scale(Window *win) { return MAX(1, win->dpi/96.); } void window_paint_surface(Window *win, cairo_surface_t *surface, int width, int height) { double scale = window_get_scale(win); cairo_xcb_surface_set_size(win->surface, round(width * scale), round(height * scale)); xcb_clear_area(win->connection, false, win->window, 0, 0, 0, 0); cairo_set_source_surface(win->cr, surface, 0, 0); cairo_paint(win->cr); cairo_show_page(win->cr); xcb_flush(win->connection); } void window_destroy(Window *win) { cairo_destroy(win->cr); cairo_surface_destroy(win->surface); xcb_xrm_database_free(win->database); xcb_disconnect(win->connection); g_free(win); } // vim: ts=4 sw=4 et