global s/loglevel & X/qemu_loglevel_mask(X)/ (Eduardo Habkost)
[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 "qemu-common.h"
25 #include "console.h"
26 #include "sysemu.h"
27
28 #include <SDL.h>
29
30 #ifndef _WIN32
31 #include <signal.h>
32 #endif
33
34 static DisplayChangeListener *dcl;
35 static SDL_Surface *real_screen;
36 static SDL_Surface *guest_screen = NULL;
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_noframe;
42 static int gui_key_modifier_pressed;
43 static int gui_keysym;
44 static int gui_fullscreen_initial_grab;
45 static int gui_grab_code = KMOD_LALT | KMOD_LCTRL;
46 static uint8_t modifiers_state[256];
47 static int width, height;
48 static SDL_Cursor *sdl_cursor_normal;
49 static SDL_Cursor *sdl_cursor_hidden;
50 static int absolute_enabled = 0;
51 static int guest_cursor = 0;
52 static int guest_x, guest_y;
53 static SDL_Cursor *guest_sprite = 0;
54
55 static void sdl_update(DisplayState *ds, int x, int y, int w, int h)
56 {
57     SDL_Rect rec;
58     rec.x = x;
59     rec.y = y;
60     rec.w = w;
61     rec.h = h;
62     //    printf("updating x=%d y=%d w=%d h=%d\n", x, y, w, h);
63
64     SDL_BlitSurface(guest_screen, &rec, real_screen, &rec);
65     SDL_Flip(real_screen);
66 }
67
68 static void sdl_setdata(DisplayState *ds)
69 {
70     SDL_Rect rec;
71     rec.x = 0;
72     rec.y = 0;
73     rec.w = real_screen->w;
74     rec.h = real_screen->h;
75
76     if (guest_screen != NULL) SDL_FreeSurface(guest_screen);
77
78     guest_screen = SDL_CreateRGBSurfaceFrom(ds_get_data(ds), ds_get_width(ds), ds_get_height(ds),
79                                             ds_get_bits_per_pixel(ds), ds_get_linesize(ds),
80                                             ds->surface->pf.rmask, ds->surface->pf.gmask,
81                                             ds->surface->pf.bmask, ds->surface->pf.amask);
82 }
83
84 static void sdl_resize(DisplayState *ds)
85 {
86     int flags;
87
88     //    printf("resizing to %d %d\n", w, h);
89
90     flags = SDL_HWSURFACE|SDL_ASYNCBLIT|SDL_HWACCEL;
91     if (gui_fullscreen)
92         flags |= SDL_FULLSCREEN;
93     if (gui_noframe)
94         flags |= SDL_NOFRAME;
95
96  again:
97     real_screen = SDL_SetVideoMode(ds_get_width(ds), ds_get_height(ds), 0, flags);
98     if (!real_screen) {
99         fprintf(stderr, "Could not open SDL display\n");
100         exit(1);
101     }
102     if (!real_screen->pixels && (flags & SDL_HWSURFACE) && (flags & SDL_FULLSCREEN)) {
103         flags &= ~SDL_HWSURFACE;
104         goto again;
105     }
106
107     if (!real_screen->pixels) {
108         fprintf(stderr, "Could not open SDL display\n");
109         exit(1);
110     }
111
112     sdl_setdata(ds);
113 }
114
115 /* generic keyboard conversion */
116
117 #include "sdl_keysym.h"
118 #include "keymaps.c"
119
120 static kbd_layout_t *kbd_layout = NULL;
121
122 static uint8_t sdl_keyevent_to_keycode_generic(const SDL_KeyboardEvent *ev)
123 {
124     int keysym;
125     /* workaround for X11+SDL bug with AltGR */
126     keysym = ev->keysym.sym;
127     if (keysym == 0 && ev->keysym.scancode == 113)
128         keysym = SDLK_MODE;
129     /* For Japanese key '\' and '|' */
130     if (keysym == 92 && ev->keysym.scancode == 133) {
131         keysym = 0xa5;
132     }
133     return keysym2scancode(kbd_layout, keysym);
134 }
135
136 /* specific keyboard conversions from scan codes */
137
138 #if defined(_WIN32)
139
140 static uint8_t sdl_keyevent_to_keycode(const SDL_KeyboardEvent *ev)
141 {
142     return ev->keysym.scancode;
143 }
144
145 #else
146
147 static uint8_t sdl_keyevent_to_keycode(const SDL_KeyboardEvent *ev)
148 {
149     int keycode;
150
151     keycode = ev->keysym.scancode;
152
153     if (keycode < 9) {
154         keycode = 0;
155     } else if (keycode < 97) {
156         keycode -= 8; /* just an offset */
157     } else if (keycode < 212) {
158         /* use conversion table */
159         keycode = _translate_keycode(keycode - 97);
160     } else {
161         keycode = 0;
162     }
163     return keycode;
164 }
165
166 #endif
167
168 static void reset_keys(void)
169 {
170     int i;
171     for(i = 0; i < 256; i++) {
172         if (modifiers_state[i]) {
173             if (i & 0x80)
174                 kbd_put_keycode(0xe0);
175             kbd_put_keycode(i | 0x80);
176             modifiers_state[i] = 0;
177         }
178     }
179 }
180
181 static void sdl_process_key(SDL_KeyboardEvent *ev)
182 {
183     int keycode, v;
184
185     if (ev->keysym.sym == SDLK_PAUSE) {
186         /* specific case */
187         v = 0;
188         if (ev->type == SDL_KEYUP)
189             v |= 0x80;
190         kbd_put_keycode(0xe1);
191         kbd_put_keycode(0x1d | v);
192         kbd_put_keycode(0x45 | v);
193         return;
194     }
195
196     if (kbd_layout) {
197         keycode = sdl_keyevent_to_keycode_generic(ev);
198     } else {
199         keycode = sdl_keyevent_to_keycode(ev);
200     }
201
202     switch(keycode) {
203     case 0x00:
204         /* sent when leaving window: reset the modifiers state */
205         reset_keys();
206         return;
207     case 0x2a:                          /* Left Shift */
208     case 0x36:                          /* Right Shift */
209     case 0x1d:                          /* Left CTRL */
210     case 0x9d:                          /* Right CTRL */
211     case 0x38:                          /* Left ALT */
212     case 0xb8:                         /* Right ALT */
213         if (ev->type == SDL_KEYUP)
214             modifiers_state[keycode] = 0;
215         else
216             modifiers_state[keycode] = 1;
217         break;
218     case 0x45: /* num lock */
219     case 0x3a: /* caps lock */
220         /* SDL does not send the key up event, so we generate it */
221         kbd_put_keycode(keycode);
222         kbd_put_keycode(keycode | 0x80);
223         return;
224     }
225
226     /* now send the key code */
227     if (keycode & 0x80)
228         kbd_put_keycode(0xe0);
229     if (ev->type == SDL_KEYUP)
230         kbd_put_keycode(keycode | 0x80);
231     else
232         kbd_put_keycode(keycode & 0x7f);
233 }
234
235 static void sdl_update_caption(void)
236 {
237     char buf[1024];
238     const char *status = "";
239
240     if (!vm_running)
241         status = " [Stopped]";
242     else if (gui_grab) {
243         if (!alt_grab)
244             status = " - Press Ctrl-Alt to exit grab";
245         else
246             status = " - Press Ctrl-Alt-Shift to exit grab";
247     }
248
249     if (qemu_name)
250         snprintf(buf, sizeof(buf), "QEMU (%s)%s", qemu_name, status);
251     else
252         snprintf(buf, sizeof(buf), "QEMU%s", status);
253
254     SDL_WM_SetCaption(buf, "QEMU");
255 }
256
257 static void sdl_hide_cursor(void)
258 {
259     if (!cursor_hide)
260         return;
261
262     if (kbd_mouse_is_absolute()) {
263         SDL_ShowCursor(1);
264         SDL_SetCursor(sdl_cursor_hidden);
265     } else {
266         SDL_ShowCursor(0);
267     }
268 }
269
270 static void sdl_show_cursor(void)
271 {
272     if (!cursor_hide)
273         return;
274
275     if (!kbd_mouse_is_absolute()) {
276         SDL_ShowCursor(1);
277         if (guest_cursor &&
278                 (gui_grab || kbd_mouse_is_absolute() || absolute_enabled))
279             SDL_SetCursor(guest_sprite);
280         else
281             SDL_SetCursor(sdl_cursor_normal);
282     }
283 }
284
285 static void sdl_grab_start(void)
286 {
287     if (guest_cursor) {
288         SDL_SetCursor(guest_sprite);
289         SDL_WarpMouse(guest_x, guest_y);
290     } else
291         sdl_hide_cursor();
292
293     if (SDL_WM_GrabInput(SDL_GRAB_ON) == SDL_GRAB_ON) {
294         gui_grab = 1;
295         sdl_update_caption();
296     } else
297         sdl_show_cursor();
298 }
299
300 static void sdl_grab_end(void)
301 {
302     SDL_WM_GrabInput(SDL_GRAB_OFF);
303     gui_grab = 0;
304     sdl_show_cursor();
305     sdl_update_caption();
306 }
307
308 static void sdl_send_mouse_event(int dx, int dy, int dz, int x, int y, int state)
309 {
310     int buttons;
311     buttons = 0;
312     if (state & SDL_BUTTON(SDL_BUTTON_LEFT))
313         buttons |= MOUSE_EVENT_LBUTTON;
314     if (state & SDL_BUTTON(SDL_BUTTON_RIGHT))
315         buttons |= MOUSE_EVENT_RBUTTON;
316     if (state & SDL_BUTTON(SDL_BUTTON_MIDDLE))
317         buttons |= MOUSE_EVENT_MBUTTON;
318
319     if (kbd_mouse_is_absolute()) {
320         if (!absolute_enabled) {
321             sdl_hide_cursor();
322             if (gui_grab) {
323                 sdl_grab_end();
324             }
325             absolute_enabled = 1;
326         }
327
328        dx = x * 0x7FFF / (width - 1);
329        dy = y * 0x7FFF / (height - 1);
330     } else if (absolute_enabled) {
331         sdl_show_cursor();
332         absolute_enabled = 0;
333     } else if (guest_cursor) {
334         x -= guest_x;
335         y -= guest_y;
336         guest_x += x;
337         guest_y += y;
338         dx = x;
339         dy = y;
340     }
341
342     kbd_mouse_event(dx, dy, dz, buttons);
343 }
344
345 static void toggle_full_screen(DisplayState *ds)
346 {
347     gui_fullscreen = !gui_fullscreen;
348     sdl_resize(ds);
349     if (gui_fullscreen) {
350         gui_saved_grab = gui_grab;
351         sdl_grab_start();
352     } else {
353         if (!gui_saved_grab)
354             sdl_grab_end();
355     }
356     vga_hw_invalidate();
357     vga_hw_update();
358 }
359
360 static void sdl_refresh(DisplayState *ds)
361 {
362     SDL_Event ev1, *ev = &ev1;
363     int mod_state;
364     int buttonstate = SDL_GetMouseState(NULL, NULL);
365
366     if (last_vm_running != vm_running) {
367         last_vm_running = vm_running;
368         sdl_update_caption();
369     }
370
371     vga_hw_update();
372     SDL_EnableUNICODE(!is_graphic_console());
373
374     while (SDL_PollEvent(ev)) {
375         switch (ev->type) {
376         case SDL_VIDEOEXPOSE:
377             sdl_update(ds, 0, 0, real_screen->w, real_screen->h);
378             break;
379         case SDL_KEYDOWN:
380         case SDL_KEYUP:
381             if (ev->type == SDL_KEYDOWN) {
382                 if (!alt_grab) {
383                     mod_state = (SDL_GetModState() & gui_grab_code) ==
384                                 gui_grab_code;
385                 } else {
386                     mod_state = (SDL_GetModState() & (gui_grab_code | KMOD_LSHIFT)) ==
387                                 (gui_grab_code | KMOD_LSHIFT);
388                 }
389                 gui_key_modifier_pressed = mod_state;
390                 if (gui_key_modifier_pressed) {
391                     int keycode;
392                     keycode = sdl_keyevent_to_keycode(&ev->key);
393                     switch(keycode) {
394                     case 0x21: /* 'f' key on US keyboard */
395                         toggle_full_screen(ds);
396                         gui_keysym = 1;
397                         break;
398                     case 0x02 ... 0x0a: /* '1' to '9' keys */
399                         /* Reset the modifiers sent to the current console */
400                         reset_keys();
401                         console_select(keycode - 0x02);
402                         if (!is_graphic_console()) {
403                             /* display grab if going to a text console */
404                             if (gui_grab)
405                                 sdl_grab_end();
406                         }
407                         gui_keysym = 1;
408                         break;
409                     default:
410                         break;
411                     }
412                 } else if (!is_graphic_console()) {
413                     int keysym;
414                     keysym = 0;
415                     if (ev->key.keysym.mod & (KMOD_LCTRL | KMOD_RCTRL)) {
416                         switch(ev->key.keysym.sym) {
417                         case SDLK_UP: keysym = QEMU_KEY_CTRL_UP; break;
418                         case SDLK_DOWN: keysym = QEMU_KEY_CTRL_DOWN; break;
419                         case SDLK_LEFT: keysym = QEMU_KEY_CTRL_LEFT; break;
420                         case SDLK_RIGHT: keysym = QEMU_KEY_CTRL_RIGHT; break;
421                         case SDLK_HOME: keysym = QEMU_KEY_CTRL_HOME; break;
422                         case SDLK_END: keysym = QEMU_KEY_CTRL_END; break;
423                         case SDLK_PAGEUP: keysym = QEMU_KEY_CTRL_PAGEUP; break;
424                         case SDLK_PAGEDOWN: keysym = QEMU_KEY_CTRL_PAGEDOWN; break;
425                         default: break;
426                         }
427                     } else {
428                         switch(ev->key.keysym.sym) {
429                         case SDLK_UP: keysym = QEMU_KEY_UP; break;
430                         case SDLK_DOWN: keysym = QEMU_KEY_DOWN; break;
431                         case SDLK_LEFT: keysym = QEMU_KEY_LEFT; break;
432                         case SDLK_RIGHT: keysym = QEMU_KEY_RIGHT; break;
433                         case SDLK_HOME: keysym = QEMU_KEY_HOME; break;
434                         case SDLK_END: keysym = QEMU_KEY_END; break;
435                         case SDLK_PAGEUP: keysym = QEMU_KEY_PAGEUP; break;
436                         case SDLK_PAGEDOWN: keysym = QEMU_KEY_PAGEDOWN; break;
437                         case SDLK_BACKSPACE: keysym = QEMU_KEY_BACKSPACE; break;
438                         case SDLK_DELETE: keysym = QEMU_KEY_DELETE; break;
439                         default: break;
440                         }
441                     }
442                     if (keysym) {
443                         kbd_put_keysym(keysym);
444                     } else if (ev->key.keysym.unicode != 0) {
445                         kbd_put_keysym(ev->key.keysym.unicode);
446                     }
447                 }
448             } else if (ev->type == SDL_KEYUP) {
449                 if (!alt_grab) {
450                     mod_state = (ev->key.keysym.mod & gui_grab_code);
451                 } else {
452                     mod_state = (ev->key.keysym.mod &
453                                  (gui_grab_code | KMOD_LSHIFT));
454                 }
455                 if (!mod_state) {
456                     if (gui_key_modifier_pressed) {
457                         gui_key_modifier_pressed = 0;
458                         if (gui_keysym == 0) {
459                             /* exit/enter grab if pressing Ctrl-Alt */
460                             if (!gui_grab) {
461                                 /* if the application is not active,
462                                    do not try to enter grab state. It
463                                    prevents
464                                    'SDL_WM_GrabInput(SDL_GRAB_ON)'
465                                    from blocking all the application
466                                    (SDL bug). */
467                                 if (SDL_GetAppState() & SDL_APPACTIVE)
468                                     sdl_grab_start();
469                             } else {
470                                 sdl_grab_end();
471                             }
472                             /* SDL does not send back all the
473                                modifiers key, so we must correct it */
474                             reset_keys();
475                             break;
476                         }
477                         gui_keysym = 0;
478                     }
479                 }
480             }
481             if (is_graphic_console() && !gui_keysym)
482                 sdl_process_key(&ev->key);
483             break;
484         case SDL_QUIT:
485             if (!no_quit)
486                 qemu_system_shutdown_request();
487             break;
488         case SDL_MOUSEMOTION:
489             if (gui_grab || kbd_mouse_is_absolute() ||
490                 absolute_enabled) {
491                 sdl_send_mouse_event(ev->motion.xrel, ev->motion.yrel, 0,
492                        ev->motion.x, ev->motion.y, ev->motion.state);
493             }
494             break;
495         case SDL_MOUSEBUTTONDOWN:
496         case SDL_MOUSEBUTTONUP:
497             {
498                 SDL_MouseButtonEvent *bev = &ev->button;
499                 if (!gui_grab && !kbd_mouse_is_absolute()) {
500                     if (ev->type == SDL_MOUSEBUTTONDOWN &&
501                         (bev->button == SDL_BUTTON_LEFT)) {
502                         /* start grabbing all events */
503                         sdl_grab_start();
504                     }
505                 } else {
506                     int dz;
507                     dz = 0;
508                     if (ev->type == SDL_MOUSEBUTTONDOWN) {
509                         buttonstate |= SDL_BUTTON(bev->button);
510                     } else {
511                         buttonstate &= ~SDL_BUTTON(bev->button);
512                     }
513 #ifdef SDL_BUTTON_WHEELUP
514                     if (bev->button == SDL_BUTTON_WHEELUP && ev->type == SDL_MOUSEBUTTONDOWN) {
515                         dz = -1;
516                     } else if (bev->button == SDL_BUTTON_WHEELDOWN && ev->type == SDL_MOUSEBUTTONDOWN) {
517                         dz = 1;
518                     }
519 #endif
520                     sdl_send_mouse_event(0, 0, dz, bev->x, bev->y, buttonstate);
521                 }
522             }
523             break;
524         case SDL_ACTIVEEVENT:
525             if (gui_grab && ev->active.state == SDL_APPINPUTFOCUS &&
526                 !ev->active.gain && !gui_fullscreen_initial_grab) {
527                 sdl_grab_end();
528             }
529             if (ev->active.state & SDL_APPACTIVE) {
530                 if (ev->active.gain) {
531                     /* Back to default interval */
532                     dcl->gui_timer_interval = 0;
533                     dcl->idle = 0;
534                 } else {
535                     /* Sleeping interval */
536                     dcl->gui_timer_interval = 500;
537                     dcl->idle = 1;
538                 }
539             }
540             break;
541         default:
542             break;
543         }
544     }
545 }
546
547 static void sdl_fill(DisplayState *ds, int x, int y, int w, int h, uint32_t c)
548 {
549     SDL_Rect dst = { x, y, w, h };
550     SDL_FillRect(real_screen, &dst, c);
551 }
552
553 static void sdl_mouse_warp(int x, int y, int on)
554 {
555     if (on) {
556         if (!guest_cursor)
557             sdl_show_cursor();
558         if (gui_grab || kbd_mouse_is_absolute() || absolute_enabled) {
559             SDL_SetCursor(guest_sprite);
560             SDL_WarpMouse(x, y);
561         }
562     } else if (gui_grab)
563         sdl_hide_cursor();
564     guest_cursor = on;
565     guest_x = x, guest_y = y;
566 }
567
568 static void sdl_mouse_define(int width, int height, int bpp,
569                              int hot_x, int hot_y,
570                              uint8_t *image, uint8_t *mask)
571 {
572     uint8_t sprite[256], *line;
573     int x, y, dst, bypl, src = 0;
574     if (guest_sprite)
575         SDL_FreeCursor(guest_sprite);
576
577     memset(sprite, 0, 256);
578     bypl = ((width * bpp + 31) >> 5) << 2;
579     for (y = 0, dst = 0; y < height; y ++, image += bypl) {
580         line = image;
581         for (x = 0; x < width; x ++, dst ++) {
582             switch (bpp) {
583             case 24:
584                 src = *(line ++); src |= *(line ++); src |= *(line ++);
585                 break;
586             case 16:
587             case 15:
588                 src = *(line ++); src |= *(line ++);
589                 break;
590             case 8:
591                 src = *(line ++);
592                 break;
593             case 4:
594                 src = 0xf & (line[x >> 1] >> ((x & 1)) << 2);
595                 break;
596             case 2:
597                 src = 3 & (line[x >> 2] >> ((x & 3)) << 1);
598                 break;
599             case 1:
600                 src = 1 & (line[x >> 3] >> (x & 7));
601                 break;
602             }
603             if (!src)
604                 sprite[dst >> 3] |= (1 << (~dst & 7)) & mask[dst >> 3];
605         }
606     }
607     guest_sprite = SDL_CreateCursor(sprite, mask, width, height, hot_x, hot_y);
608
609     if (guest_cursor &&
610             (gui_grab || kbd_mouse_is_absolute() || absolute_enabled))
611         SDL_SetCursor(guest_sprite);
612 }
613
614 static void sdl_cleanup(void)
615 {
616     if (guest_sprite)
617         SDL_FreeCursor(guest_sprite);
618     SDL_Quit();
619 }
620
621 void sdl_display_init(DisplayState *ds, int full_screen, int no_frame)
622 {
623     int flags;
624     uint8_t data = 0;
625
626 #if defined(__APPLE__)
627     /* always use generic keymaps */
628     if (!keyboard_layout)
629         keyboard_layout = "en-us";
630 #endif
631     if(keyboard_layout) {
632         kbd_layout = init_keyboard_layout(keyboard_layout);
633         if (!kbd_layout)
634             exit(1);
635     }
636
637     if (no_frame)
638         gui_noframe = 1;
639
640     flags = SDL_INIT_VIDEO | SDL_INIT_NOPARACHUTE;
641     if (SDL_Init (flags)) {
642         fprintf(stderr, "Could not initialize SDL - exiting\n");
643         exit(1);
644     }
645
646     dcl = qemu_mallocz(sizeof(DisplayChangeListener));
647     if (!dcl)
648         exit(1);
649     dcl->dpy_update = sdl_update;
650     dcl->dpy_resize = sdl_resize;
651     dcl->dpy_refresh = sdl_refresh;
652     dcl->dpy_setdata = sdl_setdata;
653     dcl->dpy_fill = sdl_fill;
654     ds->mouse_set = sdl_mouse_warp;
655     ds->cursor_define = sdl_mouse_define;
656     register_displaychangelistener(ds, dcl);
657
658     sdl_update_caption();
659     SDL_EnableKeyRepeat(250, 50);
660     gui_grab = 0;
661
662     sdl_cursor_hidden = SDL_CreateCursor(&data, &data, 8, 1, 0, 0);
663     sdl_cursor_normal = SDL_GetCursor();
664
665     atexit(sdl_cleanup);
666     if (full_screen) {
667         gui_fullscreen = 1;
668         gui_fullscreen_initial_grab = 1;
669         sdl_grab_start();
670     }
671 }