op_runfbapp: don't do console when under X
[pandora-misc.git] / op_runfbapp.c
1 /*
2  * Copyright (c) 2010, 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
34 #include <sys/types.h>
35 #include <sys/stat.h>
36 #include <sys/wait.h>
37 #include <fcntl.h>
38 #include <unistd.h>
39 #include <sys/ioctl.h>
40 #include <termios.h>
41 #include <linux/kd.h>
42
43 #define PFX "op_runfbapp: "
44
45 static struct termios g_kbd_termios_saved;
46 static int g_kbdfd = -1;
47 static int g_have_x;
48 static pthread_cond_t g_start_cond = PTHREAD_COND_INITIALIZER;
49 static pthread_mutex_t g_start_mutex = PTHREAD_MUTEX_INITIALIZER;
50
51 static void signal_main_thread(void)
52 {
53         pthread_mutex_lock(&g_start_mutex);
54         pthread_cond_signal(&g_start_cond);
55         pthread_mutex_unlock(&g_start_mutex);
56 }
57
58 static Cursor transparent_cursor(Display *display, Window win)
59 {
60         Cursor cursor;
61         Pixmap pix;
62         XColor dummy;
63         char d = 0;
64
65         memset(&dummy, 0, sizeof(dummy));
66         pix = XCreateBitmapFromData(display, win, &d, 1, 1);
67         cursor = XCreatePixmapCursor(display, pix, pix,
68                         &dummy, &dummy, 0, 0);
69         XFreePixmap(display, pix);
70         return cursor;
71 }
72
73 static void *x11_handler(void *arg)
74 {
75         unsigned int display_width, display_height;
76         XSetWindowAttributes attributes;
77         Window win;
78         XEvent report;
79         Display *display;
80         int screen;
81
82         display = XOpenDisplay(NULL);
83         if (display == NULL)
84         {
85                 fprintf(stderr, PFX "(not hiding X): Can't open display: %s\n",
86                         XDisplayName(NULL));
87                 signal_main_thread();
88                 return NULL;
89         }
90         g_have_x = 1;
91
92         screen = DefaultScreen(display);
93
94         display_width = DisplayWidth(display, screen);
95         display_height = DisplayHeight(display, screen);
96
97         win = XCreateSimpleWindow(display,
98                         RootWindow(display, screen),
99                         0, 0, display_width, display_height, 0,
100                         BlackPixel(display, screen),
101                         BlackPixel(display, screen));
102
103         attributes.override_redirect = True;
104         attributes.cursor = transparent_cursor(display, win);
105         XChangeWindowAttributes(display, win, CWOverrideRedirect | CWCursor, &attributes);
106
107         XSelectInput(display, win, ExposureMask);
108         XMapWindow(display, win);
109         XGrabKeyboard(display, win, False, GrabModeAsync, GrabModeAsync, CurrentTime);
110         XSync(display, False);
111
112         while (1)
113         {
114                 XNextEvent(display, &report);
115
116                 if (report.type == Expose) {
117                         signal_main_thread();
118                         while (XCheckTypedEvent(display, Expose, &report))
119                                 ;
120                 }
121         }
122
123         return NULL;
124 }
125
126 static void hidecon_start(void)
127 {
128         struct termios kbd_termios;
129         int mode;
130
131         g_kbdfd = open("/dev/tty", O_RDWR);
132         if (g_kbdfd == -1) {
133                 perror(PFX "open /dev/tty");
134                 return;
135         }
136
137         if (ioctl(g_kbdfd, KDGETMODE, &mode) == -1) {
138                 perror(PFX "(not hiding FB): KDGETMODE");
139                 goto fail;
140         }
141
142         if (tcgetattr(g_kbdfd, &kbd_termios) == -1) {
143                 perror(PFX "tcgetattr");
144                 goto fail;
145         }
146
147         g_kbd_termios_saved = kbd_termios;
148         kbd_termios.c_lflag &= ~(ICANON | ECHO); // | ISIG);
149         kbd_termios.c_iflag &= ~(ISTRIP | IGNCR | ICRNL | INLCR | IXOFF | IXON);
150         kbd_termios.c_cc[VMIN] = 0;
151         kbd_termios.c_cc[VTIME] = 0;
152
153         if (tcsetattr(g_kbdfd, TCSAFLUSH, &kbd_termios) == -1) {
154                 perror(PFX "tcsetattr");
155                 goto fail;
156         }
157
158         if (ioctl(g_kbdfd, KDSETMODE, KD_GRAPHICS) == -1) {
159                 perror(PFX "KDSETMODE KD_GRAPHICS");
160                 tcsetattr(g_kbdfd, TCSAFLUSH, &g_kbd_termios_saved);
161                 goto fail;
162         }
163
164         return;
165
166 fail:
167         close(g_kbdfd);
168         g_kbdfd = -1;
169 }
170
171 static void hidecon_end(void)
172 {
173         if (g_kbdfd < 0)
174                 return;
175
176         if (ioctl(g_kbdfd, KDSETMODE, KD_TEXT) == -1)
177                 perror(PFX "KDSETMODE KD_TEXT");
178
179         if (tcsetattr(g_kbdfd, TCSAFLUSH, &g_kbd_termios_saved) == -1)
180                 perror(PFX "tcsetattr");
181
182         close(g_kbdfd);
183         g_kbdfd = -1;
184 }
185
186 static void do_exec(char * const argv[])
187 {
188         int ret, status;
189         pid_t pid;
190
191         pid = fork();
192         if (pid == -1) {
193                 perror(PFX "fork");
194                 return;
195         }
196
197         if (pid == 0) {
198                 /* child */
199                 execvp(argv[0], argv);
200                 perror(PFX "execvp");
201                 exit(1);
202         }
203
204         ret = waitpid(pid, &status, 0);
205         if (ret < 0)
206                 perror(PFX "waitpid");
207 }
208
209 int main(int argc, char *argv[])
210 {
211         pthread_t tid;
212         int ret;
213
214         if (argc < 2) {
215                 printf("usage:\n%s <app> [arg]..\n", argv[0]);
216                 return 1;
217         }
218
219         pthread_mutex_lock(&g_start_mutex);
220
221         ret = pthread_create(&tid, NULL, x11_handler, NULL);
222         if (ret != 0) {
223                 fprintf(stderr, PFX "pthread_create: %d\n", ret);
224                 return 1;
225         }
226         pthread_detach(tid);
227
228         pthread_cond_wait(&g_start_cond, &g_start_mutex);
229         pthread_mutex_unlock(&g_start_mutex);
230
231         if (!g_have_x)
232                 hidecon_start();
233
234         do_exec(argv + 1);
235
236         if (!g_have_x)
237                 hidecon_end();
238
239         /* XXX: maybe stop the X thread nicely? */
240
241         return 0;
242 }
243