5a81cc7a5fd12409f3337675c84be9a2ec9a54b0
[sdl_omap.git] / src / video / omapdss / linux / xenv.c
1 /*
2  * (C) GraÅžvydas "notaz" Ignotas, 2009-2012
3  *
4  * This work is licensed under the terms of any of these licenses
5  * (at your option):
6  *  - GNU GPL, version 2 or later.
7  *  - GNU LGPL, version 2.1 or later.
8  * See the COPYING file in the top-level directory.
9  */
10
11 #include <stdio.h>
12 #include <string.h>
13 #include <pthread.h>
14
15 #include <dlfcn.h>
16 #include <X11/Xlib.h>
17 #include <X11/Xutil.h>
18 #include <X11/XKBlib.h>
19
20 #include <sys/types.h>
21 #include <sys/stat.h>
22 #include <fcntl.h>
23 #include <unistd.h>
24 #include <sys/ioctl.h>
25 #include <termios.h>
26 #include <linux/kd.h>
27
28 #include "xenv.h"
29
30 #define PFX "xenv: "
31
32 #define FPTR(f) typeof(f) * p##f
33 #define FPTR_LINK(xf, dl, f) { \
34         xf.p##f = dlsym(dl, #f); \
35         if (xf.p##f == NULL) { \
36                 fprintf(stderr, "missing symbol: %s\n", #f); \
37                 goto fail; \
38         } \
39 }
40
41 struct xstuff {
42         Display *display;
43         Window window;
44         FPTR(XCreateBitmapFromData);
45         FPTR(XCreatePixmapCursor);
46         FPTR(XFreePixmap);
47         FPTR(XOpenDisplay);
48         FPTR(XDisplayName);
49         FPTR(XCloseDisplay);
50         FPTR(XCreateSimpleWindow);
51         FPTR(XChangeWindowAttributes);
52         FPTR(XSelectInput);
53         FPTR(XMapWindow);
54         FPTR(XNextEvent);
55         FPTR(XCheckTypedEvent);
56         FPTR(XWithdrawWindow);
57         FPTR(XGrabKeyboard);
58         FPTR(XPending);
59         FPTR(XLookupKeysym);
60         FPTR(XkbSetDetectableAutoRepeat);
61         FPTR(XkbKeycodeToKeysym);
62         FPTR(XStoreName);
63         FPTR(XIconifyWindow);
64         FPTR(XMoveResizeWindow);
65         FPTR(XInternAtom);
66         FPTR(XAllocWMHints);
67         FPTR(XGetWMHints);
68         FPTR(XSetWMHints);
69         FPTR(XSync);
70         FPTR(XFree);
71 };
72
73 static struct xstuff g_xstuff;
74
75 static Cursor transparent_cursor(struct xstuff *xf, Display *display, Window win)
76 {
77         Cursor cursor;
78         Pixmap pix;
79         XColor dummy;
80         char d = 0;
81
82         memset(&dummy, 0, sizeof(dummy));
83         pix = xf->pXCreateBitmapFromData(display, win, &d, 1, 1);
84         cursor = xf->pXCreatePixmapCursor(display, pix, pix,
85                         &dummy, &dummy, 0, 0);
86         xf->pXFreePixmap(display, pix);
87         return cursor;
88 }
89
90 static int x11h_init(int *xenv_flags, const char *window_title)
91 {
92         unsigned int display_width, display_height;
93         Display *display;
94         XSetWindowAttributes attributes;
95         Window win;
96         Visual *visual;
97         long evt_mask;
98         void *x11lib;
99         int screen;
100
101         memset(&g_xstuff, 0, sizeof(g_xstuff));
102         x11lib = dlopen("libX11.so.6", RTLD_LAZY);
103         if (x11lib == NULL) {
104                 fprintf(stderr, "libX11.so load failed:\n%s\n", dlerror());
105                 goto fail;
106         }
107         FPTR_LINK(g_xstuff, x11lib, XCreateBitmapFromData);
108         FPTR_LINK(g_xstuff, x11lib, XCreatePixmapCursor);
109         FPTR_LINK(g_xstuff, x11lib, XFreePixmap);
110         FPTR_LINK(g_xstuff, x11lib, XOpenDisplay);
111         FPTR_LINK(g_xstuff, x11lib, XDisplayName);
112         FPTR_LINK(g_xstuff, x11lib, XCloseDisplay);
113         FPTR_LINK(g_xstuff, x11lib, XCreateSimpleWindow);
114         FPTR_LINK(g_xstuff, x11lib, XChangeWindowAttributes);
115         FPTR_LINK(g_xstuff, x11lib, XSelectInput);
116         FPTR_LINK(g_xstuff, x11lib, XMapWindow);
117         FPTR_LINK(g_xstuff, x11lib, XNextEvent);
118         FPTR_LINK(g_xstuff, x11lib, XCheckTypedEvent);
119         FPTR_LINK(g_xstuff, x11lib, XWithdrawWindow);
120         FPTR_LINK(g_xstuff, x11lib, XGrabKeyboard);
121         FPTR_LINK(g_xstuff, x11lib, XPending);
122         FPTR_LINK(g_xstuff, x11lib, XLookupKeysym);
123         FPTR_LINK(g_xstuff, x11lib, XkbSetDetectableAutoRepeat);
124         FPTR_LINK(g_xstuff, x11lib, XkbKeycodeToKeysym);
125         FPTR_LINK(g_xstuff, x11lib, XStoreName);
126         FPTR_LINK(g_xstuff, x11lib, XIconifyWindow);
127         FPTR_LINK(g_xstuff, x11lib, XMoveResizeWindow);
128         FPTR_LINK(g_xstuff, x11lib, XInternAtom);
129         FPTR_LINK(g_xstuff, x11lib, XAllocWMHints);
130         FPTR_LINK(g_xstuff, x11lib, XGetWMHints);
131         FPTR_LINK(g_xstuff, x11lib, XSetWMHints);
132         FPTR_LINK(g_xstuff, x11lib, XSync);
133         FPTR_LINK(g_xstuff, x11lib, XFree);
134
135         //XInitThreads();
136
137         g_xstuff.display = display = g_xstuff.pXOpenDisplay(NULL);
138         if (display == NULL)
139         {
140                 fprintf(stderr, "cannot connect to X server %s, X handling disabled.\n",
141                                 g_xstuff.pXDisplayName(NULL));
142                 goto fail2;
143         }
144
145         visual = DefaultVisual(display, 0);
146         if (visual->class != TrueColor)
147                 fprintf(stderr, PFX "warning: non true color visual\n");
148
149         printf(PFX "X vendor: %s, rel: %d, display: %s, protocol ver: %d.%d\n", ServerVendor(display),
150                 VendorRelease(display), DisplayString(display), ProtocolVersion(display),
151                 ProtocolRevision(display));
152
153         screen = DefaultScreen(display);
154
155         display_width = DisplayWidth(display, screen);
156         display_height = DisplayHeight(display, screen);
157         printf(PFX "display is %dx%d\n", display_width, display_height);
158
159         g_xstuff.window = win = g_xstuff.pXCreateSimpleWindow(display,
160                 RootWindow(display, screen), 0, 0, display_width, display_height,
161                 0, BlackPixel(display, screen), BlackPixel(display, screen));
162
163         attributes.override_redirect = True;
164         attributes.cursor = transparent_cursor(&g_xstuff, display, win);
165         g_xstuff.pXChangeWindowAttributes(display, win, CWOverrideRedirect | CWCursor, &attributes);
166
167         if (window_title != NULL)
168                 g_xstuff.pXStoreName(display, win, window_title);
169         evt_mask = ExposureMask | FocusChangeMask | PropertyChangeMask;
170         if (xenv_flags && (*xenv_flags & XENV_CAP_KEYS))
171                 evt_mask |= KeyPressMask | KeyReleaseMask;
172         if (xenv_flags && (*xenv_flags & XENV_CAP_MOUSE))
173                 evt_mask |= ButtonPressMask | ButtonReleaseMask | PointerMotionMask;
174         g_xstuff.pXSelectInput(display, win, evt_mask);
175         g_xstuff.pXMapWindow(display, win);
176         g_xstuff.pXGrabKeyboard(display, win, False, GrabModeAsync, GrabModeAsync, CurrentTime);
177         g_xstuff.pXkbSetDetectableAutoRepeat(display, 1, NULL);
178         // XSetIOErrorHandler
179
180         // we don't know when event dispatch will be called, so sync now
181         g_xstuff.pXSync(display, False);
182
183         return 0;
184 fail2:
185         dlclose(x11lib);
186 fail:
187         g_xstuff.display = NULL;
188         fprintf(stderr, "x11 handling disabled.\n");
189         return -1;
190 }
191
192 static void x11h_update(int (*key_cb)(void *cb_arg, int kc, int is_pressed),
193                         int (*mouseb_cb)(void *cb_arg, int x, int y, int button, int is_pressed),
194                         int (*mousem_cb)(void *cb_arg, int x, int y),
195                         void *cb_arg)
196 {
197         XEvent evt;
198         int keysym;
199
200         while (g_xstuff.pXPending(g_xstuff.display))
201         {
202                 g_xstuff.pXNextEvent(g_xstuff.display, &evt);
203                 switch (evt.type)
204                 {
205                 case Expose:
206                         while (g_xstuff.pXCheckTypedEvent(g_xstuff.display, Expose, &evt))
207                                 ;
208                         break;
209
210                 case KeyPress:
211                         keysym = g_xstuff.pXLookupKeysym(&evt.xkey, 0);
212                         if (key_cb != NULL)
213                                 key_cb(cb_arg, keysym, 1);
214                         break;
215
216                 case KeyRelease:
217                         keysym = g_xstuff.pXLookupKeysym(&evt.xkey, 0);
218                         if (key_cb != NULL)
219                                 key_cb(cb_arg, keysym, 0);
220                         break;
221
222                 case ButtonPress:
223                         if (mouseb_cb != NULL)
224                                 mouseb_cb(cb_arg, evt.xbutton.x, evt.xbutton.y,
225                                           evt.xbutton.button, 1);
226                         break;
227
228                 case ButtonRelease:
229                         if (mouseb_cb != NULL)
230                                 mouseb_cb(cb_arg, evt.xbutton.x, evt.xbutton.y,
231                                           evt.xbutton.button, 0);
232                         break;
233
234                 case MotionNotify:
235                         if (mousem_cb != NULL)
236                                 mousem_cb(cb_arg, evt.xmotion.x, evt.xmotion.y);
237                         break;
238                 }
239         }
240 }
241
242 static void x11h_wait_vmstate(void)
243 {
244         Atom wm_state = g_xstuff.pXInternAtom(g_xstuff.display, "WM_STATE", False);
245         XEvent evt;
246         int i;
247
248         usleep(20000);
249
250         for (i = 0; i < 20; i++) {
251                 while (g_xstuff.pXPending(g_xstuff.display)) {
252                         g_xstuff.pXNextEvent(g_xstuff.display, &evt);
253                         // printf("w event %d\n", evt.type);
254                         if (evt.type == PropertyNotify && evt.xproperty.atom == wm_state)
255                                 return;
256                 }
257                 usleep(200000);
258         }
259
260         fprintf(stderr, PFX "timeout waiting for wm_state change\n");
261 }
262
263 static int x11h_minimize(void)
264 {
265         XSetWindowAttributes attributes;
266         Display *display = g_xstuff.display;
267         Window window = g_xstuff.window;
268         int screen = DefaultScreen(g_xstuff.display);
269         int display_width, display_height;
270         XWMHints *wm_hints;
271         XEvent evt;
272
273         g_xstuff.pXWithdrawWindow(display, window, screen);
274
275         attributes.override_redirect = False;
276         g_xstuff.pXChangeWindowAttributes(display, window,
277                 CWOverrideRedirect, &attributes);
278
279         wm_hints = g_xstuff.pXGetWMHints(display, window);
280         if (wm_hints == NULL) {
281                 wm_hints = XAllocWMHints();
282                 wm_hints->flags = 0;
283         }
284         wm_hints->flags |= StateHint;
285         wm_hints->initial_state = IconicState;
286         g_xstuff.pXSetWMHints(display, window, wm_hints);
287         g_xstuff.pXFree(wm_hints);
288         wm_hints = NULL;
289
290         g_xstuff.pXMapWindow(display, window);
291
292         while (g_xstuff.pXNextEvent(display, &evt) == 0)
293         {
294                 // printf("m event %d\n", evt.type);
295                 switch (evt.type)
296                 {
297                         case FocusIn:
298                                 goto out;
299                         default:
300                                 break;
301                 }
302         }
303
304 out:
305         g_xstuff.pXWithdrawWindow(display, window, screen);
306
307         // must wait for some magic vmstate property change before setting override_redirect
308         x11h_wait_vmstate();
309
310         attributes.override_redirect = True;
311         g_xstuff.pXChangeWindowAttributes(display, window,
312                 CWOverrideRedirect, &attributes);
313
314         // fixup window after resize on override_redirect loss
315         display_width = DisplayWidth(display, screen);
316         display_height = DisplayHeight(display, screen);
317         g_xstuff.pXMoveResizeWindow(display, window, 0, 0, display_width, display_height);
318
319         g_xstuff.pXMapWindow(display, window);
320         g_xstuff.pXGrabKeyboard(display, window, False, GrabModeAsync, GrabModeAsync, CurrentTime);
321         g_xstuff.pXkbSetDetectableAutoRepeat(display, 1, NULL);
322
323         // we don't know when event dispatch will be called, so sync now
324         g_xstuff.pXSync(display, False);
325
326         return 0;
327 }
328
329 static struct termios g_kbd_termios_saved;
330 static int g_kbdfd = -1;
331
332 static int tty_init(void)
333 {
334         struct termios kbd_termios;
335         int mode;
336
337         g_kbdfd = open("/dev/tty", O_RDWR);
338         if (g_kbdfd == -1) {
339                 perror(PFX "open /dev/tty");
340                 return -1;
341         }
342
343         if (ioctl(g_kbdfd, KDGETMODE, &mode) == -1) {
344                 perror(PFX "(not hiding FB): KDGETMODE");
345                 goto fail;
346         }
347
348         if (tcgetattr(g_kbdfd, &kbd_termios) == -1) {
349                 perror(PFX "tcgetattr");
350                 goto fail;
351         }
352
353         g_kbd_termios_saved = kbd_termios;
354         kbd_termios.c_lflag &= ~(ICANON | ECHO); // | ISIG);
355         kbd_termios.c_iflag &= ~(ISTRIP | IGNCR | ICRNL | INLCR | IXOFF | IXON);
356         kbd_termios.c_cc[VMIN] = 0;
357         kbd_termios.c_cc[VTIME] = 0;
358
359         if (tcsetattr(g_kbdfd, TCSAFLUSH, &kbd_termios) == -1) {
360                 perror(PFX "tcsetattr");
361                 goto fail;
362         }
363
364         if (ioctl(g_kbdfd, KDSETMODE, KD_GRAPHICS) == -1) {
365                 perror(PFX "KDSETMODE KD_GRAPHICS");
366                 tcsetattr(g_kbdfd, TCSAFLUSH, &g_kbd_termios_saved);
367                 goto fail;
368         }
369
370         return 0;
371
372 fail:
373         close(g_kbdfd);
374         g_kbdfd = -1;
375         return -1;
376 }
377
378 static void tty_end(void)
379 {
380         if (g_kbdfd < 0)
381                 return;
382
383         if (ioctl(g_kbdfd, KDSETMODE, KD_TEXT) == -1)
384                 perror(PFX "KDSETMODE KD_TEXT");
385
386         if (tcsetattr(g_kbdfd, TCSAFLUSH, &g_kbd_termios_saved) == -1)
387                 perror(PFX "tcsetattr");
388
389         close(g_kbdfd);
390         g_kbdfd = -1;
391 }
392
393 int xenv_init(int *xenv_flags, const char *window_title)
394 {
395         int ret;
396
397         ret = x11h_init(xenv_flags, window_title);
398         if (ret == 0)
399                 goto out;
400
401         if (xenv_flags != NULL)
402                 *xenv_flags &= ~(XENV_CAP_KEYS | XENV_CAP_MOUSE); /* TODO? */
403         ret = tty_init();
404         if (ret == 0)
405                 goto out;
406
407         fprintf(stderr, PFX "error: both x11h_init and tty_init failed\n");
408         ret = -1;
409 out:
410         return ret;
411 }
412
413 int xenv_update(int (*key_cb)(void *cb_arg, int kc, int is_pressed),
414                 int (*mouseb_cb)(void *cb_arg, int x, int y, int button, int is_pressed),
415                 int (*mousem_cb)(void *cb_arg, int x, int y),
416                 void *cb_arg)
417 {
418         if (g_xstuff.display) {
419                 x11h_update(key_cb, mouseb_cb, mousem_cb, cb_arg);
420                 return 0;
421         }
422
423         // TODO: read tty?
424         return -1;
425 }
426
427 /* blocking minimize until user maximizes again */
428 int xenv_minimize(void)
429 {
430         int ret;
431
432         if (g_xstuff.display) {
433                 xenv_update(NULL, NULL, NULL, NULL);
434                 ret = x11h_minimize();
435                 xenv_update(NULL, NULL, NULL, NULL);
436                 return ret;
437         }
438
439         return -1;
440 }
441
442 int xenv_keycode_to_keysym(int kc, int shift)
443 {
444         if (g_xstuff.display)
445                 return g_xstuff.pXkbKeycodeToKeysym(g_xstuff.display,
446                         kc, 0, shift);
447
448         return -1;
449 }
450
451 int xenv_get_window(void **display, int *screen, void **window)
452 {
453         *display = *window = NULL;
454         *screen = 0;
455         if (g_xstuff.display && g_xstuff.window) {
456                 *display = g_xstuff.display;
457                 *screen = DefaultScreen(g_xstuff.display);
458                 *window = (void *)g_xstuff.window;
459                 return 0;
460         }
461
462         return -1;
463 }
464
465 void xenv_finish(void)
466 {
467         // TODO: cleanup X?
468         tty_end();
469 }
470
471 #if 0
472 int main()
473 {
474         int i, r, d;
475
476         xenv_init("just a test");
477
478         for (i = 0; i < 5; i++) {
479                 while ((r = xenv_update(&d)) > 0)
480                         printf("%d %x %d\n", d, r, r);
481                 sleep(1);
482
483                 if (i == 1)
484                         xenv_minimize();
485                 printf("ll %d\n", i);
486         }
487
488         printf("xenv_finish..\n");
489         xenv_finish();
490
491         return 0;
492 }
493 #endif