nuklear_xcb.h 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968
  1. /*****************************************************************************
  2. *
  3. * Nuklear XCB/Cairo Render Backend - v0.0.2
  4. * Copyright 2021 Richard Gill
  5. *
  6. * Grabbed and adapted from https://github.com/griebd/nuklear_xcb
  7. * Copyright 2017 Adriano Grieb
  8. *
  9. * Permission is hereby granted, free of charge, to any person obtaining a
  10. * copy of this software and associated documentation files (the "Software"),
  11. * to deal in the Software without restriction, including without limitation
  12. * the rights to use, copy, modify, merge, publish, distribute, sublicense,
  13. * and/or sell copies of the Software, and to permit persons to whom the
  14. * Software is furnished to do so, subject to the following conditions:
  15. *
  16. * The above copyright notice and this permission notice shall be included in
  17. * all copies or substantial portions of the Software.
  18. *
  19. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  20. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  21. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  22. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  23. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  24. * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  25. * DEALINGS IN THE SOFTWARE.
  26. *
  27. ****************************************************************************/
  28. /*****************************************************************************
  29. *
  30. * API
  31. *
  32. ****************************************************************************/
  33. #ifndef NK_XCB_CAIRO_H
  34. #define NK_XCB_CAIRO_H
  35. struct nk_xcb_context;
  36. struct nk_cairo_context;
  37. /* With Xcb, we work mostly on events, so to do something only when
  38. * needed it's good to know what kind of events pulled us from sleep
  39. */
  40. enum nk_xcb_event_type {
  41. NK_XCB_EVENT_PAINT = 0x02,
  42. NK_XCB_EVENT_RESIZED = 0x04,
  43. NK_XCB_EVENT_STOP = 0x08
  44. };
  45. /* Xcb part: work on windows */
  46. NK_API struct nk_xcb_context *nk_xcb_init(const char *title, int pos_x, int pos_y, int width, int height);
  47. NK_API void nk_xcb_free(struct nk_xcb_context *xcb_ctx);
  48. NK_API int nk_xcb_handle_event(struct nk_xcb_context *xcb_ctx, struct nk_context *nk_ctx);
  49. NK_API void nk_xcb_render(struct nk_xcb_context *xcb_ctx);
  50. NK_API void nk_xcb_size(struct nk_xcb_context *xcb_ctx, int *width, int *height);
  51. /* TODO: copy/paste */
  52. /* Cairo part: work on painting */
  53. NK_API struct nk_cairo_context *nk_cairo_init(struct nk_color *bg, const char *font_file, double font_size, void *surface);
  54. NK_API void nk_cairo_free(struct nk_cairo_context *cairo_ctx);
  55. NK_API struct nk_user_font *nk_cairo_default_font(struct nk_cairo_context *cairo_ctx);
  56. NK_API void nk_cairo_damage(struct nk_cairo_context *cairo_ctx);
  57. NK_API int nk_cairo_render(struct nk_cairo_context *cairo_ctx, struct nk_context *ctx);
  58. /* Bridge between xcb and cairo (so it's possible to use them like legos) */
  59. NK_API void *nk_xcb_create_cairo_surface(struct nk_xcb_context *xcb_ctx);
  60. NK_API void nk_xcb_resize_cairo_surface(struct nk_xcb_context *xcb_ctx, void *surface);
  61. #endif /* NK_XCB_CAIRO_H */
  62. /*****************************************************************************
  63. *
  64. * IMPLEMENTATION
  65. *
  66. ****************************************************************************/
  67. #ifdef NK_XCB_CAIRO_IMPLEMENTATION
  68. #include <xcb/xcb.h>
  69. #include <xcb/xcb_util.h>
  70. #include <xcb/xcb_keysyms.h>
  71. #include <X11/keysym.h>
  72. #include <cairo/cairo-xcb.h>
  73. #include <cairo/cairo-ft.h>
  74. #include <xkbcommon/xkbcommon.h>
  75. #include <xkbcommon/xkbcommon-x11.h>
  76. #if defined _XOPEN_SOURCE && _XOPEN_SOURCE >= 600 || \
  77. defined _POSIX_C_SOURCE && _POSIX_C_SOURCE >= 200112L
  78. #include <time.h>
  79. #include <errno.h>
  80. #ifndef NK_XCB_FPS
  81. #define NK_XCB_FPS 30
  82. #endif /* NK_XCB_FPS */
  83. #define NK_XCB_NSEC 1000000000
  84. #define NK_XCB_MIN_FRAME_TIME (NK_XCB_NSEC / NK_XCB_FPS)
  85. #endif
  86. #include <math.h>
  87. #ifdef __USE_GNU
  88. #define NK_XCB_PI M_PIl
  89. #elif defined __USE_BSD || defined __USE_XOPEN
  90. #define NK_XCB_PI M_PI
  91. #else
  92. #define NK_XCB_PI acos(-1.0)
  93. #endif
  94. #define NK_XCB_TO_CAIRO(x) ((double) x / 255.0)
  95. #define NK_XCB_DEG_TO_RAD(x) ((double) x * NK_XCB_PI / 180.0)
  96. typedef struct xkb_context xkb_context;
  97. typedef struct xkb_keymap xkb_keymap;
  98. typedef struct xkb_state xkb_state;
  99. typedef struct xkbcommon_context xkbcommon_context;
  100. typedef struct nk_xcb_context nk_xcb_context;
  101. struct xkbcommon_context
  102. {
  103. struct xkb_context *ctx;
  104. struct xkb_keymap *keymap;
  105. struct xkb_state *state;
  106. };
  107. NK_INTERN void xkbcommon_free(xkbcommon_context *kbdctx);
  108. NK_INTERN xkbcommon_context *xkbcommon_init(xcb_connection_t *conn);
  109. NK_INTERN xkb_keysym_t keycode_to_keysym(nk_xcb_context *ctx, xkb_keycode_t keycode, int pressed);
  110. struct nk_cairo_context {
  111. cairo_surface_t *surface;
  112. cairo_t *cr;
  113. struct nk_user_font *font;
  114. struct nk_color *bg;
  115. void *last_buffer;
  116. nk_size buffer_size;
  117. int repaint;
  118. };
  119. struct nk_xcb_context {
  120. xcb_connection_t *conn;
  121. int screennum;
  122. xcb_window_t window;
  123. /* xcb_key_symbols_t *key_symbols; */
  124. xkbcommon_context *xkbcommon_ctx;
  125. #ifdef NK_XCB_MIN_FRAME_TIME
  126. unsigned long last_render;
  127. #endif /* NK_XCB_MIN_FRAME_TIME */
  128. int events;
  129. xcb_intern_atom_reply_t* del_atom;
  130. int width, height;
  131. };
  132. NK_API struct nk_xcb_context *nk_xcb_init(const char *title, int pos_x, int pos_y, int width, int height)
  133. {
  134. int screenNum;
  135. xcb_connection_t *conn;
  136. xcb_screen_t *screen;
  137. xcb_window_t window;
  138. uint32_t values[1];
  139. struct nk_xcb_context *xcb_ctx;
  140. xcb_intern_atom_cookie_t cookie;
  141. xcb_intern_atom_reply_t *reply, *del_atom;
  142. conn = xcb_connect(NULL, &screenNum);
  143. if (xcb_connection_has_error(conn)) {
  144. xcb_disconnect(conn);
  145. return NULL;
  146. }
  147. screen = xcb_aux_get_screen(conn, screenNum);
  148. window = xcb_generate_id(conn);
  149. values[0] = XCB_EVENT_MASK_KEY_PRESS
  150. | XCB_EVENT_MASK_KEY_RELEASE
  151. | XCB_EVENT_MASK_BUTTON_PRESS
  152. | XCB_EVENT_MASK_BUTTON_RELEASE
  153. | XCB_EVENT_MASK_POINTER_MOTION
  154. | XCB_EVENT_MASK_BUTTON_1_MOTION
  155. | XCB_EVENT_MASK_BUTTON_2_MOTION
  156. | XCB_EVENT_MASK_BUTTON_3_MOTION
  157. | XCB_EVENT_MASK_BUTTON_4_MOTION
  158. | XCB_EVENT_MASK_BUTTON_5_MOTION
  159. | XCB_EVENT_MASK_BUTTON_MOTION
  160. | XCB_EVENT_MASK_KEYMAP_STATE
  161. | XCB_EVENT_MASK_EXPOSURE
  162. | XCB_EVENT_MASK_STRUCTURE_NOTIFY
  163. ;
  164. xcb_create_window(conn, XCB_COPY_FROM_PARENT, window, screen->root,
  165. pos_x, pos_y, width, height, 0, XCB_WINDOW_CLASS_INPUT_OUTPUT,
  166. XCB_COPY_FROM_PARENT, XCB_CW_EVENT_MASK, values);
  167. xcb_change_property(conn,
  168. XCB_PROP_MODE_REPLACE,
  169. window,
  170. XCB_ATOM_WM_NAME,
  171. XCB_ATOM_STRING,
  172. 8,
  173. strlen(title),
  174. title);
  175. cookie = xcb_intern_atom(conn, 1, 12, "WM_PROTOCOLS");
  176. reply = xcb_intern_atom_reply(conn, cookie, 0);
  177. cookie = xcb_intern_atom(conn, 0, 16, "WM_DELETE_WINDOW");
  178. del_atom = xcb_intern_atom_reply(conn, cookie, 0);
  179. xcb_change_property(conn, XCB_PROP_MODE_REPLACE, window, reply->atom, 4, 32, 1, &del_atom->atom);
  180. free(reply);
  181. xcb_map_window(conn, window);
  182. xcb_flush(conn);
  183. xcb_ctx = (struct nk_xcb_context *)malloc(sizeof (struct nk_xcb_context));
  184. xcb_ctx->conn = conn;
  185. xcb_ctx->screennum = screenNum;
  186. xcb_ctx->window = window;
  187. /* xcb_ctx->key_symbols = xcb_key_symbols_alloc(xcb_ctx->conn); */
  188. xcb_ctx->xkbcommon_ctx = xkbcommon_init(conn);
  189. xcb_ctx->del_atom = del_atom;
  190. xcb_ctx->width = width;
  191. xcb_ctx->height = height;
  192. return xcb_ctx;
  193. }
  194. NK_API void nk_xcb_free(struct nk_xcb_context *xcb_ctx)
  195. {
  196. xkbcommon_free(xcb_ctx->xkbcommon_ctx);
  197. free(xcb_ctx->del_atom);
  198. /* xcb_key_symbols_free(xcb_ctx->key_symbols); */
  199. xcb_disconnect(xcb_ctx->conn);
  200. free(xcb_ctx);
  201. }
  202. NK_API int nk_xcb_handle_event(struct nk_xcb_context *xcb_ctx, struct nk_context *nk_ctx)
  203. {
  204. int events = 0;
  205. xcb_generic_event_t *event;
  206. static int insert_toggle = 0;
  207. #ifdef NK_XCB_MIN_FRAME_TIME
  208. struct timespec tp;
  209. clock_gettime(CLOCK_MONOTONIC_COARSE, &tp);
  210. xcb_ctx->last_render = tp.tv_sec * NK_XCB_NSEC + tp.tv_nsec;
  211. #endif /* NK_XCB_MIN_FRAME_TIME */
  212. event = xcb_wait_for_event(xcb_ctx->conn);
  213. nk_input_begin(nk_ctx);
  214. do {
  215. switch (XCB_EVENT_RESPONSE_TYPE(event)) {
  216. case XCB_KEY_PRESS:
  217. case XCB_KEY_RELEASE:
  218. {
  219. int press = (XCB_EVENT_RESPONSE_TYPE(event)) == XCB_KEY_PRESS;
  220. xcb_key_press_event_t *kp = (xcb_key_press_event_t *)event;
  221. /* xcb_keysym_t sym = xcb_key_symbols_get_keysym(xcb_ctx->key_symbols, kp->detail, kp->state);*/
  222. xcb_keysym_t sym = keycode_to_keysym(xcb_ctx, kp->detail, press);
  223. switch (sym) {
  224. case XK_Shift_L:
  225. case XK_Shift_R:
  226. nk_input_key(nk_ctx, NK_KEY_SHIFT, press);
  227. break;
  228. case XK_Control_L:
  229. case XK_Control_R:
  230. nk_input_key(nk_ctx, NK_KEY_CTRL, press);
  231. break;
  232. case XK_Delete:
  233. nk_input_key(nk_ctx, NK_KEY_DEL, press);
  234. break;
  235. case XK_Return:
  236. case XK_KP_Enter:
  237. nk_input_key(nk_ctx, NK_KEY_ENTER, press);
  238. break;
  239. case XK_Tab:
  240. nk_input_key(nk_ctx, NK_KEY_TAB, press);
  241. break;
  242. case XK_BackSpace:
  243. nk_input_key(nk_ctx, NK_KEY_BACKSPACE, press);
  244. break;
  245. /* case NK_KEY_COPY */
  246. /* case NK_KEY_CUT */
  247. /* case NK_KEY_PASTE */
  248. case XK_Up:
  249. nk_input_key(nk_ctx, NK_KEY_UP, press);
  250. break;
  251. case XK_Down:
  252. nk_input_key(nk_ctx, NK_KEY_DOWN, press);
  253. break;
  254. case XK_Left:
  255. nk_input_key(nk_ctx, NK_KEY_LEFT, press);
  256. break;
  257. case XK_Right:
  258. nk_input_key(nk_ctx, NK_KEY_RIGHT, press);
  259. break;
  260. case XK_Insert:
  261. if (press) insert_toggle = !insert_toggle;
  262. if (insert_toggle) {
  263. nk_input_key(nk_ctx, NK_KEY_TEXT_INSERT_MODE, press);
  264. } else {
  265. nk_input_key(nk_ctx, NK_KEY_TEXT_REPLACE_MODE, press);
  266. }
  267. break;
  268. case XK_Escape:
  269. nk_input_key(nk_ctx, NK_KEY_TEXT_RESET_MODE, press);
  270. break;
  271. /* NK_KEY_TEXT_LINE_START, */
  272. /* NK_KEY_TEXT_LINE_END, */
  273. case XK_Home:
  274. {
  275. nk_input_key(nk_ctx, NK_KEY_TEXT_START, press);
  276. nk_input_key(nk_ctx, NK_KEY_SCROLL_START, press);
  277. }
  278. break;
  279. case XK_End:
  280. {
  281. nk_input_key(nk_ctx, NK_KEY_TEXT_END, press);
  282. nk_input_key(nk_ctx, NK_KEY_SCROLL_END, press);
  283. }
  284. break;
  285. /* NK_KEY_TEXT_UNDO, */
  286. /* NK_KEY_TEXT_REDO, */
  287. /* NK_KEY_TEXT_SELECT_ALL, */
  288. /* NK_KEY_TEXT_WORD_LEFT, */
  289. /* NK_KEY_TEXT_WORD_RIGHT, */
  290. case XK_Page_Down:
  291. nk_input_key(nk_ctx, NK_KEY_SCROLL_DOWN, press);
  292. break;
  293. case XK_Page_Up:
  294. nk_input_key(nk_ctx, NK_KEY_SCROLL_UP, press);
  295. break;
  296. default:
  297. if (press &&
  298. !xcb_is_keypad_key(sym) &&
  299. !xcb_is_private_keypad_key(sym) &&
  300. !xcb_is_cursor_key(sym) &&
  301. !xcb_is_pf_key(sym) &&
  302. !xcb_is_function_key(sym) &&
  303. !xcb_is_misc_function_key(sym) &&
  304. !xcb_is_modifier_key(sym)
  305. ) {
  306. /* nk_input_char(nk_ctx, sym); */
  307. nk_input_unicode(nk_ctx, sym);
  308. }
  309. else {
  310. printf("state: %x code: %x sum: %x\n", kp->state, kp->detail, sym);
  311. }
  312. break;
  313. }
  314. }
  315. break;
  316. case XCB_BUTTON_PRESS:
  317. case XCB_BUTTON_RELEASE:
  318. {
  319. int press = (XCB_EVENT_RESPONSE_TYPE(event)) == XCB_BUTTON_PRESS;
  320. xcb_button_press_event_t *bp = (xcb_button_press_event_t *)event;
  321. switch (bp->detail) {
  322. case XCB_BUTTON_INDEX_1:
  323. nk_input_button(nk_ctx, NK_BUTTON_LEFT, bp->event_x, bp->event_y, press);
  324. break;
  325. case XCB_BUTTON_INDEX_2:
  326. nk_input_button(nk_ctx, NK_BUTTON_MIDDLE, bp->event_x, bp->event_y, press);
  327. break;
  328. case XCB_BUTTON_INDEX_3:
  329. nk_input_button(nk_ctx, NK_BUTTON_RIGHT, bp->event_x, bp->event_y, press);
  330. break;
  331. case XCB_BUTTON_INDEX_4:
  332. nk_input_scroll(nk_ctx, nk_vec2(0, 1.0f));
  333. break;
  334. case XCB_BUTTON_INDEX_5:
  335. nk_input_scroll(nk_ctx, nk_vec2(0, -1.0f));
  336. break;
  337. default: break;
  338. }
  339. }
  340. break;
  341. case XCB_MOTION_NOTIFY:
  342. {
  343. xcb_motion_notify_event_t *mn = (xcb_motion_notify_event_t *)event;
  344. nk_input_motion(nk_ctx, mn->event_x, mn->event_y);
  345. }
  346. break;
  347. case XCB_SELECTION_CLEAR:
  348. {
  349. printf("Unhandled event: %s\n", xcb_event_get_label(event->response_type));
  350. }
  351. break;
  352. case XCB_SELECTION_REQUEST:
  353. {
  354. printf("Unhandled event: %s\n", xcb_event_get_label(event->response_type));
  355. }
  356. break;
  357. case XCB_SELECTION_NOTIFY:
  358. {
  359. printf("Unhandled event: %s\n", xcb_event_get_label(event->response_type));
  360. }
  361. break;
  362. case XCB_CONFIGURE_NOTIFY:
  363. {
  364. xcb_configure_notify_event_t *cn = (xcb_configure_notify_event_t *)event;
  365. xcb_ctx->width = cn->width;
  366. xcb_ctx->height = cn->height;
  367. events |= NK_XCB_EVENT_RESIZED;
  368. }
  369. break;
  370. case XCB_KEYMAP_NOTIFY:
  371. /* xcb_refresh_keyboard_mapping(xcb_ctx->key_symbols, (xcb_mapping_notify_event_t *)event); */
  372. break;
  373. case XCB_EXPOSE:
  374. case XCB_REPARENT_NOTIFY:
  375. case XCB_MAP_NOTIFY:
  376. events |= NK_XCB_EVENT_PAINT;
  377. break;
  378. case XCB_CLIENT_MESSAGE:
  379. {
  380. xcb_client_message_event_t *cm = (xcb_client_message_event_t *)event;
  381. if (cm->data.data32[0] == xcb_ctx->del_atom->atom)
  382. {
  383. events = NK_XCB_EVENT_STOP;
  384. }
  385. }
  386. break;
  387. default:
  388. printf ("Unhandled event: %s\n", xcb_event_get_label(event->response_type));
  389. break;
  390. }
  391. free(event);
  392. }
  393. while ((events != NK_XCB_EVENT_STOP) && (event = xcb_poll_for_event(xcb_ctx->conn)));
  394. nk_input_end(nk_ctx);
  395. return events;
  396. }
  397. NK_API void nk_xcb_render(struct nk_xcb_context *xcb_ctx)
  398. {
  399. xcb_flush (xcb_ctx->conn);
  400. #ifdef NK_XCB_MIN_FRAME_TIME
  401. {
  402. struct timespec tp;
  403. unsigned long spent;
  404. clock_gettime(CLOCK_MONOTONIC_COARSE, &tp);
  405. spent = tp.tv_sec * NK_XCB_NSEC + tp.tv_nsec - xcb_ctx->last_render;
  406. if (NK_XCB_MIN_FRAME_TIME > spent) {
  407. tp.tv_sec = 0;
  408. tp.tv_nsec = NK_XCB_MIN_FRAME_TIME - spent;
  409. while (clock_nanosleep(CLOCK_MONOTONIC, 0, &tp, &tp) == EINTR);
  410. }
  411. }
  412. #endif /* NK_XCB_MIN_FRAME_TIME */
  413. }
  414. NK_API void nk_xcb_size(struct nk_xcb_context *xcb_ctx, int *width, int *height)
  415. {
  416. *width = xcb_ctx->width;
  417. *height = xcb_ctx->height;
  418. }
  419. NK_API void *nk_xcb_create_cairo_surface(struct nk_xcb_context *xcb_ctx)
  420. {
  421. xcb_screen_t *screen;
  422. xcb_visualtype_t *visual;
  423. screen = xcb_aux_get_screen(xcb_ctx->conn, xcb_ctx->screennum);
  424. visual = xcb_aux_get_visualtype(xcb_ctx->conn, xcb_ctx->screennum, screen->root_visual);
  425. return cairo_xcb_surface_create(xcb_ctx->conn, xcb_ctx->window, visual, xcb_ctx->width, xcb_ctx->height);
  426. }
  427. NK_API void nk_xcb_resize_cairo_surface(struct nk_xcb_context *xcb_ctx, void *surface)
  428. {
  429. cairo_xcb_surface_set_size((cairo_surface_t *)surface, xcb_ctx->width, xcb_ctx->height);
  430. }
  431. #define NK_TO_CAIRO(x) ((double) x / 255.0)
  432. NK_INTERN float nk_cairo_text_width(nk_handle handle, float height __attribute__ ((__unused__)), const char *text, int len)
  433. {
  434. cairo_scaled_font_t *font = (cairo_scaled_font_t *)handle.ptr;
  435. cairo_glyph_t *glyphs = NULL;
  436. int num_glyphs;
  437. cairo_text_extents_t extents;
  438. cairo_scaled_font_text_to_glyphs(font, 0, 0, text, len, &glyphs, &num_glyphs, NULL, NULL, NULL);
  439. cairo_scaled_font_glyph_extents(font, glyphs, num_glyphs, &extents);
  440. cairo_glyph_free(glyphs);
  441. return extents.x_advance;
  442. }
  443. NK_API struct nk_cairo_context *nk_cairo_init(struct nk_color *bg, const char *font_file, double font_size, void *surf)
  444. {
  445. cairo_surface_t *surface = (cairo_surface_t *)surf;
  446. struct nk_cairo_context *cairo_ctx;
  447. cairo_t *cr;
  448. cairo_font_extents_t extents;
  449. cairo_scaled_font_t *default_font;
  450. struct nk_user_font *font;
  451. cr = cairo_create(surface);
  452. font = (struct nk_user_font *)malloc(sizeof (struct nk_user_font));
  453. if (font_file != NULL) {
  454. FT_Library library;
  455. FT_Face face;
  456. cairo_font_face_t *font_face;
  457. static const cairo_user_data_key_t key = {0};
  458. FT_Init_FreeType(&library);
  459. FT_New_Face(library, font_file, 0, &face);
  460. font_face = cairo_ft_font_face_create_for_ft_face(face, 0);
  461. cairo_font_face_set_user_data(font_face, &key, face, (cairo_destroy_func_t)FT_Done_Face);
  462. cairo_set_font_face(cr, font_face);
  463. }
  464. if (font_size < 0.01) {
  465. font_size = 11.0;
  466. }
  467. cairo_set_font_size(cr, font_size);
  468. default_font = cairo_get_scaled_font(cr);
  469. cairo_scaled_font_extents(default_font, &extents);
  470. font->userdata.ptr = default_font;
  471. font->height = extents.height;
  472. font->width = nk_cairo_text_width;
  473. cairo_ctx = (struct nk_cairo_context *)malloc(sizeof(struct nk_cairo_context));
  474. cairo_ctx->surface = (cairo_surface_t *)surface;
  475. cairo_ctx->cr = cr;
  476. cairo_ctx->font = font;
  477. cairo_ctx->bg = bg;
  478. cairo_ctx->last_buffer = NULL;
  479. cairo_ctx->buffer_size = 0;
  480. cairo_ctx->repaint = nk_false;
  481. return cairo_ctx;
  482. }
  483. NK_API cairo_surface_t *nk_cairo_surface(struct nk_cairo_context *cairo_ctx)
  484. {
  485. return cairo_ctx->surface;
  486. }
  487. NK_API struct nk_user_font *nk_cairo_default_font(struct nk_cairo_context *cairo_ctx)
  488. {
  489. return cairo_ctx->font;
  490. }
  491. NK_API void nk_cairo_free(struct nk_cairo_context *cairo_ctx)
  492. {
  493. free (cairo_ctx->last_buffer);
  494. cairo_destroy(cairo_ctx->cr);
  495. cairo_surface_destroy(cairo_ctx->surface);
  496. free(cairo_ctx->font);
  497. free(cairo_ctx);
  498. }
  499. NK_API void nk_cairo_damage(struct nk_cairo_context *cairo_ctx)
  500. {
  501. cairo_ctx->repaint = nk_true;
  502. }
  503. NK_API int nk_cairo_render(struct nk_cairo_context *cairo_ctx, struct nk_context *nk_ctx)
  504. {
  505. cairo_t *cr;
  506. const struct nk_command *cmd = NULL;
  507. void *cmds = nk_buffer_memory(&nk_ctx->memory);
  508. if (cairo_ctx->buffer_size != nk_ctx->memory.allocated) {
  509. cairo_ctx->buffer_size = nk_ctx->memory.allocated;
  510. cairo_ctx->last_buffer = realloc(cairo_ctx->last_buffer, cairo_ctx->buffer_size);
  511. memcpy(cairo_ctx->last_buffer, cmds, cairo_ctx->buffer_size);
  512. }
  513. else if (!memcmp(cmds, cairo_ctx->last_buffer, cairo_ctx->buffer_size)) {
  514. if (!cairo_ctx->repaint) {
  515. return nk_false;
  516. }
  517. cairo_ctx->repaint = nk_false;
  518. }
  519. else {
  520. memcpy(cairo_ctx->last_buffer, cmds, cairo_ctx->buffer_size);
  521. }
  522. cr = cairo_ctx->cr;
  523. cairo_push_group(cr);
  524. cairo_set_source_rgb(cr, NK_TO_CAIRO(cairo_ctx->bg->r), NK_TO_CAIRO(cairo_ctx->bg->g), NK_TO_CAIRO(cairo_ctx->bg->b));
  525. cairo_paint(cr);
  526. nk_foreach(cmd, nk_ctx) {
  527. switch (cmd->type) {
  528. case NK_COMMAND_NOP:
  529. break;
  530. case NK_COMMAND_SCISSOR:
  531. {
  532. const struct nk_command_scissor *s = (const struct nk_command_scissor *)cmd;
  533. cairo_reset_clip(cr);
  534. if (s->x >= 0) {
  535. cairo_rectangle(cr, s->x - 1, s->y - 1, s->w + 2, s->h + 2);
  536. cairo_clip(cr);
  537. }
  538. }
  539. break;
  540. case NK_COMMAND_LINE:
  541. {
  542. const struct nk_command_line *l = (const struct nk_command_line *)cmd;
  543. cairo_set_source_rgba(cr, NK_TO_CAIRO(l->color.r), NK_TO_CAIRO(l->color.g), NK_TO_CAIRO(l->color.b), NK_TO_CAIRO(l->color.a));
  544. cairo_set_line_width(cr, l->line_thickness);
  545. cairo_move_to(cr, l->begin.x, l->begin.y);
  546. cairo_line_to(cr, l->end.x, l->end.y);
  547. cairo_stroke(cr);
  548. }
  549. break;
  550. case NK_COMMAND_CURVE:
  551. {
  552. const struct nk_command_curve *q = (const struct nk_command_curve *)cmd;
  553. cairo_set_source_rgba(cr, NK_TO_CAIRO(q->color.r), NK_TO_CAIRO(q->color.g), NK_TO_CAIRO(q->color.b), NK_TO_CAIRO(q->color.a));
  554. cairo_set_line_width(cr, q->line_thickness);
  555. cairo_move_to(cr, q->begin.x, q->begin.y);
  556. cairo_curve_to(cr, q->ctrl[0].x, q->ctrl[0].y, q->ctrl[1].x, q->ctrl[1].y, q->end.x, q->end.y);
  557. cairo_stroke(cr);
  558. }
  559. break;
  560. case NK_COMMAND_RECT:
  561. {
  562. const struct nk_command_rect *r = (const struct nk_command_rect *)cmd;
  563. cairo_set_source_rgba(cr, NK_TO_CAIRO(r->color.r), NK_TO_CAIRO(r->color.g), NK_TO_CAIRO(r->color.b), NK_TO_CAIRO(r->color.a));
  564. cairo_set_line_width(cr, r->line_thickness);
  565. if (r->rounding == 0) {
  566. cairo_rectangle(cr, r->x, r->y, r->w, r->h);
  567. }
  568. else {
  569. int xl = r->x + r->w - r->rounding;
  570. int xr = r->x + r->rounding;
  571. int yl = r->y + r->h - r->rounding;
  572. int yr = r->y + r->rounding;
  573. cairo_new_sub_path(cr);
  574. cairo_arc(cr, xl, yr, r->rounding, NK_XCB_DEG_TO_RAD(-90), NK_XCB_DEG_TO_RAD(0));
  575. cairo_arc(cr, xl, yl, r->rounding, NK_XCB_DEG_TO_RAD(0), NK_XCB_DEG_TO_RAD(90));
  576. cairo_arc(cr, xr, yl, r->rounding, NK_XCB_DEG_TO_RAD(90), NK_XCB_DEG_TO_RAD(180));
  577. cairo_arc(cr, xr, yr, r->rounding, NK_XCB_DEG_TO_RAD(180), NK_XCB_DEG_TO_RAD(270));
  578. cairo_close_path(cr);
  579. }
  580. cairo_stroke(cr);
  581. }
  582. break;
  583. case NK_COMMAND_RECT_FILLED:
  584. {
  585. const struct nk_command_rect_filled *r = (const struct nk_command_rect_filled *)cmd;
  586. cairo_set_source_rgba(cr, NK_TO_CAIRO(r->color.r), NK_TO_CAIRO(r->color.g), NK_TO_CAIRO(r->color.b), NK_TO_CAIRO(r->color.a));
  587. if (r->rounding == 0) {
  588. cairo_rectangle(cr, r->x, r->y, r->w, r->h);
  589. } else {
  590. int xl = r->x + r->w - r->rounding;
  591. int xr = r->x + r->rounding;
  592. int yl = r->y + r->h - r->rounding;
  593. int yr = r->y + r->rounding;
  594. cairo_new_sub_path(cr);
  595. cairo_arc(cr, xl, yr, r->rounding, NK_XCB_DEG_TO_RAD(-90), NK_XCB_DEG_TO_RAD(0));
  596. cairo_arc(cr, xl, yl, r->rounding, NK_XCB_DEG_TO_RAD(0), NK_XCB_DEG_TO_RAD(90));
  597. cairo_arc(cr, xr, yl, r->rounding, NK_XCB_DEG_TO_RAD(90), NK_XCB_DEG_TO_RAD(180));
  598. cairo_arc(cr, xr, yr, r->rounding, NK_XCB_DEG_TO_RAD(180), NK_XCB_DEG_TO_RAD(270));
  599. cairo_close_path(cr);
  600. }
  601. cairo_fill(cr);
  602. }
  603. break;
  604. case NK_COMMAND_RECT_MULTI_COLOR:
  605. {
  606. /* from https://github.com/taiwins/twidgets/blob/master/src/nk_wl_cairo.c */
  607. const struct nk_command_rect_multi_color *r = (const struct nk_command_rect_multi_color *)cmd;
  608. cairo_pattern_t *pat = cairo_pattern_create_mesh();
  609. if (pat) {
  610. cairo_mesh_pattern_begin_patch(pat);
  611. cairo_mesh_pattern_move_to(pat, r->x, r->y);
  612. cairo_mesh_pattern_line_to(pat, r->x, r->y + r->h);
  613. cairo_mesh_pattern_line_to(pat, r->x + r->w, r->y + r->h);
  614. cairo_mesh_pattern_line_to(pat, r->x + r->w, r->y);
  615. cairo_mesh_pattern_set_corner_color_rgba(pat, 0, NK_TO_CAIRO(r->left.r), NK_TO_CAIRO(r->left.g), NK_TO_CAIRO(r->left.b), NK_TO_CAIRO(r->left.a));
  616. cairo_mesh_pattern_set_corner_color_rgba(pat, 1, NK_TO_CAIRO(r->bottom.r), NK_TO_CAIRO(r->bottom.g), NK_TO_CAIRO(r->bottom.b), NK_TO_CAIRO(r->bottom.a));
  617. cairo_mesh_pattern_set_corner_color_rgba(pat, 2, NK_TO_CAIRO(r->right.r), NK_TO_CAIRO(r->right.g), NK_TO_CAIRO(r->right.b), NK_TO_CAIRO(r->right.a));
  618. cairo_mesh_pattern_set_corner_color_rgba(pat, 3, NK_TO_CAIRO(r->top.r), NK_TO_CAIRO(r->top.g), NK_TO_CAIRO(r->top.b), NK_TO_CAIRO(r->top.a));
  619. cairo_mesh_pattern_end_patch(pat);
  620. cairo_rectangle(cr, r->x, r->y, r->w, r->h);
  621. cairo_set_source(cr, pat);
  622. cairo_fill(cr);
  623. cairo_pattern_destroy(pat);
  624. }
  625. }
  626. break;
  627. case NK_COMMAND_CIRCLE:
  628. {
  629. const struct nk_command_circle *c = (const struct nk_command_circle *)cmd;
  630. cairo_set_source_rgba(cr, NK_TO_CAIRO(c->color.r), NK_TO_CAIRO(c->color.g), NK_TO_CAIRO(c->color.b), NK_TO_CAIRO(c->color.a));
  631. cairo_set_line_width(cr, c->line_thickness);
  632. cairo_save(cr);
  633. cairo_translate(cr, c->x + c->w / 2.0, c->y + c->h / 2.0);
  634. cairo_scale(cr, c->w / 2.0, c->h / 2.0);
  635. cairo_arc(cr, 0, 0, 1, NK_XCB_DEG_TO_RAD(0), NK_XCB_DEG_TO_RAD(360));
  636. cairo_restore(cr);
  637. cairo_stroke(cr);
  638. }
  639. break;
  640. case NK_COMMAND_CIRCLE_FILLED:
  641. {
  642. const struct nk_command_circle_filled *c = (const struct nk_command_circle_filled *)cmd;
  643. cairo_set_source_rgba(cr, NK_TO_CAIRO(c->color.r), NK_TO_CAIRO(c->color.g), NK_TO_CAIRO(c->color.b), NK_TO_CAIRO(c->color.a));
  644. cairo_save(cr);
  645. cairo_translate(cr, c->x + c->w / 2.0, c->y + c->h / 2.0);
  646. cairo_scale(cr, c->w / 2.0, c->h / 2.0);
  647. cairo_arc(cr, 0, 0, 1, NK_XCB_DEG_TO_RAD(0), NK_XCB_DEG_TO_RAD(360));
  648. cairo_restore(cr);
  649. cairo_fill(cr);
  650. }
  651. break;
  652. case NK_COMMAND_ARC:
  653. {
  654. const struct nk_command_arc *a = (const struct nk_command_arc*) cmd;
  655. cairo_set_source_rgba(cr, NK_TO_CAIRO(a->color.r), NK_TO_CAIRO(a->color.g), NK_TO_CAIRO(a->color.b), NK_TO_CAIRO(a->color.a));
  656. cairo_set_line_width(cr, a->line_thickness);
  657. cairo_arc(cr, a->cx, a->cy, a->r, NK_XCB_DEG_TO_RAD(a->a[0]), NK_XCB_DEG_TO_RAD(a->a[1]));
  658. cairo_stroke(cr);
  659. }
  660. break;
  661. case NK_COMMAND_ARC_FILLED:
  662. {
  663. const struct nk_command_arc_filled *a = (const struct nk_command_arc_filled*)cmd;
  664. cairo_set_source_rgba(cr, NK_TO_CAIRO(a->color.r), NK_TO_CAIRO(a->color.g), NK_TO_CAIRO(a->color.b), NK_TO_CAIRO(a->color.a));
  665. cairo_arc(cr, a->cx, a->cy, a->r, NK_XCB_DEG_TO_RAD(a->a[0]), NK_XCB_DEG_TO_RAD(a->a[1]));
  666. cairo_fill(cr);
  667. }
  668. break;
  669. case NK_COMMAND_TRIANGLE:
  670. {
  671. const struct nk_command_triangle *t = (const struct nk_command_triangle *)cmd;
  672. cairo_set_source_rgba(cr, NK_TO_CAIRO(t->color.r), NK_TO_CAIRO(t->color.g), NK_TO_CAIRO(t->color.b), NK_TO_CAIRO(t->color.a));
  673. cairo_set_line_width(cr, t->line_thickness);
  674. cairo_move_to(cr, t->a.x, t->a.y);
  675. cairo_line_to(cr, t->b.x, t->b.y);
  676. cairo_line_to(cr, t->c.x, t->c.y);
  677. cairo_close_path(cr);
  678. cairo_stroke(cr);
  679. }
  680. break;
  681. case NK_COMMAND_TRIANGLE_FILLED:
  682. {
  683. const struct nk_command_triangle_filled *t = (const struct nk_command_triangle_filled *)cmd;
  684. cairo_set_source_rgba(cr, NK_TO_CAIRO(t->color.r), NK_TO_CAIRO(t->color.g), NK_TO_CAIRO(t->color.b), NK_TO_CAIRO(t->color.a));
  685. cairo_move_to(cr, t->a.x, t->a.y);
  686. cairo_line_to(cr, t->b.x, t->b.y);
  687. cairo_line_to(cr, t->c.x, t->c.y);
  688. cairo_close_path(cr);
  689. cairo_fill(cr);
  690. }
  691. break;
  692. case NK_COMMAND_POLYGON:
  693. {
  694. int i;
  695. const struct nk_command_polygon *p = (const struct nk_command_polygon *)cmd;
  696. cairo_set_source_rgba(cr, NK_TO_CAIRO(p->color.r), NK_TO_CAIRO(p->color.g), NK_TO_CAIRO(p->color.b), NK_TO_CAIRO(p->color.a));
  697. cairo_set_line_width(cr, p->line_thickness);
  698. cairo_move_to(cr, p->points[0].x, p->points[0].y);
  699. for (i = 1; i < p->point_count; ++i) {
  700. cairo_line_to(cr, p->points[i].x, p->points[i].y);
  701. }
  702. cairo_close_path(cr);
  703. cairo_stroke(cr);
  704. }
  705. break;
  706. case NK_COMMAND_POLYGON_FILLED:
  707. {
  708. int i;
  709. const struct nk_command_polygon_filled *p = (const struct nk_command_polygon_filled *)cmd;
  710. cairo_set_source_rgba (cr, NK_TO_CAIRO(p->color.r), NK_TO_CAIRO(p->color.g), NK_TO_CAIRO(p->color.b), NK_TO_CAIRO(p->color.a));
  711. cairo_move_to(cr, p->points[0].x, p->points[0].y);
  712. for (i = 1; i < p->point_count; ++i) {
  713. cairo_line_to(cr, p->points[i].x, p->points[i].y);
  714. }
  715. cairo_close_path(cr);
  716. cairo_fill(cr);
  717. }
  718. break;
  719. case NK_COMMAND_POLYLINE:
  720. {
  721. int i;
  722. const struct nk_command_polyline *p = (const struct nk_command_polyline *)cmd;
  723. cairo_set_source_rgba(cr, NK_TO_CAIRO(p->color.r), NK_TO_CAIRO(p->color.g), NK_TO_CAIRO(p->color.b), NK_TO_CAIRO(p->color.a));
  724. cairo_set_line_width(cr, p->line_thickness);
  725. cairo_move_to(cr, p->points[0].x, p->points[0].y);
  726. for (i = 1; i < p->point_count; ++i) {
  727. cairo_line_to(cr, p->points[i].x, p->points[i].y);
  728. }
  729. cairo_stroke(cr);
  730. }
  731. break;
  732. case NK_COMMAND_TEXT:
  733. {
  734. const struct nk_command_text *t = (const struct nk_command_text *)cmd;
  735. cairo_glyph_t *glyphs = NULL;
  736. int num_glyphs;
  737. cairo_text_cluster_t *clusters = NULL;
  738. int num_clusters;
  739. cairo_text_cluster_flags_t cluster_flags;
  740. cairo_font_extents_t extents;
  741. cairo_set_source_rgba(cr, NK_TO_CAIRO(t->foreground.r), NK_TO_CAIRO(t->foreground.g), NK_TO_CAIRO(t->foreground.b), NK_TO_CAIRO(t->foreground.a));
  742. cairo_scaled_font_extents((cairo_scaled_font_t *)t->font->userdata.ptr, &extents);
  743. cairo_scaled_font_text_to_glyphs((cairo_scaled_font_t *)t->font->userdata.ptr,
  744. t->x, t->y + extents.ascent, t->string, t->length,
  745. &glyphs, &num_glyphs, &clusters, &num_clusters,
  746. &cluster_flags);
  747. cairo_show_text_glyphs(cr, t->string, t->length, glyphs,
  748. num_glyphs, clusters, num_clusters,
  749. cluster_flags);
  750. cairo_glyph_free(glyphs);
  751. cairo_text_cluster_free(clusters);
  752. }
  753. break;
  754. case NK_COMMAND_IMAGE:
  755. {
  756. /* from https://github.com/taiwins/twidgets/blob/master/src/nk_wl_cairo.c */
  757. const struct nk_command_image *im = (const struct nk_command_image *)cmd;
  758. cairo_surface_t *img_surf;
  759. double sw = (double)im->w / (double)im->img.region[2];
  760. double sh = (double)im->h / (double)im->img.region[3];
  761. cairo_format_t format = CAIRO_FORMAT_ARGB32;
  762. int stride = cairo_format_stride_for_width(format, im->img.w);
  763. if (!im->img.handle.ptr) return nk_false;
  764. img_surf = cairo_image_surface_create_for_data((unsigned char *)im->img.handle.ptr, format, im->img.w, im->img.h, stride);
  765. if (!img_surf) return nk_false;
  766. cairo_save(cr);
  767. cairo_rectangle(cr, im->x, im->y, im->w, im->h);
  768. /* scale here, if after source set, the scale would not apply to source
  769. * surface
  770. */
  771. cairo_scale(cr, sw, sh);
  772. /* the coordinates system in cairo is not intuitive, scale, translate,
  773. * are applied to source. Refer to
  774. * "https://www.cairographics.org/FAQ/#paint_from_a_surface" for details
  775. *
  776. * if you set source_origin to (0,0), it would be like source origin
  777. * aligned to dest origin, then if you draw a rectangle on (x, y, w, h).
  778. * it would clip out the (x, y, w, h) of the source on you dest as well.
  779. */
  780. cairo_set_source_surface(cr, img_surf, im->x/sw - im->img.region[0], im->y/sh - im->img.region[1]);
  781. cairo_fill(cr);
  782. cairo_restore(cr);
  783. cairo_surface_destroy(img_surf);
  784. }
  785. break;
  786. case NK_COMMAND_CUSTOM:
  787. {
  788. const struct nk_command_custom *cu = (const struct nk_command_custom *)cmd;
  789. if (cu->callback) {
  790. cu->callback(cr, cu->x, cu->y, cu->w, cu->h, cu->callback_data);
  791. }
  792. }
  793. default:
  794. break;
  795. }
  796. }
  797. cairo_pop_group_to_source(cr);
  798. cairo_paint(cr);
  799. cairo_surface_flush(cairo_ctx->surface);
  800. return nk_true;
  801. }
  802. NK_INTERN xkbcommon_context *xkbcommon_init(xcb_connection_t *conn)
  803. {
  804. xkbcommon_context *kbdctx;
  805. int32_t device_id;
  806. int ret = xkb_x11_setup_xkb_extension(conn,
  807. XKB_X11_MIN_MAJOR_XKB_VERSION,
  808. XKB_X11_MIN_MINOR_XKB_VERSION,
  809. XKB_X11_SETUP_XKB_EXTENSION_NO_FLAGS,
  810. NULL, NULL, NULL, NULL);
  811. if (ret == 0)
  812. {
  813. return NULL;
  814. }
  815. kbdctx = (xkbcommon_context *)malloc(sizeof(xkbcommon_context));
  816. kbdctx->ctx = NULL;
  817. kbdctx->keymap = NULL;
  818. kbdctx->state = NULL;
  819. kbdctx->ctx = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
  820. if (!kbdctx->ctx)
  821. {
  822. xkbcommon_free(kbdctx);
  823. return NULL;
  824. }
  825. device_id = xkb_x11_get_core_keyboard_device_id(conn);
  826. if (device_id == -1)
  827. {
  828. xkbcommon_free(kbdctx);
  829. return NULL;
  830. }
  831. kbdctx->keymap = xkb_x11_keymap_new_from_device(kbdctx->ctx, conn, device_id, XKB_KEYMAP_COMPILE_NO_FLAGS);
  832. if (!kbdctx->keymap)
  833. {
  834. xkbcommon_free(kbdctx);
  835. return NULL;
  836. }
  837. kbdctx->state = xkb_x11_state_new_from_device(kbdctx->keymap, conn, device_id);
  838. if (!kbdctx->state)
  839. {
  840. xkbcommon_free(kbdctx);
  841. return NULL;
  842. }
  843. return kbdctx;
  844. }
  845. NK_INTERN void xkbcommon_free(xkbcommon_context *kbdctx)
  846. {
  847. if (kbdctx != NULL)
  848. {
  849. if (kbdctx->state) xkb_state_unref(kbdctx->state);
  850. if (kbdctx->keymap) xkb_keymap_unref(kbdctx->keymap);
  851. if (kbdctx->ctx) xkb_context_unref(kbdctx->ctx);
  852. kbdctx->ctx = NULL;
  853. kbdctx->keymap = NULL;
  854. kbdctx->state = NULL;
  855. free(kbdctx);
  856. }
  857. }
  858. NK_INTERN xkb_keysym_t keycode_to_keysym(nk_xcb_context *ctx, xkb_keycode_t keycode, int pressed)
  859. {
  860. xkb_keysym_t keysym;
  861. xkbcommon_context *kbdctx = ctx->xkbcommon_ctx;
  862. if (kbdctx != NULL)
  863. {
  864. keysym = xkb_state_key_get_one_sym(kbdctx->state, keycode);
  865. /*xkb_state_component changed = */
  866. xkb_state_update_key(kbdctx->state, keycode, pressed ? XKB_KEY_DOWN : XKB_KEY_UP);
  867. }
  868. else
  869. {
  870. keysym = 0;
  871. }
  872. return keysym;
  873. }
  874. #endif /* NK_XCB_CAIRO_IMPLEMENTATION */