Rename GP_Context -> GP_Pixmap
[gfxprim.git] / libs / backends / GP_X11.c
blobc6e812b9efaba8a75082aff1caea7c30e50ce064
1 /*****************************************************************************
2 * This file is part of gfxprim library. *
3 * *
4 * Gfxprim is free software; you can redistribute it and/or *
5 * modify it under the terms of the GNU Lesser General Public *
6 * License as published by the Free Software Foundation; either *
7 * version 2.1 of the License, or (at your option) any later version. *
8 * *
9 * Gfxprim is distributed in the hope that it will be useful, *
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
12 * Lesser General Public License for more details. *
13 * *
14 * You should have received a copy of the GNU Lesser General Public *
15 * License along with gfxprim; if not, write to the Free Software *
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, *
17 * Boston, MA 02110-1301 USA *
18 * *
19 * Copyright (C) 2009-2013 Cyril Hrubis <metan@ucw.cz> *
20 * *
21 *****************************************************************************/
23 #include <string.h>
24 #include <errno.h>
26 #include "../../config.h"
28 #include "core/GP_Debug.h"
29 #include "core/GP_Common.h"
31 #ifdef HAVE_LIBX11
33 #include <X11/Xlib.h>
34 #include <X11/Xutil.h>
35 #include <X11/Xatom.h>
36 #include <X11/Xmd.h>
38 #ifdef HAVE_X_SHM
39 #include <sys/ipc.h>
40 #include <sys/shm.h>
41 #include <X11/extensions/XShm.h>
42 #endif /* HAVE_X_SHM */
44 #include "backends/GP_X11.h"
46 #include "GP_X11_Conn.h"
47 #include "GP_X11_Win.h"
48 #include "GP_X11_Input.h"
50 static int resize_ximage(GP_Backend *self, int w, int h);
51 static int resize_shm_ximage(GP_Backend *self, int w, int h);
53 static void putimage(struct x11_win *win, int x0, int y0, int x1, int y1)
55 #ifdef HAVE_X_SHM
56 if (win->shm_flag)
57 XShmPutImage(win->dpy, win->win, DefaultGC(win->dpy, win->scr),
58 win->img, x0, y0, x0, y0, x1-x0+1, y1-y0+1, False);
59 else
60 #endif /* HAVE_X_SHM */
61 XPutImage(win->dpy, win->win, DefaultGC(win->dpy, win->scr),
62 win->img, x0, y0, x0, y0, x1-x0+1, y1-y0+1);
65 static void x11_update_rect(GP_Backend *self, GP_Coord x0, GP_Coord y0,
66 GP_Coord x1, GP_Coord y1)
68 struct x11_win *win = GP_BACKEND_PRIV(self);
70 GP_DEBUG(4, "Updating rect %ix%i-%ix%i", x0, y0, x1, y1);
72 if (win->resized_flag) {
73 GP_DEBUG(4, "Ignoring update rect, waiting for resize ack");
74 return;
77 XLockDisplay(win->dpy);
79 putimage(win, x0, y0, x1, y1);
81 XFlush(win->dpy);
83 XUnlockDisplay(win->dpy);
86 static void x11_flip(GP_Backend *self)
88 struct x11_win *win = GP_BACKEND_PRIV(self);
89 unsigned int w = self->pixmap->w;
90 unsigned int h = self->pixmap->h;
92 GP_DEBUG(4, "Flipping pixmap");
94 if (win->resized_flag) {
95 GP_DEBUG(4, "Ignoring flip, waiting for resize ack");
96 return;
99 XLockDisplay(win->dpy);
101 putimage(win, 0, 0, w - 1, h - 1);
103 XFlush(win->dpy);
105 XUnlockDisplay(win->dpy);
108 static struct x11_win *last_win = NULL;
110 static void x11_ev(XEvent *ev)
112 struct x11_win *win;
114 /* Lookup for window */
115 if (last_win == NULL || last_win->win != ev->xany.window) {
116 last_win = win_list_lookup(ev->xany.window);
118 if (last_win == NULL) {
119 GP_WARN("Event for unknown window, ignoring.");
120 return;
124 win = last_win;
126 struct GP_Backend *self = GP_CONTAINER_OF(win, struct GP_Backend, priv);
128 switch (ev->type) {
129 case Expose:
130 GP_DEBUG(4, "Expose %ix%i-%ix%i %i",
131 ev->xexpose.x, ev->xexpose.y,
132 ev->xexpose.width, ev->xexpose.height,
133 ev->xexpose.count);
135 if (win->resized_flag)
136 break;
138 /* Safety measure */
139 if (ev->xexpose.x + ev->xexpose.width > (int)self->pixmap->w) {
140 GP_WARN("Expose x + w > pixmap->w");
141 break;
144 if (ev->xexpose.y + ev->xexpose.height > (int)self->pixmap->h) {
145 GP_WARN("Expose y + h > pixmap->h");
146 break;
149 /* Update the rectangle */
150 x11_update_rect(self, ev->xexpose.x, ev->xexpose.y,
151 ev->xexpose.x + ev->xexpose.width - 1,
152 ev->xexpose.y + ev->xexpose.height - 1);
153 break;
154 case ConfigureNotify:
155 if (ev->xconfigure.width == (int)self->pixmap->w &&
156 ev->xconfigure.height == (int)self->pixmap->h)
157 break;
159 if (ev->xconfigure.width == (int)win->new_w &&
160 ev->xconfigure.height == (int)win->new_h)
161 break;
163 win->new_w = ev->xconfigure.width;
164 win->new_h = ev->xconfigure.height;
166 GP_DEBUG(4, "Configure Notify %ux%u", win->new_w, win->new_h);
168 /* Window has been resized, set flag. */
169 win->resized_flag = 1;
170 default:
171 //TODO: More accurate window w and h?
172 x11_input_event_put(&self->event_queue, ev,
173 self->pixmap->w, self->pixmap->h);
174 break;
178 static void x11_poll(GP_Backend *self)
180 struct x11_win *win = GP_BACKEND_PRIV(self);
181 XEvent ev;
183 XLockDisplay(win->dpy);
185 while (XPending(win->dpy)) {
186 XNextEvent(win->dpy, &ev);
187 x11_ev(&ev);
190 XUnlockDisplay(win->dpy);
193 #include <poll.h>
195 static void x11_wait(GP_Backend *self)
197 struct pollfd fd = {.fd = self->fd, .events = POLLIN, .revents = 0};
198 poll(&fd, 1, -1);
199 x11_poll(self);
202 static int resize_buffer(struct GP_Backend *self, uint32_t w, uint32_t h)
204 struct x11_win *win = GP_BACKEND_PRIV(self);
206 if (win->shm_flag) {
207 if (resize_shm_ximage(self, w, h))
208 return 1;
209 } else {
210 if (resize_ximage(self, w, h))
211 return 1;
214 return 0;
217 static int x11_set_attributes(struct GP_Backend *self,
218 uint32_t w, uint32_t h,
219 const char *caption)
221 struct x11_win *win = GP_BACKEND_PRIV(self);
223 XLockDisplay(win->dpy);
225 if (caption != NULL) {
226 GP_DEBUG(3, "Setting window caption to '%s'", caption);
227 XmbSetWMProperties(win->dpy, win->win, caption, caption,
228 NULL, 0, NULL, NULL, NULL);
231 if (w != 0 && h != 0) {
232 if (win->fullscreen_flag) {
233 GP_DEBUG(1, "Ignoring resize request in fullscreen");
234 goto out;
236 GP_DEBUG(3, "Setting window size to %ux%u", w, h);
237 XResizeWindow(win->dpy, win->win, w, h);
240 XFlush(win->dpy);
242 out:
243 XUnlockDisplay(win->dpy);
244 return 0;
247 static int x11_resize_ack(struct GP_Backend *self)
249 struct x11_win *win = GP_BACKEND_PRIV(self);
250 int ret;
252 XLockDisplay(win->dpy);
254 GP_DEBUG(3, "Setting buffer size to %ux%u", win->new_w, win->new_h);
256 ret = resize_buffer(self, win->new_w, win->new_h);
258 win->resized_flag = 0;
260 if (!ret) {
261 GP_EventQueueSetScreenSize(&self->event_queue,
262 win->new_w, win->new_h);
265 GP_DEBUG(3, "Done");
267 XUnlockDisplay(win->dpy);
269 return ret;
272 static const char *visual_class_name(int class)
274 switch (class) {
275 case StaticGray:
276 return "StaticGray";
277 case GrayScale:
278 return "GrayScale";
279 case StaticColor:
280 return "StaticColor";
281 case PseudoColor:
282 return "PseudoColor";
283 case TrueColor:
284 return "TrueColor";
285 case DirectColor:
286 return "DirectColor";
289 return "Unknown";
292 static enum GP_PixelType match_pixel_type(struct x11_win *win)
294 GP_DEBUG(1, "Matching image pixel type, visual=%s depth=%u",
295 visual_class_name(win->vis->class), win->img->bits_per_pixel);
297 if (win->vis->class == DirectColor || win->vis->class == TrueColor) {
298 return GP_PixelRGBMatch(win->img->red_mask,
299 win->img->green_mask,
300 win->img->blue_mask,
301 0x0, win->img->bits_per_pixel);
304 GP_FATAL("Unsupported visual %s", visual_class_name(win->vis->class));
305 return GP_PIXEL_UNKNOWN;
308 #ifdef HAVE_X_SHM
310 static int create_shm_ximage(GP_Backend *self, GP_Size w, GP_Size h)
312 struct x11_win *win = GP_BACKEND_PRIV(self);
313 enum GP_PixelType pixel_type;
315 if (XShmQueryExtension(win->dpy) == False) {
316 GP_DEBUG(1, "MIT SHM Extension not supported, "
317 "falling back to XImage");
318 return 1;
321 if (self->pixmap == NULL)
322 GP_DEBUG(1, "Using MIT SHM Extension");
324 win->img = XShmCreateImage(win->dpy, win->vis, win->scr_depth,
325 ZPixmap, NULL, &win->shminfo, w, h);
327 if (win->img == NULL) {
328 GP_WARN("Failed to create SHM XImage");
329 return 1;
332 size_t size = win->img->bytes_per_line * win->img->height;
334 pixel_type = match_pixel_type(win);
336 if (pixel_type == GP_PIXEL_UNKNOWN) {
337 GP_DEBUG(1, "Unknown pixel type");
338 goto err0;
341 win->shminfo.shmid = shmget(IPC_PRIVATE, size, 0600);
343 if (win->shminfo.shmid == -1) {
344 GP_WARN("Calling shmget() failed: %s", strerror(errno));
345 goto err0;
348 win->shminfo.shmaddr = win->img->data = shmat(win->shminfo.shmid, 0, 0);
350 if (win->shminfo.shmaddr == (void *)-1) {
351 GP_WARN("Calling shmat() failed: %s", strerror(errno));
352 goto err1;
355 /* Mark SHM for deletion after detach */
356 if (shmctl(win->shminfo.shmid, IPC_RMID, 0)) {
357 GP_WARN("Calling shmctl(..., IPC_RMID), 0) failed: %s",
358 strerror(errno));
359 goto err2;
362 win->shminfo.readOnly = False;
364 if (XShmAttach(win->dpy, &win->shminfo) == False) {
365 GP_WARN("XShmAttach failed");
366 goto err2;
369 GP_PixmapInit(&win->pixmap, w, h, pixel_type, win->shminfo.shmaddr);
370 win->pixmap.bytes_per_row = win->img->bytes_per_line;
372 self->pixmap = &win->pixmap;
374 win->shm_flag = 1;
376 //FIXME: Proper synchronization
377 XSync(win->dpy, True);
379 return 0;
380 err2:
381 shmdt(win->shminfo.shmaddr);
382 err1:
383 shmctl(win->shminfo.shmid, IPC_RMID, 0);
384 err0:
385 XDestroyImage(win->img);
386 return 1;
389 static void destroy_shm_ximage(GP_Backend *self)
391 struct x11_win *win = GP_BACKEND_PRIV(self);
393 XLockDisplay(win->dpy);
395 XShmDetach(win->dpy, &win->shminfo);
396 XFlush(win->dpy);
397 shmdt(win->shminfo.shmaddr);
398 XDestroyImage(win->img);
399 XFlush(win->dpy);
401 XUnlockDisplay(win->dpy);
404 static int resize_shm_ximage(GP_Backend *self, int w, int h)
406 struct x11_win *win = GP_BACKEND_PRIV(self);
407 int ret;
409 GP_DEBUG(4, "Resizing XShmImage %ux%u -> %ux%u",
410 self->pixmap->w, self->pixmap->h, w, h);
412 XLockDisplay(win->dpy);
414 destroy_shm_ximage(self);
415 ret = create_shm_ximage(self, w, h);
417 XUnlockDisplay(win->dpy);
419 return ret;
422 #else
424 static int create_shm_ximage(GP_Backend GP_UNUSED(*self),
425 GP_Size GP_UNUSED(w), GP_Size GP_UNUSED(h))
427 return 1;
430 static void destroy_shm_ximage(GP_Backend GP_UNUSED(*self))
432 GP_WARN("Stub called");
435 static int resize_shm_ximage(GP_Backend GP_UNUSED(*self),
436 int GP_UNUSED(w), int GP_UNUSED(h))
438 GP_WARN("Stub called");
439 return 1;
442 #endif /* HAVE_X_SHM */
444 static int create_ximage(GP_Backend *self, GP_Size w, GP_Size h)
446 struct x11_win *win = GP_BACKEND_PRIV(self);
447 enum GP_PixelType pixel_type;
448 int depth;
450 /* Get depth similiar to the default visual depth */
451 switch (win->scr_depth) {
452 case 32:
453 case 24:
454 depth = 32;
455 case 16:
456 depth = 16;
457 case 8:
458 depth = 8;
459 default:
460 /* TODO: better default */
461 depth = 32;
464 GP_DEBUG(1, "Screen depth %i, using XImage depth %i",
465 win->scr_depth, depth);
467 win->img = XCreateImage(win->dpy, win->vis, win->scr_depth, ZPixmap, 0,
468 NULL, w, h, depth, 0);
470 if (win->img == NULL) {
471 GP_DEBUG(1, "Failed to create XImage");
472 goto err0;
475 pixel_type = match_pixel_type(win);
477 if (pixel_type == GP_PIXEL_UNKNOWN) {
478 GP_DEBUG(1, "Unknown pixel type");
479 goto err1;
482 self->pixmap = GP_PixmapAlloc(w, h, pixel_type);
484 if (self->pixmap == NULL) {
485 GP_DEBUG(1, "Malloc failed :(");
486 goto err1;
489 win->shm_flag = 0;
490 win->img->data = (char*)self->pixmap->pixels;
492 return 0;
493 err1:
494 XDestroyImage(win->img);
495 err0:
496 return 1;
499 static void destroy_ximage(GP_Backend *self)
501 struct x11_win *win = GP_BACKEND_PRIV(self);
503 GP_PixmapFree(self->pixmap);
504 win->img->data = NULL;
505 XDestroyImage(win->img);
508 static int resize_ximage(GP_Backend *self, int w, int h)
510 struct x11_win *win = GP_BACKEND_PRIV(self);
511 XImage *img;
513 /* Create new X image */
514 img = XCreateImage(win->dpy, win->vis, win->scr_depth, ZPixmap, 0, NULL,
515 w, h, win->img->bitmap_pad, 0);
517 if (img == NULL) {
518 GP_DEBUG(2, "XCreateImage failed");
519 return 1;
522 /* Resize pixmap */
523 if (GP_PixmapResize(self->pixmap, w, h)) {
524 XDestroyImage(img);
525 return 1;
528 /* Free old image */
529 win->img->data = NULL;
530 XDestroyImage(win->img);
532 /* Swap the pointers */
533 img->data = (char*)self->pixmap->pixels;
534 win->img = img;
536 return 0;
539 static void window_close(GP_Backend *self)
541 struct x11_win *win = GP_BACKEND_PRIV(self);
543 XLockDisplay(win->dpy);
545 if (win->shm_flag)
546 destroy_shm_ximage(self);
547 else
548 destroy_ximage(self);
550 x11_win_close(win);
552 XUnlockDisplay(win->dpy);
555 static void x11_exit(GP_Backend *self)
557 struct x11_win *win = GP_BACKEND_PRIV(self);
559 GP_DEBUG(1, "Closing window %p", win);
561 if (win == last_win)
562 last_win = NULL;
564 window_close(self);
566 free(self);
569 GP_Backend *GP_BackendX11Init(const char *display, int x, int y,
570 unsigned int w, unsigned int h,
571 const char *caption,
572 enum GP_BackendX11Flags flags)
574 GP_Backend *backend;
575 struct x11_win *win;
577 backend = malloc(sizeof(GP_Backend) +
578 sizeof(struct x11_win));
580 if (backend == NULL)
581 return NULL;
583 win = GP_BACKEND_PRIV(backend);
585 //XSynchronize(win->dpy, True);
587 /* Pack parameters and open window */
588 struct x11_wreq wreq = {
589 .win = win,
590 .display = display,
591 .x = x,
592 .y = y,
593 .w = w,
594 .h = h,
595 .caption = caption,
596 .flags = flags,
599 x11_win_open(&wreq);
601 if (win->win == None) {
602 //TODO: Error message?
603 GP_DEBUG(1, "Failed to create window");
604 goto err1;
607 if (flags & GP_X11_FULLSCREEN)
608 x11_win_fullscreen(win, 1);
610 /* Init the event queue, once we know the window size */
611 GP_EventQueueInit(&backend->event_queue, wreq.w, wreq.h, 0);
613 backend->pixmap = NULL;
615 if ((flags & GP_X11_DISABLE_SHM || !x11_conn.local)
616 || create_shm_ximage(backend, wreq.w, wreq.h)) {
617 if (create_ximage(backend, wreq.w, wreq.h))
618 goto err1;
621 XFlush(win->dpy);
623 win->resized_flag = 0;
625 backend->name = "X11";
626 backend->Flip = x11_flip;
627 backend->UpdateRect = x11_update_rect;
628 backend->Exit = x11_exit;
629 backend->Poll = x11_poll;
630 backend->Wait = x11_wait;
631 backend->SetAttributes = x11_set_attributes;
632 backend->ResizeAck = x11_resize_ack;
633 backend->fd = XConnectionNumber(win->dpy);
634 backend->timers = NULL;
636 return backend;
637 err1:
638 x11_close();
639 free(backend);
640 return NULL;
643 void GP_BackendX11RequestFullscreen(GP_Backend *self, int mode)
645 struct x11_win *win = GP_BACKEND_PRIV(self);
647 x11_win_fullscreen(win, mode);
650 #else
652 #include "GP_Backend.h"
654 GP_Backend *GP_BackendX11Init(const char *GP_UNUSED(display),
655 int GP_UNUSED(x), int GP_UNUSED(y),
656 unsigned int GP_UNUSED(w),
657 unsigned int GP_UNUSED(h),
658 const char *GP_UNUSED(caption))
660 GP_FATAL("X11 support not compiled in");
661 return NULL;
664 void GP_BackendX11RequestFullscreen(GP_Backend *GP_UNUSED(self),
665 int GP_UNUSED(mode))
669 #endif /* HAVE_LIBX11 */
671 int GP_BackendIsX11(GP_Backend *self)
673 return !strcmp(self->name, "X11");