1 /*****************************************************************************
2 * This file is part of gfxprim library. *
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. *
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. *
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 *
19 * Copyright (C) 2009-2013 Cyril Hrubis <metan@ucw.cz> *
21 *****************************************************************************/
26 #include "../../config.h"
28 #include <core/GP_Debug.h>
29 #include <core/GP_Common.h>
30 #include <core/GP_Pixmap.h>
35 #include <X11/Xutil.h>
36 #include <X11/Xatom.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
)
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
);
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");
78 XLockDisplay(win
->dpy
);
80 putimage(win
, x0
, y0
, x1
, y1
);
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");
100 XLockDisplay(win
->dpy
);
102 putimage(win
, 0, 0, w
- 1, h
- 1);
106 XUnlockDisplay(win
->dpy
);
109 static struct x11_win
*last_win
= NULL
;
111 static void x11_ev(XEvent
*ev
)
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.");
127 struct gp_backend
*self
= GP_CONTAINER_OF(win
, gp_backend
, priv
);
131 GP_DEBUG(4, "Expose %ix%i-%ix%i %i",
132 ev
->xexpose
.x
, ev
->xexpose
.y
,
133 ev
->xexpose
.width
, ev
->xexpose
.height
,
136 if (win
->resized_flag
)
140 if (ev
->xexpose
.x
+ ev
->xexpose
.width
> (int)self
->pixmap
->w
) {
141 GP_WARN("Expose x + w > pixmap->w");
145 if (ev
->xexpose
.y
+ ev
->xexpose
.height
> (int)self
->pixmap
->h
) {
146 GP_WARN("Expose y + h > pixmap->h");
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);
155 case ConfigureNotify
:
156 if (ev
->xconfigure
.width
== (int)self
->pixmap
->w
&&
157 ev
->xconfigure
.height
== (int)self
->pixmap
->h
)
160 if (ev
->xconfigure
.width
== (int)win
->new_w
&&
161 ev
->xconfigure
.height
== (int)win
->new_h
)
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;
172 //TODO: More accurate window w and h?
173 x11_input_event_put(&self
->event_queue
, ev
,
174 self
->pixmap
->w
, self
->pixmap
->h
);
179 static void x11_poll(gp_backend
*self
)
181 struct x11_win
*win
= GP_BACKEND_PRIV(self
);
184 XLockDisplay(win
->dpy
);
186 while (XPending(win
->dpy
)) {
187 XNextEvent(win
->dpy
, &ev
);
191 XUnlockDisplay(win
->dpy
);
196 static void x11_wait(gp_backend
*self
)
198 struct pollfd fd
= {.fd
= self
->fd
, .events
= POLLIN
, .revents
= 0};
203 static int resize_buffer(struct gp_backend
*self
, uint32_t w
, uint32_t h
)
205 struct x11_win
*win
= GP_BACKEND_PRIV(self
);
208 if (resize_shm_ximage(self
, w
, h
))
211 if (resize_ximage(self
, w
, h
))
218 static int x11_set_attributes(struct gp_backend
*self
,
219 uint32_t w
, uint32_t h
,
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");
237 GP_DEBUG(3, "Setting window size to %ux%u", w
, h
);
238 XResizeWindow(win
->dpy
, win
->win
, w
, h
);
244 XUnlockDisplay(win
->dpy
);
248 static int x11_resize_ack(struct gp_backend
*self
)
250 struct x11_win
*win
= GP_BACKEND_PRIV(self
);
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;
262 gp_event_queue_set_screen_size(&self
->event_queue
,
263 win
->new_w
, win
->new_h
);
268 XUnlockDisplay(win
->dpy
);
273 static const char *visual_class_name(int class)
281 return "StaticColor";
283 return "PseudoColor";
287 return "DirectColor";
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
;
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");
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");
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");
342 win
->shminfo
.shmid
= shmget(IPC_PRIVATE
, size
, 0600);
344 if (win
->shminfo
.shmid
== -1) {
345 GP_WARN("Calling shmget() failed: %s", strerror(errno
));
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
));
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",
363 win
->shminfo
.readOnly
= False
;
365 if (XShmAttach(win
->dpy
, &win
->shminfo
) == False
) {
366 GP_WARN("XShmAttach failed");
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
;
377 //FIXME: Proper synchronization
378 XSync(win
->dpy
, True
);
382 shmdt(win
->shminfo
.shmaddr
);
384 shmctl(win
->shminfo
.shmid
, IPC_RMID
, 0);
386 XDestroyImage(win
->img
);
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
);
398 shmdt(win
->shminfo
.shmaddr
);
399 XDestroyImage(win
->img
);
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
);
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
);
425 static int create_shm_ximage(gp_backend
GP_UNUSED(*self
),
426 gp_size
GP_UNUSED(w
), gp_size
GP_UNUSED(h
))
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");
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
;
451 /* Get depth similiar to the default visual depth */
452 switch (win
->scr_depth
) {
461 /* TODO: better default */
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");
476 pixel_type
= match_pixel_type(win
);
478 if (pixel_type
== GP_PIXEL_UNKNOWN
) {
479 GP_DEBUG(1, "Unknown pixel type");
483 self
->pixmap
= gp_pixmap_alloc(w
, h
, pixel_type
);
485 if (self
->pixmap
== NULL
) {
486 GP_DEBUG(1, "Malloc failed :(");
491 win
->img
->data
= (char*)self
->pixmap
->pixels
;
495 XDestroyImage(win
->img
);
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
);
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);
519 GP_DEBUG(2, "XCreateImage failed");
524 if (gp_pixmap_resize(self
->pixmap
, w
, h
)) {
530 win
->img
->data
= NULL
;
531 XDestroyImage(win
->img
);
533 /* Swap the pointers */
534 img
->data
= (char*)self
->pixmap
->pixels
;
540 static void window_close(gp_backend
*self
)
542 struct x11_win
*win
= GP_BACKEND_PRIV(self
);
544 XLockDisplay(win
->dpy
);
547 destroy_shm_ximage(self
);
549 destroy_ximage(self
);
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
);
570 gp_backend
*gp_x11_init(const char *display
, int x
, int y
,
571 unsigned int w
, unsigned int h
,
573 enum gp_x11_flags flags
)
578 backend
= malloc(sizeof(gp_backend
) +
579 sizeof(struct x11_win
));
584 win
= GP_BACKEND_PRIV(backend
);
586 //XSynchronize(win->dpy, True);
588 /* Pack parameters and open window */
589 struct x11_wreq wreq
= {
602 if (win
->win
== None
) {
603 //TODO: Error message?
604 GP_DEBUG(1, "Failed to create window");
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
))
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
;
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
);
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");
665 void gp_x11_fullscreen(gp_backend
*GP_UNUSED(self
),
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");