|
|
@@ -0,0 +1,346 @@ |
|
|
|
#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); |
|
|
|
} |