diff options
| author | Federico Angelilli <code@fedang.net> | 2024-07-08 16:41:06 +0200 |
|---|---|---|
| committer | Federico Angelilli <code@fedang.net> | 2024-07-08 16:41:06 +0200 |
| commit | 8e392c583c7c0b68bae6204bf98e7f8e077c5bbf (patch) | |
| tree | eec9e15918194a6f4bca9ce4de479783c0ddbada /src/window.c | |
| parent | 5d170a634ead0119f6e5a9f63c23b2b064126f75 (diff) | |
Rewrite display code
Diffstat (limited to 'src/window.c')
| -rw-r--r-- | src/window.c | 296 |
1 files changed, 296 insertions, 0 deletions
diff --git a/src/window.c b/src/window.c new file mode 100644 index 0000000..e45eb27 --- /dev/null +++ b/src/window.c @@ -0,0 +1,296 @@ +#include <string.h> +#include <math.h> +#include <assert.h> +#include <unistd.h> + +#include <cairo.h> +#include <cairo-xcb.h> +#include <xcb/xcb.h> +#include <xcb/xproto.h> +#include <xcb/xcb_aux.h> +#include <xcb/xcb_icccm.h> +#include <xcb/xcb_ewmh.h> +#include <xcb/xcb_xrm.h> +#include <xcb/xcb_errors.h> +#include <xcb/randr.h> +#include <xcb/shape.h> + +#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_debug("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_debug("Xcb 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_generic_error_t *error; + xcb_intern_atom_cookie_t *ewmh_cookie = xcb_ewmh_init_atoms(window->display->connection, &window->display->ewmh); + + assert(xcb_ewmh_init_atoms_replies(&window->display->ewmh, ewmh_cookie, &error)); + assert(error == NULL); + log_debug("Xcb EWMH connected"); + + 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("Xcb window created", + "u:id", window->window); + + wm_setup(window); + log_debug("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("Cairo surface created"); + + window->cr = cairo_create(window->surface); + assert(cairo_status(window->cr) == CAIRO_STATUS_SUCCESS); + log_trace("Cairo context created"); +} + +//double window_get_scale(window_t *window) +//{ +// const int n = 4; +// return MAX(1, floor((window->display->screen_dpi / 96.0) * n) * (1.0 / n)); +//} + +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_debug("Xcb pixmap created [id=%u]", bitmap); + + 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); + + log_trace("Xcb shape painted"); + + 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("Xcb shape mask updated"); + + 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); + + 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); + + 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); +} |
