USB tablet support (Brad Campbell, Anthony Liguori)
[qemu] / sdl.c
1 /*
2  * QEMU SDL display driver
3  * 
4  * Copyright (c) 2003 Fabrice Bellard
5  * 
6  * Permission is hereby granted, free of charge, to any person obtaining a copy
7  * of this software and associated documentation files (the "Software"), to deal
8  * in the Software without restriction, including without limitation the rights
9  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10  * copies of the Software, and to permit persons to whom the Software is
11  * furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included in
14  * all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22  * THE SOFTWARE.
23  */
24 #include "vl.h"
25
26 #include <SDL.h>
27
28 #ifndef _WIN32
29 #include <signal.h>
30 #endif
31
32 static SDL_Surface *screen;
33 static int gui_grab; /* if true, all keyboard/mouse events are grabbed */
34 static int last_vm_running;
35 static int gui_saved_grab;
36 static int gui_fullscreen;
37 static int gui_key_modifier_pressed;
38 static int gui_keysym;
39 static int gui_fullscreen_initial_grab;
40 static int gui_grab_code = KMOD_LALT | KMOD_LCTRL;
41 static uint8_t modifiers_state[256];
42 static int width, height;
43 static SDL_Cursor *sdl_cursor_normal;
44 static SDL_Cursor *sdl_cursor_hidden;
45 static int absolute_enabled = 0;
46
47 static void sdl_update(DisplayState *ds, int x, int y, int w, int h)
48 {
49     //    printf("updating x=%d y=%d w=%d h=%d\n", x, y, w, h);
50     SDL_UpdateRect(screen, x, y, w, h);
51 }
52
53 static void sdl_resize(DisplayState *ds, int w, int h)
54 {
55     int flags;
56
57     //    printf("resizing to %d %d\n", w, h);
58
59     flags = SDL_HWSURFACE|SDL_ASYNCBLIT|SDL_HWACCEL;
60     if (gui_fullscreen)
61         flags |= SDL_FULLSCREEN;
62
63     width = w;
64     height = h;
65
66  again:
67     screen = SDL_SetVideoMode(w, h, 0, flags);
68     if (!screen) {
69         fprintf(stderr, "Could not open SDL display\n");
70         exit(1);
71     }
72     if (!screen->pixels && (flags & SDL_HWSURFACE) && (flags & SDL_FULLSCREEN)) {
73         flags &= ~SDL_HWSURFACE;
74         goto again;
75     }
76
77     if (!screen->pixels) {
78         fprintf(stderr, "Could not open SDL display\n");
79         exit(1);
80     }
81     ds->data = screen->pixels;
82     ds->linesize = screen->pitch;
83     ds->depth = screen->format->BitsPerPixel;
84     ds->width = w;
85     ds->height = h;
86 }
87
88 /* generic keyboard conversion */
89
90 #include "sdl_keysym.h"
91 #include "keymaps.c"
92
93 static kbd_layout_t *kbd_layout = NULL;
94
95 static uint8_t sdl_keyevent_to_keycode_generic(const SDL_KeyboardEvent *ev)
96 {
97     int keysym;
98     /* workaround for X11+SDL bug with AltGR */
99     keysym = ev->keysym.sym;
100     if (keysym == 0 && ev->keysym.scancode == 113)
101         keysym = SDLK_MODE;
102     return keysym2scancode(kbd_layout, keysym);
103 }
104
105 /* specific keyboard conversions from scan codes */
106
107 #if defined(_WIN32)
108
109 static uint8_t sdl_keyevent_to_keycode(const SDL_KeyboardEvent *ev)
110 {
111     return ev->keysym.scancode;
112 }
113
114 #else
115
116 static const uint8_t x_keycode_to_pc_keycode[61] = {
117    0xc7,      /*  97  Home   */
118    0xc8,      /*  98  Up     */
119    0xc9,      /*  99  PgUp   */
120    0xcb,      /* 100  Left   */
121    0x4c,        /* 101  KP-5   */
122    0xcd,      /* 102  Right  */
123    0xcf,      /* 103  End    */
124    0xd0,      /* 104  Down   */
125    0xd1,      /* 105  PgDn   */
126    0xd2,      /* 106  Ins    */
127    0xd3,      /* 107  Del    */
128    0x9c,      /* 108  Enter  */
129    0x9d,      /* 109  Ctrl-R */
130    0x0,       /* 110  Pause  */
131    0xb7,      /* 111  Print  */
132    0xb5,      /* 112  Divide */
133    0xb8,      /* 113  Alt-R  */
134    0xc6,      /* 114  Break  */   
135    0x0,         /* 115 */
136    0x0,         /* 116 */
137    0x0,         /* 117 */
138    0x0,         /* 118 */
139    0x0,         /* 119 */
140    0x70,         /* 120 Hiragana_Katakana */
141    0x0,         /* 121 */
142    0x0,         /* 122 */
143    0x73,         /* 123 backslash */
144    0x0,         /* 124 */
145    0x0,         /* 125 */
146    0x0,         /* 126 */
147    0x0,         /* 127 */
148    0x0,         /* 128 */
149    0x79,         /* 129 Henkan */
150    0x0,         /* 130 */
151    0x7b,         /* 131 Muhenkan */
152    0x0,         /* 132 */
153    0x7d,         /* 133 Yen */
154    0x0,         /* 134 */
155    0x0,         /* 135 */
156    0x47,         /* 136 KP_7 */
157    0x48,         /* 137 KP_8 */
158    0x49,         /* 138 KP_9 */
159    0x4b,         /* 139 KP_4 */
160    0x4c,         /* 140 KP_5 */
161    0x4d,         /* 141 KP_6 */
162    0x4f,         /* 142 KP_1 */
163    0x50,         /* 143 KP_2 */
164    0x51,         /* 144 KP_3 */
165    0x52,         /* 145 KP_0 */
166    0x53,         /* 146 KP_. */
167    0x47,         /* 147 KP_HOME */
168    0x48,         /* 148 KP_UP */
169    0x49,         /* 149 KP_PgUp */
170    0x4b,         /* 150 KP_Left */
171    0x4c,         /* 151 KP_ */
172    0x4d,         /* 152 KP_Right */
173    0x4f,         /* 153 KP_End */
174    0x50,         /* 154 KP_Down */
175    0x51,         /* 155 KP_PgDn */
176    0x52,         /* 156 KP_Ins */
177    0x53,         /* 157 KP_Del */
178 };
179
180 static uint8_t sdl_keyevent_to_keycode(const SDL_KeyboardEvent *ev)
181 {
182     int keycode;
183
184     keycode = ev->keysym.scancode;
185
186     if (keycode < 9) {
187         keycode = 0;
188     } else if (keycode < 97) {
189         keycode -= 8; /* just an offset */
190     } else if (keycode < 158) {
191         /* use conversion table */
192         keycode = x_keycode_to_pc_keycode[keycode - 97];
193     } else {
194         keycode = 0;
195     }
196     return keycode;
197 }
198
199 #endif
200
201 static void reset_keys(void)
202 {
203     int i;
204     for(i = 0; i < 256; i++) {
205         if (modifiers_state[i]) {
206             if (i & 0x80)
207                 kbd_put_keycode(0xe0);
208             kbd_put_keycode(i | 0x80);
209             modifiers_state[i] = 0;
210         }
211     }
212 }
213
214 static void sdl_process_key(SDL_KeyboardEvent *ev)
215 {
216     int keycode, v;
217
218     if (ev->keysym.sym == SDLK_PAUSE) {
219         /* specific case */
220         v = 0;
221         if (ev->type == SDL_KEYUP)
222             v |= 0x80;
223         kbd_put_keycode(0xe1);
224         kbd_put_keycode(0x1d | v);
225         kbd_put_keycode(0x45 | v);
226         return;
227     }
228
229     if (kbd_layout) {
230         keycode = sdl_keyevent_to_keycode_generic(ev);
231     } else {
232         keycode = sdl_keyevent_to_keycode(ev);
233     }
234
235     switch(keycode) {
236     case 0x00:
237         /* sent when leaving window: reset the modifiers state */
238         reset_keys();
239         return;
240     case 0x2a:                          /* Left Shift */
241     case 0x36:                          /* Right Shift */
242     case 0x1d:                          /* Left CTRL */
243     case 0x9d:                          /* Right CTRL */
244     case 0x38:                          /* Left ALT */
245     case 0xb8:                         /* Right ALT */
246         if (ev->type == SDL_KEYUP)
247             modifiers_state[keycode] = 0;
248         else
249             modifiers_state[keycode] = 1;
250         break;
251     case 0x45: /* num lock */
252     case 0x3a: /* caps lock */
253         /* SDL does not send the key up event, so we generate it */
254         kbd_put_keycode(keycode);
255         kbd_put_keycode(keycode | 0x80);
256         return;
257     }
258
259     /* now send the key code */
260     if (keycode & 0x80)
261         kbd_put_keycode(0xe0);
262     if (ev->type == SDL_KEYUP)
263         kbd_put_keycode(keycode | 0x80);
264     else
265         kbd_put_keycode(keycode & 0x7f);
266 }
267
268 static void sdl_update_caption(void)
269 {
270     char buf[1024];
271     strcpy(buf, "QEMU");
272     if (!vm_running) {
273         strcat(buf, " [Stopped]");
274     }
275     if (gui_grab) {
276         strcat(buf, " - Press Ctrl-Alt to exit grab");
277     }
278     SDL_WM_SetCaption(buf, "QEMU");
279 }
280
281 static void sdl_hide_cursor(void)
282 {
283     SDL_SetCursor(sdl_cursor_hidden);
284 }
285
286 static void sdl_show_cursor(void)
287 {
288     if (!kbd_mouse_is_absolute()) {
289         SDL_SetCursor(sdl_cursor_normal);
290     }
291 }
292
293 static void sdl_grab_start(void)
294 {
295     sdl_hide_cursor();
296     SDL_WM_GrabInput(SDL_GRAB_ON);
297     /* dummy read to avoid moving the mouse */
298     SDL_GetRelativeMouseState(NULL, NULL);
299     gui_grab = 1;
300     sdl_update_caption();
301 }
302
303 static void sdl_grab_end(void)
304 {
305     SDL_WM_GrabInput(SDL_GRAB_OFF);
306     sdl_show_cursor();
307     gui_grab = 0;
308     sdl_update_caption();
309 }
310
311 static void sdl_send_mouse_event(int dz)
312 {
313     int dx, dy, state, buttons;
314     state = SDL_GetRelativeMouseState(&dx, &dy);
315     buttons = 0;
316     if (state & SDL_BUTTON(SDL_BUTTON_LEFT))
317         buttons |= MOUSE_EVENT_LBUTTON;
318     if (state & SDL_BUTTON(SDL_BUTTON_RIGHT))
319         buttons |= MOUSE_EVENT_RBUTTON;
320     if (state & SDL_BUTTON(SDL_BUTTON_MIDDLE))
321         buttons |= MOUSE_EVENT_MBUTTON;
322
323     if (kbd_mouse_is_absolute()) {
324         if (!absolute_enabled) {
325             sdl_hide_cursor();
326             if (gui_grab) {
327                 sdl_grab_end();
328             }
329             absolute_enabled = 1;
330         }
331
332         SDL_GetMouseState(&dx, &dy);
333         dx = dx * 0x7FFF / width;
334         dy = dy * 0x7FFF / height;
335     }
336
337     kbd_mouse_event(dx, dy, dz, buttons);
338 }
339
340 static void toggle_full_screen(DisplayState *ds)
341 {
342     gui_fullscreen = !gui_fullscreen;
343     sdl_resize(ds, screen->w, screen->h);
344     if (gui_fullscreen) {
345         gui_saved_grab = gui_grab;
346         sdl_grab_start();
347     } else {
348         if (!gui_saved_grab)
349             sdl_grab_end();
350     }
351     vga_hw_invalidate();
352     vga_hw_update();
353 }
354
355 static void sdl_refresh(DisplayState *ds)
356 {
357     SDL_Event ev1, *ev = &ev1;
358     int mod_state;
359                      
360     if (last_vm_running != vm_running) {
361         last_vm_running = vm_running;
362         sdl_update_caption();
363     }
364
365     vga_hw_update();
366
367     while (SDL_PollEvent(ev)) {
368         switch (ev->type) {
369         case SDL_VIDEOEXPOSE:
370             sdl_update(ds, 0, 0, screen->w, screen->h);
371             break;
372         case SDL_KEYDOWN:
373         case SDL_KEYUP:
374             if (ev->type == SDL_KEYDOWN) {
375                 mod_state = (SDL_GetModState() & gui_grab_code) ==
376                     gui_grab_code;
377                 gui_key_modifier_pressed = mod_state;
378                 if (gui_key_modifier_pressed) {
379                     int keycode;
380                     keycode = sdl_keyevent_to_keycode(&ev->key);
381                     switch(keycode) {
382                     case 0x21: /* 'f' key on US keyboard */
383                         toggle_full_screen(ds);
384                         gui_keysym = 1;
385                         break;
386                     case 0x02 ... 0x0a: /* '1' to '9' keys */ 
387                         console_select(keycode - 0x02);
388                         if (!is_graphic_console()) {
389                             /* display grab if going to a text console */
390                             if (gui_grab)
391                                 sdl_grab_end();
392                         }
393                         gui_keysym = 1;
394                         break;
395                     default:
396                         break;
397                     }
398                 } else if (!is_graphic_console()) {
399                     int keysym;
400                     keysym = 0;
401                     if (ev->key.keysym.mod & (KMOD_LCTRL | KMOD_RCTRL)) {
402                         switch(ev->key.keysym.sym) {
403                         case SDLK_UP: keysym = QEMU_KEY_CTRL_UP; break;
404                         case SDLK_DOWN: keysym = QEMU_KEY_CTRL_DOWN; break;
405                         case SDLK_LEFT: keysym = QEMU_KEY_CTRL_LEFT; break;
406                         case SDLK_RIGHT: keysym = QEMU_KEY_CTRL_RIGHT; break;
407                         case SDLK_HOME: keysym = QEMU_KEY_CTRL_HOME; break;
408                         case SDLK_END: keysym = QEMU_KEY_CTRL_END; break;
409                         case SDLK_PAGEUP: keysym = QEMU_KEY_CTRL_PAGEUP; break;
410                         case SDLK_PAGEDOWN: keysym = QEMU_KEY_CTRL_PAGEDOWN; break;
411                         default: break;
412                         }
413                     } else {
414                         switch(ev->key.keysym.sym) {
415                         case SDLK_UP: keysym = QEMU_KEY_UP; break;
416                         case SDLK_DOWN: keysym = QEMU_KEY_DOWN; break;
417                         case SDLK_LEFT: keysym = QEMU_KEY_LEFT; break;
418                         case SDLK_RIGHT: keysym = QEMU_KEY_RIGHT; break;
419                         case SDLK_HOME: keysym = QEMU_KEY_HOME; break;
420                         case SDLK_END: keysym = QEMU_KEY_END; break;
421                         case SDLK_PAGEUP: keysym = QEMU_KEY_PAGEUP; break;
422                         case SDLK_PAGEDOWN: keysym = QEMU_KEY_PAGEDOWN; break;
423                         case SDLK_BACKSPACE: keysym = QEMU_KEY_BACKSPACE; break;                        case SDLK_DELETE: keysym = QEMU_KEY_DELETE; break;
424                         default: break;
425                         }
426                     }
427                     if (keysym) {
428                         kbd_put_keysym(keysym);
429                     } else if (ev->key.keysym.unicode != 0) {
430                         kbd_put_keysym(ev->key.keysym.unicode);
431                     }
432                 }
433             } else if (ev->type == SDL_KEYUP) {
434                 mod_state = (ev->key.keysym.mod & gui_grab_code);
435                 if (!mod_state) {
436                     if (gui_key_modifier_pressed) {
437                         gui_key_modifier_pressed = 0;
438                         if (gui_keysym == 0) {
439                             /* exit/enter grab if pressing Ctrl-Alt */
440                             if (!gui_grab)
441                                 sdl_grab_start();
442                             else
443                                 sdl_grab_end();
444                             /* SDL does not send back all the
445                                modifiers key, so we must correct it */
446                             reset_keys();
447                             break;
448                         }
449                         gui_keysym = 0;
450                     }
451                 }
452             }
453             if (is_graphic_console()) 
454                 sdl_process_key(&ev->key);
455             break;
456         case SDL_QUIT:
457             qemu_system_shutdown_request();
458             break;
459         case SDL_MOUSEMOTION:
460             if (gui_grab || kbd_mouse_is_absolute()) {
461                 sdl_send_mouse_event(0);
462             }
463             break;
464         case SDL_MOUSEBUTTONDOWN:
465         case SDL_MOUSEBUTTONUP:
466             {
467                 SDL_MouseButtonEvent *bev = &ev->button;
468                 if (!gui_grab && !kbd_mouse_is_absolute()) {
469                     if (ev->type == SDL_MOUSEBUTTONDOWN &&
470                         (bev->state & SDL_BUTTON_LMASK)) {
471                         /* start grabbing all events */
472                         sdl_grab_start();
473                     }
474                 } else {
475                     int dz;
476                     dz = 0;
477 #ifdef SDL_BUTTON_WHEELUP
478                     if (bev->button == SDL_BUTTON_WHEELUP && ev->type == SDL_MOUSEBUTTONDOWN) {
479                         dz = -1;
480                     } else if (bev->button == SDL_BUTTON_WHEELDOWN && ev->type == SDL_MOUSEBUTTONDOWN) {
481                         dz = 1;
482                     }
483 #endif               
484                     sdl_send_mouse_event(dz);
485                 }
486             }
487             break;
488         case SDL_ACTIVEEVENT:
489             if (gui_grab && ev->active.state == SDL_APPINPUTFOCUS &&
490                 !ev->active.gain && !gui_fullscreen_initial_grab) {
491                 sdl_grab_end();
492             }
493             break;
494         default:
495             break;
496         }
497     }
498 }
499
500 static void sdl_cleanup(void) 
501 {
502     SDL_Quit();
503 }
504
505 void sdl_display_init(DisplayState *ds, int full_screen)
506 {
507     int flags;
508     uint8_t data = 0;
509
510 #if defined(__APPLE__)
511     /* always use generic keymaps */
512     if (!keyboard_layout)
513         keyboard_layout = "en-us";
514 #endif
515     if(keyboard_layout) {
516         kbd_layout = init_keyboard_layout(keyboard_layout);
517         if (!kbd_layout)
518             exit(1);
519     }
520
521     flags = SDL_INIT_VIDEO | SDL_INIT_NOPARACHUTE;
522     if (SDL_Init (flags)) {
523         fprintf(stderr, "Could not initialize SDL - exiting\n");
524         exit(1);
525     }
526 #ifndef _WIN32
527     /* NOTE: we still want Ctrl-C to work, so we undo the SDL redirections */
528     signal(SIGINT, SIG_DFL);
529     signal(SIGQUIT, SIG_DFL);
530 #endif
531
532     ds->dpy_update = sdl_update;
533     ds->dpy_resize = sdl_resize;
534     ds->dpy_refresh = sdl_refresh;
535
536     sdl_resize(ds, 640, 400);
537     sdl_update_caption();
538     SDL_EnableKeyRepeat(250, 50);
539     SDL_EnableUNICODE(1);
540     gui_grab = 0;
541
542     sdl_cursor_hidden = SDL_CreateCursor(&data, &data, 8, 1, 0, 0);
543     sdl_cursor_normal = SDL_GetCursor();
544
545     atexit(sdl_cleanup);
546     if (full_screen) {
547         gui_fullscreen = 1;
548         gui_fullscreen_initial_grab = 1;
549         sdl_grab_start();
550     }
551 }