diff --git a/makefile b/makefile new file mode 100644 index 0000000..030b887 --- /dev/null +++ b/makefile @@ -0,0 +1,2 @@ +test: test.c + gcc -o test test.c -lxcb -lxcb-util -lcairo -lrt -lxcb-present -lxcb-randr diff --git a/test.c b/test.c new file mode 100644 index 0000000..dc878cb --- /dev/null +++ b/test.c @@ -0,0 +1,340 @@ +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +/************************************************************************************************************/ +/* _ ********************************************************************************************************/ +/************************************************************************************************************/ + +static void _draw (void); +static void _init_cairo (void); +static void _init_xcb (void); +static void _reset (void); +static void _run (void); + +/************************************************************************************************************/ +/************************************************************************************************************/ +/************************************************************************************************************/ + +static uint32_t _opt_frame_divider = 1; +static int _opt_async = 0; + +static xcb_connection_t *_x_con = NULL; +static xcb_screen_t *_x_scr = NULL; +static xcb_visualtype_t *_x_vis = NULL; +static xcb_special_event_t *_x_sev = NULL; +static xcb_window_t _x_win = 0; +static xcb_pixmap_t _x_pix = 0; + +static cairo_surface_t *_c_srf = NULL; +static cairo_t *_c_ctx = NULL; + +static int _speed1 = 60; +static int _speed2 = 5; + +static double _pos1 = 0; +static double _inc1 = 1; + +static double _pos2 = 0; +static double _inc2 = 1; + +static long _t_full = 0; +static long _t_paint = 0; +static long _t_xsync = 0; + +static double _delay = 0; +static double _fps = 0; +static long _total = 0; + +static uint32_t stamp = 0; + +/************************************************************************************************************/ +/************************************************************************************************************/ +/************************************************************************************************************/ + +int +main(int argc, char **argv) +{ + /* options */ + + int opt; + while ((opt = getopt(argc, argv, "al:")) != -1) { + switch (opt) { + + case 'a': + _opt_async = 1; + break; + + case 'l': + _opt_frame_divider = (unsigned int)atoi(optarg); + break; + } + } + + /* program */ + + _init_xcb(); + _init_cairo(); + _run(); + _reset(); + + return 0; +} + +/************************************************************************************************************/ +/* _ ********************************************************************************************************/ +/************************************************************************************************************/ + +static void +_draw(void) +{ + /* background */ + + cairo_set_source_rgb(_c_ctx, 0.1, 0.1, 0.1); + cairo_rectangle(_c_ctx, 0, 0, 700, 700); + cairo_fill(_c_ctx); + + /* big square */ + + double adv1 = (double)(_speed1 * _t_full) / 1000000.0; + _pos1 += adv1 * _inc1; + while (_pos1 > 600.0 || _pos1 < 0.0) { + if (_pos1 > 600.0) { + _inc1 = -1; + adv1 = _pos1 - 600; + _pos1 = 600; + } else { + _inc1 = 1; + adv1 = -_pos1; + _pos1 = 0; + } + _pos1 += adv1 * _inc1; + } + + cairo_set_source_rgb(_c_ctx, 0.8, 0.8, 0.8); + cairo_rectangle(_c_ctx, _pos1, _pos1, 100, 100); + cairo_fill(_c_ctx); + + /* small square */ + + double adv2 = (double)(_speed2 * _t_full) / 1000000.0; + _pos2 += adv2 * _inc2; + while (_pos2 > 80.0 || _pos2 < 0.0) { + if (_pos2 > 80.0) { + _inc2 = -1; + adv2 = _pos2 - 80; + _pos2 = 80; + } else { + _inc2 = 1; + adv2 = -_pos2; + _pos2 = 0; + } + _pos2 += adv2 * _inc2; + } + + cairo_set_source_rgb(_c_ctx, 0.3, 0.3, 0.3); + cairo_rectangle(_c_ctx, _pos1 + 80 - _pos2, _pos1 + _pos2, 20, 20); + cairo_fill(_c_ctx); + + /* counters */ + + char str[128]; + cairo_set_source_rgb(_c_ctx, 1.0, 1.0, 1.0); + + sprintf(str, "fps : %f", _fps); + cairo_move_to(_c_ctx, 25, 555); + cairo_show_text(_c_ctx, str); + + sprintf(str, "frame time (µs) : %li", _t_full); + cairo_move_to(_c_ctx, 25, 585); + cairo_show_text(_c_ctx, str); + + sprintf(str, "paint time (µs) : %li", _t_paint); + cairo_move_to(_c_ctx, 25, 615); + cairo_show_text(_c_ctx, str); + + sprintf(str, "x sync time (µs) : %li", _t_xsync); + cairo_move_to(_c_ctx, 25, 645); + cairo_show_text(_c_ctx, str); + + sprintf(str, "total frames : %li", _total); + cairo_move_to(_c_ctx, 25, 675); + cairo_show_text(_c_ctx, str); +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ + +static void +_init_cairo(void) +{ + _c_srf = cairo_xcb_surface_create(_x_con, _x_pix, _x_vis, 700, 700); + _c_ctx = cairo_create(_c_srf); + + cairo_set_font_size(_c_ctx, 20); + cairo_select_font_face(_c_ctx, "Terminus", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL); + + cairo_surface_flush(_c_srf); +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ + +static void +_init_xcb(void) +{ + /* connection */ + + _x_con = xcb_connect(NULL, NULL); + _x_scr = xcb_setup_roots_iterator(xcb_get_setup(_x_con)).data; + + xcb_visualtype_iterator_t x_vi; + xcb_depth_iterator_t x_di; + + x_di = xcb_screen_allowed_depths_iterator(_x_scr); + for (; x_di.rem; xcb_depth_next(&x_di)) { + x_vi = xcb_depth_visuals_iterator(x_di.data); + for (; x_vi.rem; xcb_visualtype_next(&x_vi)) { + if (_x_scr->root_visual == x_vi.data->visual_id) { + _x_vis = x_vi.data; + break; + } + } + } + + /* window */ + + uint32_t mask_vals[2]; + mask_vals[0] = _x_scr->black_pixel; + mask_vals[1] = XCB_EVENT_MASK_EXPOSURE; + + _x_win = xcb_generate_id(_x_con); + xcb_create_window( + _x_con, + XCB_COPY_FROM_PARENT, + _x_win, + _x_scr->root, + 0, 0, + 700, 700, + 0, + XCB_WINDOW_CLASS_INPUT_OUTPUT, + _x_scr->root_visual, + XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK, + mask_vals); + + xcb_map_window(_x_con, _x_win); + xcb_flush(_x_con); + + /* pixmap */ + + _x_pix = xcb_generate_id(_x_con); + xcb_create_pixmap(_x_con, _x_scr->root_depth, _x_pix, _x_win, 700, 700); + + /* present extension */ + + uint32_t id = xcb_generate_id(_x_con); + + xcb_present_select_input( + _x_con, + id, + _x_win, + XCB_PRESENT_EVENT_MASK_COMPLETE_NOTIFY); + + /* optional setup a special event queue for the present extension */ + /* if not done present completion events can still be recoved from the regular */ + /* xcb_wait_for_event() queue */ + + _x_sev = xcb_register_for_special_xge(_x_con, &xcb_present_id, id, &stamp); + + /* get frame delay */ + + xcb_randr_get_screen_info_cookie_t k = xcb_randr_get_screen_info(_x_con, _x_scr->root); + xcb_randr_get_screen_info_reply_t *r = xcb_randr_get_screen_info_reply(_x_con, k, NULL); + + _delay = 1000000.0 / (double)r->rate; + + free(r); +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ + +static void +_reset(void) +{ + cairo_destroy(_c_ctx); + cairo_surface_destroy(_c_srf); + + xcb_free_pixmap(_x_con, _x_pix); + xcb_destroy_window(_x_con, _x_win); + xcb_disconnect(_x_con); +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ + +static void +_run(void) +{ + xcb_generic_event_t *ev = NULL; + + int n = 0; + long t1 = 0; + long t2 = 0; + long t3 = 0; + + struct timespec ts = {0}; + + while (1) { + + _total++; + + clock_gettime(CLOCK_MONOTONIC, &ts); + t1 = ts.tv_nsec / 1000 + ts.tv_sec * 1000000; + + _draw(); + + clock_gettime(CLOCK_MONOTONIC, &ts); + t2 = ts.tv_nsec / 1000 + ts.tv_sec * 1000000; + + xcb_present_pixmap( + _x_con, + _x_win, + _x_pix, + 123456, + XCB_XFIXES_REGION_NONE, + XCB_XFIXES_REGION_NONE, + 0, 0, + 0, + 0, + 0, + _opt_async ? XCB_PRESENT_OPTION_ASYNC : XCB_PRESENT_OPTION_NONE, + 0, _opt_frame_divider, 0, + 0, + NULL); + xcb_flush(_x_con); + + clock_gettime(CLOCK_MONOTONIC, &ts); + t3 = ts.tv_nsec / 1000 + ts.tv_sec * 1000000; + + _t_paint = t2 - t1; + _t_xsync = t3 - t2; + + ev = xcb_wait_for_special_event(_x_con, _x_sev); + if (!ev) { + printf("exiting\n"); + return; + } + + free(ev); + + clock_gettime(CLOCK_MONOTONIC, &ts); + _t_full = ts.tv_nsec / 1000 + ts.tv_sec * 1000000 - t1; + _fps = 1000000.0 / (double)_t_full; + } +}