Hotpot-proto/btk/btk.c
2022-07-06 22:10:46 -04:00

360 lines
9.4 KiB
C

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include "btk.h"
#include "btk-log.h"
void event_button_press (btk_session_t *, xcb_button_press_event_t *);
void event_button_release (btk_session_t *, xcb_button_release_event_t *);
void event_client_message (btk_session_t *, xcb_client_message_event_t *);
void event_enter_window (btk_session_t *, xcb_enter_notify_event_t *);
void event_expose (btk_session_t *, xcb_expose_event_t *);
void event_key_press (btk_session_t *, xcb_key_press_event_t *);
void event_leave_window (btk_session_t *, xcb_leave_notify_event_t *);
void event_pointer_motion (btk_session_t *, xcb_motion_notify_event_t *);
int get_btk_window_id (btk_session_t *, xcb_window_t);
void
btk_close(btk_session_t *s)
{
for (int i = 0; i < s->windows_n; i++)
if (!s->windows[i])
btk_window_destroy(s->windows[i]);
xcb_disconnect(s->x_con);
btk_log(BTK_LOG_SESSION_CLOSE);
}
void
btk_loop(btk_session_t *s, btk_window_t **w, int windows_n)
{
if (!w || !windows_n) {
btk_log_warning(BTK_WARNING_EMPTY_LOOP);
btk_log(BTK_LOG_EVENT_LOOP_OUT);
return;
}
s->windows = w;
s->windows_n = windows_n;
s->window_focus = 0;
s->window_press = -1;
/* set initial cell geometry and fonts */
for (int i = 0; i < windows_n; i++) {
btk_cell_setup_font(w[i]->c_ctx);
btk_window_init_cells(w[i]);
}
/* actual event loop */
btk_log(BTK_LOG_EVENT_LOOP_IN);
xcb_generic_event_t *x_ev;
while ((x_ev = xcb_wait_for_event(s->x_con))) {
switch (x_ev->response_type & ~0x80) {
case XCB_EXPOSE:
event_expose(s, (xcb_expose_event_t*)x_ev);
break;
case XCB_MOTION_NOTIFY:
event_pointer_motion(s, (xcb_motion_notify_event_t*)x_ev);
break;
case XCB_BUTTON_PRESS:
event_button_press(s, (xcb_button_press_event_t*)x_ev);
break;
case XCB_BUTTON_RELEASE:
event_button_release(s, (xcb_button_release_event_t*)x_ev);
break;
case XCB_KEY_PRESS:
event_key_press(s, (xcb_key_press_event_t*)x_ev);
break;
case XCB_ENTER_NOTIFY:
event_enter_window(s, (xcb_enter_notify_event_t*)x_ev);
break;
case XCB_LEAVE_NOTIFY:
event_leave_window(s, (xcb_leave_notify_event_t*)x_ev);
break;
case XCB_CLIENT_MESSAGE:
event_client_message(s, (xcb_client_message_event_t*)x_ev);
break;
default:
btk_log(BTK_LOG_GENERIC_EVENT_NOTIFY);
break;
}
free(x_ev);
xcb_flush(s->x_con);
}
btk_log(BTK_LOG_EVENT_LOOP_OUT);
}
void
btk_map(btk_session_t *s, btk_window_t *w)
{
w->state |= BTK_WINDOW_STATE_MAPPED;
xcb_map_window(s->x_con, w->x_win);
xcb_flush(s->x_con);
btk_log(BTK_LOG_WINDOW_MAP);
}
btk_session_t*
btk_open()
{
btk_session_t *s = (btk_session_t*)malloc(sizeof(btk_session_t));
/* base X stuff */
s->x_con = xcb_connect(NULL, NULL);
if (!s->x_con)
btk_log_error(BTK_ERROR_X_CONNECTION);
s->x_scr = xcb_setup_roots_iterator(xcb_get_setup(s->x_con)).data;
if (!s->x_scr)
btk_log_error(BTK_ERROR_X_SCREEN);
/* setup x visual type for cairo surfaces */
/* magic function from documentation */
xcb_depth_iterator_t x_di = xcb_screen_allowed_depths_iterator(s->x_scr);
for (; x_di.rem; xcb_depth_next(&x_di)) {
xcb_visualtype_iterator_t x_vi;
x_vi = xcb_depth_visuals_iterator(x_di.data);
for (; x_vi.rem; xcb_visualtype_next(&x_vi)) {
if (s->x_scr->root_visual == x_vi.data->visual_id) {
s->x_vis = x_vi.data;
break;
}
}
}
if (!s->x_vis)
btk_log_error(BTK_ERROR_X_VISUAL);
/* setup keysym table for keyboard input */
const xcb_setup_t *x_stp = xcb_get_setup(s->x_con);
xcb_get_keyboard_mapping_reply_t* x_km =
xcb_get_keyboard_mapping_reply(
s->x_con,
xcb_get_keyboard_mapping(
s->x_con,
x_stp->min_keycode,
x_stp->max_keycode - x_stp->min_keycode + 1),
NULL);
if (!x_km)
btk_log_error(BTK_ERROR_X_KEYSYMS);
xcb_keysym_t *x_ks = (xcb_keysym_t*)(x_km + 1);
s->x_nks = x_km->keysyms_per_keycode;
s->x_nkc = x_km->length / s->x_nks;
/* copy x_ks into s->x_ksm because x_ks breaks due to cairo for some reason */
s->x_ksm = (xcb_keysym_t*)malloc(sizeof(xcb_keysym_t) * s->x_nkc * s->x_nks);
for (int i = 0; i < s->x_nkc * s->x_nks; i++) {
s->x_ksm[i] = x_ks[i];
}
free(x_km);
xcb_flush(s->x_con);
btk_log(BTK_LOG_SESSION_OPEN);
return s;
}
void
btk_unmap(btk_session_t *s, btk_window_t *w)
{
w->state &= ~BTK_WINDOW_STATE_MAPPED;
xcb_unmap_window(s->x_con, w->x_win);
xcb_flush(s->x_con);
btk_log(BTK_LOG_WINDOW_UNMAP);
/* undo all and focus press states if any */
if (w->cell_press >= 0) {
w->cells[w->cell_press].state &= ~BTK_CELL_STATE_PRESSED;
w->cell_press = -1;
}
if (w->cell_focus >= 0) {
w->cells[w->cell_focus].state &= ~BTK_CELL_STATE_FOCUSED;
w->cell_focus = -1;
}
}
void
event_button_press(btk_session_t *s, xcb_button_press_event_t *x_ev)
{
btk_log(BTK_LOG_EVENT_BUTTON_PRESS);
if (x_ev->time - s->combo_t < btk_click_speed && x_ev->detail == s->combo_b) {
s->combo_n++;
btk_log(BTK_LOG_EVENT_BUTTON_COMBO);
} else {
s->combo_n = 1;
s->combo_b = x_ev->detail;
btk_log(BTK_LOG_EVENT_BUTTON_COMBO_BREAK);
}
s->combo_t = x_ev->time;
/* only applies for left click */
if (s->combo_b == 1)
s->window_press = s->window_focus;
btk_window_input_button(s->windows[s->window_focus], s->combo_b, s->combo_n);
}
void
event_button_release(btk_session_t *s, xcb_button_release_event_t *x_ev)
{
btk_log(BTK_LOG_EVENT_BUTTON_RELEASE);
/* only applies for left-middle-right click */
if (x_ev->detail > 1)
return;
/* undo all press states */
btk_window_t *w = s->windows[s->window_press];
s->window_press = -1;
if (w->cell_press < 0)
return;
btk_cell_t *c = &(w->cells[w->cell_press]);
c->state &= ~BTK_CELL_STATE_PRESSED;
btk_window_redraw_cell(w, w->cell_press);
w->cell_press = -1;
}
void
event_client_message(btk_session_t *s, xcb_client_message_event_t *x_ev)
{
btk_log(BTK_LOG_EVENT_CLIENT_MESSAGE);
btk_window_t *w = s->windows[get_btk_window_id(s, x_ev->window)];
/* when receiving a WM_DELETE_WINDOW atom, execute func_kill of the
* corresponding window, it func_kill = NULL, destroy window */
if (x_ev->type && 384) { /* 384 = id of atom for WM_WINDOW_DELETE */
if (w->func_kill) {
w->func_kill();
} else {
btk_window_destroy(w);
}
}
/* if anyone ever reads this, I know that it is a wrong method,
* that I first need to check to an WM_PROTOCOL atom then for
* WM_DELETE_WINDOW one */
}
void
event_enter_window(btk_session_t *s, xcb_enter_notify_event_t *x_ev)
{
btk_log(BTK_LOG_EVENT_WINDOW_ENTER);
int w_id = get_btk_window_id(s, x_ev->event);
s->window_focus = w_id;
s->windows[w_id]->state |= BTK_WINDOW_STATE_FOCUSED;
}
void
event_expose(btk_session_t *s, xcb_expose_event_t *x_ev)
{
btk_log(BTK_LOG_EVENT_EXPOSE);
btk_window_t *w = s->windows[get_btk_window_id(s, x_ev->window)];
/* c->fd need to be created when window is mapped to be able to display stuff */
/* TODO better way to do the first field creations */
if (!s->start) {
btk_window_init_cells(w);
s->start = 1;
}
btk_window_resize(w);
btk_window_redraw(w);
}
void
event_key_press(btk_session_t *s, xcb_key_press_event_t *x_ev)
{
/* XCB_MOD_MASK_SHIFT (1) : shift
* XCB_MOD_MASK_LOCK (2) : caps
* XCB_MOD_MASK_CONTROL (4) : ctrl
* XCB_MOD_MASK_1 (8) : alt
* XCB_MOD_MASK_2 (16) : numlock
* XCB_MOD_MASK_3 (32) : ???
* XCB_MOD_MASK_4 (64) : super
* XCB_MOD_MASK_5 (128) : alt-gr */
btk_log(BTK_LOG_EVENT_KEY_PRESS);
unsigned int keysym_mod = 0;
if (((x_ev->state & XCB_MOD_MASK_LOCK) >> 1) ^ (x_ev->state & XCB_MOD_MASK_SHIFT))
keysym_mod = 1;
if ((x_ev->state & XCB_MOD_MASK_5))
keysym_mod = 4;
unsigned int keysym = s->x_ksm[(x_ev->detail - 8) * s->x_nks + keysym_mod];
/* convert utf-8 numpad chars to equivalent ascii */
if ((x_ev->state & XCB_MOD_MASK_2)) {
switch (keysym) {
case 0xffaf: keysym = '/'; break;
case 0xffaa: keysym = '*'; break;
case 0xffad: keysym = '-'; break;
case 0xff95: keysym = '7'; break;
case 0xff97: keysym = '8'; break;
case 0xff9a: keysym = '9'; break;
case 0xff96: keysym = '4'; break;
case 0xff9d: keysym = '5'; break;
case 0xff98: keysym = '6'; break;
case 0xff9c: keysym = '1'; break;
case 0xff99: keysym = '2'; break;
case 0xff9b: keysym = '3'; break;
case 0xff9e: keysym = '0'; break;
case 0xff9f: keysym = '.'; break;
case 0xffab: keysym = '+'; break;
default: break;
}
}
/* custom keysym conversions for reasons */
switch (keysym) {
case 0x00a7: keysym = 0x0000; break;
default: break;
}
/* special cases that are keycode dependent only */
switch (x_ev->detail) {
case 65: keysym = ' '; break;
default: break;
}
btk_window_input_key(s->windows[s->window_focus], keysym);
}
void
event_leave_window(btk_session_t *s, xcb_leave_notify_event_t *x_ev)
{
btk_log(BTK_LOG_EVENT_WINDOW_LEAVE);
/* remove all focuses */
/* TODO
btk_window_t *w = s->windows[get_btk_window_id(s, x_ev->event)];
if (w->cell_focus >= 0) {
btk_cell_t *c = &(w->cells[w->cell_focus]);
c->state &= ~(BTK_CELL_STATE_FOCUSED);
btk_window_redraw_cell_focus(w, c);
}
w->state &= ~(BTK_WINDOW_STATE_FOCUSED);
w->cell_focus = -1;
*/
}
void
event_pointer_motion(btk_session_t *s, xcb_motion_notify_event_t *x_ev)
{
btk_log(BTK_LOG_EVENT_POINTER_MOTION);
btk_pos_t pt;
pt.x = x_ev->event_x;
pt.y = x_ev->event_y;
btk_window_input_pointer(s->windows[s->window_focus], pt);
}
int
get_btk_window_id(btk_session_t *s, xcb_window_t x_win)
{
for (int i = 0; i < s->windows_n; i++) {
if (s->windows[i]->x_win == x_win)
return i;
}
btk_log_warning(BTK_WARNING_WINDOW_GET);
return -1;
}