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.

341 line
8.1KB

  1. #include <stdlib.h>
  2. #include <stdio.h>
  3. #include <string.h>
  4. #include <time.h>
  5. #include <unistd.h>
  6. #include <cairo/cairo.h>
  7. #include <cairo/cairo-xcb.h>
  8. #include <xcb/xcb.h>
  9. #include <xcb/present.h>
  10. #include <xcb/xfixes.h>
  11. #include <xcb/randr.h>
  12. /************************************************************************************************************/
  13. /* _ ********************************************************************************************************/
  14. /************************************************************************************************************/
  15. static void _draw (void);
  16. static void _init_cairo (void);
  17. static void _init_xcb (void);
  18. static void _reset (void);
  19. static void _run (void);
  20. /************************************************************************************************************/
  21. /************************************************************************************************************/
  22. /************************************************************************************************************/
  23. static uint32_t _opt_frame_divider = 1;
  24. static int _opt_async = 0;
  25. static xcb_connection_t *_x_con = NULL;
  26. static xcb_screen_t *_x_scr = NULL;
  27. static xcb_visualtype_t *_x_vis = NULL;
  28. static xcb_special_event_t *_x_sev = NULL;
  29. static xcb_window_t _x_win = 0;
  30. static xcb_pixmap_t _x_pix = 0;
  31. static cairo_surface_t *_c_srf = NULL;
  32. static cairo_t *_c_ctx = NULL;
  33. static int _speed1 = 60;
  34. static int _speed2 = 5;
  35. static double _pos1 = 0;
  36. static double _inc1 = 1;
  37. static double _pos2 = 0;
  38. static double _inc2 = 1;
  39. static long _t_full = 0;
  40. static long _t_paint = 0;
  41. static long _t_xsync = 0;
  42. static double _delay = 0;
  43. static double _fps = 0;
  44. static long _total = 0;
  45. static uint32_t stamp = 0;
  46. /************************************************************************************************************/
  47. /************************************************************************************************************/
  48. /************************************************************************************************************/
  49. int
  50. main(int argc, char **argv)
  51. {
  52. /* options */
  53. int opt;
  54. while ((opt = getopt(argc, argv, "al:")) != -1) {
  55. switch (opt) {
  56. case 'a':
  57. _opt_async = 1;
  58. break;
  59. case 'l':
  60. _opt_frame_divider = (unsigned int)atoi(optarg);
  61. break;
  62. }
  63. }
  64. /* program */
  65. _init_xcb();
  66. _init_cairo();
  67. _run();
  68. _reset();
  69. return 0;
  70. }
  71. /************************************************************************************************************/
  72. /* _ ********************************************************************************************************/
  73. /************************************************************************************************************/
  74. static void
  75. _draw(void)
  76. {
  77. /* background */
  78. cairo_set_source_rgb(_c_ctx, 0.1, 0.1, 0.1);
  79. cairo_rectangle(_c_ctx, 0, 0, 700, 700);
  80. cairo_fill(_c_ctx);
  81. /* big square */
  82. double adv1 = (double)(_speed1 * _t_full) / 1000000.0;
  83. _pos1 += adv1 * _inc1;
  84. while (_pos1 > 600.0 || _pos1 < 0.0) {
  85. if (_pos1 > 600.0) {
  86. _inc1 = -1;
  87. adv1 = _pos1 - 600;
  88. _pos1 = 600;
  89. } else {
  90. _inc1 = 1;
  91. adv1 = -_pos1;
  92. _pos1 = 0;
  93. }
  94. _pos1 += adv1 * _inc1;
  95. }
  96. cairo_set_source_rgb(_c_ctx, 0.8, 0.8, 0.8);
  97. cairo_rectangle(_c_ctx, _pos1, _pos1, 100, 100);
  98. cairo_fill(_c_ctx);
  99. /* small square */
  100. double adv2 = (double)(_speed2 * _t_full) / 1000000.0;
  101. _pos2 += adv2 * _inc2;
  102. while (_pos2 > 80.0 || _pos2 < 0.0) {
  103. if (_pos2 > 80.0) {
  104. _inc2 = -1;
  105. adv2 = _pos2 - 80;
  106. _pos2 = 80;
  107. } else {
  108. _inc2 = 1;
  109. adv2 = -_pos2;
  110. _pos2 = 0;
  111. }
  112. _pos2 += adv2 * _inc2;
  113. }
  114. cairo_set_source_rgb(_c_ctx, 0.3, 0.3, 0.3);
  115. cairo_rectangle(_c_ctx, _pos1 + 80 - _pos2, _pos1 + _pos2, 20, 20);
  116. cairo_fill(_c_ctx);
  117. /* counters */
  118. char str[128];
  119. cairo_set_source_rgb(_c_ctx, 1.0, 1.0, 1.0);
  120. sprintf(str, "fps : %f", _fps);
  121. cairo_move_to(_c_ctx, 25, 555);
  122. cairo_show_text(_c_ctx, str);
  123. sprintf(str, "frame time (µs) : %li", _t_full);
  124. cairo_move_to(_c_ctx, 25, 585);
  125. cairo_show_text(_c_ctx, str);
  126. sprintf(str, "paint time (µs) : %li", _t_paint);
  127. cairo_move_to(_c_ctx, 25, 615);
  128. cairo_show_text(_c_ctx, str);
  129. sprintf(str, "x sync time (µs) : %li", _t_xsync);
  130. cairo_move_to(_c_ctx, 25, 645);
  131. cairo_show_text(_c_ctx, str);
  132. sprintf(str, "total frames : %li", _total);
  133. cairo_move_to(_c_ctx, 25, 675);
  134. cairo_show_text(_c_ctx, str);
  135. }
  136. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
  137. static void
  138. _init_cairo(void)
  139. {
  140. _c_srf = cairo_xcb_surface_create(_x_con, _x_pix, _x_vis, 700, 700);
  141. _c_ctx = cairo_create(_c_srf);
  142. cairo_set_font_size(_c_ctx, 20);
  143. cairo_select_font_face(_c_ctx, "Terminus", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL);
  144. cairo_surface_flush(_c_srf);
  145. }
  146. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
  147. static void
  148. _init_xcb(void)
  149. {
  150. /* connection */
  151. _x_con = xcb_connect(NULL, NULL);
  152. _x_scr = xcb_setup_roots_iterator(xcb_get_setup(_x_con)).data;
  153. xcb_visualtype_iterator_t x_vi;
  154. xcb_depth_iterator_t x_di;
  155. x_di = xcb_screen_allowed_depths_iterator(_x_scr);
  156. for (; x_di.rem; xcb_depth_next(&x_di)) {
  157. x_vi = xcb_depth_visuals_iterator(x_di.data);
  158. for (; x_vi.rem; xcb_visualtype_next(&x_vi)) {
  159. if (_x_scr->root_visual == x_vi.data->visual_id) {
  160. _x_vis = x_vi.data;
  161. break;
  162. }
  163. }
  164. }
  165. /* window */
  166. uint32_t mask_vals[2];
  167. mask_vals[0] = _x_scr->black_pixel;
  168. mask_vals[1] = XCB_EVENT_MASK_EXPOSURE;
  169. _x_win = xcb_generate_id(_x_con);
  170. xcb_create_window(
  171. _x_con,
  172. XCB_COPY_FROM_PARENT,
  173. _x_win,
  174. _x_scr->root,
  175. 0, 0,
  176. 700, 700,
  177. 0,
  178. XCB_WINDOW_CLASS_INPUT_OUTPUT,
  179. _x_scr->root_visual,
  180. XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK,
  181. mask_vals);
  182. xcb_map_window(_x_con, _x_win);
  183. xcb_flush(_x_con);
  184. /* pixmap */
  185. _x_pix = xcb_generate_id(_x_con);
  186. xcb_create_pixmap(_x_con, _x_scr->root_depth, _x_pix, _x_win, 700, 700);
  187. /* present extension */
  188. uint32_t id = xcb_generate_id(_x_con);
  189. xcb_present_select_input(
  190. _x_con,
  191. id,
  192. _x_win,
  193. XCB_PRESENT_EVENT_MASK_COMPLETE_NOTIFY);
  194. /* optional setup a special event queue for the present extension */
  195. /* if not done present completion events can still be recoved from the regular */
  196. /* xcb_wait_for_event() queue */
  197. _x_sev = xcb_register_for_special_xge(_x_con, &xcb_present_id, id, &stamp);
  198. /* get frame delay */
  199. xcb_randr_get_screen_info_cookie_t k = xcb_randr_get_screen_info(_x_con, _x_scr->root);
  200. xcb_randr_get_screen_info_reply_t *r = xcb_randr_get_screen_info_reply(_x_con, k, NULL);
  201. _delay = 1000000.0 / (double)r->rate;
  202. free(r);
  203. }
  204. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
  205. static void
  206. _reset(void)
  207. {
  208. cairo_destroy(_c_ctx);
  209. cairo_surface_destroy(_c_srf);
  210. xcb_free_pixmap(_x_con, _x_pix);
  211. xcb_destroy_window(_x_con, _x_win);
  212. xcb_disconnect(_x_con);
  213. }
  214. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
  215. static void
  216. _run(void)
  217. {
  218. xcb_generic_event_t *ev = NULL;
  219. int n = 0;
  220. long t1 = 0;
  221. long t2 = 0;
  222. long t3 = 0;
  223. struct timespec ts = {0};
  224. while (1) {
  225. _total++;
  226. clock_gettime(CLOCK_MONOTONIC, &ts);
  227. t1 = ts.tv_nsec / 1000 + ts.tv_sec * 1000000;
  228. _draw();
  229. clock_gettime(CLOCK_MONOTONIC, &ts);
  230. t2 = ts.tv_nsec / 1000 + ts.tv_sec * 1000000;
  231. xcb_present_pixmap(
  232. _x_con,
  233. _x_win,
  234. _x_pix,
  235. 123456,
  236. XCB_XFIXES_REGION_NONE,
  237. XCB_XFIXES_REGION_NONE,
  238. 0, 0,
  239. 0,
  240. 0,
  241. 0,
  242. _opt_async ? XCB_PRESENT_OPTION_ASYNC : XCB_PRESENT_OPTION_NONE,
  243. 0, _opt_frame_divider, 0,
  244. 0,
  245. NULL);
  246. xcb_flush(_x_con);
  247. clock_gettime(CLOCK_MONOTONIC, &ts);
  248. t3 = ts.tv_nsec / 1000 + ts.tv_sec * 1000000;
  249. _t_paint = t2 - t1;
  250. _t_xsync = t3 - t2;
  251. ev = xcb_wait_for_special_event(_x_con, _x_sev);
  252. if (!ev) {
  253. printf("exiting\n");
  254. return;
  255. }
  256. free(ev);
  257. clock_gettime(CLOCK_MONOTONIC, &ts);
  258. _t_full = ts.tv_nsec / 1000 + ts.tv_sec * 1000000 - t1;
  259. _fps = 1000000.0 / (double)_t_full;
  260. }
  261. }