#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "any_log.h" #include "window.h" static xcb_atom_t intern_atom(window_t *window, const char *atom) { xcb_generic_error_t *error; xcb_intern_atom_cookie_t cookie = xcb_intern_atom(window->display->connection, false, strlen(atom), atom); xcb_intern_atom_reply_t *reply = xcb_intern_atom_reply(window->display->connection, cookie, &error); assert(error == NULL); xcb_atom_t id = reply->atom; free(reply); return id; } static void wm_set_size(window_t *window) { xcb_size_hints_t hints = { 0 }; xcb_icccm_size_hints_set_size(&hints, false, window->width, window->height); xcb_icccm_size_hints_set_min_size(&hints, window->width, window->height); xcb_icccm_size_hints_set_max_size(&hints, window->width, window->height); xcb_icccm_size_hints_set_base_size(&hints, window->width, window->height); xcb_icccm_size_hints_set_position(&hints, false, window->x, window->y); xcb_icccm_set_wm_size_hints(window->display->connection, window->window, XCB_ATOM_WM_NORMAL_HINTS, &hints); log_trace("Xcb icccm size hints updated"); } static void wm_set_struts(window_t *window) { const long end = window->x + window->width - 1; uint32_t values[12] = { 0 }; values[2] = window->height; // top values[8] = window->x; // top y0 values[9] = end > 0 ? end : 0; // top y1 xcb_change_property(window->display->connection, XCB_PROP_MODE_REPLACE, window->window, window->display->ewmh._NET_WM_STRUT, XCB_ATOM_CARDINAL, 32, 4, values); xcb_change_property(window->display->connection, XCB_PROP_MODE_REPLACE, window->window, window->display->ewmh._NET_WM_STRUT_PARTIAL, XCB_ATOM_CARDINAL, 32, 12, values); log_trace("EWMH struts updated"); } static void wm_setup(window_t *window) { const char *title = "comet"; xcb_icccm_set_wm_name(window->display->connection, window->window, XCB_ATOM_STRING, 8, strlen(title), title); const char class[] = "comet\0Comet"; xcb_icccm_set_wm_class(window->display->connection, window->window, strlen(class), class); log_value_debug("Updated window information", "s:title", title, "s:class", "comet\\0Comet"); xcb_ewmh_set_wm_window_type(&window->display->ewmh, window->window, 1, &window->display->ewmh._NET_WM_WINDOW_TYPE_DOCK); xcb_ewmh_set_wm_desktop(&window->display->ewmh, window->window, 0xFFFFFFFF); xcb_ewmh_set_wm_pid(&window->display->ewmh, window->window, getpid()); xcb_atom_t state[] = { window->display->ewmh._NET_WM_STATE_STICKY, window->display->ewmh._NET_WM_STATE_ABOVE }; xcb_ewmh_set_wm_state(&window->display->ewmh, window->window, 2, state); // TODO: These should be updated depdending on the situation! wm_set_size(window); wm_set_struts(window); } void window_init(window_t *window, display_t *display) { memset(window, 0, sizeof(window_t)); window->display = display; // Set mask and params for CreateWindow window->cw_mask = XCB_CW_BACK_PIXMAP | XCB_CW_BACK_PIXEL | XCB_CW_BORDER_PIXEL | XCB_CW_OVERRIDE_REDIRECT | XCB_CW_EVENT_MASK | XCB_CW_COLORMAP; 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_EVENT_MASK_POINTER_MOTION | XCB_EVENT_MASK_POINTER_MOTION_HINT; xcb_colormap_t colormap = xcb_generate_id(display->connection); xcb_create_colormap(display->connection, XCB_COLORMAP_ALLOC_NONE, colormap, display->screen->root, display->visual_type->visual_id); XCB_AUX_ADD_PARAM(&window->cw_mask, &window->cw_params, back_pixmap, XCB_NONE); XCB_AUX_ADD_PARAM(&window->cw_mask, &window->cw_params, back_pixel, 0x00000000); XCB_AUX_ADD_PARAM(&window->cw_mask, &window->cw_params, border_pixel, 0x00000000); XCB_AUX_ADD_PARAM(&window->cw_mask, &window->cw_params, override_redirect, true); XCB_AUX_ADD_PARAM(&window->cw_mask, &window->cw_params, event_mask, event_mask); XCB_AUX_ADD_PARAM(&window->cw_mask, &window->cw_params, colormap, colormap); // Temporary position and size window->width = window->height = 1; window->x = window->y = 0; window->window = xcb_generate_id(display->connection); xcb_aux_create_window(display->connection, display->screen_depth, window->window, display->screen->root, window->x, window->y, window->width, window->height, 0, // border XCB_WINDOW_CLASS_COPY_FROM_PARENT, display->visual_type->visual_id, window->cw_mask, &window->cw_params); log_value_debug("Created window", "u:id", window->window); wm_setup(window); log_trace("Updated window WM options"); xcb_map_window(display->connection, window->window); xcb_flush(display->connection); window->surface = cairo_xcb_surface_create(display->connection, window->window, display->visual_type, display->screen_size->width, display->screen_size->height); assert(cairo_surface_status(window->surface) == CAIRO_STATUS_SUCCESS); log_trace("Created cairo xcb surface"); window->cr = cairo_create(window->surface); assert(cairo_status(window->cr) == CAIRO_STATUS_SUCCESS); log_trace("Created cairo window context"); } void window_move(window_t *window, int x, int y) { if (window->x == x && window->y == y) return; const uint32_t values[] = { x, y }; xcb_configure_window(window->display->connection, window->window, XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y, values); log_value_debug("Updated window position", "i:x", x, "i:y", y); window->x = x; window->y = y; } static void window_reshape(window_t *window) { // TODO: Actually make this a parameter int radius = window->height / 2; double degree = M_PI / 180.0; // TODO: Check this value int depth = 1; xcb_pixmap_t bitmap = xcb_generate_id(window->display->connection); xcb_create_pixmap(window->display->connection, depth, bitmap, window->window, window->width, window->height); log_trace("Created xcb pixmap"); cairo_surface_t *surface = cairo_xcb_surface_create_for_bitmap(window->display->connection, window->display->screen, bitmap, window->width, window->height); cairo_t *cr = cairo_create(surface); // TODO: Fix antialiasing situation //cairo_set_antialias(cr, CAIRO_ANTIALIAS_GOOD); cairo_set_antialias(cr, CAIRO_ANTIALIAS_NONE); cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE); cairo_set_source_rgba(cr, 0, 0, 0, 0); cairo_paint(cr); cairo_set_source_rgba(cr, 1, 1, 1, 1); cairo_arc(cr, radius, radius, radius, 90.0 * degree, 270 * degree); cairo_arc(cr, window->width - radius, radius, radius, 270 * degree, 450 * degree); cairo_fill(cr); cairo_show_page(cr); cairo_destroy(cr); cairo_surface_flush(surface); cairo_surface_destroy(surface); xcb_shape_mask(window->display->connection, XCB_SHAPE_SO_SET, XCB_SHAPE_SK_BOUNDING, window->window, 0, 0, bitmap); xcb_shape_select_input(window->display->connection, window->window, XCB_SHAPE_NOTIFY); log_debug("Updated window shape mask"); xcb_free_pixmap(window->display->connection, bitmap); } void window_resize(window_t *window, int width, int height) { if (window->width == width && window->height == height) return; const uint32_t values[] = { width, height }; xcb_configure_window(window->display->connection, window->window, XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT, values); log_value_debug("Updated window size", "i:width", width, "i:height", height); window->width = width; window->height = height; // Update wm hints wm_set_size(window); // Update shape mask window_reshape(window); } void window_present(window_t *window, cairo_surface_t *surface, int width, int height) { cairo_xcb_surface_set_size(window->surface, width, height); xcb_clear_area(window->display->connection, false, window->window, 0, 0, 0, 0); log_trace("Cleared window area"); cairo_save(window->cr); cairo_set_source_surface(window->cr, surface, 0, 0); cairo_paint(window->cr); cairo_show_page(window->cr); cairo_surface_flush(window->surface); cairo_restore(window->cr); log_trace("Flushed window surface"); xcb_circulate_window(window->display->connection, XCB_CIRCULATE_RAISE_LOWEST, window->window); xcb_flush(window->display->connection); } void window_close(window_t *window) { cairo_destroy(window->cr); cairo_surface_destroy(window->surface); }