ctrl-alt is the default grab key - reset modifiers when toggling grab state
[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 #if defined(__APPLE__)
33 #define CONFIG_SDL_GENERIC_KBD
34 #endif
35
36 static SDL_Surface *screen;
37 static int gui_grab; /* if true, all keyboard/mouse events are grabbed */
38 static int last_vm_running;
39 static int gui_saved_grab;
40 static int gui_fullscreen;
41 static int gui_key_modifier_pressed;
42 static int gui_keysym;
43 static int gui_fullscreen_initial_grab;
44 static int gui_grab_code = KMOD_LALT | KMOD_LCTRL;
45 static uint8_t modifiers_state[256];
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     flags |= SDL_RESIZABLE;
61     if (gui_fullscreen)
62         flags |= SDL_FULLSCREEN;
63     screen = SDL_SetVideoMode(w, h, 0, flags);
64     if (!screen) {
65         fprintf(stderr, "Could not open SDL display\n");
66         exit(1);
67     }
68     ds->data = screen->pixels;
69     ds->linesize = screen->pitch;
70     ds->depth = screen->format->BitsPerPixel;
71     ds->width = w;
72     ds->height = h;
73 }
74
75 #ifdef CONFIG_SDL_GENERIC_KBD
76
77 /* XXX: use keymap tables defined in the VNC patch because the
78    following code suppose you have a US keyboard. */
79
80 static const uint8_t scancodes[SDLK_LAST] = {
81     [SDLK_ESCAPE]   = 0x01,
82     [SDLK_1]        = 0x02,
83     [SDLK_2]        = 0x03,
84     [SDLK_3]        = 0x04,
85     [SDLK_4]        = 0x05,
86     [SDLK_5]        = 0x06,
87     [SDLK_6]        = 0x07,
88     [SDLK_7]        = 0x08,
89     [SDLK_8]        = 0x09,
90     [SDLK_9]        = 0x0a,
91     [SDLK_0]        = 0x0b,
92     [SDLK_MINUS]    = 0x0c,
93     [SDLK_EQUALS]   = 0x0d,
94     [SDLK_BACKSPACE]        = 0x0e,
95     [SDLK_TAB]      = 0x0f,
96     [SDLK_q]        = 0x10,
97     [SDLK_w]        = 0x11,
98     [SDLK_e]        = 0x12,
99     [SDLK_r]        = 0x13,
100     [SDLK_t]        = 0x14,
101     [SDLK_y]        = 0x15,
102     [SDLK_u]        = 0x16,
103     [SDLK_i]        = 0x17,
104     [SDLK_o]        = 0x18,
105     [SDLK_p]        = 0x19,
106     [SDLK_LEFTBRACKET]      = 0x1a,
107     [SDLK_RIGHTBRACKET]     = 0x1b,
108     [SDLK_RETURN]   = 0x1c,
109     [SDLK_LCTRL]    = 0x1d,
110     [SDLK_a]        = 0x1e,
111     [SDLK_s]        = 0x1f,
112     [SDLK_d]        = 0x20,
113     [SDLK_f]        = 0x21,
114     [SDLK_g]        = 0x22,
115     [SDLK_h]        = 0x23,
116     [SDLK_j]        = 0x24,
117     [SDLK_k]        = 0x25,
118     [SDLK_l]        = 0x26,
119     [SDLK_SEMICOLON]        = 0x27,
120     [SDLK_QUOTE]    = 0x28,
121     [SDLK_BACKQUOTE]        = 0x29,
122     [SDLK_LSHIFT]   = 0x2a,
123     [SDLK_BACKSLASH]        = 0x2b,
124     [SDLK_z]        = 0x2c,
125     [SDLK_x]        = 0x2d,
126     [SDLK_c]        = 0x2e,
127     [SDLK_v]        = 0x2f,
128     [SDLK_b]        = 0x30,
129     [SDLK_n]        = 0x31,
130     [SDLK_m]        = 0x32,
131     [SDLK_COMMA]    = 0x33,
132     [SDLK_PERIOD]   = 0x34,
133     [SDLK_SLASH]    = 0x35,
134     [SDLK_KP_MULTIPLY]      = 0x37,
135     [SDLK_LALT]     = 0x38,
136     [SDLK_SPACE]    = 0x39,
137     [SDLK_CAPSLOCK] = 0x3a,
138     [SDLK_F1]       = 0x3b,
139     [SDLK_F2]       = 0x3c,
140     [SDLK_F3]       = 0x3d,
141     [SDLK_F4]       = 0x3e,
142     [SDLK_F5]       = 0x3f,
143     [SDLK_F6]       = 0x40,
144     [SDLK_F7]       = 0x41,
145     [SDLK_F8]       = 0x42,
146     [SDLK_F9]       = 0x43,
147     [SDLK_F10]      = 0x44,
148     [SDLK_NUMLOCK]  = 0x45,
149     [SDLK_SCROLLOCK]        = 0x46,
150     [SDLK_KP7]      = 0x47,
151     [SDLK_KP8]      = 0x48,
152     [SDLK_KP9]      = 0x49,
153     [SDLK_KP_MINUS] = 0x4a,
154     [SDLK_KP4]      = 0x4b,
155     [SDLK_KP5]      = 0x4c,
156     [SDLK_KP6]      = 0x4d,
157     [SDLK_KP_PLUS]  = 0x4e,
158     [SDLK_KP1]      = 0x4f,
159     [SDLK_KP2]      = 0x50,
160     [SDLK_KP3]      = 0x51,
161     [SDLK_KP0]      = 0x52,
162     [SDLK_KP_PERIOD]        = 0x53,
163     [SDLK_PRINT]    = 0x54,
164     [SDLK_LMETA]    = 0x56,
165
166     [SDLK_KP_ENTER]  = 0x9c,
167     [SDLK_KP_DIVIDE] = 0xb5,
168     
169     [SDLK_UP]       = 0xc8,
170     [SDLK_DOWN]     = 0xd0,
171     [SDLK_RIGHT]    = 0xcd,
172     [SDLK_LEFT]     = 0xcb,
173     [SDLK_INSERT]   = 0xd2,
174     [SDLK_HOME]     = 0xc7,
175     [SDLK_END]      = 0xcf,
176     [SDLK_PAGEUP]   = 0xc9,
177     [SDLK_PAGEDOWN] = 0xd1,
178     [SDLK_DELETE]   = 0xd3,
179 };
180
181 static uint8_t sdl_keyevent_to_keycode(const SDL_KeyboardEvent *ev)
182 {
183     return scancodes[ev->keysym.sym];
184 }
185
186 #elif defined(_WIN32)
187
188 static uint8_t sdl_keyevent_to_keycode(const SDL_KeyboardEvent *ev)
189 {
190     return ev->keysym.scancode;
191 }
192
193 #else
194
195 static const uint8_t x_keycode_to_pc_keycode[61] = {
196    0xc7,      /*  97  Home   */
197    0xc8,      /*  98  Up     */
198    0xc9,      /*  99  PgUp   */
199    0xcb,      /* 100  Left   */
200    0x4c,        /* 101  KP-5   */
201    0xcd,      /* 102  Right  */
202    0xcf,      /* 103  End    */
203    0xd0,      /* 104  Down   */
204    0xd1,      /* 105  PgDn   */
205    0xd2,      /* 106  Ins    */
206    0xd3,      /* 107  Del    */
207    0x9c,      /* 108  Enter  */
208    0x9d,      /* 109  Ctrl-R */
209    0x0,       /* 110  Pause  */
210    0xb7,      /* 111  Print  */
211    0xb5,      /* 112  Divide */
212    0xb8,      /* 113  Alt-R  */
213    0xc6,      /* 114  Break  */   
214    0x0,         /* 115 */
215    0x0,         /* 116 */
216    0x0,         /* 117 */
217    0x0,         /* 118 */
218    0x0,         /* 119 */
219    0x70,         /* 120 Hiragana_Katakana */
220    0x0,         /* 121 */
221    0x0,         /* 122 */
222    0x73,         /* 123 backslash */
223    0x0,         /* 124 */
224    0x0,         /* 125 */
225    0x0,         /* 126 */
226    0x0,         /* 127 */
227    0x0,         /* 128 */
228    0x79,         /* 129 Henkan */
229    0x0,         /* 130 */
230    0x7b,         /* 131 Muhenkan */
231    0x0,         /* 132 */
232    0x7d,         /* 133 Yen */
233    0x0,         /* 134 */
234    0x0,         /* 135 */
235    0x47,         /* 136 KP_7 */
236    0x48,         /* 137 KP_8 */
237    0x49,         /* 138 KP_9 */
238    0x4b,         /* 139 KP_4 */
239    0x4c,         /* 140 KP_5 */
240    0x4d,         /* 141 KP_6 */
241    0x4f,         /* 142 KP_1 */
242    0x50,         /* 143 KP_2 */
243    0x51,         /* 144 KP_3 */
244    0x52,         /* 145 KP_0 */
245    0x53,         /* 146 KP_. */
246    0x47,         /* 147 KP_HOME */
247    0x48,         /* 148 KP_UP */
248    0x49,         /* 149 KP_PgUp */
249    0x4b,         /* 150 KP_Left */
250    0x4c,         /* 151 KP_ */
251    0x4d,         /* 152 KP_Right */
252    0x4f,         /* 153 KP_End */
253    0x50,         /* 154 KP_Down */
254    0x51,         /* 155 KP_PgDn */
255    0x52,         /* 156 KP_Ins */
256    0x53,         /* 157 KP_Del */
257 };
258
259 static uint8_t sdl_keyevent_to_keycode(const SDL_KeyboardEvent *ev)
260 {
261     int keycode;
262
263     keycode = ev->keysym.scancode;
264
265     if (keycode < 9) {
266         keycode = 0;
267     } else if (keycode < 97) {
268         keycode -= 8; /* just an offset */
269     } else if (keycode < 158) {
270         /* use conversion table */
271         keycode = x_keycode_to_pc_keycode[keycode - 97];
272     } else {
273         keycode = 0;
274     }
275     return keycode;
276 }
277
278 #endif
279
280 static void reset_keys(void)
281 {
282     int i;
283     for(i = 0; i < 256; i++) {
284         if (modifiers_state[i]) {
285             if (i & 0x80)
286                 kbd_put_keycode(0xe0);
287             kbd_put_keycode(i | 0x80);
288             modifiers_state[i] = 0;
289         }
290     }
291 }
292
293 static void sdl_process_key(SDL_KeyboardEvent *ev)
294 {
295     int keycode, v;
296
297     if (ev->keysym.sym == SDLK_PAUSE) {
298         /* specific case */
299         v = 0;
300         if (ev->type == SDL_KEYUP)
301             v |= 0x80;
302         kbd_put_keycode(0xe1);
303         kbd_put_keycode(0x1d | v);
304         kbd_put_keycode(0x45 | v);
305         return;
306     }
307
308     /* XXX: not portable, but avoids complicated mappings */
309     keycode = sdl_keyevent_to_keycode(ev);
310
311     switch(keycode) {
312     case 0x00:
313         /* sent when leaving window: reset the modifiers state */
314         reset_keys();
315         return;
316     case 0x2a:                          /* Left Shift */
317     case 0x36:                          /* Right Shift */
318     case 0x1d:                          /* Left CTRL */
319     case 0x9d:                          /* Right CTRL */
320     case 0x38:                          /* Left ALT */
321     case 0xb8:                         /* Right ALT */
322         if (ev->type == SDL_KEYUP)
323             modifiers_state[keycode] = 0;
324         else
325             modifiers_state[keycode] = 1;
326         break;
327     case 0x45: /* num lock */
328     case 0x3a: /* caps lock */
329         /* SDL does not send the key up event, so we generate it */
330         kbd_put_keycode(keycode);
331         kbd_put_keycode(keycode | 0x80);
332         return;
333     }
334
335     /* now send the key code */
336     if (keycode & 0x80)
337         kbd_put_keycode(0xe0);
338     if (ev->type == SDL_KEYUP)
339         kbd_put_keycode(keycode | 0x80);
340     else
341         kbd_put_keycode(keycode & 0x7f);
342 }
343
344 static void sdl_update_caption(void)
345 {
346     char buf[1024];
347     strcpy(buf, "QEMU");
348     if (!vm_running) {
349         strcat(buf, " [Stopped]");
350     }
351     if (gui_grab) {
352         strcat(buf, " - Press Ctrl-Alt to exit grab");
353     }
354     SDL_WM_SetCaption(buf, "QEMU");
355 }
356
357 static void sdl_grab_start(void)
358 {
359     SDL_ShowCursor(0);
360     SDL_WM_GrabInput(SDL_GRAB_ON);
361     /* dummy read to avoid moving the mouse */
362     SDL_GetRelativeMouseState(NULL, NULL);
363     gui_grab = 1;
364     sdl_update_caption();
365 }
366
367 static void sdl_grab_end(void)
368 {
369     SDL_WM_GrabInput(SDL_GRAB_OFF);
370     SDL_ShowCursor(1);
371     gui_grab = 0;
372     sdl_update_caption();
373 }
374
375 static void sdl_send_mouse_event(void)
376 {
377     int dx, dy, dz, state, buttons;
378     state = SDL_GetRelativeMouseState(&dx, &dy);
379     buttons = 0;
380     if (state & SDL_BUTTON(SDL_BUTTON_LEFT))
381         buttons |= MOUSE_EVENT_LBUTTON;
382     if (state & SDL_BUTTON(SDL_BUTTON_RIGHT))
383         buttons |= MOUSE_EVENT_RBUTTON;
384     if (state & SDL_BUTTON(SDL_BUTTON_MIDDLE))
385         buttons |= MOUSE_EVENT_MBUTTON;
386     /* XXX: test wheel */
387     dz = 0;
388 #ifdef SDL_BUTTON_WHEELUP
389     if (state & SDL_BUTTON(SDL_BUTTON_WHEELUP))
390         dz--;
391     if (state & SDL_BUTTON(SDL_BUTTON_WHEELDOWN))
392         dz++;
393 #endif
394     kbd_mouse_event(dx, dy, dz, buttons);
395 }
396
397 static void toggle_full_screen(DisplayState *ds)
398 {
399     gui_fullscreen = !gui_fullscreen;
400     sdl_resize(ds, screen->w, screen->h);
401     if (gui_fullscreen) {
402         gui_saved_grab = gui_grab;
403         sdl_grab_start();
404     } else {
405         if (!gui_saved_grab)
406             sdl_grab_end();
407     }
408     vga_invalidate_display();
409     vga_update_display();
410 }
411
412 static void sdl_refresh(DisplayState *ds)
413 {
414     SDL_Event ev1, *ev = &ev1;
415     int mod_state;
416                      
417     if (last_vm_running != vm_running) {
418         last_vm_running = vm_running;
419         sdl_update_caption();
420     }
421
422     if (is_active_console(vga_console)) 
423         vga_update_display();
424
425     while (SDL_PollEvent(ev)) {
426         switch (ev->type) {
427         case SDL_VIDEOEXPOSE:
428             sdl_update(ds, 0, 0, screen->w, screen->h);
429             break;
430         case SDL_KEYDOWN:
431         case SDL_KEYUP:
432             if (ev->type == SDL_KEYDOWN) {
433                 mod_state = (SDL_GetModState() & gui_grab_code) ==
434                     gui_grab_code;
435                 gui_key_modifier_pressed = mod_state;
436                 if (gui_key_modifier_pressed) {
437                     int keycode;
438                     keycode = sdl_keyevent_to_keycode(&ev->key);
439                     switch(keycode) {
440                     case 0x21: /* 'f' key on US keyboard */
441                         toggle_full_screen(ds);
442                         gui_keysym = 1;
443                         break;
444                     case 0x02 ... 0x0a: /* '1' to '9' keys */ 
445                         console_select(keycode - 0x02);
446                         if (is_active_console(vga_console)) {
447                             /* tell the vga console to redisplay itself */
448                             vga_invalidate_display();
449                         } else {
450                             /* display grab if going to a text console */
451                             if (gui_grab)
452                                 sdl_grab_end();
453                         }
454                         gui_keysym = 1;
455                         break;
456                     default:
457                         break;
458                     }
459                 } else if (!is_active_console(vga_console)) {
460                     int keysym;
461                     keysym = 0;
462                     if (ev->key.keysym.mod & (KMOD_LCTRL | KMOD_RCTRL)) {
463                         switch(ev->key.keysym.sym) {
464                         case SDLK_UP: keysym = QEMU_KEY_CTRL_UP; break;
465                         case SDLK_DOWN: keysym = QEMU_KEY_CTRL_DOWN; break;
466                         case SDLK_LEFT: keysym = QEMU_KEY_CTRL_LEFT; break;
467                         case SDLK_RIGHT: keysym = QEMU_KEY_CTRL_RIGHT; break;
468                         case SDLK_HOME: keysym = QEMU_KEY_CTRL_HOME; break;
469                         case SDLK_END: keysym = QEMU_KEY_CTRL_END; break;
470                         case SDLK_PAGEUP: keysym = QEMU_KEY_CTRL_PAGEUP; break;
471                         case SDLK_PAGEDOWN: keysym = QEMU_KEY_CTRL_PAGEDOWN; break;
472                         default: break;
473                         }
474                     } else {
475                         switch(ev->key.keysym.sym) {
476                         case SDLK_UP: keysym = QEMU_KEY_UP; break;
477                         case SDLK_DOWN: keysym = QEMU_KEY_DOWN; break;
478                         case SDLK_LEFT: keysym = QEMU_KEY_LEFT; break;
479                         case SDLK_RIGHT: keysym = QEMU_KEY_RIGHT; break;
480                         case SDLK_HOME: keysym = QEMU_KEY_HOME; break;
481                         case SDLK_END: keysym = QEMU_KEY_END; break;
482                         case SDLK_PAGEUP: keysym = QEMU_KEY_PAGEUP; break;
483                         case SDLK_PAGEDOWN: keysym = QEMU_KEY_PAGEDOWN; break;
484                         case SDLK_BACKSPACE: keysym = QEMU_KEY_BACKSPACE; break;                        case SDLK_DELETE: keysym = QEMU_KEY_DELETE; break;
485                         default: break;
486                         }
487                     }
488                     if (keysym) {
489                         kbd_put_keysym(keysym);
490                     } else if (ev->key.keysym.unicode != 0) {
491                         kbd_put_keysym(ev->key.keysym.unicode);
492                     }
493                 }
494             } else if (ev->type == SDL_KEYUP) {
495                 mod_state = (SDL_GetModState() & gui_grab_code);
496                 if (!mod_state) {
497                     if (gui_key_modifier_pressed) {
498                         if (gui_keysym == 0) {
499                             /* exit/enter grab if pressing Ctrl-Alt */
500                             if (!gui_grab)
501                                 sdl_grab_start();
502                             else
503                                 sdl_grab_end();
504                             /* SDL does not send back all the
505                                modifiers key, so we must correct it */
506                             reset_keys();
507                             break;
508                         }
509                         gui_key_modifier_pressed = 0;
510                         gui_keysym = 0;
511                     }
512                 }
513             }
514             if (is_active_console(vga_console)) 
515                 sdl_process_key(&ev->key);
516             break;
517         case SDL_QUIT:
518             qemu_system_shutdown_request();
519             break;
520         case SDL_MOUSEMOTION:
521             if (gui_grab) {
522                 sdl_send_mouse_event();
523             }
524             break;
525         case SDL_MOUSEBUTTONDOWN:
526         case SDL_MOUSEBUTTONUP:
527             {
528                 SDL_MouseButtonEvent *bev = &ev->button;
529                 if (!gui_grab) {
530                     if (ev->type == SDL_MOUSEBUTTONDOWN &&
531                         (bev->state & SDL_BUTTON_LMASK)) {
532                         /* start grabbing all events */
533                         sdl_grab_start();
534                     }
535                 } else {
536                     sdl_send_mouse_event();
537                 }
538             }
539             break;
540         case SDL_ACTIVEEVENT:
541             if (gui_grab && (ev->active.gain & SDL_ACTIVEEVENTMASK) == 0 &&
542                 !gui_fullscreen_initial_grab) {
543                 sdl_grab_end();
544             }
545             break;
546         default:
547             break;
548         }
549     }
550 }
551
552 static void sdl_cleanup(void) 
553 {
554     SDL_Quit();
555 }
556
557 void sdl_display_init(DisplayState *ds, int full_screen)
558 {
559     int flags;
560
561     flags = SDL_INIT_VIDEO | SDL_INIT_NOPARACHUTE;
562     if (SDL_Init (flags)) {
563         fprintf(stderr, "Could not initialize SDL - exiting\n");
564         exit(1);
565     }
566 #ifndef _WIN32
567     /* NOTE: we still want Ctrl-C to work, so we undo the SDL redirections */
568     signal(SIGINT, SIG_DFL);
569     signal(SIGQUIT, SIG_DFL);
570 #endif
571
572     ds->dpy_update = sdl_update;
573     ds->dpy_resize = sdl_resize;
574     ds->dpy_refresh = sdl_refresh;
575
576     sdl_resize(ds, 640, 400);
577     sdl_update_caption();
578     SDL_EnableKeyRepeat(250, 50);
579     SDL_EnableUNICODE(1);
580     gui_grab = 0;
581
582     atexit(sdl_cleanup);
583     if (full_screen) {
584         gui_fullscreen = 1;
585         gui_fullscreen_initial_grab = 1;
586         sdl_grab_start();
587     }
588 }