Split out SDL X keymap, by Bernhard Fischer.
[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     if (ds->depth == 32 && screen->format->Rshift == 0) {
85         ds->bgr = 1;
86     } else {
87         ds->bgr = 0;
88     }
89     ds->width = w;
90     ds->height = h;
91 }
92
93 /* generic keyboard conversion */
94
95 #include "sdl_keysym.h"
96 #include "keymaps.c"
97
98 static kbd_layout_t *kbd_layout = NULL;
99
100 static uint8_t sdl_keyevent_to_keycode_generic(const SDL_KeyboardEvent *ev)
101 {
102     int keysym;
103     /* workaround for X11+SDL bug with AltGR */
104     keysym = ev->keysym.sym;
105     if (keysym == 0 && ev->keysym.scancode == 113)
106         keysym = SDLK_MODE;
107     /* For Japanese key '\' and '|' */
108     if (keysym == 92 && ev->keysym.scancode == 133) {
109         keysym = 0xa5;
110     }
111     return keysym2scancode(kbd_layout, keysym);
112 }
113
114 /* specific keyboard conversions from scan codes */
115
116 #if defined(_WIN32)
117
118 static uint8_t sdl_keyevent_to_keycode(const SDL_KeyboardEvent *ev)
119 {
120     return ev->keysym.scancode;
121 }
122
123 #else
124
125 static uint8_t sdl_keyevent_to_keycode(const SDL_KeyboardEvent *ev)
126 {
127     int keycode;
128
129     keycode = ev->keysym.scancode;
130
131     if (keycode < 9) {
132         keycode = 0;
133     } else if (keycode < 97) {
134         keycode -= 8; /* just an offset */
135     } else if (keycode < 212) {
136         /* use conversion table */
137         keycode = _translate_keycode(keycode - 97);
138     } else {
139         keycode = 0;
140     }
141     return keycode;
142 }
143
144 #endif
145
146 static void reset_keys(void)
147 {
148     int i;
149     for(i = 0; i < 256; i++) {
150         if (modifiers_state[i]) {
151             if (i & 0x80)
152                 kbd_put_keycode(0xe0);
153             kbd_put_keycode(i | 0x80);
154             modifiers_state[i] = 0;
155         }
156     }
157 }
158
159 static void sdl_process_key(SDL_KeyboardEvent *ev)
160 {
161     int keycode, v;
162
163     if (ev->keysym.sym == SDLK_PAUSE) {
164         /* specific case */
165         v = 0;
166         if (ev->type == SDL_KEYUP)
167             v |= 0x80;
168         kbd_put_keycode(0xe1);
169         kbd_put_keycode(0x1d | v);
170         kbd_put_keycode(0x45 | v);
171         return;
172     }
173
174     if (kbd_layout) {
175         keycode = sdl_keyevent_to_keycode_generic(ev);
176     } else {
177         keycode = sdl_keyevent_to_keycode(ev);
178     }
179
180     switch(keycode) {
181     case 0x00:
182         /* sent when leaving window: reset the modifiers state */
183         reset_keys();
184         return;
185     case 0x2a:                          /* Left Shift */
186     case 0x36:                          /* Right Shift */
187     case 0x1d:                          /* Left CTRL */
188     case 0x9d:                          /* Right CTRL */
189     case 0x38:                          /* Left ALT */
190     case 0xb8:                         /* Right ALT */
191         if (ev->type == SDL_KEYUP)
192             modifiers_state[keycode] = 0;
193         else
194             modifiers_state[keycode] = 1;
195         break;
196     case 0x45: /* num lock */
197     case 0x3a: /* caps lock */
198         /* SDL does not send the key up event, so we generate it */
199         kbd_put_keycode(keycode);
200         kbd_put_keycode(keycode | 0x80);
201         return;
202     }
203
204     /* now send the key code */
205     if (keycode & 0x80)
206         kbd_put_keycode(0xe0);
207     if (ev->type == SDL_KEYUP)
208         kbd_put_keycode(keycode | 0x80);
209     else
210         kbd_put_keycode(keycode & 0x7f);
211 }
212
213 static void sdl_update_caption(void)
214 {
215     char buf[1024];
216     strcpy(buf, "QEMU");
217     if (!vm_running) {
218         strcat(buf, " [Stopped]");
219     }
220     if (gui_grab) {
221         strcat(buf, " - Press Ctrl-Alt to exit grab");
222     }
223     SDL_WM_SetCaption(buf, "QEMU");
224 }
225
226 static void sdl_hide_cursor(void)
227 {
228     if (kbd_mouse_is_absolute()) {
229         SDL_ShowCursor(1);
230         SDL_SetCursor(sdl_cursor_hidden);
231     } else {
232         SDL_ShowCursor(0);
233     }
234 }
235
236 static void sdl_show_cursor(void)
237 {
238     if (!kbd_mouse_is_absolute()) {
239         SDL_ShowCursor(1);
240         SDL_SetCursor(sdl_cursor_normal);
241     }
242 }
243
244 static void sdl_grab_start(void)
245 {
246     sdl_hide_cursor();
247     SDL_WM_GrabInput(SDL_GRAB_ON);
248     /* dummy read to avoid moving the mouse */
249     SDL_GetRelativeMouseState(NULL, NULL);
250     gui_grab = 1;
251     sdl_update_caption();
252 }
253
254 static void sdl_grab_end(void)
255 {
256     SDL_WM_GrabInput(SDL_GRAB_OFF);
257     sdl_show_cursor();
258     gui_grab = 0;
259     sdl_update_caption();
260 }
261
262 static void sdl_send_mouse_event(int dz)
263 {
264     int dx, dy, state, buttons;
265     state = SDL_GetRelativeMouseState(&dx, &dy);
266     buttons = 0;
267     if (state & SDL_BUTTON(SDL_BUTTON_LEFT))
268         buttons |= MOUSE_EVENT_LBUTTON;
269     if (state & SDL_BUTTON(SDL_BUTTON_RIGHT))
270         buttons |= MOUSE_EVENT_RBUTTON;
271     if (state & SDL_BUTTON(SDL_BUTTON_MIDDLE))
272         buttons |= MOUSE_EVENT_MBUTTON;
273
274     if (kbd_mouse_is_absolute()) {
275         if (!absolute_enabled) {
276             sdl_hide_cursor();
277             if (gui_grab) {
278                 sdl_grab_end();
279             }
280             absolute_enabled = 1;
281         }
282
283         SDL_GetMouseState(&dx, &dy);
284         dx = dx * 0x7FFF / width;
285         dy = dy * 0x7FFF / height;
286     } else if (absolute_enabled) {
287         sdl_show_cursor();
288         absolute_enabled = 0;
289     }
290
291     kbd_mouse_event(dx, dy, dz, buttons);
292 }
293
294 static void toggle_full_screen(DisplayState *ds)
295 {
296     gui_fullscreen = !gui_fullscreen;
297     sdl_resize(ds, screen->w, screen->h);
298     if (gui_fullscreen) {
299         gui_saved_grab = gui_grab;
300         sdl_grab_start();
301     } else {
302         if (!gui_saved_grab)
303             sdl_grab_end();
304     }
305     vga_hw_invalidate();
306     vga_hw_update();
307 }
308
309 static void sdl_refresh(DisplayState *ds)
310 {
311     SDL_Event ev1, *ev = &ev1;
312     int mod_state;
313                      
314     if (last_vm_running != vm_running) {
315         last_vm_running = vm_running;
316         sdl_update_caption();
317     }
318
319     vga_hw_update();
320
321     while (SDL_PollEvent(ev)) {
322         switch (ev->type) {
323         case SDL_VIDEOEXPOSE:
324             sdl_update(ds, 0, 0, screen->w, screen->h);
325             break;
326         case SDL_KEYDOWN:
327         case SDL_KEYUP:
328             if (ev->type == SDL_KEYDOWN) {
329                 mod_state = (SDL_GetModState() & gui_grab_code) ==
330                     gui_grab_code;
331                 gui_key_modifier_pressed = mod_state;
332                 if (gui_key_modifier_pressed) {
333                     int keycode;
334                     keycode = sdl_keyevent_to_keycode(&ev->key);
335                     switch(keycode) {
336                     case 0x21: /* 'f' key on US keyboard */
337                         toggle_full_screen(ds);
338                         gui_keysym = 1;
339                         break;
340                     case 0x02 ... 0x0a: /* '1' to '9' keys */ 
341                         /* Reset the modifiers sent to the current console */
342                         reset_keys();
343                         console_select(keycode - 0x02);
344                         if (!is_graphic_console()) {
345                             /* display grab if going to a text console */
346                             if (gui_grab)
347                                 sdl_grab_end();
348                         }
349                         gui_keysym = 1;
350                         break;
351                     default:
352                         break;
353                     }
354                 } else if (!is_graphic_console()) {
355                     int keysym;
356                     keysym = 0;
357                     if (ev->key.keysym.mod & (KMOD_LCTRL | KMOD_RCTRL)) {
358                         switch(ev->key.keysym.sym) {
359                         case SDLK_UP: keysym = QEMU_KEY_CTRL_UP; break;
360                         case SDLK_DOWN: keysym = QEMU_KEY_CTRL_DOWN; break;
361                         case SDLK_LEFT: keysym = QEMU_KEY_CTRL_LEFT; break;
362                         case SDLK_RIGHT: keysym = QEMU_KEY_CTRL_RIGHT; break;
363                         case SDLK_HOME: keysym = QEMU_KEY_CTRL_HOME; break;
364                         case SDLK_END: keysym = QEMU_KEY_CTRL_END; break;
365                         case SDLK_PAGEUP: keysym = QEMU_KEY_CTRL_PAGEUP; break;
366                         case SDLK_PAGEDOWN: keysym = QEMU_KEY_CTRL_PAGEDOWN; break;
367                         default: break;
368                         }
369                     } else {
370                         switch(ev->key.keysym.sym) {
371                         case SDLK_UP: keysym = QEMU_KEY_UP; break;
372                         case SDLK_DOWN: keysym = QEMU_KEY_DOWN; break;
373                         case SDLK_LEFT: keysym = QEMU_KEY_LEFT; break;
374                         case SDLK_RIGHT: keysym = QEMU_KEY_RIGHT; break;
375                         case SDLK_HOME: keysym = QEMU_KEY_HOME; break;
376                         case SDLK_END: keysym = QEMU_KEY_END; break;
377                         case SDLK_PAGEUP: keysym = QEMU_KEY_PAGEUP; break;
378                         case SDLK_PAGEDOWN: keysym = QEMU_KEY_PAGEDOWN; break;
379                         case SDLK_BACKSPACE: keysym = QEMU_KEY_BACKSPACE; break;                        case SDLK_DELETE: keysym = QEMU_KEY_DELETE; break;
380                         default: break;
381                         }
382                     }
383                     if (keysym) {
384                         kbd_put_keysym(keysym);
385                     } else if (ev->key.keysym.unicode != 0) {
386                         kbd_put_keysym(ev->key.keysym.unicode);
387                     }
388                 }
389             } else if (ev->type == SDL_KEYUP) {
390                 mod_state = (ev->key.keysym.mod & gui_grab_code);
391                 if (!mod_state) {
392                     if (gui_key_modifier_pressed) {
393                         gui_key_modifier_pressed = 0;
394                         if (gui_keysym == 0) {
395                             /* exit/enter grab if pressing Ctrl-Alt */
396                             if (!gui_grab) {
397                                 /* if the application is not active,
398                                    do not try to enter grab state. It
399                                    prevents
400                                    'SDL_WM_GrabInput(SDL_GRAB_ON)'
401                                    from blocking all the application
402                                    (SDL bug). */
403                                 if (SDL_GetAppState() & SDL_APPACTIVE)
404                                     sdl_grab_start();
405                             } else {
406                                 sdl_grab_end();
407                             }
408                             /* SDL does not send back all the
409                                modifiers key, so we must correct it */
410                             reset_keys();
411                             break;
412                         }
413                         gui_keysym = 0;
414                     }
415                 }
416             }
417             if (is_graphic_console() && !gui_keysym) 
418                 sdl_process_key(&ev->key);
419             break;
420         case SDL_QUIT:
421             if (!no_quit) {
422                qemu_system_shutdown_request();
423             }
424             break;
425         case SDL_MOUSEMOTION:
426             if (gui_grab || kbd_mouse_is_absolute() ||
427                 absolute_enabled) {
428                 sdl_send_mouse_event(0);
429             }
430             break;
431         case SDL_MOUSEBUTTONDOWN:
432         case SDL_MOUSEBUTTONUP:
433             {
434                 SDL_MouseButtonEvent *bev = &ev->button;
435                 if (!gui_grab && !kbd_mouse_is_absolute()) {
436                     if (ev->type == SDL_MOUSEBUTTONDOWN &&
437                         (bev->state & SDL_BUTTON_LMASK)) {
438                         /* start grabbing all events */
439                         sdl_grab_start();
440                     }
441                 } else {
442                     int dz;
443                     dz = 0;
444 #ifdef SDL_BUTTON_WHEELUP
445                     if (bev->button == SDL_BUTTON_WHEELUP && ev->type == SDL_MOUSEBUTTONDOWN) {
446                         dz = -1;
447                     } else if (bev->button == SDL_BUTTON_WHEELDOWN && ev->type == SDL_MOUSEBUTTONDOWN) {
448                         dz = 1;
449                     }
450 #endif               
451                     sdl_send_mouse_event(dz);
452                 }
453             }
454             break;
455         case SDL_ACTIVEEVENT:
456             if (gui_grab && ev->active.state == SDL_APPINPUTFOCUS &&
457                 !ev->active.gain && !gui_fullscreen_initial_grab) {
458                 sdl_grab_end();
459             }
460             break;
461         default:
462             break;
463         }
464     }
465 }
466
467 static void sdl_cleanup(void) 
468 {
469     SDL_Quit();
470 }
471
472 void sdl_display_init(DisplayState *ds, int full_screen)
473 {
474     int flags;
475     uint8_t data = 0;
476
477 #if defined(__APPLE__)
478     /* always use generic keymaps */
479     if (!keyboard_layout)
480         keyboard_layout = "en-us";
481 #endif
482     if(keyboard_layout) {
483         kbd_layout = init_keyboard_layout(keyboard_layout);
484         if (!kbd_layout)
485             exit(1);
486     }
487
488     flags = SDL_INIT_VIDEO | SDL_INIT_NOPARACHUTE;
489     if (SDL_Init (flags)) {
490         fprintf(stderr, "Could not initialize SDL - exiting\n");
491         exit(1);
492     }
493 #ifndef _WIN32
494     /* NOTE: we still want Ctrl-C to work, so we undo the SDL redirections */
495     signal(SIGINT, SIG_DFL);
496     signal(SIGQUIT, SIG_DFL);
497 #endif
498
499     ds->dpy_update = sdl_update;
500     ds->dpy_resize = sdl_resize;
501     ds->dpy_refresh = sdl_refresh;
502
503     sdl_resize(ds, 640, 400);
504     sdl_update_caption();
505     SDL_EnableKeyRepeat(250, 50);
506     SDL_EnableUNICODE(1);
507     gui_grab = 0;
508
509     sdl_cursor_hidden = SDL_CreateCursor(&data, &data, 8, 1, 0, 0);
510     sdl_cursor_normal = SDL_GetCursor();
511
512     atexit(sdl_cleanup);
513     if (full_screen) {
514         gui_fullscreen = 1;
515         gui_fullscreen_initial_grab = 1;
516         sdl_grab_start();
517     }
518 }