add op_lidstate
[pandora-misc.git] / op_runfbapp.c
index 495b493..1c73d59 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, Gražvydas Ignotas
+ * Copyright (c) 2010,2012 Gražvydas Ignotas
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -30,6 +30,8 @@
 #include <pthread.h>
 #include <X11/Xlib.h>
 #include <X11/Xutil.h>
+#include <X11/XKBlib.h>
+#include <X11/XF86keysym.h>
 
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <unistd.h>
 #include <sys/ioctl.h>
 #include <termios.h>
+#include <signal.h>
 #include <linux/kd.h>
+#include <linux/fb.h>
+
+#define __user
+#include "omapfb.h"
 
 #define PFX "op_runfbapp: "
 
 static struct termios g_kbd_termios_saved;
-static int g_kbdfd;
+static int g_kbdfd = -1;
+static int g_have_x;
+static pid_t g_child_pid;
 static pthread_cond_t g_start_cond = PTHREAD_COND_INITIALIZER;
 static pthread_mutex_t g_start_mutex = PTHREAD_MUTEX_INITIALIZER;
 
@@ -69,14 +78,246 @@ static Cursor transparent_cursor(Display *display, Window win)
        return cursor;
 }
 
+static void x11h_wait_vmstate(Display *display)
+{
+       Atom wm_state = XInternAtom(display, "WM_STATE", False);
+       XEvent evt;
+       int i;
+
+       for (i = 0; i < 20; i++) {
+               while (XPending(display)) {
+                       XNextEvent(display, &evt);
+                       if (evt.type == PropertyNotify && evt.xproperty.atom == wm_state)
+                               return;
+               }
+               usleep(200000);
+       }
+
+       fprintf(stderr, "timeout waiting for wm_state change\n");
+}
+
+static struct omapfb_plane_info desktop_pi, app_pi[2];
+static struct fb_var_screeninfo desktop_si, app_si[2];
+static int desktop_saved;
+
+static int save_fb_state(int index, struct omapfb_plane_info *pi,
+                        struct fb_var_screeninfo *si)
+{
+       int retval = -1;
+       char buf[16];
+       int fd = -1;
+       int ret;
+
+       snprintf(buf, sizeof(buf), "/dev/fb%d", index);
+       fd = open(buf, O_RDWR);
+       if (fd == -1) {
+               fprintf(stderr, PFX);
+               perror(buf);
+               goto out;
+       }
+
+       ret = ioctl(fd, OMAPFB_QUERY_PLANE, pi);
+       if (ret != 0) {
+               perror(PFX "QUERY_PLANE");
+               goto out;
+       }
+
+       ret = ioctl(fd, FBIOGET_VSCREENINFO, si);
+       if (ret == -1) {
+               perror(PFX "FBIOGET_VSCREENINFO");
+               goto out;
+       }
+
+       retval = 0;
+out:
+       if (fd != -1)
+               close(fd);
+       return retval;
+}
+
+static int load_fb_state(int index, struct omapfb_plane_info *pi,
+                        struct fb_var_screeninfo *si)
+{
+       struct omapfb_plane_info tmp_pi;
+       int retval = -1;
+       char buf[16];
+       int fd = -1;
+       int ret;
+
+       snprintf(buf, sizeof(buf), "/dev/fb%d", index);
+       fd = open(buf, O_RDWR);
+       if (fd == -1) {
+               fprintf(stderr, PFX);
+               perror(buf);
+               goto out;
+       }
+
+       // resolution can sometimes only be restored if position is right,
+       // so it's a bit more tricky..
+       ret = ioctl(fd, OMAPFB_QUERY_PLANE, &tmp_pi);
+       if (ret != 0) {
+               perror(PFX "QUERY_PLANE");
+               goto out;
+       }
+
+       tmp_pi.enabled = 0;
+       tmp_pi.pos_x = 0;
+       tmp_pi.pos_y = 0;
+       ret = ioctl(fd, OMAPFB_SETUP_PLANE, &tmp_pi);
+       if (ret != 0) {
+               perror(PFX "SETUP_PLANE");
+               goto out;
+       }
+
+       ret = ioctl(fd, FBIOPUT_VSCREENINFO, si);
+       if (ret == -1) {
+               perror(PFX "FBIOPUT_VSCREENINFO");
+               goto out;
+       }
+
+       ret = ioctl(fd, OMAPFB_SETUP_PLANE, pi);
+       if (ret != 0) {
+               perror(PFX "SETUP_PLANE(2)");
+               goto out;
+       }
+
+       retval = 0;
+out:
+       if (fd != -1)
+               close(fd);
+       return retval;
+}
+
+// prepare OMAP layers for desktop
+static int minimize_omap_layers(void)
+{
+       struct omapfb_plane_info pi_set;
+       int retval = -1;
+       int fd = -1;
+       int i, ret;
+
+       if (!desktop_saved) {
+               fprintf(stderr, PFX "desktop not saved\n");
+               goto out;
+       }
+
+       for (i = 0; i < 2; i++) {
+               ret = save_fb_state(i, &app_pi[i], &app_si[i]);
+               if (ret != 0) {
+                       fprintf(stderr, PFX "layer %d state save failed\n", i);
+                       goto out;
+               }
+       }
+
+       retval = 0; // no restoring needed
+
+       if (memcmp(&app_pi[0], &desktop_pi, sizeof(desktop_pi)) != 0) {
+               ret = load_fb_state(0, &desktop_pi, &desktop_si);
+               if (ret != 0) {
+                       fprintf(stderr, PFX "desktop restore failed\n");
+                       // but continue, I guess?
+               }
+               retval = 1;
+       }
+
+       if (app_pi[1].enabled) {
+               pi_set = app_pi[1];
+               pi_set.enabled = 0;
+
+               fd = open("/dev/fb1", O_RDWR);
+               ret = ioctl(fd, OMAPFB_SETUP_PLANE, &pi_set);
+               if (ret != 0) {
+                       perror(PFX "SETUP_PLANE");
+               }
+               retval = 1;
+       }
+
+out:
+       if (fd != -1)
+               close(fd);
+       return retval;
+}
+
+static void restore_omap_layers(void)
+{
+       int i, ret;
+
+       for (i = 0; i < 2; i++) {
+               ret = load_fb_state(i, &app_pi[i], &app_si[i]);
+               if (ret != 0) {
+                       fprintf(stderr, PFX "layer %d state load failed\n", i);
+                       // .. but try to continue ..
+               }
+       }
+}
+
+static int x11h_minimize(Display *display, Window window)
+{
+       XSetWindowAttributes attributes;
+       int screen = DefaultScreen(display);
+       int display_width, display_height;
+       XWMHints wm_hints;
+       XEvent evt;
+
+       XWithdrawWindow(display, window, screen);
+
+       attributes.override_redirect = False;
+       XChangeWindowAttributes(display, window,
+               CWOverrideRedirect, &attributes);
+
+       wm_hints.flags = StateHint;
+       wm_hints.initial_state = IconicState;
+       XSetWMHints(display, window, &wm_hints);
+
+       XMapWindow(display, window);
+
+       while (XNextEvent(display, &evt) == 0)
+       {
+               // printf("m event %d\n", evt.type);
+               switch (evt.type)
+               {
+                       case FocusIn:
+                               goto out;
+                       default:
+                               break;
+               }
+       }
+
+out:
+       XWithdrawWindow(display, window, screen);
+
+       // must wait for some magic vmstate property change before setting override_redirect
+       x11h_wait_vmstate(display);
+
+       attributes.override_redirect = True;
+       XChangeWindowAttributes(display, window,
+               CWOverrideRedirect, &attributes);
+
+       // fixup window after resize on override_redirect loss
+       display_width = DisplayWidth(display, screen);
+       display_height = DisplayHeight(display, screen);
+       XMoveResizeWindow(display, window, 0, 0, display_width, display_height);
+
+       XMapWindow(display, window);
+       XGrabKeyboard(display, window, False, GrabModeAsync, GrabModeAsync, CurrentTime);
+       XkbSetDetectableAutoRepeat(display, 1, NULL);
+
+       // we don't know when event dispatch will be called, so sync now
+       XSync(display, False);
+
+       return 0;
+}
+
 static void *x11_handler(void *arg)
 {
        unsigned int display_width, display_height;
        XSetWindowAttributes attributes;
+       const char *window_title = arg;
        Window win;
-       XEvent report;
+       XEvent evt;
        Display *display;
        int screen;
+       int ret;
 
        display = XOpenDisplay(NULL);
        if (display == NULL)
@@ -86,6 +327,7 @@ static void *x11_handler(void *arg)
                signal_main_thread();
                return NULL;
        }
+       g_have_x = 1;
 
        screen = DefaultScreen(display);
 
@@ -102,19 +344,45 @@ static void *x11_handler(void *arg)
        attributes.cursor = transparent_cursor(display, win);
        XChangeWindowAttributes(display, win, CWOverrideRedirect | CWCursor, &attributes);
 
-       XSelectInput(display, win, ExposureMask);
+       XStoreName(display, win, window_title);
+       XSelectInput(display, win, ExposureMask | FocusChangeMask
+               | KeyPressMask | KeyReleaseMask | PropertyChangeMask);
        XMapWindow(display, win);
        XGrabKeyboard(display, win, False, GrabModeAsync, GrabModeAsync, CurrentTime);
+       XkbSetDetectableAutoRepeat(display, 1, NULL);
        XSync(display, False);
 
        while (1)
        {
-               XNextEvent(display, &report);
+               XNextEvent(display, &evt);
 
-               if (report.type == Expose) {
+               switch (evt.type) {
+               case Expose:
                        signal_main_thread();
-                       while (XCheckTypedEvent(display, Expose, &report))
+                       while (XCheckTypedEvent(display, Expose, &evt))
                                ;
+                       break;
+
+               case KeyPress:
+                       if (XLookupKeysym(&evt.xkey, 0) == XF86XK_MenuKB
+                           && g_child_pid != 0) {
+                               kill(g_child_pid, SIGSTOP);
+
+                               ret = minimize_omap_layers();
+                               if (ret >= 0) {
+                                       fflush(stdout);
+                                       fflush(stderr);
+                                       x11h_minimize(display, win);
+                               }
+                               if (ret > 0) {
+                                       restore_omap_layers();
+                                       fflush(stdout);
+                                       fflush(stderr);
+                               }
+
+                               kill(g_child_pid, SIGCONT);
+                       }
+                       break;
                }
        }
 
@@ -199,6 +467,8 @@ static void do_exec(char * const argv[])
                exit(1);
        }
 
+       g_child_pid = pid;
+
        ret = waitpid(pid, &status, 0);
        if (ret < 0)
                perror(PFX "waitpid");
@@ -207,25 +477,46 @@ static void do_exec(char * const argv[])
 int main(int argc, char *argv[])
 {
        pthread_t tid;
+       char *name;
        int ret;
 
+       if (argc < 2) {
+               printf("usage:\n%s <app> [arg]..\n", argv[0]);
+               return 1;
+       }
+
        pthread_mutex_lock(&g_start_mutex);
 
-       ret = pthread_create(&tid, NULL, x11_handler, NULL);
+       name = strrchr(argv[1], '/');
+       if (name == NULL)
+               name = argv[1];
+       else
+               name++;
+
+       ret = pthread_create(&tid, NULL, x11_handler, name);
        if (ret != 0) {
                fprintf(stderr, PFX "pthread_create: %d\n", ret);
                return 1;
        }
        pthread_detach(tid);
 
+       // assume desktop is in good state..
+       ret = save_fb_state(0, &desktop_pi, &desktop_si);
+       if (ret == 0)
+               desktop_saved = 1;
+       else
+               fprintf(stderr, PFX "initial desktop state save failed\n");
+
        pthread_cond_wait(&g_start_cond, &g_start_mutex);
        pthread_mutex_unlock(&g_start_mutex);
 
-       hidecon_start();
+       if (!g_have_x)
+               hidecon_start();
 
        do_exec(argv + 1);
 
-       hidecon_end();
+       if (!g_have_x)
+               hidecon_end();
 
        /* XXX: maybe stop the X thread nicely? */