#include #include #include #include #include "initcairo.h" #define ERROR(condition, msg, ...) if(condition){fprintf(stderr, msg, ##__VA_ARGS__); return 0;} static Bool alwaysTruePredicate(Display *display, XEvent *event, XPointer arg) { return true; } int initCairo(int screen, int mask, int width, int height, const char *name, GraphicsInfo *info) { mask |= StructureNotifyMask | ExposureMask | KeyPressMask | ButtonPressMask | PointerMotionMask; info->display = XOpenDisplay(NULL); ERROR(!info->display, "Failed to open X display\n"); Visual *visual = XDefaultVisual(info->display, screen); int depth = XDefaultDepth(info->display, screen); Window root = XRootWindow(info->display, screen); XSetWindowAttributes swa; info->cmap = XCreateColormap(info->display, root, visual, AllocNone); swa.colormap = info->cmap; swa.background_pixmap = None; swa.border_pixel = 0; swa.event_mask = mask; info->win = XCreateWindow(info->display, root, 0, 0, width, height, 0, depth, InputOutput, visual, CWBorderPixel|CWColormap|CWEventMask, &swa); ERROR(!info->win, "Failed to create window.\n"); XStoreName(info->display, info->win, name); XMapWindow(info->display, info->win); info->sfc = cairo_xlib_surface_create(info->display, info->win, visual, width, height); cairo_xlib_surface_set_size(info->sfc, width, height); info->ctx = cairo_create(info->sfc); cairo_matrix_init_identity(&info->matrix); info->scalefactor = 1.0; info->wm_delete_window = XInternAtom(info->display, "WM_DELETE_WINDOW", 0); info->wm_protocols = XInternAtom(info->display, "WM_PROTOCOLS", 0); XSetWMProtocols(info->display, info->win, &info->wm_delete_window, 1); return 1; } void destroyCairo(GraphicsInfo *info) { XDestroyWindow(info->display, info->win); XFreeColormap(info->display, info->cmap); XCloseDisplay(info->display); } void startTimer(GraphicsInfo *info) { gettimeofday(&info->start_time, NULL); info->frames = 0; } void waitUpdateTimer(GraphicsInfo *info) { struct timeval current_time; double new_elapsed; gettimeofday(¤t_time, NULL); new_elapsed = current_time.tv_sec - info->start_time.tv_sec; new_elapsed += (current_time.tv_usec - info->start_time.tv_usec) * 1e-6; info->frametime = new_elapsed - info->elapsed; if(info->frametime < 0.01) { // frames < 10ms are considered too short; sleep a while and then measure again usleep(10000 - info->frametime*1e6); gettimeofday(¤t_time, NULL); new_elapsed = current_time.tv_sec - info->start_time.tv_sec; new_elapsed += (current_time.tv_usec - info->start_time.tv_usec) * 1e-6; info->frametime = new_elapsed - info->elapsed; } info->elapsed = new_elapsed; info->frames++; } int processStandardEvent(GraphicsInfo *info, XEvent *ev, void (*draw)(void*), void *data) { int state; static int last_x, last_y; switch(ev->type) { case Expose: draw(data); break; 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->width = ev->xconfigure.width; info->height = ev->xconfigure.height; cairo_xlib_surface_set_size(info->sfc, info->width, info->height); break; case KeyPress: state = ev->xkey.state & (ShiftMask | LockMask | ControlMask); if(state == 0 && ev->xkey.keycode == 24) return true; break; case ButtonPress: if(ev->xbutton.button == 4) { cairo_matrix_t transform; cairo_matrix_init_identity(&transform); cairo_matrix_translate(&transform, ev->xbutton.x, ev->xbutton.y); 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->matrix, &info->matrix, &transform); info->scalefactor *= 5.0/4.0; } else if(ev->xbutton.button == 5) { cairo_matrix_t transform; cairo_matrix_init_identity(&transform); cairo_matrix_translate(&transform, ev->xbutton.x, ev->xbutton.y); 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->matrix, &info->matrix, &transform); info->scalefactor *= 4.0/5.0; } last_x = ev->xbutton.x; last_y = ev->xbutton.y; draw(data); break; case MotionNotify: if(ev->xmotion.state & Button1Mask && !(ev->xmotion.state & ShiftMask)) { int dx = ev->xmotion.x - last_x; int dy = ev->xmotion.y - last_y; cairo_matrix_t transform; cairo_matrix_init_identity(&transform); cairo_matrix_translate(&transform, dx, dy); cairo_matrix_multiply(&info->matrix, &info->matrix, &transform); printf("Button1Motion Event, dx: %d, dy: %d\n", dx, dy); draw(data); } else if(ev->xmotion.state & Button1Mask && ev->xmotion.state & ShiftMask) { double angle = atan2((double)ev->xmotion.y - (double)info->height/2, (double)ev->xmotion.x - (double)info->width/2)- atan2((double)last_y - (double)info->height/2, (double)last_x - (double)info->width/2); cairo_matrix_t transform; cairo_matrix_init_identity(&transform); cairo_matrix_translate(&transform, (double)info->width/2, (double)info->height/2); cairo_matrix_rotate(&transform, angle); cairo_matrix_translate(&transform, -(double)info->width/2, -(double)info->height/2); cairo_matrix_multiply(&info->matrix, &info->matrix, &transform); printf("Button1Motion Event, angle: %f\n", angle); draw(data); } last_x = ev->xmotion.x; last_y = ev->xmotion.y; break; 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; } break; } return 0; } int checkEvents(GraphicsInfo *info, int (*process)(GraphicsInfo*, XEvent*, void*), void (*draw)(void*), void *data) // get any events from the queue and the server, process them if neccessary, quit if wanted { XEvent ev; // 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 XNextEvent(info->display, &ev); if(ev.xany.window != info->win) return 0; /* 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 XPutBackEvent(info->display, &nev); // otherwise put the event back, we will consider it in the next round } } */ if(processStandardEvent(info, &ev, draw, data)) return 1; if(process(info, &ev, data)) return 1; // quit event queue and application return 0; }