|
- #include <pthread.h>
- #include <stdlib.h>
- #include <stdio.h>
-
- #include <cairo/cairo.h>
- #include <cairo/cairo-xcb.h>
- #include <xcb/xcb.h>
- #include <xcb/present.h>
-
- /************************************************************************************************************/
- /************************************************************************************************************/
- /************************************************************************************************************/
-
- #define WIN_W 600
- #define WIN_H 400
-
- #define RECT_W 100
- #define RECT_SPEED 5 /* 300 px/s at 60 Hz */
-
- /************************************************************************************************************/
- /************************************************************************************************************/
- /************************************************************************************************************/
-
- static void _draw (void);
- static void _init_graphics (void);
- static void _init_threads (void);
- static void _reset (void);
-
- static void * _thread_event (void *p);
- static void * _thread_render (void *p);
-
- /************************************************************************************************************/
- /************************************************************************************************************/
- /************************************************************************************************************/
-
- static xcb_connection_t *_x_con = NULL;
- static xcb_screen_t *_x_scr = NULL;
- static xcb_visualtype_t *_x_vis = NULL;
- static xcb_window_t _x_win = 0;
- static xcb_pixmap_t _x_pix = 0;
- static xcb_gcontext_t _x_ctx = 0;
-
- static uint8_t _x_opcode = 0;
-
- static cairo_surface_t *_c_srf = NULL;
- static cairo_t *_c_ctx = NULL;
-
- static pthread_t _t_id_ev;
- static pthread_t _t_id_rd;
- static pthread_cond_t _t_cond_rd_1;
- static pthread_cond_t _t_cond_rd_2;
- static pthread_mutex_t _t_mutex_rd_1;
- static pthread_mutex_t _t_mutex_rd_2;
-
- static int _rect_pos = WIN_W;
- static int _update = 1;
- static int _serial = 0;
-
- /************************************************************************************************************/
- /************************************************************************************************************/
- /************************************************************************************************************/
-
- int
- main(int argc, char **argv)
- {
- /* initialisation */
-
- _init_graphics();
- _init_threads();
-
- /* initial render for first expose event */
-
- _draw();
-
- /* start the threads */
- /* the event thread leads the tempo */
-
- pthread_create(&_t_id_ev, NULL, _thread_event, NULL);
- pthread_create(&_t_id_rd, NULL, _thread_render, NULL);
-
- pthread_join(_t_id_ev, NULL);
-
- pthread_cancel(_t_id_rd);
- pthread_join(_t_id_rd, NULL);
-
- /* end */
-
- _reset();
- }
-
- /************************************************************************************************************/
- /************************************************************************************************************/
- /************************************************************************************************************/
-
- static void
- _draw(void)
- {
- /* update position */
-
- _rect_pos += RECT_SPEED;
- if (_rect_pos >= WIN_W) {
- _rect_pos = -RECT_W;
- }
-
- /* update pixmap */
-
- cairo_set_source_rgb(_c_ctx, 0.3, 0.3, 0.3);
- cairo_paint(_c_ctx);
-
- cairo_set_source_rgb(_c_ctx, 0.7, 0.7, 0.7);
- cairo_rectangle(_c_ctx, _rect_pos, 0, RECT_W, WIN_H);
- cairo_fill(_c_ctx);
-
- cairo_surface_flush(_c_srf);
- }
-
- /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
-
- static void
- _init_graphics(void)
- {
- /* setup xcb */
-
- _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;
- }
- }
- }
-
- /* setup generic xcb graphical context for copying the pixmap */
-
- _x_ctx = xcb_generate_id(_x_con);
- xcb_create_gc(
- _x_con,
- _x_ctx,
- _x_scr->root,
- XCB_GC_FOREGROUND | XCB_GC_GRAPHICS_EXPOSURES,
- &(uint32_t[2]){_x_scr->black_pixel, 0});
-
- /* setup xcb window */
-
- uint32_t mask_vals[2];
- mask_vals[0] = _x_scr->black_pixel;
- mask_vals[1] = XCB_EVENT_MASK_EXPOSURE |
- XCB_EVENT_MASK_STRUCTURE_NOTIFY |
- XCB_EVENT_MASK_KEY_PRESS |
- XCB_EVENT_MASK_BUTTON_PRESS;
-
- _x_win = xcb_generate_id(_x_con);
- xcb_create_window(
- _x_con,
- XCB_COPY_FROM_PARENT,
- _x_win,
- _x_scr->root,
- 0, 0,
- WIN_W, WIN_H,
- 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);
-
- /* setup xcb pixmap */
-
- _x_pix = xcb_generate_id(_x_con);
- xcb_create_pixmap(_x_con, _x_scr->root_depth, _x_pix, _x_win, WIN_W, WIN_H);
-
- /* setup xcb present extension */
-
- xcb_present_select_input(
- _x_con,
- xcb_generate_id(_x_con),
- _x_win,
- XCB_PRESENT_EVENT_MASK_COMPLETE_NOTIFY);
-
- /* dynamicaly get the present extention opcode for event identification */
-
- xcb_query_extension_reply_t *r =
- xcb_query_extension_reply(_x_con, xcb_query_extension(_x_con, 7, "Present"), NULL);
-
- _x_opcode = r->major_opcode;
- free(r);
-
- /* setup cairo drawable */
-
- _c_srf = cairo_xcb_surface_create(_x_con, _x_pix, _x_vis, WIN_W, WIN_H);
- _c_ctx = cairo_create(_c_srf);
-
- /* end */
-
- cairo_surface_flush(_c_srf);
- xcb_flush(_x_con);
- }
-
- /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
-
- static void
- _init_threads(void)
- {
- pthread_mutex_init(&_t_mutex_rd_1, NULL);
- pthread_mutex_init(&_t_mutex_rd_2, NULL);
-
- pthread_cond_init(&_t_cond_rd_1, NULL);
- pthread_cond_init(&_t_cond_rd_2, NULL);
- }
-
- /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
-
- void
- _reset(void)
- {
- pthread_mutex_destroy(&_t_mutex_rd_1);
- pthread_mutex_destroy(&_t_mutex_rd_2);
-
- pthread_cond_destroy(&_t_cond_rd_1);
- pthread_cond_destroy(&_t_cond_rd_2);
-
- cairo_destroy(_c_ctx);
- cairo_surface_destroy(_c_srf);
-
- xcb_free_gc(_x_con, _x_ctx);
- xcb_free_pixmap(_x_con, _x_pix);
- xcb_destroy_window(_x_con, _x_win);
- xcb_disconnect(_x_con);
- }
-
- /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
-
- static void *
- _thread_event(void *p)
- {
- int run = 1;
- int x_pev_serial;
-
- xcb_generic_event_t *x_ev;
- xcb_ge_generic_event_t *x_gev;
- xcb_present_complete_notify_event_t *x_pev;
-
- while (run && (x_ev = xcb_wait_for_event(_x_con))) {
- switch (x_ev->response_type & ~0x80) {
-
- case XCB_EXPOSE:
- /* immediate update without syncing to the refresh rate */
- /* without it there are visual glitches when resizing the window */
- xcb_copy_area(_x_con, _x_pix, _x_win, _x_ctx, 0, 0, 0, 0, WIN_W, WIN_H);
- break;
-
- case XCB_BUTTON_PRESS:
- pthread_mutex_lock(&_t_mutex_rd_2);
- _update = !_update;
- pthread_mutex_unlock(&_t_mutex_rd_2);
- pthread_cond_signal(&_t_cond_rd_2);
- break;
-
- case XCB_KEY_PRESS:
- run = 0;
- break;
-
- case XCB_GE_GENERIC:
- /* most of this is just checking that we got the right event */
- /* unecessary in simple cases */
- x_gev = (xcb_ge_generic_event_t*)x_ev;
- if (x_gev->extension == _x_opcode && x_gev->event_type == XCB_PRESENT_EVENT_COMPLETE_NOTIFY) {
- x_pev = (xcb_present_complete_notify_event_t*)x_ev;
- pthread_mutex_lock(&_t_mutex_rd_2);
- x_pev_serial = _serial;
- pthread_mutex_unlock(&_t_mutex_rd_2);
- if (x_pev->kind == XCB_PRESENT_COMPLETE_KIND_PIXMAP && x_pev->serial == x_pev_serial) {
- pthread_cond_signal(&_t_cond_rd_1);
- }
- }
- break;
- }
- xcb_flush(_x_con);
- free(x_ev);
- }
-
- pthread_exit(NULL);
- }
-
- /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
-
- static void *
- _thread_render(void *p)
- {
- pthread_mutex_lock(&_t_mutex_rd_1);
-
- while (1) {
-
- /* check if an update is needed, if not, wait */
-
- pthread_mutex_lock(&_t_mutex_rd_2);
- if (!_update) {
- pthread_cond_wait(&_t_cond_rd_2, &_t_mutex_rd_2);
- }
- pthread_mutex_unlock(&_t_mutex_rd_2);
-
- /* update pixmap */
-
- _draw();
-
- /* update window with pixmap */
-
- _serial++;
-
- xcb_present_pixmap(
- _x_con,
- _x_win,
- _x_pix,
- _serial,
- XCB_XFIXES_REGION_NONE,
- XCB_XFIXES_REGION_NONE,
- 0, 0,
- 0,
- 0,
- 0,
- XCB_PRESENT_OPTION_NONE,
- 0, 0, 0,
- 0,
- NULL);
-
- /* important af, don't forget it */
-
- xcb_flush(_x_con);
-
- /* wait for present event notification in the event thread */
-
- pthread_cond_wait(&_t_cond_rd_1, &_t_mutex_rd_1);
- }
-
- pthread_mutex_unlock(&_t_mutex_rd_1);
- pthread_exit(NULL);
- }
|