0123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287
|
// Copyright (C) 2024, Jakob Wakeling
// All rights reserved.
#include "../draw.h"
#include <xcb/xcb.h>
#include <xcb/xcb_keysyms.h>
#include <xcb/xproto.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
enum : xcb_atom_t {
ATOM_REPAINT = 0x0f00,
};
struct keymap { xcb_keysym_t keysym; draw_key_k key; };
struct draw_window_s {
xcb_connection_t *connection;
xcb_screen_t *screen;
xcb_window_t window;
xcb_key_symbols_t *symbols;
uint32_t x, y, w, h;
};
extern const struct keymap keymap[];
extern const size_t keymap_size;
static inline draw_mod_k mod(uint16_t state);
static inline draw_key_k keymap_find(draw_window *window, xcb_keycode_t keycode);
int draw_window_init(draw_window **window, int32_t w, int32_t h, const char *title, uint32_t flags) {
(*window) = calloc(1, sizeof(draw_window)); if ((*window) == NULL) { return -1; }
(*window)->connection = xcb_connect(NULL, NULL);
if (xcb_connection_has_error((*window)->connection)) { return -1; }
xcb_void_cookie_t cookie;
xcb_generic_error_t *error = NULL;
(*window)->screen = xcb_setup_roots_iterator(xcb_get_setup((*window)->connection)).data;
(*window)->window = xcb_generate_id((*window)->connection);
if ((*window)->window == -1) { xcb_disconnect((*window)->connection); return -1; }
uint32_t mask = XCB_CW_BACK_PIXMAP | XCB_CW_BORDER_PIXEL | XCB_CW_EVENT_MASK | XCB_CW_COLORMAP;
uint32_t values[4] = {
0, 0,
XCB_EVENT_MASK_KEY_PRESS | XCB_EVENT_MASK_KEY_RELEASE | XCB_EVENT_MASK_BUTTON_PRESS |
XCB_EVENT_MASK_BUTTON_RELEASE | XCB_EVENT_MASK_BUTTON_MOTION | XCB_EVENT_MASK_EXPOSURE |
XCB_EVENT_MASK_STRUCTURE_NOTIFY, XCB_VISUAL_CLASS_TRUE_COLOR
};
/**/
xcb_depth_iterator_t depth_iter = xcb_screen_allowed_depths_iterator((*window)->screen);
xcb_visualtype_t *visual = NULL;
for (; depth_iter.rem; xcb_depth_next(&depth_iter)) {
if (depth_iter.data->depth != 32) continue;
xcb_visualtype_iterator_t visual_iter = xcb_depth_visuals_iterator(depth_iter.data);
for (; visual_iter.rem; xcb_visualtype_next(&visual_iter)) {
visual = visual_iter.data;
break;
}
if (visual != NULL) break;
}
if (visual == NULL) { xcb_disconnect((*window)->connection); return -1; }
xcb_colormap_t colormap = xcb_generate_id((*window)->connection);
cookie = xcb_create_colormap(
(*window)->connection, XCB_COLORMAP_ALLOC_NONE, colormap, (*window)->screen->root, visual->visual_id
);
error = xcb_request_check((*window)->connection, cookie);
if (error != NULL) {
printf("Error %d while creating colormap\n", error->error_code);
free(error); xcb_disconnect((*window)->connection); return -1;
}
values[3] = colormap;
cookie = xcb_create_window_checked(
(*window)->connection, 32, (*window)->window, (*window)->screen->root, 0, 0, w, h, 0,
XCB_WINDOW_CLASS_INPUT_OUTPUT, visual->visual_id, mask, values
);
error = xcb_request_check((*window)->connection, cookie);
if (error != NULL) {
printf("Error %d while creating window\n", error->error_code);
free(error); xcb_disconnect((*window)->connection); return -1;
}
xcb_change_property(
(*window)->connection, XCB_PROP_MODE_REPLACE, (*window)->window, XCB_ATOM_WM_NAME, XCB_ATOM_STRING, 8,
strlen(title), title
);
xcb_change_property(
(*window)->connection, XCB_PROP_MODE_REPLACE, (*window)->window, XCB_ATOM_WM_CLASS, XCB_ATOM_STRING, 8,
strlen(title), title
);
(*window)->symbols = xcb_key_symbols_alloc((*window)->connection);
(*window)->w = w; (*window)->h = h;
xcb_map_window((*window)->connection, (*window)->window);
xcb_flush((*window)->connection);
return 0;
}
void draw_window_free(draw_window **window) {
if (*window == NULL) { return; }
xcb_key_symbols_free((*window)->symbols);
xcb_destroy_window((*window)->connection, (*window)->window);
xcb_disconnect((*window)->connection);
free(*window); *window = NULL;
}
draw_event draw_window_event(draw_window *window) {
xcb_generic_event_t *_e = xcb_wait_for_event(window->connection);
if (_e == NULL) {
if (draw_debug) { printf("xcb_wait_for_event returned NULL\n"); }
return (draw_event){ .kind = DRAW_EVENT_EXIT };
}
draw_event event = {};
/* TODO handle modifiers */
switch (_e->response_type & ~0x80) {
case XCB_KEY_PRESS: {
xcb_key_press_event_t *e = (xcb_key_press_event_t *)_e;
if (draw_debug_verbose) { printf("KeyPress (%d, %d, %d)\n", e->detail, e->event_x, e->event_y); }
event = (draw_event){ .kind = DRAW_EVENT_KEY, .key = {
.code = keymap_find(window, e->detail), .modifiers = mod(e->state), .direction = DRAW_PRESS,
}};
} break;
case XCB_KEY_RELEASE: {
xcb_key_release_event_t *e = (xcb_key_release_event_t *)_e;
if (draw_debug_verbose) { printf("KeyRelease (%d, %d, %d)\n", e->detail, e->event_x, e->event_y); }
event = (draw_event){ .kind = DRAW_EVENT_KEY, .key = {
.code = keymap_find(window, e->detail), .modifiers = mod(e->state), .direction = DRAW_RELEASE,
}};
} break;
case XCB_BUTTON_PRESS: {
xcb_button_press_event_t *e = (xcb_button_press_event_t *)_e;
if (draw_debug_verbose) { printf("ButtonPress (%d, %d, %d)\n", e->detail, e->event_x, e->event_y); }
event = (draw_event){ .kind = DRAW_EVENT_MOUSE, .mouse = {
.x = e->event_x, .y = e->event_y, .button = e->detail, .modifiers = mod(e->state),
.direction = DRAW_PRESS,
}};
} break;
case XCB_BUTTON_RELEASE: {
xcb_button_release_event_t *e = (xcb_button_release_event_t *)_e;
if (draw_debug_verbose) { printf("ButtonRelease (%d, %d, %d)\n", e->detail, e->event_x, e->event_y); }
event = (draw_event){ .kind = DRAW_EVENT_MOUSE, .mouse = {
.x = e->event_x, .y = e->event_y, .button = e->detail, .modifiers = mod(e->state),
.direction = DRAW_RELEASE,
}};
} break;
case XCB_MOTION_NOTIFY: {
xcb_motion_notify_event_t *e = (xcb_motion_notify_event_t *)_e;
if (draw_debug_verbose) { printf("MotionNotify (%d, %d)\n", e->event_x, e->event_y); }
event = (draw_event){ .kind = DRAW_EVENT_MOUSE, .mouse = {
.x = e->event_x, .y = e->event_y, .button = e->detail, .modifiers = mod(e->state),
.direction = DRAW_REPEAT,
}};
} break;
case XCB_EXPOSE: {
xcb_expose_event_t *e = (xcb_expose_event_t *)_e;
if (draw_debug) { printf("Expose (%d, %d, %d, %d)\n", e->x, e->y, e->width, e->height); }
event = (draw_event){ .kind = DRAW_EVENT_PAINT, .paint = { e->x, e->y, e->width, e->height }};
} break;
case XCB_UNMAP_NOTIFY: {
xcb_unmap_notify_event_t *e = (xcb_unmap_notify_event_t *)_e;
if (draw_debug) { printf("UnmapNotify\n"); }
event = (draw_event){ .kind = DRAW_EVENT_HIDE };
} break;
case XCB_MAP_NOTIFY: {
xcb_map_notify_event_t *e = (xcb_map_notify_event_t *)_e;
if (draw_debug) { printf("MapNotify\n"); }
event = (draw_event){ .kind = DRAW_EVENT_SHOW };
} break;
case XCB_CONFIGURE_NOTIFY: {
xcb_configure_notify_event_t *e = (xcb_configure_notify_event_t *)_e;
if (draw_debug) { printf("ConfigureNotify (%d, %d, %d, %d)\n", e->x, e->y, e->width, e->height); }
if (e->width != window->w || e->height != window->h) {
window->w = e->width; window->h = e->height;
event = (draw_event){ .kind = DRAW_EVENT_RESIZE, .resize = { e->width, e->height }};
}
} break;
case XCB_CLIENT_MESSAGE: {
xcb_client_message_event_t *e = (xcb_client_message_event_t *)_e;
switch (e->type) {
case ATOM_REPAINT: {
if (draw_debug) { printf("ClientMessage (REPAINT)\n"); }
event = (draw_event){ .kind = DRAW_EVENT_PAINT, .paint = { 0, 0, window->w, window->h }};
} break;
}
} break;
default: { if (draw_debug) {
printf("Unhandled X11 event: %d ", _e->response_type & ~0x80);
printf("Sequence: %u ", _e->sequence);
printf("Full sequence: %u ", _e->full_sequence);
printf("Pad0: %u ", _e->pad0);
printf("Pad: %u\n", *_e->pad);
}} break;
}
free(_e); return event;
}
void draw_window_set_title(draw_window *window, const char *title) {
xcb_change_property(
window->connection, XCB_PROP_MODE_REPLACE, window->window, XCB_ATOM_WM_NAME, XCB_ATOM_STRING, 8, strlen(title),
title
);
xcb_flush(window->connection);
}
void draw_window_repaint(draw_window *window) {
xcb_client_message_event_t e = {
.response_type = XCB_CLIENT_MESSAGE, .format = 32, .window = window->window, .type = ATOM_REPAINT,
.data.data32 = {}
};
xcb_send_event(window->connection, 0, window->window, XCB_EVENT_MASK_NO_EVENT, (const char *)&e);
xcb_flush(window->connection);
}
int draw_window_draw(draw_window *w, point dp, draw_buffer *buf, rect br) {
if (w == NULL) { return 1; }
xcb_gcontext_t gc = xcb_generate_id(w->connection);
xcb_create_gc(w->connection, gc, w->window, 0, NULL);
xcb_put_image(
w->connection, XCB_IMAGE_FORMAT_Z_PIXMAP, w->window, gc, br.w, br.h, dp.x, dp.y, 0, 32,
(buf->bounds.w * buf->bounds.h) * sizeof (*buf->buf), (uint8_t *)buf->buf
);
xcb_free_gc(w->connection, gc);
xcb_flush(w->connection);
return 0;
}
void draw_window_clear(draw_window *w, uint32_t colour) {
if (w == NULL) { return; }
xcb_gcontext_t gc = xcb_generate_id(w->connection);
xcb_create_gc(w->connection, gc, w->window, 0, NULL);
xcb_change_gc(w->connection, gc, XCB_GC_FOREGROUND, (uint32_t[]){colour});
xcb_poly_fill_rectangle(w->connection, w->window, gc, 1, (xcb_rectangle_t[]){{
.x = 0, .y = 0, .width = w->w, .height = w->h,
}});
xcb_free_gc(w->connection, gc);
xcb_flush(w->connection);
}
static inline draw_mod_k mod(uint16_t state) {
draw_mod_k mod = 0;
mod |= ((state & XCB_MOD_MASK_SHIFT) != 0) * DRAW_MOD_SHIFT;
mod |= ((state & XCB_MOD_MASK_CONTROL) != 0) * DRAW_MOD_CONTROL;
mod |= ((state & XCB_MOD_MASK_1) != 0) * DRAW_MOD_ALT;
mod |= ((state & XCB_MOD_MASK_4) != 0) * DRAW_MOD_SUPER;
mod |= ((state & XCB_MOD_MASK_LOCK) != 0) * DRAW_MOD_CAPS_LOCK;
mod |= ((state & XCB_MOD_MASK_2) != 0) * DRAW_MOD_NUM_LOCK;
return mod;
}
static inline draw_key_k keymap_find(draw_window *window, xcb_keycode_t keycode) {
xcb_keysym_t keysym = xcb_key_symbols_get_keysym(window->symbols, keycode, 0);
for (size_t i = 0; i < keymap_size; i += 1) { if (keymap[i].keysym == keysym) { return keymap[i].key; }}
return DRAW_KEY_UNKNOWN;
}
|