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