aboutsummaryrefslogtreecommitdiff
path: root/src/window.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/window.c')
-rw-r--r--src/window.c296
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);
+}