diff --git a/initcairo.c b/initcairo.c index cba49bf..807fce3 100644 --- a/initcairo.c +++ b/initcairo.c @@ -54,9 +54,17 @@ GraphicsInfo *initCairo(int screen, int mask, int width, int height, const char XMapWindow(info->display, info->win); + // create xlib surface info->surface = cairo_xlib_surface_create(info->display, info->win, visual, width, height); cairo_xlib_surface_set_size(info->surface, width, height); - info->context->cairo = cairo_create(info->surface); + info->front_context = cairo_create(info->surface); + cairo_set_antialias(info->front_context, CAIRO_ANTIALIAS_NONE); + + // create backbuffer surface + int stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, width); + info->buffer = malloc(stride*height); + info->buffer_surface = cairo_image_surface_create_for_data(info->buffer, CAIRO_FORMAT_ARGB32, width, height, stride); + info->context->cairo = cairo_create(info->buffer_surface); cairo_matrix_init_identity(&info->context->matrix); info->context->width = width; info->context->height = height; @@ -65,11 +73,18 @@ GraphicsInfo *initCairo(int screen, int mask, int width, int height, const char info->wm_protocols = XInternAtom(info->display, "WM_PROTOCOLS", 0); XSetWMProtocols(info->display, info->win, &info->wm_delete_window, 1); + XFlush(info->display); + return info; } void destroyCairo(GraphicsInfo *info) { + cairo_destroy(info->context->cairo); + cairo_destroy(info->front_context); + cairo_surface_destroy(info->surface); + cairo_surface_destroy(info->buffer_surface); + free(info->buffer); XDestroyWindow(info->display, info->win); XFreeColormap(info->display, info->cmap); XCloseDisplay(info->display); @@ -107,26 +122,35 @@ int processStandardEvent(GraphicsInfo *info, XEvent *ev, void (*draw)(DrawingCon { int state; static int last_x, last_y; + int stride; + int status = STATUS_NOTHING; switch(ev->type) { case Expose: - draw(info->context); - break; + return STATUS_REDRAW; case ConfigureNotify: printf("ConfigureNotify Event, new dimensions: %d %d %d %d\n", ev->xconfigure.x, ev->xconfigure.y, ev->xconfigure.width, ev->xconfigure.height); info->context->width = ev->xconfigure.width; info->context->height = ev->xconfigure.height; - cairo_xlib_surface_set_size(info->surface, ev->xconfigure.width, ev->xconfigure.height); - draw(info->context); - break; + cairo_xlib_surface_set_size(info->surface, info->context->width, info->context->height); + + cairo_destroy(info->context->cairo); + cairo_surface_destroy(info->buffer_surface); + free(info->buffer); + stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, info->context->width); + info->buffer = malloc(stride*info->context->height); + info->buffer_surface = cairo_image_surface_create_for_data(info->buffer, CAIRO_FORMAT_ARGB32, info->context->width, info->context->height, stride); + info->context->cairo = cairo_create(info->buffer_surface); + + return STATUS_REDRAW; case KeyPress: state = ev->xkey.state & (ShiftMask | LockMask | ControlMask); if(state == 0 && ev->xkey.keycode == 24) - return true; - break; + return STATUS_QUIT; + return STATUS_NOTHING; case ButtonPress: if(ev->xbutton.button == 4) { @@ -136,7 +160,7 @@ int processStandardEvent(GraphicsInfo *info, XEvent *ev, void (*draw)(DrawingCon cairo_matrix_scale(&transform, 5.0/4.0, 5.0/4.0); cairo_matrix_translate(&transform, -ev->xbutton.x, -ev->xbutton.y); cairo_matrix_multiply(&info->context->matrix, &info->context->matrix, &transform); - draw(info->context); + status = STATUS_REDRAW; } else if(ev->xbutton.button == 5) { cairo_matrix_t transform; cairo_matrix_init_identity(&transform); @@ -144,11 +168,12 @@ int processStandardEvent(GraphicsInfo *info, XEvent *ev, void (*draw)(DrawingCon cairo_matrix_scale(&transform, 4.0/5.0, 4.0/5.0); cairo_matrix_translate(&transform, -ev->xbutton.x, -ev->xbutton.y); cairo_matrix_multiply(&info->context->matrix, &info->context->matrix, &transform); - draw(info->context); + status = STATUS_REDRAW; } + last_x = ev->xbutton.x; last_y = ev->xbutton.y; - break; + return status; case MotionNotify: if(ev->xmotion.state & Button1Mask && !(ev->xmotion.state & ShiftMask)) { @@ -159,7 +184,7 @@ int processStandardEvent(GraphicsInfo *info, XEvent *ev, void (*draw)(DrawingCon cairo_matrix_init_identity(&transform); cairo_matrix_translate(&transform, dx, dy); cairo_matrix_multiply(&info->context->matrix, &info->context->matrix, &transform); - draw(info->context); + status = STATUS_REDRAW; } else if(ev->xmotion.state & Button1Mask && ev->xmotion.state & ShiftMask) { double width = (double) cairo_xlib_surface_get_width(info->surface); double height = (double) cairo_xlib_surface_get_height(info->surface); @@ -174,50 +199,67 @@ int processStandardEvent(GraphicsInfo *info, XEvent *ev, void (*draw)(DrawingCon cairo_matrix_rotate(&transform, angle); cairo_matrix_translate(&transform, -width/2, -height/2); cairo_matrix_multiply(&info->context->matrix, &info->context->matrix, &transform); - draw(info->context); + status = STATUS_REDRAW; } + last_x = ev->xmotion.x; last_y = ev->xmotion.y; - break; + + return status; case ClientMessage: if((Atom)ev->xclient.message_type == info->wm_protocols && (Atom)ev->xclient.data.l[0] == info->wm_delete_window) { // printf("Window closed\n"); - return true; + return STATUS_QUIT; } - break; + return STATUS_NOTHING; } - return 0; - + return STATUS_NOTHING; } int checkEvents(GraphicsInfo *info, int (*process)(GraphicsInfo*, XEvent*), void (*draw)(DrawingContext*)) // get any events from the queue and the server, process them if neccessary, quit if wanted { XEvent ev; + int x11_fd; + fd_set fds; + int status = STATUS_NOTHING, result; -// while(XCheckIfEvent(info->display, &ev, alwaysTruePredicate, NULL)) { // we essentially want XCheckWindowEvent, but we want to avoid that events for other windows fill up the queue + int x11_event = 0; - XNextEvent(info->display, &ev); + if(XPending(info->display)) { // select() would miss events which are picked up already, so we have to catch them first + x11_event = 1; + } else { + x11_fd = ConnectionNumber(info->display); + FD_ZERO(&fds); + FD_SET(x11_fd, &fds); - if(ev.xany.window != info->win) - return 0; + select(x11_fd + 1, &fds, NULL, NULL, NULL); -/* if(ev.type == KeyRelease) { // deal with autorepeat - XEvent nev; - if(XCheckIfEvent(info->display, &nev, alwaysTruePredicate, NULL)) { // is there another event? - if (nev.type == KeyPress && nev.xkey.time == ev.xkey.time && nev.xkey.keycode == ev.xkey.keycode) // which is equal, but KeyPress? Then it's just auto-repeat - return 0; // so we ignore both + if(FD_ISSET(x11_fd, &fds)) + x11_event = 1; + } - XPutBackEvent(info->display, &nev); // otherwise put the event back, we will consider it in the next round + if(x11_event) { + while(XPending(info->display)) { + XNextEvent(info->display, &ev); + + if(ev.xany.window != info->win) + continue; + + result = processStandardEvent(info, &ev, draw); + if(result == STATUS_QUIT) + return STATUS_QUIT; + else if(result == STATUS_REDRAW) + status = STATUS_REDRAW; + + result = process(info, &ev); + if(result == STATUS_QUIT) + return STATUS_QUIT; + else if(result == STATUS_REDRAW) + status = STATUS_REDRAW; } - } */ + } - if(processStandardEvent(info, &ev, draw)) - return 1; - - if(process(info, &ev)) - return 1; // quit event queue and application - - return 0; + return status; } diff --git a/initcairo.h b/initcairo.h index 2259707..83514ec 100644 --- a/initcairo.h +++ b/initcairo.h @@ -12,6 +12,10 @@ #include #include +#define STATUS_NOTHING 0 +#define STATUS_REDRAW 1 +#define STATUS_QUIT 2 + typedef struct { cairo_t *cairo; cairo_matrix_t matrix; @@ -33,6 +37,11 @@ typedef struct { Atom wm_protocols; Atom wm_delete_window; cairo_surface_t *surface; + cairo_t *front_context; + + cairo_surface_t *buffer_surface; + unsigned char *buffer; + struct timeval start_time; unsigned long frames; double elapsed, frametime; diff --git a/limit_set.c b/limit_set.c index c32552c..b5edf73 100644 --- a/limit_set.c +++ b/limit_set.c @@ -358,14 +358,8 @@ void drawAttractors(DrawingContext *ctx) void draw(DrawingContext *ctx) { - struct timeval current_time; - gettimeofday(¤t_time, 0); - double start_time = current_time.tv_sec + current_time.tv_usec*1e-6; - cairo_t *C = ctx->cairo; - updateDimensions(ctx); - cairo_push_group(C); cairo_set_source_rgb(C, 1, 1, 1); cairo_paint(C); @@ -378,7 +372,6 @@ void draw(DrawingContext *ctx) cairo_set_line_cap(C, CAIRO_LINE_CAP_ROUND); if(limit_curve_valid) { - if(show_limit) { int last_inside = 0; for(int i = 0; i < n_group_elements; i++) { @@ -421,13 +414,7 @@ void draw(DrawingContext *ctx) sprintf(buf, "t = %.8f", parameter); cairo_show_text(C, buf); - cairo_pop_group_to_source(C); - cairo_paint(C); cairo_surface_flush(cairo_get_target(C)); - - gettimeofday(¤t_time, 0); - double end_time = current_time.tv_sec + current_time.tv_usec*1e-6; - printf("draw() finished in %g seconds\n", end_time - start_time); } void setup() @@ -544,10 +531,10 @@ int processEvent(GraphicsInfo *info, XEvent *ev) break; } - draw(info->context); + return STATUS_REDRAW; } - return 0; + return STATUS_NOTHING; } int main() @@ -586,7 +573,24 @@ int main() startTimer(info); - while(!checkEvents(info, processEvent, draw)) { + while(1) { + int result = checkEvents(info, processEvent, NULL); + if(result == STATUS_QUIT) + return 0; + else if(result == STATUS_REDRAW) { + struct timeval current_time; + gettimeofday(¤t_time, 0); + double start_time = current_time.tv_sec + current_time.tv_usec*1e-6; + + updateDimensions(info->context); + draw(info->context); + cairo_set_source_surface(info->front_context, info->buffer_surface, 0, 0); + cairo_paint(info->front_context); + + gettimeofday(¤t_time, 0); + double end_time = current_time.tv_sec + current_time.tv_usec*1e-6; + printf("drawing finished in %g seconds\n", end_time - start_time); + } waitUpdateTimer(info); }