triangle_group_limit_set/initcairo.c

266 lines
8.8 KiB
C
Raw Normal View History

2018-08-08 14:24:03 +00:00
#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;
}
2018-08-17 13:41:17 +00:00
void updateDimensions(DrawingContext *ctx)
2018-08-08 14:24:03 +00:00
{
2018-08-17 13:41:17 +00:00
double det = ctx->matrix.xx * ctx->matrix.yy - ctx->matrix.xy * ctx->matrix.yx;
double cx = (double)ctx->width/2;
double cy = (double)ctx->height/2;
double r = 2*sqrt((cx*cx + cy*cy)/det); // this is not safe anymore if we have non-uniform scaling
// don't use cairo_device_to_user() since the matrix might not be our CTM yet
cx -= ctx->matrix.x0;
cy -= ctx->matrix.y0;
ctx->center_x = ( ctx->matrix.yy * cx - ctx->matrix.xy * cy) / det;
ctx->center_y = (- ctx->matrix.yx * cx + ctx->matrix.xx * cy) / det;
ctx->radius = r;
ctx->scalefactor = sqrt(det);
}
GraphicsInfo *initCairo(int screen, int mask, int width, int height, const char *name)
{
GraphicsInfo *info = malloc(sizeof(GraphicsInfo));
DrawingContext *ctx = malloc(sizeof(DrawingContext));
info->context = ctx;
2018-08-08 14:24:03 +00:00
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);
2018-08-18 16:27:33 +00:00
// create xlib surface
2018-08-17 13:41:17 +00:00
info->surface = cairo_xlib_surface_create(info->display, info->win, visual, width, height);
cairo_xlib_surface_set_size(info->surface, width, height);
2018-08-18 16:27:33 +00:00
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);
2018-08-17 13:41:17 +00:00
cairo_matrix_init_identity(&info->context->matrix);
info->context->width = width;
info->context->height = height;
2018-08-08 14:24:03 +00:00
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);
2018-08-18 16:27:33 +00:00
XFlush(info->display);
2018-08-17 13:41:17 +00:00
return info;
2018-08-08 14:24:03 +00:00
}
void destroyCairo(GraphicsInfo *info)
{
2018-08-18 16:27:33 +00:00
cairo_destroy(info->context->cairo);
cairo_destroy(info->front_context);
cairo_surface_destroy(info->surface);
cairo_surface_destroy(info->buffer_surface);
free(info->buffer);
2018-08-08 14:24:03 +00:00
XDestroyWindow(info->display, info->win);
XFreeColormap(info->display, info->cmap);
XCloseDisplay(info->display);
2018-08-17 13:41:17 +00:00
free(info->context);
free(info);
2018-08-08 14:24:03 +00:00
}
void startTimer(GraphicsInfo *info)
{
gettimeofday(&info->start_time, NULL);
info->frames = 0;
}
void waitUpdateTimer(GraphicsInfo *info)
{
struct timeval current_time;
double new_elapsed;
gettimeofday(&current_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(&current_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++;
}
2018-08-17 13:41:17 +00:00
int processStandardEvent(GraphicsInfo *info, XEvent *ev, void (*draw)(DrawingContext *))
2018-08-08 14:24:03 +00:00
{
int state;
static int last_x, last_y;
2018-08-18 16:27:33 +00:00
int stride;
int status = STATUS_NOTHING;
2018-08-08 14:24:03 +00:00
switch(ev->type) {
case Expose:
2018-08-18 16:27:33 +00:00
return STATUS_REDRAW;
2018-08-08 14:24:03 +00:00
case ConfigureNotify:
printf("ConfigureNotify Event, new dimensions: %d %d %d %d\n", ev->xconfigure.x, ev->xconfigure.y, ev->xconfigure.width, ev->xconfigure.height);
2018-08-17 13:41:17 +00:00
info->context->width = ev->xconfigure.width;
info->context->height = ev->xconfigure.height;
2018-08-18 16:27:33 +00:00
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;
2018-08-08 14:24:03 +00:00
case KeyPress:
state = ev->xkey.state & (ShiftMask | LockMask | ControlMask);
if(state == 0 && ev->xkey.keycode == 24)
2018-08-18 16:27:33 +00:00
return STATUS_QUIT;
return STATUS_NOTHING;
2018-08-08 14:24:03 +00:00
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);
2018-08-17 13:41:17 +00:00
cairo_matrix_multiply(&info->context->matrix, &info->context->matrix, &transform);
2018-08-18 16:27:33 +00:00
status = STATUS_REDRAW;
2018-08-08 14:24:03 +00:00
} 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);
2018-08-17 13:41:17 +00:00
cairo_matrix_multiply(&info->context->matrix, &info->context->matrix, &transform);
2018-08-18 16:27:33 +00:00
status = STATUS_REDRAW;
2018-08-08 14:24:03 +00:00
}
2018-08-18 16:27:33 +00:00
2018-08-08 14:24:03 +00:00
last_x = ev->xbutton.x;
last_y = ev->xbutton.y;
2018-08-18 16:27:33 +00:00
return status;
2018-08-08 14:24:03 +00:00
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);
2018-08-17 13:41:17 +00:00
cairo_matrix_multiply(&info->context->matrix, &info->context->matrix, &transform);
2018-08-18 16:27:33 +00:00
status = STATUS_REDRAW;
2018-08-08 14:24:03 +00:00
} else if(ev->xmotion.state & Button1Mask && ev->xmotion.state & ShiftMask) {
2018-08-17 13:41:17 +00:00
double width = (double) cairo_xlib_surface_get_width(info->surface);
double height = (double) cairo_xlib_surface_get_height(info->surface);
2018-08-08 14:24:03 +00:00
double angle =
2018-08-17 13:41:17 +00:00
atan2((double)ev->xmotion.y - height/2, (double)ev->xmotion.x - width/2)-
atan2((double)last_y - height/2, (double)last_x - width/2);
2018-08-08 14:24:03 +00:00
cairo_matrix_t transform;
cairo_matrix_init_identity(&transform);
2018-08-17 13:41:17 +00:00
cairo_matrix_translate(&transform, width/2, height/2);
2018-08-08 14:24:03 +00:00
cairo_matrix_rotate(&transform, angle);
2018-08-17 13:41:17 +00:00
cairo_matrix_translate(&transform, -width/2, -height/2);
cairo_matrix_multiply(&info->context->matrix, &info->context->matrix, &transform);
2018-08-18 16:27:33 +00:00
status = STATUS_REDRAW;
2018-08-08 14:24:03 +00:00
}
2018-08-18 16:27:33 +00:00
2018-08-08 14:24:03 +00:00
last_x = ev->xmotion.x;
last_y = ev->xmotion.y;
2018-08-18 16:27:33 +00:00
return status;
2018-08-08 14:24:03 +00:00
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");
2018-08-18 16:27:33 +00:00
return STATUS_QUIT;
2018-08-08 14:24:03 +00:00
}
2018-08-18 16:27:33 +00:00
return STATUS_NOTHING;
2018-08-08 14:24:03 +00:00
}
2018-08-18 16:27:33 +00:00
return STATUS_NOTHING;
2018-08-08 14:24:03 +00:00
}
2018-08-17 13:41:17 +00:00
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
2018-08-08 14:24:03 +00:00
{
XEvent ev;
2018-08-18 16:27:33 +00:00
int x11_fd;
fd_set fds;
int status = STATUS_NOTHING, result;
2018-08-08 14:24:03 +00:00
2018-08-18 16:27:33 +00:00
int x11_event = 0;
2018-08-08 14:24:03 +00:00
2018-08-18 16:27:33 +00:00
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);
2018-08-08 14:24:03 +00:00
2018-08-18 16:27:33 +00:00
select(x11_fd + 1, &fds, NULL, NULL, NULL);
2018-08-08 14:24:03 +00:00
2018-08-18 16:27:33 +00:00
if(FD_ISSET(x11_fd, &fds))
x11_event = 1;
}
2018-08-08 14:24:03 +00:00
2018-08-18 16:27:33 +00:00
if(x11_event) {
while(XPending(info->display)) {
XNextEvent(info->display, &ev);
2018-08-08 14:24:03 +00:00
2018-08-18 16:27:33 +00:00
if(ev.xany.window != info->win)
continue;
2018-08-08 14:24:03 +00:00
2018-08-18 16:27:33 +00:00
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;
}
}
2018-08-08 14:24:03 +00:00
2018-08-18 16:27:33 +00:00
return status;
2018-08-08 14:24:03 +00:00
}