#include #include #include #include #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; }