op_runfbapp: handle scaler layer on minimize
[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 __user
47 #include "omapfb.h"
48
49 #define PFX "op_runfbapp: "
50
51 static struct termios g_kbd_termios_saved;
52 static int g_kbdfd = -1;
53 static int g_have_x;
54 static pid_t g_child_pid;
55 static pthread_cond_t g_start_cond = PTHREAD_COND_INITIALIZER;
56 static pthread_mutex_t g_start_mutex = PTHREAD_MUTEX_INITIALIZER;
57
58 static void signal_main_thread(void)
59 {
60         pthread_mutex_lock(&g_start_mutex);
61         pthread_cond_signal(&g_start_cond);
62         pthread_mutex_unlock(&g_start_mutex);
63 }
64
65 static Cursor transparent_cursor(Display *display, Window win)
66 {
67         Cursor cursor;
68         Pixmap pix;
69         XColor dummy;
70         char d = 0;
71
72         memset(&dummy, 0, sizeof(dummy));
73         pix = XCreateBitmapFromData(display, win, &d, 1, 1);
74         cursor = XCreatePixmapCursor(display, pix, pix,
75                         &dummy, &dummy, 0, 0);
76         XFreePixmap(display, pix);
77         return cursor;
78 }
79
80 static void x11h_wait_vmstate(Display *display)
81 {
82         Atom wm_state = XInternAtom(display, "WM_STATE", False);
83         XEvent evt;
84         int i;
85
86         for (i = 0; i < 20; i++) {
87                 while (XPending(display)) {
88                         XNextEvent(display, &evt);
89                         if (evt.type == PropertyNotify && evt.xproperty.atom == wm_state)
90                                 return;
91                 }
92                 usleep(200000);
93         }
94
95         fprintf(stderr, "timeout waiting for wm_state change\n");
96 }
97
98 static struct omapfb_plane_info omap_pi;
99
100 static int hide_omap_layer(void)
101 {
102         int retval = -1;
103         int ret;
104         int fd;
105
106         fd = open("/dev/fb1", O_RDWR);
107         if (fd == -1) {
108                 perror("open(/dev/fb1)");
109                 return -1;
110         }
111
112         ret = ioctl(fd, OMAPFB_QUERY_PLANE, &omap_pi);
113         if (ret != 0) {
114                 perror("QUERY_PLANE");
115                 goto out;
116         }
117
118         if (omap_pi.enabled) {
119                 omap_pi.enabled = 0;
120                 ret = ioctl(fd, OMAPFB_SETUP_PLANE, &omap_pi);
121                 if (ret != 0) {
122                         perror("SETUP_PLANE");
123                         goto out;
124                 }
125                 retval = 1;
126         }
127         else {
128                 retval = 0;
129         }
130
131 out:
132         close(fd);
133         return retval;
134 }
135
136 static void show_omap_layer(void)
137 {
138         int ret;
139         int fd;
140
141         fd = open("/dev/fb1", O_RDWR);
142         if (fd == -1) {
143                 perror("open(/dev/fb1)");
144                 return;
145         }
146
147         omap_pi.enabled = 1;
148         ret = ioctl(fd, OMAPFB_SETUP_PLANE, &omap_pi);
149         if (ret != 0)
150                 perror("SETUP_PLANE");
151
152         close(fd);
153 }
154
155 static int x11h_minimize(Display *display, Window window)
156 {
157         XSetWindowAttributes attributes;
158         int screen = DefaultScreen(display);
159         int display_width, display_height;
160         XWMHints wm_hints;
161         XEvent evt;
162
163         XWithdrawWindow(display, window, screen);
164
165         attributes.override_redirect = False;
166         XChangeWindowAttributes(display, window,
167                 CWOverrideRedirect, &attributes);
168
169         wm_hints.flags = StateHint;
170         wm_hints.initial_state = IconicState;
171         XSetWMHints(display, window, &wm_hints);
172
173         XMapWindow(display, window);
174
175         while (XNextEvent(display, &evt) == 0)
176         {
177                 // printf("m event %d\n", evt.type);
178                 switch (evt.type)
179                 {
180                         case FocusIn:
181                                 goto out;
182                         default:
183                                 break;
184                 }
185         }
186
187 out:
188         XWithdrawWindow(display, window, screen);
189
190         // must wait for some magic vmstate property change before setting override_redirect
191         x11h_wait_vmstate(display);
192
193         attributes.override_redirect = True;
194         XChangeWindowAttributes(display, window,
195                 CWOverrideRedirect, &attributes);
196
197         // fixup window after resize on override_redirect loss
198         display_width = DisplayWidth(display, screen);
199         display_height = DisplayHeight(display, screen);
200         XMoveResizeWindow(display, window, 0, 0, display_width, display_height);
201
202         XMapWindow(display, window);
203         XGrabKeyboard(display, window, False, GrabModeAsync, GrabModeAsync, CurrentTime);
204         XkbSetDetectableAutoRepeat(display, 1, NULL);
205
206         // we don't know when event dispatch will be called, so sync now
207         XSync(display, False);
208
209         return 0;
210 }
211
212 static void *x11_handler(void *arg)
213 {
214         unsigned int display_width, display_height;
215         XSetWindowAttributes attributes;
216         const char *window_title = arg;
217         Window win;
218         XEvent evt;
219         Display *display;
220         int screen;
221         int ret;
222
223         display = XOpenDisplay(NULL);
224         if (display == NULL)
225         {
226                 fprintf(stderr, PFX "(not hiding X): Can't open display: %s\n",
227                         XDisplayName(NULL));
228                 signal_main_thread();
229                 return NULL;
230         }
231         g_have_x = 1;
232
233         screen = DefaultScreen(display);
234
235         display_width = DisplayWidth(display, screen);
236         display_height = DisplayHeight(display, screen);
237
238         win = XCreateSimpleWindow(display,
239                         RootWindow(display, screen),
240                         0, 0, display_width, display_height, 0,
241                         BlackPixel(display, screen),
242                         BlackPixel(display, screen));
243
244         attributes.override_redirect = True;
245         attributes.cursor = transparent_cursor(display, win);
246         XChangeWindowAttributes(display, win, CWOverrideRedirect | CWCursor, &attributes);
247
248         XStoreName(display, win, window_title);
249         XSelectInput(display, win, ExposureMask | FocusChangeMask
250                 | KeyPressMask | KeyReleaseMask | PropertyChangeMask);
251         XMapWindow(display, win);
252         XGrabKeyboard(display, win, False, GrabModeAsync, GrabModeAsync, CurrentTime);
253         XkbSetDetectableAutoRepeat(display, 1, NULL);
254         XSync(display, False);
255
256         while (1)
257         {
258                 XNextEvent(display, &evt);
259
260                 switch (evt.type) {
261                 case Expose:
262                         signal_main_thread();
263                         while (XCheckTypedEvent(display, Expose, &evt))
264                                 ;
265                         break;
266
267                 case KeyPress:
268                         if (XLookupKeysym(&evt.xkey, 0) == XF86XK_MenuKB
269                             && g_child_pid != 0) {
270                                 kill(g_child_pid, SIGSTOP);
271
272                                 ret = hide_omap_layer();
273                                 x11h_minimize(display, win);
274                                 if (ret > 0)
275                                         show_omap_layer();
276
277                                 kill(g_child_pid, SIGCONT);
278                         }
279                         break;
280                 }
281         }
282
283         return NULL;
284 }
285
286 static void hidecon_start(void)
287 {
288         struct termios kbd_termios;
289         int mode;
290
291         g_kbdfd = open("/dev/tty", O_RDWR);
292         if (g_kbdfd == -1) {
293                 perror(PFX "open /dev/tty");
294                 return;
295         }
296
297         if (ioctl(g_kbdfd, KDGETMODE, &mode) == -1) {
298                 perror(PFX "(not hiding FB): KDGETMODE");
299                 goto fail;
300         }
301
302         if (tcgetattr(g_kbdfd, &kbd_termios) == -1) {
303                 perror(PFX "tcgetattr");
304                 goto fail;
305         }
306
307         g_kbd_termios_saved = kbd_termios;
308         kbd_termios.c_lflag &= ~(ICANON | ECHO); // | ISIG);
309         kbd_termios.c_iflag &= ~(ISTRIP | IGNCR | ICRNL | INLCR | IXOFF | IXON);
310         kbd_termios.c_cc[VMIN] = 0;
311         kbd_termios.c_cc[VTIME] = 0;
312
313         if (tcsetattr(g_kbdfd, TCSAFLUSH, &kbd_termios) == -1) {
314                 perror(PFX "tcsetattr");
315                 goto fail;
316         }
317
318         if (ioctl(g_kbdfd, KDSETMODE, KD_GRAPHICS) == -1) {
319                 perror(PFX "KDSETMODE KD_GRAPHICS");
320                 tcsetattr(g_kbdfd, TCSAFLUSH, &g_kbd_termios_saved);
321                 goto fail;
322         }
323
324         return;
325
326 fail:
327         close(g_kbdfd);
328         g_kbdfd = -1;
329 }
330
331 static void hidecon_end(void)
332 {
333         if (g_kbdfd < 0)
334                 return;
335
336         if (ioctl(g_kbdfd, KDSETMODE, KD_TEXT) == -1)
337                 perror(PFX "KDSETMODE KD_TEXT");
338
339         if (tcsetattr(g_kbdfd, TCSAFLUSH, &g_kbd_termios_saved) == -1)
340                 perror(PFX "tcsetattr");
341
342         close(g_kbdfd);
343         g_kbdfd = -1;
344 }
345
346 static void do_exec(char * const argv[])
347 {
348         int ret, status;
349         pid_t pid;
350
351         pid = fork();
352         if (pid == -1) {
353                 perror(PFX "fork");
354                 return;
355         }
356
357         if (pid == 0) {
358                 /* child */
359                 execvp(argv[0], argv);
360                 perror(PFX "execvp");
361                 exit(1);
362         }
363
364         g_child_pid = pid;
365
366         ret = waitpid(pid, &status, 0);
367         if (ret < 0)
368                 perror(PFX "waitpid");
369 }
370
371 int main(int argc, char *argv[])
372 {
373         pthread_t tid;
374         char *name;
375         int ret;
376
377         if (argc < 2) {
378                 printf("usage:\n%s <app> [arg]..\n", argv[0]);
379                 return 1;
380         }
381
382         pthread_mutex_lock(&g_start_mutex);
383
384         name = strrchr(argv[1], '/');
385         if (name == NULL)
386                 name = argv[1];
387         else
388                 name++;
389
390         ret = pthread_create(&tid, NULL, x11_handler, name);
391         if (ret != 0) {
392                 fprintf(stderr, PFX "pthread_create: %d\n", ret);
393                 return 1;
394         }
395         pthread_detach(tid);
396
397         pthread_cond_wait(&g_start_cond, &g_start_mutex);
398         pthread_mutex_unlock(&g_start_mutex);
399
400         if (!g_have_x)
401                 hidecon_start();
402
403         do_exec(argv + 1);
404
405         if (!g_have_x)
406                 hidecon_end();
407
408         /* XXX: maybe stop the X thread nicely? */
409
410         return 0;
411 }
412