Little test that I made to figure out how use the Present extension of X11 with XCB.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

347 lines
8.9KB

  1. #include <pthread.h>
  2. #include <stdlib.h>
  3. #include <stdio.h>
  4. #include <cairo/cairo.h>
  5. #include <cairo/cairo-xcb.h>
  6. #include <xcb/xcb.h>
  7. #include <xcb/present.h>
  8. /************************************************************************************************************/
  9. /************************************************************************************************************/
  10. /************************************************************************************************************/
  11. #define WIN_W 600
  12. #define WIN_H 400
  13. #define RECT_W 100
  14. #define RECT_SPEED 5 /* 300 px/s at 60 Hz */
  15. /************************************************************************************************************/
  16. /************************************************************************************************************/
  17. /************************************************************************************************************/
  18. static void _draw (void);
  19. static void _init_graphics (void);
  20. static void _init_threads (void);
  21. static void _reset (void);
  22. static void * _thread_event (void *p);
  23. static void * _thread_render (void *p);
  24. /************************************************************************************************************/
  25. /************************************************************************************************************/
  26. /************************************************************************************************************/
  27. static xcb_connection_t *_x_con = NULL;
  28. static xcb_screen_t *_x_scr = NULL;
  29. static xcb_visualtype_t *_x_vis = NULL;
  30. static xcb_window_t _x_win = 0;
  31. static xcb_pixmap_t _x_pix = 0;
  32. static xcb_gcontext_t _x_ctx = 0;
  33. static uint8_t _x_opcode = 0;
  34. static cairo_surface_t *_c_srf = NULL;
  35. static cairo_t *_c_ctx = NULL;
  36. static pthread_t _t_id_ev;
  37. static pthread_t _t_id_rd;
  38. static pthread_cond_t _t_cond_rd_1;
  39. static pthread_cond_t _t_cond_rd_2;
  40. static pthread_mutex_t _t_mutex_rd_1;
  41. static pthread_mutex_t _t_mutex_rd_2;
  42. static int _rect_pos = WIN_W;
  43. static int _update = 1;
  44. static int _serial = 0;
  45. /************************************************************************************************************/
  46. /************************************************************************************************************/
  47. /************************************************************************************************************/
  48. int
  49. main(int argc, char **argv)
  50. {
  51. /* initialisation */
  52. _init_graphics();
  53. _init_threads();
  54. /* initial render for first expose event */
  55. _draw();
  56. /* start the threads */
  57. /* the event thread leads the tempo */
  58. pthread_create(&_t_id_ev, NULL, _thread_event, NULL);
  59. pthread_create(&_t_id_rd, NULL, _thread_render, NULL);
  60. pthread_join(_t_id_ev, NULL);
  61. pthread_cancel(_t_id_rd);
  62. pthread_join(_t_id_rd, NULL);
  63. /* end */
  64. _reset();
  65. }
  66. /************************************************************************************************************/
  67. /************************************************************************************************************/
  68. /************************************************************************************************************/
  69. static void
  70. _draw(void)
  71. {
  72. /* update position */
  73. _rect_pos += RECT_SPEED;
  74. if (_rect_pos >= WIN_W) {
  75. _rect_pos = -RECT_W;
  76. }
  77. /* update pixmap */
  78. cairo_set_source_rgb(_c_ctx, 0.3, 0.3, 0.3);
  79. cairo_paint(_c_ctx);
  80. cairo_set_source_rgb(_c_ctx, 0.7, 0.7, 0.7);
  81. cairo_rectangle(_c_ctx, _rect_pos, 0, RECT_W, WIN_H);
  82. cairo_fill(_c_ctx);
  83. cairo_surface_flush(_c_srf);
  84. }
  85. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
  86. static void
  87. _init_graphics(void)
  88. {
  89. /* setup xcb */
  90. _x_con = xcb_connect(NULL, NULL);
  91. _x_scr = xcb_setup_roots_iterator(xcb_get_setup(_x_con)).data;
  92. xcb_visualtype_iterator_t x_vi;
  93. xcb_depth_iterator_t x_di;
  94. x_di = xcb_screen_allowed_depths_iterator(_x_scr);
  95. for (; x_di.rem; xcb_depth_next(&x_di)) {
  96. x_vi = xcb_depth_visuals_iterator(x_di.data);
  97. for (; x_vi.rem; xcb_visualtype_next(&x_vi)) {
  98. if (_x_scr->root_visual == x_vi.data->visual_id) {
  99. _x_vis = x_vi.data;
  100. break;
  101. }
  102. }
  103. }
  104. /* setup generic xcb graphical context for copying the pixmap */
  105. _x_ctx = xcb_generate_id(_x_con);
  106. xcb_create_gc(
  107. _x_con,
  108. _x_ctx,
  109. _x_scr->root,
  110. XCB_GC_FOREGROUND | XCB_GC_GRAPHICS_EXPOSURES,
  111. &(uint32_t[2]){_x_scr->black_pixel, 0});
  112. /* setup xcb window */
  113. uint32_t mask_vals[2];
  114. mask_vals[0] = _x_scr->black_pixel;
  115. mask_vals[1] = XCB_EVENT_MASK_EXPOSURE |
  116. XCB_EVENT_MASK_STRUCTURE_NOTIFY |
  117. XCB_EVENT_MASK_KEY_PRESS |
  118. XCB_EVENT_MASK_BUTTON_PRESS;
  119. _x_win = xcb_generate_id(_x_con);
  120. xcb_create_window(
  121. _x_con,
  122. XCB_COPY_FROM_PARENT,
  123. _x_win,
  124. _x_scr->root,
  125. 0, 0,
  126. WIN_W, WIN_H,
  127. 0,
  128. XCB_WINDOW_CLASS_INPUT_OUTPUT,
  129. _x_scr->root_visual,
  130. XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK,
  131. mask_vals);
  132. xcb_map_window(_x_con, _x_win);
  133. /* setup xcb pixmap */
  134. _x_pix = xcb_generate_id(_x_con);
  135. xcb_create_pixmap(_x_con, _x_scr->root_depth, _x_pix, _x_win, WIN_W, WIN_H);
  136. /* setup xcb present extension */
  137. xcb_present_select_input(
  138. _x_con,
  139. xcb_generate_id(_x_con),
  140. _x_win,
  141. XCB_PRESENT_EVENT_MASK_COMPLETE_NOTIFY);
  142. /* dynamicaly get the present extention opcode for event identification */
  143. xcb_query_extension_reply_t *r =
  144. xcb_query_extension_reply(_x_con, xcb_query_extension(_x_con, 7, "Present"), NULL);
  145. _x_opcode = r->major_opcode;
  146. free(r);
  147. /* setup cairo drawable */
  148. _c_srf = cairo_xcb_surface_create(_x_con, _x_pix, _x_vis, WIN_W, WIN_H);
  149. _c_ctx = cairo_create(_c_srf);
  150. /* end */
  151. cairo_surface_flush(_c_srf);
  152. xcb_flush(_x_con);
  153. }
  154. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
  155. static void
  156. _init_threads(void)
  157. {
  158. pthread_mutex_init(&_t_mutex_rd_1, NULL);
  159. pthread_mutex_init(&_t_mutex_rd_2, NULL);
  160. pthread_cond_init(&_t_cond_rd_1, NULL);
  161. pthread_cond_init(&_t_cond_rd_2, NULL);
  162. }
  163. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
  164. void
  165. _reset(void)
  166. {
  167. pthread_mutex_destroy(&_t_mutex_rd_1);
  168. pthread_mutex_destroy(&_t_mutex_rd_2);
  169. pthread_cond_destroy(&_t_cond_rd_1);
  170. pthread_cond_destroy(&_t_cond_rd_2);
  171. cairo_destroy(_c_ctx);
  172. cairo_surface_destroy(_c_srf);
  173. xcb_free_gc(_x_con, _x_ctx);
  174. xcb_free_pixmap(_x_con, _x_pix);
  175. xcb_destroy_window(_x_con, _x_win);
  176. xcb_disconnect(_x_con);
  177. }
  178. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
  179. static void *
  180. _thread_event(void *p)
  181. {
  182. int run = 1;
  183. int x_pev_serial;
  184. xcb_generic_event_t *x_ev;
  185. xcb_ge_generic_event_t *x_gev;
  186. xcb_present_complete_notify_event_t *x_pev;
  187. while (run && (x_ev = xcb_wait_for_event(_x_con))) {
  188. switch (x_ev->response_type & ~0x80) {
  189. case XCB_EXPOSE:
  190. /* immediate update without syncing to the refresh rate */
  191. /* without it there are visual glitches when resizing the window */
  192. xcb_copy_area(_x_con, _x_pix, _x_win, _x_ctx, 0, 0, 0, 0, WIN_W, WIN_H);
  193. break;
  194. case XCB_BUTTON_PRESS:
  195. pthread_mutex_lock(&_t_mutex_rd_2);
  196. _update = !_update;
  197. pthread_mutex_unlock(&_t_mutex_rd_2);
  198. pthread_cond_signal(&_t_cond_rd_2);
  199. break;
  200. case XCB_KEY_PRESS:
  201. run = 0;
  202. break;
  203. case XCB_GE_GENERIC:
  204. /* most of this is just checking that we got the right event */
  205. /* unecessary in simple cases */
  206. x_gev = (xcb_ge_generic_event_t*)x_ev;
  207. if (x_gev->extension == _x_opcode && x_gev->event_type == XCB_PRESENT_EVENT_COMPLETE_NOTIFY) {
  208. x_pev = (xcb_present_complete_notify_event_t*)x_ev;
  209. pthread_mutex_lock(&_t_mutex_rd_2);
  210. x_pev_serial = _serial;
  211. pthread_mutex_unlock(&_t_mutex_rd_2);
  212. if (x_pev->kind == XCB_PRESENT_COMPLETE_KIND_PIXMAP && x_pev->serial == x_pev_serial) {
  213. pthread_cond_signal(&_t_cond_rd_1);
  214. }
  215. }
  216. break;
  217. }
  218. xcb_flush(_x_con);
  219. free(x_ev);
  220. }
  221. pthread_exit(NULL);
  222. }
  223. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
  224. static void *
  225. _thread_render(void *p)
  226. {
  227. pthread_mutex_lock(&_t_mutex_rd_1);
  228. while (1) {
  229. /* check if an update is needed, if not, wait */
  230. pthread_mutex_lock(&_t_mutex_rd_2);
  231. if (!_update) {
  232. pthread_cond_wait(&_t_cond_rd_2, &_t_mutex_rd_2);
  233. }
  234. pthread_mutex_unlock(&_t_mutex_rd_2);
  235. /* update pixmap */
  236. _draw();
  237. /* update window with pixmap */
  238. _serial++;
  239. xcb_present_pixmap(
  240. _x_con,
  241. _x_win,
  242. _x_pix,
  243. _serial,
  244. XCB_XFIXES_REGION_NONE,
  245. XCB_XFIXES_REGION_NONE,
  246. 0, 0,
  247. 0,
  248. 0,
  249. 0,
  250. XCB_PRESENT_OPTION_NONE,
  251. 0, 0, 0,
  252. 0,
  253. NULL);
  254. /* important af, don't forget it */
  255. xcb_flush(_x_con);
  256. /* wait for present event notification in the event thread */
  257. pthread_cond_wait(&_t_cond_rd_1, &_t_mutex_rd_1);
  258. }
  259. pthread_mutex_unlock(&_t_mutex_rd_1);
  260. pthread_exit(NULL);
  261. }