xcb_impl.c (42632B)
1 ////////////////////////////////////////////////////////////////// 2 // xcb_impl.c 3 // 4 // on linux, requires 5 // -lxcb -lxcb-xkb 6 // and for opengl: 7 // -lEGL -lGL 8 9 #include <xcb/xcb.h> 10 #include <xcb/xkb.h> 11 12 #include "../../external/xkb-keysym-utf.c" 13 14 #define RV_XKB_DEBUG 0 15 16 17 ////////////////////////////////////////////////////////////////// 18 // Window 19 20 struct rv_window_handle_t { 21 rv_window_handle_t* next; 22 xcb_window_t window_id; 23 rv_vec2 last_size; 24 xcb_intern_atom_reply_t* close_reply; 25 void* render; 26 bool32 sent_initial_events; 27 28 bool32 vsync; 29 }; 30 31 ////////////////////////////////////////////////////////////////// 32 // Context 33 34 #define KEYSYM_LOOKUP_SIZE 6 35 36 RV_INTERNAL struct { 37 bool32 is_init; 38 xcb_connection_t* conn; 39 40 rv_window_handle_t* window_list; 41 42 const xcb_query_extension_reply_t *xkb_extension_reply; 43 44 struct { 45 xcb_keysym_t keysyms[KEYSYM_LOOKUP_SIZE]; 46 s32 num_groups; 47 u8 kt_index[4]; 48 } keycode_converter_list[256]; 49 50 u8 mods; 51 u8 group; 52 53 struct { 54 u32 mask; 55 struct { 56 bool32 active; 57 u32 mask; 58 s32 level; 59 } entries[64]; 60 s32 entries_size; 61 } type_map[64]; 62 s32 type_map_size; 63 } xcb_ctx; 64 65 #define xcb_maybe_window(w) ((w) ? (w) : (xcb_ctx.window_list)) 66 67 68 ////////////////////////////////////////////////////////////////// 69 // EGL 70 71 #if RV_RENDER_OPENGL 72 73 #include <EGL/egl.h> 74 #include <EGL/eglext.h> 75 76 RV_INTERNAL struct { 77 bool32 is_init; 78 EGLContext* egl_context; 79 EGLDisplay egl_display; 80 EGLConfig config; 81 } egl_context; 82 83 84 RV_GLOBAL void* egl_attach(xcb_window_t window, xcb_connection_t* connection, bool32 vsync) 85 { 86 { // make egl context 87 if (!egl_context.is_init) { 88 egl_context.is_init = true; 89 90 { // egl_display 91 PFNEGLGETPLATFORMDISPLAYEXTPROC GetPlatformDisplayEXT = (PFNEGLGETPLATFORMDISPLAYEXTPROC)eglGetProcAddress("eglGetPlatformDisplayEXT"); 92 // initialize EGL 93 { 94 egl_context.egl_display = GetPlatformDisplayEXT(EGL_PLATFORM_XCB_EXT, connection, 95 (const EGLint[]){ 96 EGL_PLATFORM_XCB_SCREEN_EXT, 97 0, // This is a screenp that you can get from 2nd argument of xcb_connect 98 EGL_NONE, 99 }); 100 rv_assert(egl_context.egl_display != EGL_NO_DISPLAY); 101 102 EGLint major, minor; 103 if (!eglInitialize(egl_context.egl_display, &major, &minor)) 104 { 105 rv_abort_msg(1, "Cannot initialize EGL display"); 106 } 107 if (major < 1 || (major == 1 && minor < 5)) 108 { 109 rv_abort_msg(1, "EGL version 1.5 or higher required"); 110 } 111 } 112 } 113 114 // choose OpenGL API for EGL, by default it uses OpenGL ES 115 EGLBoolean ok = eglBindAPI(EGL_OPENGL_API); 116 rv_assert(ok); 117 118 { // egl_context 119 // choose EGL configuration 120 { 121 EGLint attr[] = 122 { 123 EGL_SURFACE_TYPE, EGL_WINDOW_BIT, 124 EGL_CONFORMANT, EGL_OPENGL_BIT, 125 EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT, 126 EGL_COLOR_BUFFER_TYPE, EGL_RGB_BUFFER, 127 128 EGL_RED_SIZE, 8, 129 EGL_GREEN_SIZE, 8, 130 EGL_BLUE_SIZE, 8, 131 EGL_DEPTH_SIZE, 24, 132 EGL_STENCIL_SIZE, 8, 133 134 // uncomment for multisampled framebuffer 135 //EGL_SAMPLE_BUFFERS, 1, 136 //EGL_SAMPLES, 4, // 4x MSAA 137 138 EGL_NONE, 139 }; 140 141 EGLint count; 142 if (!eglChooseConfig(egl_context.egl_display, attr, &egl_context.config, 1, &count) || count != 1) 143 { 144 rv_abort_msg(1, "Cannot choose EGL config"); 145 } 146 } 147 148 { 149 EGLint attr[] = 150 { 151 EGL_CONTEXT_MAJOR_VERSION, 3, 152 EGL_CONTEXT_MINOR_VERSION, 3, 153 EGL_CONTEXT_OPENGL_PROFILE_MASK, EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT, 154 // ask for debug context for non "Release" builds 155 // this is so we can enable debug callback 156 EGL_CONTEXT_OPENGL_DEBUG, EGL_TRUE, 157 EGL_NONE, 158 }; 159 160 egl_context.egl_context = eglCreateContext(egl_context.egl_display, egl_context.config, EGL_NO_CONTEXT, attr); 161 if (egl_context.egl_context == EGL_NO_CONTEXT) 162 { 163 rv_abort_msg(1, "Cannot create EGL context, OpenGL 4.5 not supported?"); 164 } 165 } 166 } 167 } 168 } 169 170 171 EGLSurface* surface; 172 { // surface 173 EGLint attr[] = { 174 EGL_GL_COLORSPACE, EGL_GL_COLORSPACE_LINEAR, // or use EGL_GL_COLORSPACE_SRGB for sRGB framebuffer 175 EGL_RENDER_BUFFER, EGL_BACK_BUFFER, 176 EGL_NONE, 177 }; 178 179 surface = eglCreateWindowSurface(egl_context.egl_display, egl_context.config, window, attr); 180 if (surface == EGL_NO_SURFACE) { 181 rv_abort_msg(1, "Cannot create EGL surface"); 182 } 183 } 184 185 eglMakeCurrent(egl_context.egl_display, surface, surface, egl_context.egl_context); 186 187 if (vsync) 188 eglSwapInterval(egl_context.egl_display, 1); 189 else 190 eglSwapInterval(egl_context.egl_display, 0); 191 192 { // load opengl functions 193 RV_LOCAL_PERSIST bool32 glad_loaded = false; 194 if (!glad_loaded) { 195 glad_loaded = true; 196 gladLoadGLLoader((GLADloadproc)eglGetProcAddress); 197 } 198 } 199 200 return surface; 201 } 202 203 RV_GLOBAL void rv_window_render_begin(rv_window_handle_t* window) 204 { 205 rv_window_handle_t* w = xcb_maybe_window(window); 206 if (w) { 207 EGLSurface* surface = w->render; 208 eglMakeCurrent(egl_context.egl_display, surface, surface, egl_context.egl_context); 209 } 210 } 211 212 RV_GLOBAL void rv_window_render_end(rv_window_handle_t* window) 213 { 214 rv_window_handle_t* w = xcb_maybe_window(window); 215 if (w) { 216 EGLSurface* surface = w->render; 217 eglSwapBuffers(egl_context.egl_display, surface); 218 } 219 xcb_flush(xcb_ctx.conn); 220 } 221 222 #else // RV_RENDER_OPENGL 223 224 RV_GLOBAL void rv_window_render_begin(rv_window_handle_t* window) 225 { 226 227 } 228 229 RV_GLOBAL void rv_window_render_end(rv_window_handle_t* window) 230 { 231 232 } 233 234 #endif // !RV_RENDER_OPENGL 235 236 237 ////////////////////////////////////////////////////////////////// 238 // Context Creation 239 240 u32 mods_from_real_virtual(u32 rmod, u32 vmod_low, u32 vmod_high) { 241 return (rmod) | (vmod_low << 8) | (vmod_high << 16); 242 } 243 244 #define XKB_COMPONENTS_MASK \ 245 (XCB_XKB_MAP_PART_KEY_TYPES | \ 246 XCB_XKB_MAP_PART_KEY_SYMS ) 247 248 RV_INTERNAL void make_xcb_context(void) { 249 if (xcb_ctx.is_init) return; 250 xcb_ctx.is_init = true; 251 252 xcb_ctx.conn = xcb_connect(NULL, NULL); 253 254 { // xkb 255 256 // NOTE(Samdal): 257 // we only set up the keyboard map once 258 // this does not support changing the keyboard layout during runtime... 259 // do we care? 260 261 // NOTE(Samdal): 262 // Sources 263 // https://gist.github.com/bluetech/6038239 264 // https://www.cl.cam.ac.uk/~mgk25/ucs/keysymdef.h 265 // https://www.x.org/docs/XKB/XKBlib.pdf 266 // https://tronche.com/gui/x/xlib/ 267 268 { // set up xkb extension 269 { 270 const xcb_query_extension_reply_t *reply; 271 272 xcb_ctx.xkb_extension_reply = xcb_get_extension_data(xcb_ctx.conn, &xcb_xkb_id); 273 if (!xcb_ctx.xkb_extension_reply) 274 rv_abort_msg(1, "no XKB in X server"); 275 } 276 277 { 278 xcb_xkb_use_extension_cookie_t cookie; 279 xcb_xkb_use_extension_reply_t *reply = NULL; 280 281 cookie = xcb_xkb_use_extension(xcb_ctx.conn, 282 XCB_XKB_MAJOR_VERSION, 283 XCB_XKB_MINOR_VERSION); 284 reply = xcb_xkb_use_extension_reply(xcb_ctx.conn, cookie, NULL); 285 if (!reply) 286 rv_abort_msg(1, "couldn't use XKB extension"); 287 288 if (!reply->supported) 289 rv_abort_msg(1, "the XKB extension is not supported in X server"); 290 } 291 } 292 293 294 xcb_xkb_get_map_reply_t *reply = NULL; 295 296 { 297 xcb_xkb_get_map_cookie_t cookie; 298 xcb_generic_error_t *error = NULL; 299 300 // Send the XkbGetMap request. 301 cookie = xcb_xkb_get_map(xcb_ctx.conn, 302 XCB_XKB_ID_USE_CORE_KBD, 303 XKB_COMPONENTS_MASK, 304 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); 305 reply = xcb_xkb_get_map_reply(xcb_ctx.conn, cookie, &error); 306 if (!reply || error) 307 rv_abort_msg(1, "couldn't get get_map reply: error_code %d", error->error_code); 308 309 if ((reply->present & XKB_COMPONENTS_MASK) != XKB_COMPONENTS_MASK) 310 rv_abort_msg(1, "didn't get all components"); 311 312 free(error); 313 } 314 315 316 // Get the map itself, with all the details. 317 xcb_xkb_get_map_map_t map; 318 { 319 void *buffer; 320 321 buffer = xcb_xkb_get_map_map(reply); 322 xcb_xkb_get_map_map_unpack(buffer, 323 reply->nTypes, 324 reply->nKeySyms, 325 reply->nKeyActions, 326 reply->totalActions, 327 reply->totalKeyBehaviors, 328 reply->nVModMapKeys, 329 reply->totalKeyExplicit, 330 reply->totalModMapKeys, 331 reply->totalVModMapKeys, 332 reply->present, 333 &map); 334 } 335 336 rv_mem_zero(xcb_ctx.keycode_converter_list, sizeof(xcb_ctx.keycode_converter_list)); 337 338 // Get types 339 { 340 s32 length = xcb_xkb_get_map_map_types_rtrn_length(reply, &map); 341 342 rv_assert(length <= rv_array_size(xcb_ctx.type_map)); 343 length = rv_min(length, rv_array_size(xcb_ctx.type_map)); 344 xcb_ctx.type_map_size = length; 345 346 xcb_xkb_key_type_iterator_t iter = xcb_xkb_get_map_map_types_rtrn_iterator(reply, &map); 347 348 for (int i = 0; i < length; i++) { 349 xcb_xkb_key_type_t *type = iter.data; 350 351 xcb_ctx.type_map[i].mask = type->mods_mask; 352 #if RV_XKB_DEBUG 353 printf("type (%d):\n", i); 354 #endif 355 356 { 357 s32 length2 = xcb_xkb_key_type_map_length(type); 358 359 rv_assert(length2 <= rv_array_size(xcb_ctx.type_map[i].entries)); 360 length2 = rv_min(length2, rv_array_size(xcb_ctx.type_map[i].entries)); 361 xcb_ctx.type_map[i].entries_size = length2; 362 363 xcb_xkb_kt_map_entry_iterator_t iter2 = xcb_xkb_key_type_map_iterator(type); 364 365 for (int j = 0; j < length2; j++) { 366 xcb_xkb_kt_map_entry_t *entry = iter2.data; 367 368 369 #if RV_XKB_DEBUG 370 printf("\tentry(%d) (active: %d | mask: %d | lvl: %d):\n", j, 371 entry->active, 372 entry->mods_mask, 373 entry->level 374 ); 375 #endif 376 xcb_ctx.type_map[i].entries[j].active = entry->active; 377 xcb_ctx.type_map[i].entries[j].mask = entry->mods_mask; 378 xcb_ctx.type_map[i].entries[j].level = entry->level; 379 380 xcb_xkb_kt_map_entry_next(&iter2); 381 } 382 } 383 384 xcb_xkb_key_type_next(&iter); 385 } 386 } 387 388 { // map keysyms 389 int length; 390 xcb_xkb_key_sym_map_iterator_t iter; 391 392 length = xcb_xkb_get_map_map_syms_rtrn_length(reply, &map); 393 iter = xcb_xkb_get_map_map_syms_rtrn_iterator(reply, &map); 394 395 rv_assert(length <= 256); 396 length = rv_min(length, 256); 397 for (int i = 0; i < length; i++) { 398 xcb_xkb_key_sym_map_t *sym_map = iter.data; 399 400 s32 num_groups = sym_map->groupInfo; 401 xcb_ctx.keycode_converter_list[i + reply->firstKeySym].num_groups = num_groups; 402 for (s32 g = 0; g < rv_min(4, num_groups); g++) { 403 xcb_ctx.keycode_converter_list[i + reply->firstKeySym].kt_index[g] = sym_map->kt_index[g]; 404 } 405 406 #if RV_XKB_DEBUG 407 printf("ksyms(%d) [%d]=>[%d, %d, %d, %d]:\n", i, sym_map->groupInfo, 408 sym_map->kt_index[0], 409 sym_map->kt_index[1], 410 sym_map->kt_index[2], 411 sym_map->kt_index[3] 412 ); 413 #endif 414 415 int length2; 416 xcb_keysym_t *syms; 417 418 length2 = xcb_xkb_key_sym_map_syms_length(sym_map); 419 syms = xcb_xkb_key_sym_map_syms(sym_map); 420 421 rv_assert(length2 <= KEYSYM_LOOKUP_SIZE); 422 length2 = rv_min(length2, KEYSYM_LOOKUP_SIZE); 423 for (int j = 0; j < length2; j++) { 424 xcb_keysym_t keysym = syms[j]; 425 426 #if RV_XKB_DEBUG 427 printf("\tsym(%d): 0x%x\n", j, keysym); 428 #endif 429 430 xcb_ctx.keycode_converter_list[i + reply->firstKeySym].keysyms[j] = keysym; 431 } 432 433 xcb_xkb_key_sym_map_next(&iter); 434 } 435 } 436 437 { // add xkb events 438 xcb_void_cookie_t cookie = xcb_xkb_select_events(xcb_ctx.conn, 439 XCB_XKB_ID_USE_CORE_KBD, 440 XCB_XKB_EVENT_TYPE_STATE_NOTIFY, 441 0, 442 XCB_XKB_EVENT_TYPE_STATE_NOTIFY, 443 0xff, 444 0xff, 445 NULL); 446 } 447 448 { 449 xcb_xkb_get_state_cookie_t cookie = xcb_xkb_get_state(xcb_ctx.conn, XCB_XKB_ID_USE_CORE_KBD); 450 xcb_xkb_get_state_reply_t* reply = xcb_xkb_get_state_reply(xcb_ctx.conn, cookie, NULL); 451 452 xcb_ctx.mods = reply->mods; 453 xcb_ctx.group = reply->group; 454 455 free(reply); 456 } 457 458 } 459 460 } 461 462 RV_INTERNAL xcb_keysym_t keycode_to_keysym(u8 keycode, u32 mods) 463 { 464 s32 level = 0; 465 { // look up key level 466 467 s32 group = rv_min(xcb_ctx.keycode_converter_list[keycode].num_groups, xcb_ctx.group); 468 469 if (xcb_ctx.keycode_converter_list[keycode].num_groups) { 470 s32 key_type = xcb_ctx.keycode_converter_list[keycode].kt_index[group]; 471 472 mods &= xcb_ctx.type_map[key_type].mask; 473 for (s32 e = 0; e < xcb_ctx.type_map[key_type].entries_size; e++) { 474 if (xcb_ctx.type_map[key_type].entries[e].active && 475 xcb_ctx.type_map[key_type].entries[e].mask == mods) { 476 level = xcb_ctx.type_map[key_type].entries[e].level; 477 break; 478 } 479 } 480 } 481 } 482 483 return xcb_ctx.keycode_converter_list[keycode].keysyms[level]; 484 } 485 486 ////////////////////////////////////////////////////////////////// 487 // window creation/destruction 488 489 RV_GLOBAL rv_window_handle_t* rv_create_window(rv_window_desc_t desc) 490 { 491 make_xcb_context(); 492 493 rv_window_handle_t* res = rv_mem_global_alloc(rv_window_handle_t, 1); 494 495 { // create the window 496 497 // get the first screen 498 xcb_screen_t* screen = xcb_setup_roots_iterator(xcb_get_setup(xcb_ctx.conn)).data; 499 500 // set up masks 501 uint32_t mask = 0; 502 uint32_t values[2]; 503 res->window_id = xcb_generate_id(xcb_ctx.conn); 504 505 mask = XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK; 506 507 values[0] = screen->black_pixel; 508 values[1] = XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_STRUCTURE_NOTIFY | 509 XCB_EVENT_MASK_KEY_PRESS | XCB_EVENT_MASK_KEY_RELEASE | 510 XCB_EVENT_MASK_BUTTON_PRESS | XCB_EVENT_MASK_BUTTON_RELEASE | 511 XCB_EVENT_MASK_ENTER_WINDOW | XCB_EVENT_MASK_LEAVE_WINDOW ; 512 513 514 // default window size 515 rv_vec2 size = desc.rect.wh; 516 if (size.x == 0) size.x = 150; 517 if (size.y == 0) size.y = 150; 518 519 xcb_create_window(xcb_ctx.conn, // connection 520 XCB_COPY_FROM_PARENT, // depth 521 res->window_id, // window Id 522 screen->root, // parent window 523 desc.rect.x, desc.rect.y, // x, y 524 size.x, size.y, // width, height 525 0, // border_width 526 XCB_WINDOW_CLASS_INPUT_OUTPUT, // class 527 screen->root_visual, // visual 528 mask, values); // masks 529 530 // map the window on the screen 531 xcb_map_window(xcb_ctx.conn, res->window_id); 532 } 533 534 { // change window title 535 xcb_intern_atom_cookie_t utf8_string_cookie = xcb_intern_atom(xcb_ctx.conn, 0, 11, "UTF8_STRING"); 536 xcb_intern_atom_reply_t* utf8_string_reply = xcb_intern_atom_reply(xcb_ctx.conn, utf8_string_cookie, 0); 537 xcb_atom_t m_utf8_string_atom = utf8_string_reply->atom; 538 free(utf8_string_reply); 539 540 xcb_change_property(xcb_ctx.conn, XCB_PROP_MODE_REPLACE, res->window_id, 541 XCB_ATOM_WM_NAME, m_utf8_string_atom, 8, 542 desc.name.len, desc.name.str); 543 } 544 545 { // setup close handler event 546 xcb_intern_atom_cookie_t protocolCookie = xcb_intern_atom_unchecked(xcb_ctx.conn, 1, 12, "WM_PROTOCOLS"); 547 xcb_intern_atom_reply_t* protocolReply = xcb_intern_atom_reply(xcb_ctx.conn, protocolCookie, 0); 548 xcb_intern_atom_cookie_t closeCookie = xcb_intern_atom_unchecked(xcb_ctx.conn, 0, 16, "WM_DELETE_WINDOW"); 549 res->close_reply = xcb_intern_atom_reply(xcb_ctx.conn, closeCookie, 0); 550 xcb_change_property(xcb_ctx.conn, XCB_PROP_MODE_REPLACE, res->window_id, protocolReply->atom, 4, 32, 1, &(res->close_reply->atom)); 551 552 free(protocolReply); 553 } 554 555 if (desc.attach_render) { 556 #if RV_RENDER_OPENGL 557 res->render = egl_attach(res->window_id, xcb_ctx.conn, desc.vsync); 558 #endif 559 } 560 561 res->vsync = desc.vsync; 562 563 xcb_flush(xcb_ctx.conn); 564 565 xcb_generic_event_t *e; 566 while ((e = xcb_wait_for_event(xcb_ctx.conn))) { 567 bool32 done = false; 568 569 if ((e->response_type & ~0x80) == XCB_EXPOSE) { 570 done = true; 571 } 572 573 free(e); 574 575 if (done) break; 576 } 577 578 { 579 xcb_get_geometry_cookie_t geom_cookie = xcb_get_geometry(xcb_ctx.conn, res->window_id); 580 xcb_get_geometry_reply_t* reply = xcb_get_geometry_reply(xcb_ctx.conn, geom_cookie, NULL); 581 if (reply) { 582 res->last_size = rv_v2(reply->width, reply->height); 583 free(reply); 584 } 585 } 586 587 rv_llist_stack_push(xcb_ctx.window_list, res); 588 589 return res; 590 } 591 592 RV_GLOBAL void rv_destroy_window(rv_window_handle_t** handle) 593 { 594 if (!*handle) { 595 rv_unreachable(); 596 return; 597 } 598 599 #if RV_RENDER_OPENGL 600 { 601 eglMakeCurrent(egl_context.egl_display, NULL, NULL, egl_context.egl_context); 602 EGLSurface* surface = handle[0]->render; 603 eglDestroySurface(egl_context.egl_display, surface); 604 } 605 #endif 606 607 free(handle[0]->close_reply); 608 609 xcb_destroy_window(xcb_ctx.conn, handle[0]->window_id); 610 611 xcb_flush(xcb_ctx.conn); 612 613 for (rv_window_handle_t** window_iter = &xcb_ctx.window_list; *window_iter; window_iter = &window_iter[0]->next) { 614 if (*window_iter == *handle) { 615 *window_iter = handle[0]->next; 616 break; 617 } 618 } 619 620 rv_mem_global_alloc_free(*handle, sizeof(**handle)); 621 *handle = 0; 622 } 623 624 rv_window_handle_t* xcb_get_window(xcb_window_t window) { 625 for (rv_window_handle_t* w = xcb_ctx.window_list; w; w = w->next) { 626 if (window == w->window_id) { 627 return w; 628 } 629 } 630 return NULL; 631 } 632 633 ////////////////////////////////////////////////////////////////// 634 // event polling 635 636 RV_GLOBAL rv_event_t* rv_get_events(rv_arena* arena, rv_event_flags_t flags) 637 { 638 xcb_flush(xcb_ctx.conn); 639 640 rv_event_t* res_first = NULL; 641 rv_event_t* res_last = NULL; 642 643 // force send initial events... 644 for (rv_window_handle_t* w = xcb_ctx.window_list; w; w = w->next) { 645 if (!w->sent_initial_events) { 646 w->sent_initial_events = true; 647 rv_event_t* n; 648 649 n = rv_push_compound(arena, rv_event_t, {.type = RV_EVENT_WINDOW_RESIZE, .window_resize = w}); 650 rv_llist_queue_push(res_first, res_last, n); 651 652 n = rv_push_compound(arena, rv_event_t, {.type = RV_EVENT_KEY_PRESS, .key_press.mods = xcb_ctx.mods, .key_press.group = xcb_ctx.group}); 653 rv_llist_queue_push(res_first, res_last, n); 654 } 655 } 656 657 xcb_generic_event_t *event; 658 while ((event = xcb_poll_for_event(xcb_ctx.conn))) { 659 rv_event_t new_ev = {0}; 660 661 bool32 is_press = false; 662 663 switch (event->response_type & ~0x80) { 664 case XCB_EXPOSE: { 665 xcb_flush(xcb_ctx.conn); 666 } break; 667 668 case XCB_CLIENT_MESSAGE: { 669 for (rv_window_handle_t* w = xcb_ctx.window_list; w; w = w->next) { 670 if (((xcb_client_message_event_t*)event)->data.data32[0] == w->close_reply->atom) { 671 new_ev.type = RV_EVENT_WINDOW_CLOSE; 672 new_ev.window_close = w; 673 break; 674 } 675 } 676 } break; 677 678 // TODO: handle closing 679 680 case XCB_KEY_PRESS: { 681 _Static_assert(sizeof(xcb_key_release_event_t) == sizeof(xcb_key_press_event_t), "press/release were not the same"); 682 is_press = true; 683 case XCB_KEY_RELEASE:; 684 685 if (is_press) new_ev.type = RV_EVENT_KEY_PRESS; 686 else new_ev.type = RV_EVENT_KEY_RELEASE; 687 688 xcb_key_press_event_t* key_event = (xcb_key_press_event_t*)event; 689 690 u8 keycode = key_event->detail; 691 692 new_ev.key_press.window = xcb_get_window(key_event->event); 693 694 xcb_keysym_t keysym = keycode_to_keysym(keycode, xcb_ctx.mods); 695 696 // TODO(Samdal): look up conversion map for multi-keys and dead keys 697 { // convert (some) dead keys to real keys... 698 switch (keysym) { 699 case 0xfe50: { keysym = 0x0060; } break; 700 case 0xfe51: { keysym = 0x00b4; } break; 701 case 0xfe57: { keysym = 0x00a8; } break; 702 case 0xfe52: { keysym = 0x005e; } break; 703 case 0xfe53: { keysym = 0x007e; } break; 704 } 705 } 706 707 rv_keycode_t kc = RV_KEYCODE_INVALID; 708 { // convert to RV keycode 709 switch (keysym) { 710 case 0x0020: { kc = RV_KEYCODE_SPACE; } break; 711 case 0x0027: { kc = RV_KEYCODE_APOSTROPHE; } break; 712 case 0x002c: { kc = RV_KEYCODE_COMMA; } break; 713 case 0x002d: { kc = RV_KEYCODE_MINUS; } break; 714 case 0x002e: { kc = RV_KEYCODE_PERIOD; } break; 715 case 0x002f: { kc = RV_KEYCODE_SLASH; } break; 716 case 0x0030: { kc = RV_KEYCODE_0; } break; 717 case 0x0031: { kc = RV_KEYCODE_1; } break; 718 case 0x0032: { kc = RV_KEYCODE_2; } break; 719 case 0x0033: { kc = RV_KEYCODE_3; } break; 720 case 0x0034: { kc = RV_KEYCODE_4; } break; 721 case 0x0035: { kc = RV_KEYCODE_5; } break; 722 case 0x0036: { kc = RV_KEYCODE_6; } break; 723 case 0x0037: { kc = RV_KEYCODE_7; } break; 724 case 0x0038: { kc = RV_KEYCODE_8; } break; 725 case 0x0039: { kc = RV_KEYCODE_9; } break; 726 case 0x003b: { kc = RV_KEYCODE_SEMICOLON; } break; 727 case 0x003d: { kc = RV_KEYCODE_EQUAL; } break; 728 729 // capital letters 730 case 0x0041: { kc = RV_KEYCODE_A; } break; 731 case 0x0042: { kc = RV_KEYCODE_B; } break; 732 case 0x0043: { kc = RV_KEYCODE_C; } break; 733 case 0x0044: { kc = RV_KEYCODE_D; } break; 734 case 0x0045: { kc = RV_KEYCODE_E; } break; 735 case 0x0046: { kc = RV_KEYCODE_F; } break; 736 case 0x0047: { kc = RV_KEYCODE_G; } break; 737 case 0x0048: { kc = RV_KEYCODE_H; } break; 738 case 0x0049: { kc = RV_KEYCODE_I; } break; 739 case 0x004a: { kc = RV_KEYCODE_J; } break; 740 case 0x004b: { kc = RV_KEYCODE_K; } break; 741 case 0x004c: { kc = RV_KEYCODE_L; } break; 742 case 0x004d: { kc = RV_KEYCODE_M; } break; 743 case 0x004e: { kc = RV_KEYCODE_N; } break; 744 case 0x004f: { kc = RV_KEYCODE_O; } break; 745 case 0x0050: { kc = RV_KEYCODE_P; } break; 746 case 0x0051: { kc = RV_KEYCODE_Q; } break; 747 case 0x0052: { kc = RV_KEYCODE_R; } break; 748 case 0x0053: { kc = RV_KEYCODE_S; } break; 749 case 0x0054: { kc = RV_KEYCODE_T; } break; 750 case 0x0055: { kc = RV_KEYCODE_U; } break; 751 case 0x0056: { kc = RV_KEYCODE_V; } break; 752 case 0x0057: { kc = RV_KEYCODE_W; } break; 753 case 0x0058: { kc = RV_KEYCODE_X; } break; 754 case 0x0059: { kc = RV_KEYCODE_Y; } break; 755 case 0x005a: { kc = RV_KEYCODE_Z; } break; 756 757 // small letters 758 case 0x0061: { kc = RV_KEYCODE_A; } break; 759 case 0x0062: { kc = RV_KEYCODE_B; } break; 760 case 0x0063: { kc = RV_KEYCODE_C; } break; 761 case 0x0064: { kc = RV_KEYCODE_D; } break; 762 case 0x0065: { kc = RV_KEYCODE_E; } break; 763 case 0x0066: { kc = RV_KEYCODE_F; } break; 764 case 0x0067: { kc = RV_KEYCODE_G; } break; 765 case 0x0068: { kc = RV_KEYCODE_H; } break; 766 case 0x0069: { kc = RV_KEYCODE_I; } break; 767 case 0x006a: { kc = RV_KEYCODE_J; } break; 768 case 0x006b: { kc = RV_KEYCODE_K; } break; 769 case 0x006c: { kc = RV_KEYCODE_L; } break; 770 case 0x006d: { kc = RV_KEYCODE_M; } break; 771 case 0x006e: { kc = RV_KEYCODE_N; } break; 772 case 0x006f: { kc = RV_KEYCODE_O; } break; 773 case 0x0070: { kc = RV_KEYCODE_P; } break; 774 case 0x0071: { kc = RV_KEYCODE_Q; } break; 775 case 0x0072: { kc = RV_KEYCODE_R; } break; 776 case 0x0073: { kc = RV_KEYCODE_S; } break; 777 case 0x0074: { kc = RV_KEYCODE_T; } break; 778 case 0x0075: { kc = RV_KEYCODE_U; } break; 779 case 0x0076: { kc = RV_KEYCODE_V; } break; 780 case 0x0077: { kc = RV_KEYCODE_W; } break; 781 case 0x0078: { kc = RV_KEYCODE_X; } break; 782 case 0x0079: { kc = RV_KEYCODE_Y; } break; 783 case 0x007a: { kc = RV_KEYCODE_Z; } break; 784 785 case 0x007c: { kc = RV_KEYCODE_BAR; } break; 786 case 0x005b: { kc = RV_KEYCODE_LEFT_BRACKET; } break; 787 case 0x005c: { kc = RV_KEYCODE_BACKSLASH; } break; 788 case 0x005d: { kc = RV_KEYCODE_RIGHT_BRACKET; } break; 789 case 0x0060: { kc = RV_KEYCODE_GRAVE_ACCENT; } break; // ascii grave 790 case 0x00b4: { kc = RV_KEYCODE_ACUTE_ACCENT; } break; // ascii acute 791 case 0x00a8: { kc = RV_KEYCODE_DIAERESIS; } break; // ascii diaeresis 792 case 0x005e: { kc = RV_KEYCODE_CIRCUMFLEX; } break; // ascii circumflex 793 case 0x007e: { kc = RV_KEYCODE_TILDE; } break; // ascii tilde 794 //case 0x0000: { kc = RV_KEYCODE_WORLD_1; } break; 795 //case 0x0000: { kc = RV_KEYCODE_WORLD_2; } break; 796 case 0xff1b: { kc = RV_KEYCODE_ESC; } break; 797 case 0xff0d: { kc = RV_KEYCODE_ENTER; } break; 798 case 0xff09: { kc = RV_KEYCODE_TAB; } break; 799 case 0xff08: { kc = RV_KEYCODE_BACKSPACE; } break; 800 801 // nav cluster 802 case 0xff63: { kc = RV_KEYCODE_INSERT; } break; 803 case 0xffff: { kc = RV_KEYCODE_DELETE; } break; 804 case 0xff53: { kc = RV_KEYCODE_RIGHT; } break; 805 case 0xff51: { kc = RV_KEYCODE_LEFT; } break; 806 case 0xff54: { kc = RV_KEYCODE_DOWN; } break; 807 case 0xff52: { kc = RV_KEYCODE_UP; } break; 808 case 0xff55: { kc = RV_KEYCODE_PAGE_UP; } break; 809 case 0xff56: { kc = RV_KEYCODE_PAGE_DOWN; } break; 810 case 0xff50: { kc = RV_KEYCODE_HOME; } break; 811 case 0xff57: { kc = RV_KEYCODE_END; } break; 812 // non-numlock nav 813 case 0xff9e: { kc = RV_KEYCODE_INSERT; } break; 814 case 0xff9f: { kc = RV_KEYCODE_DELETE; } break; 815 case 0xff98: { kc = RV_KEYCODE_RIGHT; } break; 816 case 0xff96: { kc = RV_KEYCODE_LEFT; } break; 817 case 0xff99: { kc = RV_KEYCODE_DOWN; } break; 818 case 0xff97: { kc = RV_KEYCODE_UP; } break; 819 case 0xff9a: { kc = RV_KEYCODE_PAGE_UP; } break; 820 case 0xff9b: { kc = RV_KEYCODE_PAGE_DOWN; } break; 821 case 0xff95: { kc = RV_KEYCODE_HOME; } break; 822 case 0xff9c: { kc = RV_KEYCODE_END; } break; 823 824 case 0xffe5: { kc = RV_KEYCODE_CAPS_LOCK; } break; 825 case 0xff14: { kc = RV_KEYCODE_SCROLL_LOCK; } break; 826 case 0xff7f: { kc = RV_KEYCODE_NUM_LOCK; } break; 827 case 0xff61: { kc = RV_KEYCODE_PRINT_SCREEN; } break; 828 case 0xff13: { kc = RV_KEYCODE_PAUSE; } break; 829 case 0xffbe: { kc = RV_KEYCODE_F1; } break; 830 case 0xffbf: { kc = RV_KEYCODE_F2; } break; 831 case 0xffc0: { kc = RV_KEYCODE_F3; } break; 832 case 0xffc1: { kc = RV_KEYCODE_F4; } break; 833 case 0xffc2: { kc = RV_KEYCODE_F5; } break; 834 case 0xffc3: { kc = RV_KEYCODE_F6; } break; 835 case 0xffc4: { kc = RV_KEYCODE_F7; } break; 836 case 0xffc5: { kc = RV_KEYCODE_F8; } break; 837 case 0xffc6: { kc = RV_KEYCODE_F9; } break; 838 case 0xffc7: { kc = RV_KEYCODE_F10; } break; 839 case 0xffc8: { kc = RV_KEYCODE_F11; } break; 840 case 0xffc9: { kc = RV_KEYCODE_F12; } break; 841 case 0xffca: { kc = RV_KEYCODE_F13; } break; 842 case 0xffcb: { kc = RV_KEYCODE_F14; } break; 843 case 0xffcc: { kc = RV_KEYCODE_F15; } break; 844 case 0xffcd: { kc = RV_KEYCODE_F16; } break; 845 case 0xffce: { kc = RV_KEYCODE_F17; } break; 846 case 0xffcf: { kc = RV_KEYCODE_F18; } break; 847 case 0xffd0: { kc = RV_KEYCODE_F19; } break; 848 case 0xffd1: { kc = RV_KEYCODE_F20; } break; 849 case 0xffd2: { kc = RV_KEYCODE_F21; } break; 850 case 0xffd3: { kc = RV_KEYCODE_F22; } break; 851 case 0xffd4: { kc = RV_KEYCODE_F23; } break; 852 case 0xffd5: { kc = RV_KEYCODE_F24; } break; 853 case 0xffd6: { kc = RV_KEYCODE_F25; } break; 854 case 0xffb0: { kc = RV_KEYCODE_KP_0; } break; 855 case 0xffb1: { kc = RV_KEYCODE_KP_1; } break; 856 case 0xffb2: { kc = RV_KEYCODE_KP_2; } break; 857 case 0xffb3: { kc = RV_KEYCODE_KP_3; } break; 858 case 0xffb4: { kc = RV_KEYCODE_KP_4; } break; 859 case 0xffb5: { kc = RV_KEYCODE_KP_5; } break; 860 case 0xffb6: { kc = RV_KEYCODE_KP_6; } break; 861 case 0xffb7: { kc = RV_KEYCODE_KP_7; } break; 862 case 0xffb8: { kc = RV_KEYCODE_KP_8; } break; 863 case 0xffb9: { kc = RV_KEYCODE_KP_9; } break; 864 case 0xffae: { kc = RV_KEYCODE_KP_DECIMAL; } break; 865 case 0xffaf: { kc = RV_KEYCODE_KP_DIVIDE; } break; 866 case 0xffaa: { kc = RV_KEYCODE_KP_MULTIPLY; } break; 867 case 0xffad: { kc = RV_KEYCODE_KP_SUBTRACT; } break; 868 case 0xffab: { kc = RV_KEYCODE_KP_ADD; } break; 869 case 0xff8d: { kc = RV_KEYCODE_KP_ENTER; } break; // same as normal enter... 870 case 0xffbd: { kc = RV_KEYCODE_KP_EQUAL; } break; 871 case 0xffe1: { kc = RV_KEYCODE_LEFT_SHIFT; } break; 872 case 0xffe3: { kc = RV_KEYCODE_LEFT_CONTROL; } break; 873 case 0xffe9: { kc = RV_KEYCODE_LEFT_ALT; } break; 874 case 0xffeb: { kc = RV_KEYCODE_LEFT_SUPER; } break; 875 case 0xffe2: { kc = RV_KEYCODE_RIGHT_SHIFT; } break; 876 case 0xffe4: { kc = RV_KEYCODE_RIGHT_CONTROL; } break; 877 case 0xffea: { kc = RV_KEYCODE_RIGHT_ALT; } break; 878 case 0xfe03: { kc = RV_KEYCODE_RIGHT_ALT; } break; // ISO level 3 shift 879 case 0xffec: { kc = RV_KEYCODE_RIGHT_SUPER; } break; 880 case 0xff67: { kc = RV_KEYCODE_MENU; } break; 881 default: { kc = RV_KEYCODE_INVALID; } break; 882 } 883 } 884 new_ev.key_press.keycode = kc; 885 new_ev.key_press.mods = xcb_ctx.mods; 886 new_ev.key_press.group = xcb_ctx.group; 887 new_ev.key_press.native_keycode = keycode; 888 889 { 890 char buf[XKB_KEYSYM_UTF8_MAX_SIZE]; 891 s32 len = xkb_keysym_to_utf8(keysym, buf, sizeof(buf)); 892 if (len) { 893 new_ev.key_press.string = rv_str8_from_cstr(buf); 894 new_ev.key_press.string = rv_str8_copy(arena, new_ev.key_press.string); 895 if (new_ev.key_press.string.len == 1) { 896 if (new_ev.key_press.string.str[0] == '\r') { 897 new_ev.key_press.string.str[0] = '\n'; 898 } 899 } 900 } 901 } 902 903 // invalidate event if nothing gave any results 904 if (new_ev.key_press.keycode == RV_KEYCODE_INVALID && new_ev.key_press.string.len == 0) { 905 new_ev.type = RV_EVENT_INVALID; 906 } 907 } break; 908 case XCB_BUTTON_PRESS: { 909 is_press = true; 910 case XCB_BUTTON_RELEASE: 911 if (is_press) new_ev.type = RV_EVENT_BUTTON_PRESS; 912 else new_ev.type = RV_EVENT_BUTTON_RELEASE; 913 xcb_button_press_event_t* button_event = (xcb_button_press_event_t*)event; 914 915 // remap buttons from x11 916 switch (button_event->detail) { 917 case 0: 918 case 1: 919 new_ev.button_press.button = 0; 920 break; 921 case 2: 922 new_ev.button_press.button = 1; 923 break; 924 case 3: 925 new_ev.button_press.button = 2; 926 break; 927 default: 928 new_ev.button_press.button = button_event->detail - 1; 929 break; 930 } 931 932 new_ev.button_press.window = xcb_get_window(button_event->event); 933 } break; 934 case XCB_CONFIGURE_NOTIFY: { 935 xcb_configure_notify_event_t* conf_event = (xcb_configure_notify_event_t*)event; 936 rv_window_handle_t* w = xcb_get_window(conf_event->event); 937 new_ev.window_resize = w; 938 if (w) { 939 if (w->last_size.x != conf_event->width || w->last_size.y != conf_event->height) { 940 w->last_size = rv_v2(conf_event->width, conf_event->height); 941 new_ev.type = RV_EVENT_WINDOW_RESIZE; 942 } 943 } 944 xcb_flush(xcb_ctx.conn); 945 } break; 946 case XCB_ENTER_NOTIFY: { 947 xcb_enter_notify_event_t* enter_event = (xcb_leave_notify_event_t*)event; 948 new_ev.type = RV_EVENT_WINDOW_ENTER; 949 new_ev.window_enter = xcb_get_window(enter_event->event); 950 } break; 951 case XCB_LEAVE_NOTIFY: { 952 xcb_leave_notify_event_t* leave_event = (xcb_leave_notify_event_t*)event; 953 new_ev.type = RV_EVENT_WINDOW_LEAVE; 954 new_ev.window_leave = xcb_get_window(leave_event->event); 955 } break; 956 957 case XCB_MOTION_NOTIFY: 958 case XCB_FOCUS_IN: 959 case XCB_FOCUS_OUT: 960 case XCB_GRAPHICS_EXPOSURE: 961 case XCB_NO_EXPOSURE: 962 case XCB_VISIBILITY_NOTIFY: 963 case XCB_CREATE_NOTIFY: 964 case XCB_DESTROY_NOTIFY: 965 case XCB_UNMAP_NOTIFY: 966 case XCB_MAP_NOTIFY: 967 case XCB_MAP_REQUEST: 968 case XCB_REPARENT_NOTIFY: 969 case XCB_CONFIGURE_REQUEST: 970 case XCB_GRAVITY_NOTIFY: 971 case XCB_RESIZE_REQUEST: 972 case XCB_CIRCULATE_NOTIFY: 973 case XCB_CIRCULATE_REQUEST: 974 case XCB_PROPERTY_NOTIFY: 975 case XCB_COLORMAP_NOTIFY: 976 { 977 printf("unhandled event (one of above in source code): %d\n", event->response_type & ~0x80); 978 } break; 979 case XCB_GE_GENERIC: { 980 // GE generic event... 981 } break; 982 case 0: { 983 puts("X11 error!"); 984 } break; 985 986 default: { 987 if (xcb_ctx.xkb_extension_reply->first_event == (event->response_type & ~0x80)) { 988 989 xcb_xkb_state_notify_event_t *state = (xcb_xkb_state_notify_event_t *)event; 990 xcb_ctx.mods = state->mods; 991 xcb_ctx.group = state->group; 992 993 } else { 994 printf("unhandled event: %d\n", event->response_type & ~0x80); 995 } 996 } break; 997 } 998 999 if (new_ev.type != RV_EVENT_INVALID) { 1000 rv_event_t* n = rv_push_copy(arena, rv_event_t, &new_ev); 1001 rv_llist_queue_push(res_first, res_last, n); 1002 } 1003 1004 free(event); 1005 } 1006 1007 xcb_flush(xcb_ctx.conn); 1008 1009 rv_keyboard_context_update(&rv_keyboard_context, res_first); 1010 1011 return res_first; 1012 } 1013 1014 ////////////////////////////////////////////////////////////////// 1015 // Querying 1016 1017 RV_GLOBAL rv_vec2 rv_window_size(rv_window_handle_t* window) 1018 { 1019 return xcb_maybe_window(window)->last_size; 1020 } 1021 1022 RV_GLOBAL rv_vec2 rv_window_mouse_pos(rv_window_handle_t* window) 1023 { 1024 rv_vec2 ret = {0}; 1025 xcb_query_pointer_cookie_t cookie = xcb_query_pointer(xcb_ctx.conn, xcb_maybe_window(window)->window_id); 1026 xcb_query_pointer_reply_t* reply = xcb_query_pointer_reply(xcb_ctx.conn, cookie, NULL); 1027 if (reply) { 1028 ret = rv_v2( 1029 reply->win_x, 1030 reply->win_y, 1031 ); 1032 free(reply); 1033 } 1034 return ret; 1035 }