filters/gp_filter_resize_alloc: Check w and h
[gfxprim.git] / libs / backends / GP_X11.c
blob7dfc395031f1924959dbc795df47dffdba9a45e0
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>
30 #include <core/GP_Pixmap.h>
32 #ifdef HAVE_LIBX11
34 #include <X11/Xlib.h>
35 #include <X11/Xutil.h>
36 #include <X11/Xatom.h>
37 #include <X11/Xmd.h>
39 #ifdef HAVE_X_SHM
40 #include <sys/ipc.h>
41 #include <sys/shm.h>
42 #include <X11/extensions/XShm.h>
43 #endif /* HAVE_X_SHM */
45 #include "backends/GP_X11.h"
47 #include "GP_X11_Conn.h"
48 #include "GP_X11_Win.h"
49 #include "GP_X11_Input.h"
51 static int resize_ximage(gp_backend *self, int w, int h);
52 static int resize_shm_ximage(gp_backend *self, int w, int h);
54 static void putimage(struct x11_win *win, int x0, int y0, int x1, int y1)
56 #ifdef HAVE_X_SHM
57 if (win->shm_flag)
58 XShmPutImage(win->dpy, win->win, DefaultGC(win->dpy, win->scr),
59 win->img, x0, y0, x0, y0, x1-x0+1, y1-y0+1, False);
60 else
61 #endif /* HAVE_X_SHM */
62 XPutImage(win->dpy, win->win, DefaultGC(win->dpy, win->scr),
63 win->img, x0, y0, x0, y0, x1-x0+1, y1-y0+1);
66 static void x11_update_rect(gp_backend *self, gp_coord x0, gp_coord y0,
67 gp_coord x1, gp_coord y1)
69 struct x11_win *win = GP_BACKEND_PRIV(self);
71 GP_DEBUG(4, "Updating rect %ix%i-%ix%i", x0, y0, x1, y1);
73 if (win->resized_flag) {
74 GP_DEBUG(4, "Ignoring update rect, waiting for resize ack");
75 return;
78 XLockDisplay(win->dpy);
80 putimage(win, x0, y0, x1, y1);
82 XFlush(win->dpy);
84 XUnlockDisplay(win->dpy);
87 static void x11_flip(gp_backend *self)
89 struct x11_win *win = GP_BACKEND_PRIV(self);
90 unsigned int w = self->pixmap->w;
91 unsigned int h = self->pixmap->h;
93 GP_DEBUG(4, "Flipping pixmap");
95 if (win->resized_flag) {
96 GP_DEBUG(4, "Ignoring flip, waiting for resize ack");
97 return;
100 XLockDisplay(win->dpy);
102 putimage(win, 0, 0, w - 1, h - 1);
104 XFlush(win->dpy);
106 XUnlockDisplay(win->dpy);
109 static struct x11_win *last_win = NULL;
111 static void x11_ev(XEvent *ev)
113 struct x11_win *win;
115 /* Lookup for window */
116 if (last_win == NULL || last_win->win != ev->xany.window) {
117 last_win = win_list_lookup(ev->xany.window);
119 if (last_win == NULL) {
120 GP_WARN("Event for unknown window, ignoring.");
121 return;
125 win = last_win;
127 struct gp_backend *self = GP_CONTAINER_OF(win, gp_backend, priv);
129 switch (ev->type) {
130 case Expose:
131 GP_DEBUG(4, "Expose %ix%i-%ix%i %i",
132 ev->xexpose.x, ev->xexpose.y,
133 ev->xexpose.width, ev->xexpose.height,
134 ev->xexpose.count);
136 if (win->resized_flag)
137 break;
139 /* Safety measure */
140 if (ev->xexpose.x + ev->xexpose.width > (int)self->pixmap->w) {
141 GP_WARN("Expose x + w > pixmap->w");
142 break;
145 if (ev->xexpose.y + ev->xexpose.height > (int)self->pixmap->h) {
146 GP_WARN("Expose y + h > pixmap->h");
147 break;
150 /* Update the rectangle */
151 x11_update_rect(self, ev->xexpose.x, ev->xexpose.y,
152 ev->xexpose.x + ev->xexpose.width - 1,
153 ev->xexpose.y + ev->xexpose.height - 1);
154 break;
155 case ConfigureNotify:
156 if (ev->xconfigure.width == (int)self->pixmap->w &&
157 ev->xconfigure.height == (int)self->pixmap->h)
158 break;
160 if (ev->xconfigure.width == (int)win->new_w &&
161 ev->xconfigure.height == (int)win->new_h)
162 break;
164 win->new_w = ev->xconfigure.width;
165 win->new_h = ev->xconfigure.height;
167 GP_DEBUG(4, "Configure Notify %ux%u", win->new_w, win->new_h);
169 /* Window has been resized, set flag. */
170 win->resized_flag = 1;
171 default:
172 //TODO: More accurate window w and h?
173 x11_input_event_put(&self->event_queue, ev,
174 self->pixmap->w, self->pixmap->h);
175 break;
179 static void x11_poll(gp_backend *self)
181 struct x11_win *win = GP_BACKEND_PRIV(self);
182 XEvent ev;
184 XLockDisplay(win->dpy);
186 while (XPending(win->dpy)) {
187 XNextEvent(win->dpy, &ev);
188 x11_ev(&ev);
191 XUnlockDisplay(win->dpy);
194 #include <poll.h>
196 static void x11_wait(gp_backend *self)
198 struct pollfd fd = {.fd = self->fd, .events = POLLIN, .revents = 0};
199 poll(&fd, 1, -1);
200 x11_poll(self);
203 static int resize_buffer(struct gp_backend *self, uint32_t w, uint32_t h)
205 struct x11_win *win = GP_BACKEND_PRIV(self);
207 if (win->shm_flag) {
208 if (resize_shm_ximage(self, w, h))
209 return 1;
210 } else {
211 if (resize_ximage(self, w, h))
212 return 1;
215 return 0;
218 static int x11_set_attributes(struct gp_backend *self,
219 uint32_t w, uint32_t h,
220 const char *caption)
222 struct x11_win *win = GP_BACKEND_PRIV(self);
224 XLockDisplay(win->dpy);
226 if (caption != NULL) {
227 GP_DEBUG(3, "Setting window caption to '%s'", caption);
228 XmbSetWMProperties(win->dpy, win->win, caption, caption,
229 NULL, 0, NULL, NULL, NULL);
232 if (w != 0 && h != 0) {
233 if (win->fullscreen_flag) {
234 GP_DEBUG(1, "Ignoring resize request in fullscreen");
235 goto out;
237 GP_DEBUG(3, "Setting window size to %ux%u", w, h);
238 XResizeWindow(win->dpy, win->win, w, h);
241 XFlush(win->dpy);
243 out:
244 XUnlockDisplay(win->dpy);
245 return 0;
248 static int x11_resize_ack(struct gp_backend *self)
250 struct x11_win *win = GP_BACKEND_PRIV(self);
251 int ret;
253 XLockDisplay(win->dpy);
255 GP_DEBUG(3, "Setting buffer size to %ux%u", win->new_w, win->new_h);
257 ret = resize_buffer(self, win->new_w, win->new_h);
259 win->resized_flag = 0;
261 if (!ret) {
262 gp_event_queue_set_screen_size(&self->event_queue,
263 win->new_w, win->new_h);
266 GP_DEBUG(3, "Done");
268 XUnlockDisplay(win->dpy);
270 return ret;
273 static const char *visual_class_name(int class)
275 switch (class) {
276 case StaticGray:
277 return "StaticGray";
278 case GrayScale:
279 return "GrayScale";
280 case StaticColor:
281 return "StaticColor";
282 case PseudoColor:
283 return "PseudoColor";
284 case TrueColor:
285 return "TrueColor";
286 case DirectColor:
287 return "DirectColor";
290 return "Unknown";
293 static enum gp_pixel_type match_pixel_type(struct x11_win *win)
295 GP_DEBUG(1, "Matching image pixel type, visual=%s depth=%u",
296 visual_class_name(win->vis->class), win->img->bits_per_pixel);
298 if (win->vis->class == DirectColor || win->vis->class == TrueColor) {
299 return gp_pixel_rgb_match(win->img->red_mask,
300 win->img->green_mask,
301 win->img->blue_mask, 0x0,
302 win->img->bits_per_pixel);
305 GP_FATAL("Unsupported visual %s", visual_class_name(win->vis->class));
306 return GP_PIXEL_UNKNOWN;
309 #ifdef HAVE_X_SHM
311 static int create_shm_ximage(gp_backend *self, gp_size w, gp_size h)
313 struct x11_win *win = GP_BACKEND_PRIV(self);
314 enum gp_pixel_type pixel_type;
316 if (XShmQueryExtension(win->dpy) == False) {
317 GP_DEBUG(1, "MIT SHM Extension not supported, "
318 "falling back to XImage");
319 return 1;
322 if (self->pixmap == NULL)
323 GP_DEBUG(1, "Using MIT SHM Extension");
325 win->img = XShmCreateImage(win->dpy, win->vis, win->scr_depth,
326 ZPixmap, NULL, &win->shminfo, w, h);
328 if (win->img == NULL) {
329 GP_WARN("Failed to create SHM XImage");
330 return 1;
333 size_t size = win->img->bytes_per_line * win->img->height;
335 pixel_type = match_pixel_type(win);
337 if (pixel_type == GP_PIXEL_UNKNOWN) {
338 GP_DEBUG(1, "Unknown pixel type");
339 goto err0;
342 win->shminfo.shmid = shmget(IPC_PRIVATE, size, 0600);
344 if (win->shminfo.shmid == -1) {
345 GP_WARN("Calling shmget() failed: %s", strerror(errno));
346 goto err0;
349 win->shminfo.shmaddr = win->img->data = shmat(win->shminfo.shmid, 0, 0);
351 if (win->shminfo.shmaddr == (void *)-1) {
352 GP_WARN("Calling shmat() failed: %s", strerror(errno));
353 goto err1;
356 /* Mark SHM for deletion after detach */
357 if (shmctl(win->shminfo.shmid, IPC_RMID, 0)) {
358 GP_WARN("Calling shmctl(..., IPC_RMID), 0) failed: %s",
359 strerror(errno));
360 goto err2;
363 win->shminfo.readOnly = False;
365 if (XShmAttach(win->dpy, &win->shminfo) == False) {
366 GP_WARN("XShmAttach failed");
367 goto err2;
370 gp_pixmap_init(&win->pixmap, w, h, pixel_type, win->shminfo.shmaddr);
371 win->pixmap.bytes_per_row = win->img->bytes_per_line;
373 self->pixmap = &win->pixmap;
375 win->shm_flag = 1;
377 //FIXME: Proper synchronization
378 XSync(win->dpy, True);
380 return 0;
381 err2:
382 shmdt(win->shminfo.shmaddr);
383 err1:
384 shmctl(win->shminfo.shmid, IPC_RMID, 0);
385 err0:
386 XDestroyImage(win->img);
387 return 1;
390 static void destroy_shm_ximage(gp_backend *self)
392 struct x11_win *win = GP_BACKEND_PRIV(self);
394 XLockDisplay(win->dpy);
396 XShmDetach(win->dpy, &win->shminfo);
397 XFlush(win->dpy);
398 shmdt(win->shminfo.shmaddr);
399 XDestroyImage(win->img);
400 XFlush(win->dpy);
402 XUnlockDisplay(win->dpy);
405 static int resize_shm_ximage(gp_backend *self, int w, int h)
407 struct x11_win *win = GP_BACKEND_PRIV(self);
408 int ret;
410 GP_DEBUG(4, "Resizing XShmImage %ux%u -> %ux%u",
411 self->pixmap->w, self->pixmap->h, w, h);
413 XLockDisplay(win->dpy);
415 destroy_shm_ximage(self);
416 ret = create_shm_ximage(self, w, h);
418 XUnlockDisplay(win->dpy);
420 return ret;
423 #else
425 static int create_shm_ximage(gp_backend GP_UNUSED(*self),
426 gp_size GP_UNUSED(w), gp_size GP_UNUSED(h))
428 return 1;
431 static void destroy_shm_ximage(gp_backend GP_UNUSED(*self))
433 GP_WARN("Stub called");
436 static int resize_shm_ximage(gp_backend GP_UNUSED(*self),
437 int GP_UNUSED(w), int GP_UNUSED(h))
439 GP_WARN("Stub called");
440 return 1;
443 #endif /* HAVE_X_SHM */
445 static int create_ximage(gp_backend *self, gp_size w, gp_size h)
447 struct x11_win *win = GP_BACKEND_PRIV(self);
448 enum gp_pixel_type pixel_type;
449 int depth;
451 /* Get depth similiar to the default visual depth */
452 switch (win->scr_depth) {
453 case 32:
454 case 24:
455 depth = 32;
456 case 16:
457 depth = 16;
458 case 8:
459 depth = 8;
460 default:
461 /* TODO: better default */
462 depth = 32;
465 GP_DEBUG(1, "Screen depth %i, using XImage depth %i",
466 win->scr_depth, depth);
468 win->img = XCreateImage(win->dpy, win->vis, win->scr_depth, ZPixmap, 0,
469 NULL, w, h, depth, 0);
471 if (win->img == NULL) {
472 GP_DEBUG(1, "Failed to create XImage");
473 goto err0;
476 pixel_type = match_pixel_type(win);
478 if (pixel_type == GP_PIXEL_UNKNOWN) {
479 GP_DEBUG(1, "Unknown pixel type");
480 goto err1;
483 self->pixmap = gp_pixmap_alloc(w, h, pixel_type);
485 if (self->pixmap == NULL) {
486 GP_DEBUG(1, "Malloc failed :(");
487 goto err1;
490 win->shm_flag = 0;
491 win->img->data = (char*)self->pixmap->pixels;
493 return 0;
494 err1:
495 XDestroyImage(win->img);
496 err0:
497 return 1;
500 static void destroy_ximage(gp_backend *self)
502 struct x11_win *win = GP_BACKEND_PRIV(self);
504 gp_pixmap_free(self->pixmap);
505 win->img->data = NULL;
506 XDestroyImage(win->img);
509 static int resize_ximage(gp_backend *self, int w, int h)
511 struct x11_win *win = GP_BACKEND_PRIV(self);
512 XImage *img;
514 /* Create new X image */
515 img = XCreateImage(win->dpy, win->vis, win->scr_depth, ZPixmap, 0, NULL,
516 w, h, win->img->bitmap_pad, 0);
518 if (img == NULL) {
519 GP_DEBUG(2, "XCreateImage failed");
520 return 1;
523 /* Resize pixmap */
524 if (gp_pixmap_resize(self->pixmap, w, h)) {
525 XDestroyImage(img);
526 return 1;
529 /* Free old image */
530 win->img->data = NULL;
531 XDestroyImage(win->img);
533 /* Swap the pointers */
534 img->data = (char*)self->pixmap->pixels;
535 win->img = img;
537 return 0;
540 static void window_close(gp_backend *self)
542 struct x11_win *win = GP_BACKEND_PRIV(self);
544 XLockDisplay(win->dpy);
546 if (win->shm_flag)
547 destroy_shm_ximage(self);
548 else
549 destroy_ximage(self);
551 x11_win_close(win);
553 XUnlockDisplay(win->dpy);
556 static void x11_exit(gp_backend *self)
558 struct x11_win *win = GP_BACKEND_PRIV(self);
560 GP_DEBUG(1, "Closing window %p", win);
562 if (win == last_win)
563 last_win = NULL;
565 window_close(self);
567 free(self);
570 gp_backend *gp_x11_init(const char *display, int x, int y,
571 unsigned int w, unsigned int h,
572 const char *caption,
573 enum gp_x11_flags flags)
575 gp_backend *backend;
576 struct x11_win *win;
578 backend = malloc(sizeof(gp_backend) +
579 sizeof(struct x11_win));
581 if (backend == NULL)
582 return NULL;
584 win = GP_BACKEND_PRIV(backend);
586 //XSynchronize(win->dpy, True);
588 /* Pack parameters and open window */
589 struct x11_wreq wreq = {
590 .win = win,
591 .display = display,
592 .x = x,
593 .y = y,
594 .w = w,
595 .h = h,
596 .caption = caption,
597 .flags = flags,
600 x11_win_open(&wreq);
602 if (win->win == None) {
603 //TODO: Error message?
604 GP_DEBUG(1, "Failed to create window");
605 goto err1;
608 if (flags & GP_X11_FULLSCREEN)
609 x11_win_fullscreen(win, 1);
611 /* Init the event queue, once we know the window size */
612 gp_event_queue_init(&backend->event_queue, wreq.w, wreq.h, 0);
614 backend->pixmap = NULL;
616 if ((flags & GP_X11_DISABLE_SHM || !x11_conn.local)
617 || create_shm_ximage(backend, wreq.w, wreq.h)) {
618 if (create_ximage(backend, wreq.w, wreq.h))
619 goto err1;
622 XFlush(win->dpy);
624 win->resized_flag = 0;
626 backend->name = "X11";
627 backend->flip = x11_flip;
628 backend->update_rect = x11_update_rect;
629 backend->exit = x11_exit;
630 backend->poll = x11_poll;
631 backend->wait = x11_wait;
632 backend->set_attrs = x11_set_attributes;
633 backend->resize_ack = x11_resize_ack;
634 backend->fd = XConnectionNumber(win->dpy);
635 backend->timers = NULL;
637 return backend;
638 err1:
639 x11_close();
640 free(backend);
641 return NULL;
644 void gp_x11_fullscreen(gp_backend *self, int mode)
646 struct x11_win *win = GP_BACKEND_PRIV(self);
648 x11_win_fullscreen(win, mode);
651 #else
653 #include <backends/GP_Backend.h>
655 gp_backend *gp_x11_init(const char *GP_UNUSED(display),
656 int GP_UNUSED(x), int GP_UNUSED(y),
657 unsigned int GP_UNUSED(w),
658 unsigned int GP_UNUSED(h),
659 const char *GP_UNUSED(caption))
661 GP_FATAL("X11 support not compiled in");
662 return NULL;
665 void gp_x11_fullscreen(gp_backend *GP_UNUSED(self),
666 int GP_UNUSED(mode))
668 GP_FATAL("X11 support not compiled in");
671 #endif /* HAVE_LIBX11 */
673 int gp_backend_is_x11(gp_backend *self)
675 return !strcmp(self->name, "X11");