#include #include #include #include #include #include #include /************************************************************************************************************/ /************************************************************************************************************/ /************************************************************************************************************/ #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); }