/*
- * 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
#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;
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)
signal_main_thread();
return NULL;
}
+ g_have_x = 1;
screen = DefaultScreen(display);
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;
}
}
exit(1);
}
+ g_child_pid = pid;
+
ret = waitpid(pid, &status, 0);
if (ret < 0)
perror(PFX "waitpid");
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? */