204 lines
6.7 KiB
C
204 lines
6.7 KiB
C
|
#include <sys/stat.h>
|
||
|
#include <sys/time.h>
|
||
|
#include <math.h>
|
||
|
#include <cairo-xlib.h>
|
||
|
|
||
|
#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;
|
||
|
}
|