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"
34 #include <X11/Xutil.h>
35 #include <X11/Xatom.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
)
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
);
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");
77 XLockDisplay(win
->dpy
);
79 putimage(win
, x0
, y0
, x1
, y1
);
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");
99 XLockDisplay(win
->dpy
);
101 putimage(win
, 0, 0, w
- 1, h
- 1);
105 XUnlockDisplay(win
->dpy
);
108 static struct x11_win
*last_win
= NULL
;
110 static void x11_ev(XEvent
*ev
)
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.");
126 struct GP_Backend
*self
= GP_CONTAINER_OF(win
, struct GP_Backend
, priv
);
130 GP_DEBUG(4, "Expose %ix%i-%ix%i %i",
131 ev
->xexpose
.x
, ev
->xexpose
.y
,
132 ev
->xexpose
.width
, ev
->xexpose
.height
,
135 if (win
->resized_flag
)
139 if (ev
->xexpose
.x
+ ev
->xexpose
.width
> (int)self
->pixmap
->w
) {
140 GP_WARN("Expose x + w > pixmap->w");
144 if (ev
->xexpose
.y
+ ev
->xexpose
.height
> (int)self
->pixmap
->h
) {
145 GP_WARN("Expose y + h > pixmap->h");
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);
154 case ConfigureNotify
:
155 if (ev
->xconfigure
.width
== (int)self
->pixmap
->w
&&
156 ev
->xconfigure
.height
== (int)self
->pixmap
->h
)
159 if (ev
->xconfigure
.width
== (int)win
->new_w
&&
160 ev
->xconfigure
.height
== (int)win
->new_h
)
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;
171 //TODO: More accurate window w and h?
172 x11_input_event_put(&self
->event_queue
, ev
,
173 self
->pixmap
->w
, self
->pixmap
->h
);
178 static void x11_poll(GP_Backend
*self
)
180 struct x11_win
*win
= GP_BACKEND_PRIV(self
);
183 XLockDisplay(win
->dpy
);
185 while (XPending(win
->dpy
)) {
186 XNextEvent(win
->dpy
, &ev
);
190 XUnlockDisplay(win
->dpy
);
195 static void x11_wait(GP_Backend
*self
)
197 struct pollfd fd
= {.fd
= self
->fd
, .events
= POLLIN
, .revents
= 0};
202 static int resize_buffer(struct GP_Backend
*self
, uint32_t w
, uint32_t h
)
204 struct x11_win
*win
= GP_BACKEND_PRIV(self
);
207 if (resize_shm_ximage(self
, w
, h
))
210 if (resize_ximage(self
, w
, h
))
217 static int x11_set_attributes(struct GP_Backend
*self
,
218 uint32_t w
, uint32_t h
,
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");
236 GP_DEBUG(3, "Setting window size to %ux%u", w
, h
);
237 XResizeWindow(win
->dpy
, win
->win
, w
, h
);
243 XUnlockDisplay(win
->dpy
);
247 static int x11_resize_ack(struct GP_Backend
*self
)
249 struct x11_win
*win
= GP_BACKEND_PRIV(self
);
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;
261 GP_EventQueueSetScreenSize(&self
->event_queue
,
262 win
->new_w
, win
->new_h
);
267 XUnlockDisplay(win
->dpy
);
272 static const char *visual_class_name(int class)
280 return "StaticColor";
282 return "PseudoColor";
286 return "DirectColor";
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
,
301 0x0, win
->img
->bits_per_pixel
);
304 GP_FATAL("Unsupported visual %s", visual_class_name(win
->vis
->class));
305 return GP_PIXEL_UNKNOWN
;
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");
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");
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");
341 win
->shminfo
.shmid
= shmget(IPC_PRIVATE
, size
, 0600);
343 if (win
->shminfo
.shmid
== -1) {
344 GP_WARN("Calling shmget() failed: %s", strerror(errno
));
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
));
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",
362 win
->shminfo
.readOnly
= False
;
364 if (XShmAttach(win
->dpy
, &win
->shminfo
) == False
) {
365 GP_WARN("XShmAttach failed");
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
;
376 //FIXME: Proper synchronization
377 XSync(win
->dpy
, True
);
381 shmdt(win
->shminfo
.shmaddr
);
383 shmctl(win
->shminfo
.shmid
, IPC_RMID
, 0);
385 XDestroyImage(win
->img
);
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
);
397 shmdt(win
->shminfo
.shmaddr
);
398 XDestroyImage(win
->img
);
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
);
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
);
424 static int create_shm_ximage(GP_Backend
GP_UNUSED(*self
),
425 GP_Size
GP_UNUSED(w
), GP_Size
GP_UNUSED(h
))
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");
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
;
450 /* Get depth similiar to the default visual depth */
451 switch (win
->scr_depth
) {
460 /* TODO: better default */
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");
475 pixel_type
= match_pixel_type(win
);
477 if (pixel_type
== GP_PIXEL_UNKNOWN
) {
478 GP_DEBUG(1, "Unknown pixel type");
482 self
->pixmap
= GP_PixmapAlloc(w
, h
, pixel_type
);
484 if (self
->pixmap
== NULL
) {
485 GP_DEBUG(1, "Malloc failed :(");
490 win
->img
->data
= (char*)self
->pixmap
->pixels
;
494 XDestroyImage(win
->img
);
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
);
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);
518 GP_DEBUG(2, "XCreateImage failed");
523 if (GP_PixmapResize(self
->pixmap
, w
, h
)) {
529 win
->img
->data
= NULL
;
530 XDestroyImage(win
->img
);
532 /* Swap the pointers */
533 img
->data
= (char*)self
->pixmap
->pixels
;
539 static void window_close(GP_Backend
*self
)
541 struct x11_win
*win
= GP_BACKEND_PRIV(self
);
543 XLockDisplay(win
->dpy
);
546 destroy_shm_ximage(self
);
548 destroy_ximage(self
);
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
);
569 GP_Backend
*GP_BackendX11Init(const char *display
, int x
, int y
,
570 unsigned int w
, unsigned int h
,
572 enum GP_BackendX11Flags flags
)
577 backend
= malloc(sizeof(GP_Backend
) +
578 sizeof(struct x11_win
));
583 win
= GP_BACKEND_PRIV(backend
);
585 //XSynchronize(win->dpy, True);
587 /* Pack parameters and open window */
588 struct x11_wreq wreq
= {
601 if (win
->win
== None
) {
602 //TODO: Error message?
603 GP_DEBUG(1, "Failed to create window");
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
))
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
;
643 void GP_BackendX11RequestFullscreen(GP_Backend
*self
, int mode
)
645 struct x11_win
*win
= GP_BACKEND_PRIV(self
);
647 x11_win_fullscreen(win
, mode
);
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");
664 void GP_BackendX11RequestFullscreen(GP_Backend
*GP_UNUSED(self
),
669 #endif /* HAVE_LIBX11 */
671 int GP_BackendIsX11(GP_Backend
*self
)
673 return !strcmp(self
->name
, "X11");