2226827c1e933960a940f2fe8e43413f8304e763
[pandora-misc.git] / op_runfbapp.c
1 /*
2  * Copyright (c) 2010,2012 GraÅžvydas Ignotas
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  *     * Redistributions of source code must retain the above copyright
8  *       notice, this list of conditions and the following disclaimer.
9  *     * Redistributions in binary form must reproduce the above copyright
10  *       notice, this list of conditions and the following disclaimer in the
11  *       documentation and/or other materials provided with the distribution.
12  *     * Neither the name of the organization nor the
13  *       names of its contributors may be used to endorse or promote products
14  *       derived from this software without specific prior written permission.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
23  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <pthread.h>
31 #include <X11/Xlib.h>
32 #include <X11/Xutil.h>
33 #include <X11/XKBlib.h>
34 #include <X11/XF86keysym.h>
35
36 #include <sys/types.h>
37 #include <sys/stat.h>
38 #include <sys/wait.h>
39 #include <fcntl.h>
40 #include <unistd.h>
41 #include <sys/ioctl.h>
42 #include <termios.h>
43 #include <signal.h>
44 #include <linux/kd.h>
45
46 #define PFX "op_runfbapp: "
47
48 static struct termios g_kbd_termios_saved;
49 static int g_kbdfd = -1;
50 static int g_have_x;
51 static pid_t g_child_pid;
52 static pthread_cond_t g_start_cond = PTHREAD_COND_INITIALIZER;
53 static pthread_mutex_t g_start_mutex = PTHREAD_MUTEX_INITIALIZER;
54
55 static void signal_main_thread(void)
56 {
57         pthread_mutex_lock(&g_start_mutex);
58         pthread_cond_signal(&g_start_cond);
59         pthread_mutex_unlock(&g_start_mutex);
60 }
61
62 static Cursor transparent_cursor(Display *display, Window win)
63 {
64         Cursor cursor;
65         Pixmap pix;
66         XColor dummy;
67         char d = 0;
68
69         memset(&dummy, 0, sizeof(dummy));
70         pix = XCreateBitmapFromData(display, win, &d, 1, 1);
71         cursor = XCreatePixmapCursor(display, pix, pix,
72                         &dummy, &dummy, 0, 0);
73         XFreePixmap(display, pix);
74         return cursor;
75 }
76
77 static void x11h_wait_vmstate(Display *display)
78 {
79         Atom wm_state = XInternAtom(display, "WM_STATE", False);
80         XEvent evt;
81         int i;
82
83         for (i = 0; i < 20; i++) {
84                 while (XPending(display)) {
85                         XNextEvent(display, &evt);
86                         if (evt.type == PropertyNotify && evt.xproperty.atom == wm_state)
87                                 return;
88                 }
89                 usleep(200000);
90         }
91
92         fprintf(stderr, "timeout waiting for wm_state change\n");
93 }
94
95 static int x11h_minimize(Display *display, Window window)
96 {
97         XSetWindowAttributes attributes;
98         int screen = DefaultScreen(display);
99         int display_width, display_height;
100         XWMHints wm_hints;
101         XEvent evt;
102
103         XWithdrawWindow(display, window, screen);
104
105         attributes.override_redirect = False;
106         XChangeWindowAttributes(display, window,
107                 CWOverrideRedirect, &attributes);
108
109         wm_hints.flags = StateHint;
110         wm_hints.initial_state = IconicState;
111         XSetWMHints(display, window, &wm_hints);
112
113         XMapWindow(display, window);
114
115         while (XNextEvent(display, &evt) == 0)
116         {
117                 // printf("m event %d\n", evt.type);
118                 switch (evt.type)
119                 {
120                         case FocusIn:
121                                 goto out;
122                         default:
123                                 break;
124                 }
125         }
126
127 out:
128         XWithdrawWindow(display, window, screen);
129
130         // must wait for some magic vmstate property change before setting override_redirect
131         x11h_wait_vmstate(display);
132
133         attributes.override_redirect = True;
134         XChangeWindowAttributes(display, window,
135                 CWOverrideRedirect, &attributes);
136
137         // fixup window after resize on override_redirect loss
138         display_width = DisplayWidth(display, screen);
139         display_height = DisplayHeight(display, screen);
140         XMoveResizeWindow(display, window, 0, 0, display_width, display_height);
141
142         XMapWindow(display, window);
143         XGrabKeyboard(display, window, False, GrabModeAsync, GrabModeAsync, CurrentTime);
144         XkbSetDetectableAutoRepeat(display, 1, NULL);
145
146         // we don't know when event dispatch will be called, so sync now
147         XSync(display, False);
148
149         return 0;
150 }
151
152 static void *x11_handler(void *arg)
153 {
154         unsigned int display_width, display_height;
155         XSetWindowAttributes attributes;
156         const char *window_title = arg;
157         Window win;
158         XEvent evt;
159         Display *display;
160         int screen;
161
162         display = XOpenDisplay(NULL);
163         if (display == NULL)
164         {
165                 fprintf(stderr, PFX "(not hiding X): Can't open display: %s\n",
166                         XDisplayName(NULL));
167                 signal_main_thread();
168                 return NULL;
169         }
170         g_have_x = 1;
171
172         screen = DefaultScreen(display);
173
174         display_width = DisplayWidth(display, screen);
175         display_height = DisplayHeight(display, screen);
176
177         win = XCreateSimpleWindow(display,
178                         RootWindow(display, screen),
179                         0, 0, display_width, display_height, 0,
180                         BlackPixel(display, screen),
181                         BlackPixel(display, screen));
182
183         attributes.override_redirect = True;
184         attributes.cursor = transparent_cursor(display, win);
185         XChangeWindowAttributes(display, win, CWOverrideRedirect | CWCursor, &attributes);
186
187         XStoreName(display, win, window_title);
188         XSelectInput(display, win, ExposureMask | FocusChangeMask
189                 | KeyPressMask | KeyReleaseMask | PropertyChangeMask);
190         XMapWindow(display, win);
191         XGrabKeyboard(display, win, False, GrabModeAsync, GrabModeAsync, CurrentTime);
192         XkbSetDetectableAutoRepeat(display, 1, NULL);
193         XSync(display, False);
194
195         while (1)
196         {
197                 XNextEvent(display, &evt);
198
199                 switch (evt.type) {
200                 case Expose:
201                         signal_main_thread();
202                         while (XCheckTypedEvent(display, Expose, &evt))
203                                 ;
204                         break;
205
206                 case KeyPress:
207                         if (XLookupKeysym(&evt.xkey, 0) == XF86XK_MenuKB
208                             && g_child_pid != 0) {
209                                 kill(g_child_pid, SIGSTOP);
210                                 x11h_minimize(display, win);
211                                 kill(g_child_pid, SIGCONT);
212                         }
213                         break;
214                 }
215         }
216
217         return NULL;
218 }
219
220 static void hidecon_start(void)
221 {
222         struct termios kbd_termios;
223         int mode;
224
225         g_kbdfd = open("/dev/tty", O_RDWR);
226         if (g_kbdfd == -1) {
227                 perror(PFX "open /dev/tty");
228                 return;
229         }
230
231         if (ioctl(g_kbdfd, KDGETMODE, &mode) == -1) {
232                 perror(PFX "(not hiding FB): KDGETMODE");
233                 goto fail;
234         }
235
236         if (tcgetattr(g_kbdfd, &kbd_termios) == -1) {
237                 perror(PFX "tcgetattr");
238                 goto fail;
239         }
240
241         g_kbd_termios_saved = kbd_termios;
242         kbd_termios.c_lflag &= ~(ICANON | ECHO); // | ISIG);
243         kbd_termios.c_iflag &= ~(ISTRIP | IGNCR | ICRNL | INLCR | IXOFF | IXON);
244         kbd_termios.c_cc[VMIN] = 0;
245         kbd_termios.c_cc[VTIME] = 0;
246
247         if (tcsetattr(g_kbdfd, TCSAFLUSH, &kbd_termios) == -1) {
248                 perror(PFX "tcsetattr");
249                 goto fail;
250         }
251
252         if (ioctl(g_kbdfd, KDSETMODE, KD_GRAPHICS) == -1) {
253                 perror(PFX "KDSETMODE KD_GRAPHICS");
254                 tcsetattr(g_kbdfd, TCSAFLUSH, &g_kbd_termios_saved);
255                 goto fail;
256         }
257
258         return;
259
260 fail:
261         close(g_kbdfd);
262         g_kbdfd = -1;
263 }
264
265 static void hidecon_end(void)
266 {
267         if (g_kbdfd < 0)
268                 return;
269
270         if (ioctl(g_kbdfd, KDSETMODE, KD_TEXT) == -1)
271                 perror(PFX "KDSETMODE KD_TEXT");
272
273         if (tcsetattr(g_kbdfd, TCSAFLUSH, &g_kbd_termios_saved) == -1)
274                 perror(PFX "tcsetattr");
275
276         close(g_kbdfd);
277         g_kbdfd = -1;
278 }
279
280 static void do_exec(char * const argv[])
281 {
282         int ret, status;
283         pid_t pid;
284
285         pid = fork();
286         if (pid == -1) {
287                 perror(PFX "fork");
288                 return;
289         }
290
291         if (pid == 0) {
292                 /* child */
293                 execvp(argv[0], argv);
294                 perror(PFX "execvp");
295                 exit(1);
296         }
297
298         g_child_pid = pid;
299
300         ret = waitpid(pid, &status, 0);
301         if (ret < 0)
302                 perror(PFX "waitpid");
303 }
304
305 int main(int argc, char *argv[])
306 {
307         pthread_t tid;
308         char *name;
309         int ret;
310
311         if (argc < 2) {
312                 printf("usage:\n%s <app> [arg]..\n", argv[0]);
313                 return 1;
314         }
315
316         pthread_mutex_lock(&g_start_mutex);
317
318         name = strrchr(argv[1], '/');
319         if (name == NULL)
320                 name = argv[1];
321         else
322                 name++;
323
324         ret = pthread_create(&tid, NULL, x11_handler, name);
325         if (ret != 0) {
326                 fprintf(stderr, PFX "pthread_create: %d\n", ret);
327                 return 1;
328         }
329         pthread_detach(tid);
330
331         pthread_cond_wait(&g_start_cond, &g_start_mutex);
332         pthread_mutex_unlock(&g_start_mutex);
333
334         if (!g_have_x)
335                 hidecon_start();
336
337         do_exec(argv + 1);
338
339         if (!g_have_x)
340                 hidecon_end();
341
342         /* XXX: maybe stop the X thread nicely? */
343
344         return 0;
345 }
346