]> git.proxmox.com Git - mirror_qemu.git/blob - ui/sdl.c
b89182a2c0fd8c0a1bf46e38cc74d67d992b57e7
[mirror_qemu.git] / ui / 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
25 /* Avoid compiler warning because macro is redefined in SDL_syswm.h. */
26 #undef WIN32_LEAN_AND_MEAN
27
28 #include <SDL.h>
29 #include <SDL_syswm.h>
30
31 #include "qemu-common.h"
32 #include "ui/console.h"
33 #include "ui/input.h"
34 #include "sysemu/sysemu.h"
35 #include "x_keymap.h"
36 #include "sdl_zoom.h"
37
38 static DisplayChangeListener *dcl;
39 static DisplaySurface *surface;
40 static SDL_Surface *real_screen;
41 static SDL_Surface *guest_screen = NULL;
42 static int gui_grab; /* if true, all keyboard/mouse events are grabbed */
43 static int last_vm_running;
44 static bool gui_saved_scaling;
45 static int gui_saved_width;
46 static int gui_saved_height;
47 static int gui_saved_grab;
48 static int gui_fullscreen;
49 static int gui_noframe;
50 static int gui_key_modifier_pressed;
51 static int gui_keysym;
52 static int gui_grab_code = KMOD_LALT | KMOD_LCTRL;
53 static uint8_t modifiers_state[256];
54 static SDL_Cursor *sdl_cursor_normal;
55 static SDL_Cursor *sdl_cursor_hidden;
56 static int absolute_enabled = 0;
57 static int guest_cursor = 0;
58 static int guest_x, guest_y;
59 static SDL_Cursor *guest_sprite = NULL;
60 static SDL_PixelFormat host_format;
61 static int scaling_active = 0;
62 static Notifier mouse_mode_notifier;
63
64 #if 0
65 #define DEBUG_SDL
66 #endif
67
68 static void sdl_update(DisplayChangeListener *dcl,
69 int x, int y, int w, int h)
70 {
71 SDL_Rect rec;
72 rec.x = x;
73 rec.y = y;
74 rec.w = w;
75 rec.h = h;
76
77 #ifdef DEBUG_SDL
78 printf("SDL: Updating x=%d y=%d w=%d h=%d (scaling: %d)\n",
79 x, y, w, h, scaling_active);
80 #endif
81
82 if (guest_screen) {
83 if (!scaling_active) {
84 SDL_BlitSurface(guest_screen, &rec, real_screen, &rec);
85 } else {
86 if (sdl_zoom_blit(guest_screen, real_screen, SMOOTHING_ON, &rec) < 0) {
87 fprintf(stderr, "Zoom blit failed\n");
88 exit(1);
89 }
90 }
91 }
92 SDL_UpdateRect(real_screen, rec.x, rec.y, rec.w, rec.h);
93 }
94
95 static void do_sdl_resize(int width, int height, int bpp)
96 {
97 int flags;
98 SDL_Surface *tmp_screen;
99
100 #ifdef DEBUG_SDL
101 printf("SDL: Resizing to %dx%d bpp %d\n", width, height, bpp);
102 #endif
103
104 flags = SDL_HWSURFACE | SDL_ASYNCBLIT | SDL_HWACCEL;
105 if (gui_fullscreen) {
106 flags |= SDL_FULLSCREEN;
107 } else {
108 flags |= SDL_RESIZABLE;
109 }
110 if (gui_noframe)
111 flags |= SDL_NOFRAME;
112
113 tmp_screen = SDL_SetVideoMode(width, height, bpp, flags);
114 if (!real_screen) {
115 if (!tmp_screen) {
116 fprintf(stderr, "Could not open SDL display (%dx%dx%d): %s\n",
117 width, height, bpp, SDL_GetError());
118 exit(1);
119 }
120 } else {
121 /*
122 * Revert to the previous video mode if the change of resizing or
123 * resolution failed.
124 */
125 if (!tmp_screen) {
126 fprintf(stderr, "Failed to set SDL display (%dx%dx%d): %s\n",
127 width, height, bpp, SDL_GetError());
128 return;
129 }
130 }
131
132 real_screen = tmp_screen;
133 }
134
135 static void sdl_switch(DisplayChangeListener *dcl,
136 DisplaySurface *new_surface)
137 {
138 PixelFormat pf = qemu_pixelformat_from_pixman(new_surface->format);
139
140 /* temporary hack: allows to call sdl_switch to handle scaling changes */
141 if (new_surface) {
142 surface = new_surface;
143 }
144
145 if (!scaling_active) {
146 do_sdl_resize(surface_width(surface), surface_height(surface), 0);
147 } else if (real_screen->format->BitsPerPixel !=
148 surface_bits_per_pixel(surface)) {
149 do_sdl_resize(real_screen->w, real_screen->h,
150 surface_bits_per_pixel(surface));
151 }
152
153 if (guest_screen != NULL) {
154 SDL_FreeSurface(guest_screen);
155 }
156
157 #ifdef DEBUG_SDL
158 printf("SDL: Creating surface with masks: %08x %08x %08x %08x\n",
159 pf.rmask, pf.gmask, pf.bmask, pf.amask);
160 #endif
161
162 guest_screen = SDL_CreateRGBSurfaceFrom
163 (surface_data(surface),
164 surface_width(surface), surface_height(surface),
165 surface_bits_per_pixel(surface), surface_stride(surface),
166 pf.rmask, pf.gmask,
167 pf.bmask, pf.amask);
168 }
169
170 static bool sdl_check_format(DisplayChangeListener *dcl,
171 pixman_format_code_t format)
172 {
173 /*
174 * We let SDL convert for us a few more formats than,
175 * the native ones. Thes are the ones I have tested.
176 */
177 return (format == PIXMAN_x8r8g8b8 ||
178 format == PIXMAN_b8g8r8x8 ||
179 format == PIXMAN_x1r5g5b5 ||
180 format == PIXMAN_r5g6b5);
181 }
182
183 /* generic keyboard conversion */
184
185 #include "sdl_keysym.h"
186
187 static kbd_layout_t *kbd_layout = NULL;
188
189 static uint8_t sdl_keyevent_to_keycode_generic(const SDL_KeyboardEvent *ev)
190 {
191 int keysym;
192 /* workaround for X11+SDL bug with AltGR */
193 keysym = ev->keysym.sym;
194 if (keysym == 0 && ev->keysym.scancode == 113)
195 keysym = SDLK_MODE;
196 /* For Japanese key '\' and '|' */
197 if (keysym == 92 && ev->keysym.scancode == 133) {
198 keysym = 0xa5;
199 }
200 return keysym2scancode(kbd_layout, keysym) & SCANCODE_KEYMASK;
201 }
202
203 /* specific keyboard conversions from scan codes */
204
205 #if defined(_WIN32)
206
207 static uint8_t sdl_keyevent_to_keycode(const SDL_KeyboardEvent *ev)
208 {
209 return ev->keysym.scancode;
210 }
211
212 #else
213
214 #if defined(SDL_VIDEO_DRIVER_X11)
215 #include <X11/XKBlib.h>
216
217 static int check_for_evdev(void)
218 {
219 SDL_SysWMinfo info;
220 XkbDescPtr desc = NULL;
221 int has_evdev = 0;
222 char *keycodes = NULL;
223
224 SDL_VERSION(&info.version);
225 if (!SDL_GetWMInfo(&info)) {
226 return 0;
227 }
228 desc = XkbGetKeyboard(info.info.x11.display,
229 XkbGBN_AllComponentsMask,
230 XkbUseCoreKbd);
231 if (desc && desc->names) {
232 keycodes = XGetAtomName(info.info.x11.display, desc->names->keycodes);
233 if (keycodes == NULL) {
234 fprintf(stderr, "could not lookup keycode name\n");
235 } else if (strstart(keycodes, "evdev", NULL)) {
236 has_evdev = 1;
237 } else if (!strstart(keycodes, "xfree86", NULL)) {
238 fprintf(stderr, "unknown keycodes `%s', please report to "
239 "qemu-devel@nongnu.org\n", keycodes);
240 }
241 }
242
243 if (desc) {
244 XkbFreeKeyboard(desc, XkbGBN_AllComponentsMask, True);
245 }
246 if (keycodes) {
247 XFree(keycodes);
248 }
249 return has_evdev;
250 }
251 #else
252 static int check_for_evdev(void)
253 {
254 return 0;
255 }
256 #endif
257
258 static uint8_t sdl_keyevent_to_keycode(const SDL_KeyboardEvent *ev)
259 {
260 int keycode;
261 static int has_evdev = -1;
262
263 if (has_evdev == -1)
264 has_evdev = check_for_evdev();
265
266 keycode = ev->keysym.scancode;
267
268 if (keycode < 9) {
269 keycode = 0;
270 } else if (keycode < 97) {
271 keycode -= 8; /* just an offset */
272 } else if (keycode < 158) {
273 /* use conversion table */
274 if (has_evdev)
275 keycode = translate_evdev_keycode(keycode - 97);
276 else
277 keycode = translate_xfree86_keycode(keycode - 97);
278 } else if (keycode == 208) { /* Hiragana_Katakana */
279 keycode = 0x70;
280 } else if (keycode == 211) { /* backslash */
281 keycode = 0x73;
282 } else {
283 keycode = 0;
284 }
285 return keycode;
286 }
287
288 #endif
289
290 static void reset_keys(void)
291 {
292 int i;
293 for(i = 0; i < 256; i++) {
294 if (modifiers_state[i]) {
295 qemu_input_event_send_key_number(dcl->con, i, false);
296 modifiers_state[i] = 0;
297 }
298 }
299 }
300
301 static void sdl_process_key(SDL_KeyboardEvent *ev)
302 {
303 int keycode;
304
305 if (ev->keysym.sym == SDLK_PAUSE) {
306 /* specific case */
307 qemu_input_event_send_key_qcode(dcl->con, Q_KEY_CODE_PAUSE,
308 ev->type == SDL_KEYDOWN);
309 return;
310 }
311
312 if (kbd_layout) {
313 keycode = sdl_keyevent_to_keycode_generic(ev);
314 } else {
315 keycode = sdl_keyevent_to_keycode(ev);
316 }
317
318 switch(keycode) {
319 case 0x00:
320 /* sent when leaving window: reset the modifiers state */
321 reset_keys();
322 return;
323 case 0x2a: /* Left Shift */
324 case 0x36: /* Right Shift */
325 case 0x1d: /* Left CTRL */
326 case 0x9d: /* Right CTRL */
327 case 0x38: /* Left ALT */
328 case 0xb8: /* Right ALT */
329 if (ev->type == SDL_KEYUP)
330 modifiers_state[keycode] = 0;
331 else
332 modifiers_state[keycode] = 1;
333 break;
334 #define QEMU_SDL_VERSION ((SDL_MAJOR_VERSION << 8) + SDL_MINOR_VERSION)
335 #if QEMU_SDL_VERSION < 0x102 || QEMU_SDL_VERSION == 0x102 && SDL_PATCHLEVEL < 14
336 /* SDL versions before 1.2.14 don't support key up for caps/num lock. */
337 case 0x45: /* num lock */
338 case 0x3a: /* caps lock */
339 /* SDL does not send the key up event, so we generate it */
340 qemu_input_event_send_key_number(dcl->con, keycode, true);
341 qemu_input_event_send_key_number(dcl->con, keycode, false);
342 return;
343 #endif
344 }
345
346 /* now send the key code */
347 qemu_input_event_send_key_number(dcl->con, keycode,
348 ev->type == SDL_KEYDOWN);
349 }
350
351 static void sdl_update_caption(void)
352 {
353 char win_title[1024];
354 char icon_title[1024];
355 const char *status = "";
356
357 if (!runstate_is_running())
358 status = " [Stopped]";
359 else if (gui_grab) {
360 if (alt_grab)
361 status = " - Press Ctrl-Alt-Shift to exit mouse grab";
362 else if (ctrl_grab)
363 status = " - Press Right-Ctrl to exit mouse grab";
364 else
365 status = " - Press Ctrl-Alt to exit mouse grab";
366 }
367
368 if (qemu_name) {
369 snprintf(win_title, sizeof(win_title), "QEMU (%s)%s", qemu_name, status);
370 snprintf(icon_title, sizeof(icon_title), "QEMU (%s)", qemu_name);
371 } else {
372 snprintf(win_title, sizeof(win_title), "QEMU%s", status);
373 snprintf(icon_title, sizeof(icon_title), "QEMU");
374 }
375
376 SDL_WM_SetCaption(win_title, icon_title);
377 }
378
379 static void sdl_hide_cursor(void)
380 {
381 if (!cursor_hide)
382 return;
383
384 if (qemu_input_is_absolute()) {
385 SDL_ShowCursor(1);
386 SDL_SetCursor(sdl_cursor_hidden);
387 } else {
388 SDL_ShowCursor(0);
389 }
390 }
391
392 static void sdl_show_cursor(void)
393 {
394 if (!cursor_hide)
395 return;
396
397 if (!qemu_input_is_absolute() || !qemu_console_is_graphic(NULL)) {
398 SDL_ShowCursor(1);
399 if (guest_cursor &&
400 (gui_grab || qemu_input_is_absolute() || absolute_enabled))
401 SDL_SetCursor(guest_sprite);
402 else
403 SDL_SetCursor(sdl_cursor_normal);
404 }
405 }
406
407 static void sdl_grab_start(void)
408 {
409 /*
410 * If the application is not active, do not try to enter grab state. This
411 * prevents 'SDL_WM_GrabInput(SDL_GRAB_ON)' from blocking all the
412 * application (SDL bug).
413 */
414 if (!(SDL_GetAppState() & SDL_APPINPUTFOCUS)) {
415 return;
416 }
417 if (guest_cursor) {
418 SDL_SetCursor(guest_sprite);
419 if (!qemu_input_is_absolute() && !absolute_enabled) {
420 SDL_WarpMouse(guest_x, guest_y);
421 }
422 } else
423 sdl_hide_cursor();
424 SDL_WM_GrabInput(SDL_GRAB_ON);
425 gui_grab = 1;
426 sdl_update_caption();
427 }
428
429 static void sdl_grab_end(void)
430 {
431 SDL_WM_GrabInput(SDL_GRAB_OFF);
432 gui_grab = 0;
433 sdl_show_cursor();
434 sdl_update_caption();
435 }
436
437 static void absolute_mouse_grab(void)
438 {
439 int mouse_x, mouse_y;
440
441 SDL_GetMouseState(&mouse_x, &mouse_y);
442 if (mouse_x > 0 && mouse_x < real_screen->w - 1 &&
443 mouse_y > 0 && mouse_y < real_screen->h - 1) {
444 sdl_grab_start();
445 }
446 }
447
448 static void sdl_mouse_mode_change(Notifier *notify, void *data)
449 {
450 if (qemu_input_is_absolute()) {
451 if (!absolute_enabled) {
452 absolute_enabled = 1;
453 if (qemu_console_is_graphic(NULL)) {
454 absolute_mouse_grab();
455 }
456 }
457 } else if (absolute_enabled) {
458 if (!gui_fullscreen) {
459 sdl_grab_end();
460 }
461 absolute_enabled = 0;
462 }
463 }
464
465 static void sdl_send_mouse_event(int dx, int dy, int x, int y, int state)
466 {
467 static uint32_t bmap[INPUT_BUTTON_MAX] = {
468 [INPUT_BUTTON_LEFT] = SDL_BUTTON(SDL_BUTTON_LEFT),
469 [INPUT_BUTTON_MIDDLE] = SDL_BUTTON(SDL_BUTTON_MIDDLE),
470 [INPUT_BUTTON_RIGHT] = SDL_BUTTON(SDL_BUTTON_RIGHT),
471 [INPUT_BUTTON_WHEEL_UP] = SDL_BUTTON(SDL_BUTTON_WHEELUP),
472 [INPUT_BUTTON_WHEEL_DOWN] = SDL_BUTTON(SDL_BUTTON_WHEELDOWN),
473 };
474 static uint32_t prev_state;
475
476 if (prev_state != state) {
477 qemu_input_update_buttons(dcl->con, bmap, prev_state, state);
478 prev_state = state;
479 }
480
481 if (qemu_input_is_absolute()) {
482 qemu_input_queue_abs(dcl->con, INPUT_AXIS_X, x,
483 real_screen->w);
484 qemu_input_queue_abs(dcl->con, INPUT_AXIS_Y, y,
485 real_screen->h);
486 } else {
487 if (guest_cursor) {
488 x -= guest_x;
489 y -= guest_y;
490 guest_x += x;
491 guest_y += y;
492 dx = x;
493 dy = y;
494 }
495 qemu_input_queue_rel(dcl->con, INPUT_AXIS_X, dx);
496 qemu_input_queue_rel(dcl->con, INPUT_AXIS_Y, dy);
497 }
498 qemu_input_event_sync();
499 }
500
501 static void sdl_scale(int width, int height)
502 {
503 int bpp = real_screen->format->BitsPerPixel;
504
505 #ifdef DEBUG_SDL
506 printf("SDL: Scaling to %dx%d bpp %d\n", width, height, bpp);
507 #endif
508
509 if (bpp != 16 && bpp != 32) {
510 bpp = 32;
511 }
512 do_sdl_resize(width, height, bpp);
513 scaling_active = 1;
514 }
515
516 static void toggle_full_screen(void)
517 {
518 int width = surface_width(surface);
519 int height = surface_height(surface);
520 int bpp = surface_bits_per_pixel(surface);
521
522 gui_fullscreen = !gui_fullscreen;
523 if (gui_fullscreen) {
524 gui_saved_width = real_screen->w;
525 gui_saved_height = real_screen->h;
526 gui_saved_scaling = scaling_active;
527
528 do_sdl_resize(width, height, bpp);
529 scaling_active = 0;
530
531 gui_saved_grab = gui_grab;
532 sdl_grab_start();
533 } else {
534 if (gui_saved_scaling) {
535 sdl_scale(gui_saved_width, gui_saved_height);
536 } else {
537 do_sdl_resize(width, height, 0);
538 }
539 if (!gui_saved_grab || !qemu_console_is_graphic(NULL)) {
540 sdl_grab_end();
541 }
542 }
543 graphic_hw_invalidate(NULL);
544 graphic_hw_update(NULL);
545 }
546
547 static void handle_keydown(SDL_Event *ev)
548 {
549 int mod_state;
550 int keycode;
551
552 if (alt_grab) {
553 mod_state = (SDL_GetModState() & (gui_grab_code | KMOD_LSHIFT)) ==
554 (gui_grab_code | KMOD_LSHIFT);
555 } else if (ctrl_grab) {
556 mod_state = (SDL_GetModState() & KMOD_RCTRL) == KMOD_RCTRL;
557 } else {
558 mod_state = (SDL_GetModState() & gui_grab_code) == gui_grab_code;
559 }
560 gui_key_modifier_pressed = mod_state;
561
562 if (gui_key_modifier_pressed) {
563 keycode = sdl_keyevent_to_keycode(&ev->key);
564 switch (keycode) {
565 case 0x21: /* 'f' key on US keyboard */
566 toggle_full_screen();
567 gui_keysym = 1;
568 break;
569 case 0x16: /* 'u' key on US keyboard */
570 if (scaling_active) {
571 scaling_active = 0;
572 sdl_switch(dcl, NULL);
573 graphic_hw_invalidate(NULL);
574 graphic_hw_update(NULL);
575 }
576 gui_keysym = 1;
577 break;
578 case 0x02 ... 0x0a: /* '1' to '9' keys */
579 /* Reset the modifiers sent to the current console */
580 reset_keys();
581 console_select(keycode - 0x02);
582 gui_keysym = 1;
583 if (gui_fullscreen) {
584 break;
585 }
586 if (!qemu_console_is_graphic(NULL)) {
587 /* release grab if going to a text console */
588 if (gui_grab) {
589 sdl_grab_end();
590 } else if (absolute_enabled) {
591 sdl_show_cursor();
592 }
593 } else if (absolute_enabled) {
594 sdl_hide_cursor();
595 absolute_mouse_grab();
596 }
597 break;
598 case 0x1b: /* '+' */
599 case 0x35: /* '-' */
600 if (!gui_fullscreen) {
601 int width = MAX(real_screen->w + (keycode == 0x1b ? 50 : -50),
602 160);
603 int height = (surface_height(surface) * width) /
604 surface_width(surface);
605
606 sdl_scale(width, height);
607 graphic_hw_invalidate(NULL);
608 graphic_hw_update(NULL);
609 gui_keysym = 1;
610 }
611 default:
612 break;
613 }
614 } else if (!qemu_console_is_graphic(NULL)) {
615 int keysym = 0;
616
617 if (ev->key.keysym.mod & (KMOD_LCTRL | KMOD_RCTRL)) {
618 switch (ev->key.keysym.sym) {
619 case SDLK_UP:
620 keysym = QEMU_KEY_CTRL_UP;
621 break;
622 case SDLK_DOWN:
623 keysym = QEMU_KEY_CTRL_DOWN;
624 break;
625 case SDLK_LEFT:
626 keysym = QEMU_KEY_CTRL_LEFT;
627 break;
628 case SDLK_RIGHT:
629 keysym = QEMU_KEY_CTRL_RIGHT;
630 break;
631 case SDLK_HOME:
632 keysym = QEMU_KEY_CTRL_HOME;
633 break;
634 case SDLK_END:
635 keysym = QEMU_KEY_CTRL_END;
636 break;
637 case SDLK_PAGEUP:
638 keysym = QEMU_KEY_CTRL_PAGEUP;
639 break;
640 case SDLK_PAGEDOWN:
641 keysym = QEMU_KEY_CTRL_PAGEDOWN;
642 break;
643 default:
644 break;
645 }
646 } else {
647 switch (ev->key.keysym.sym) {
648 case SDLK_UP:
649 keysym = QEMU_KEY_UP;
650 break;
651 case SDLK_DOWN:
652 keysym = QEMU_KEY_DOWN;
653 break;
654 case SDLK_LEFT:
655 keysym = QEMU_KEY_LEFT;
656 break;
657 case SDLK_RIGHT:
658 keysym = QEMU_KEY_RIGHT;
659 break;
660 case SDLK_HOME:
661 keysym = QEMU_KEY_HOME;
662 break;
663 case SDLK_END:
664 keysym = QEMU_KEY_END;
665 break;
666 case SDLK_PAGEUP:
667 keysym = QEMU_KEY_PAGEUP;
668 break;
669 case SDLK_PAGEDOWN:
670 keysym = QEMU_KEY_PAGEDOWN;
671 break;
672 case SDLK_BACKSPACE:
673 keysym = QEMU_KEY_BACKSPACE;
674 break;
675 case SDLK_DELETE:
676 keysym = QEMU_KEY_DELETE;
677 break;
678 default:
679 break;
680 }
681 }
682 if (keysym) {
683 kbd_put_keysym(keysym);
684 } else if (ev->key.keysym.unicode != 0) {
685 kbd_put_keysym(ev->key.keysym.unicode);
686 }
687 }
688 if (qemu_console_is_graphic(NULL) && !gui_keysym) {
689 sdl_process_key(&ev->key);
690 }
691 }
692
693 static void handle_keyup(SDL_Event *ev)
694 {
695 int mod_state;
696
697 if (!alt_grab) {
698 mod_state = (ev->key.keysym.mod & gui_grab_code);
699 } else {
700 mod_state = (ev->key.keysym.mod & (gui_grab_code | KMOD_LSHIFT));
701 }
702 if (!mod_state && gui_key_modifier_pressed) {
703 gui_key_modifier_pressed = 0;
704 if (gui_keysym == 0) {
705 /* exit/enter grab if pressing Ctrl-Alt */
706 if (!gui_grab) {
707 if (qemu_console_is_graphic(NULL)) {
708 sdl_grab_start();
709 }
710 } else if (!gui_fullscreen) {
711 sdl_grab_end();
712 }
713 /* SDL does not send back all the modifiers key, so we must
714 * correct it. */
715 reset_keys();
716 return;
717 }
718 gui_keysym = 0;
719 }
720 if (qemu_console_is_graphic(NULL) && !gui_keysym) {
721 sdl_process_key(&ev->key);
722 }
723 }
724
725 static void handle_mousemotion(SDL_Event *ev)
726 {
727 int max_x, max_y;
728
729 if (qemu_console_is_graphic(NULL) &&
730 (qemu_input_is_absolute() || absolute_enabled)) {
731 max_x = real_screen->w - 1;
732 max_y = real_screen->h - 1;
733 if (gui_grab && (ev->motion.x == 0 || ev->motion.y == 0 ||
734 ev->motion.x == max_x || ev->motion.y == max_y)) {
735 sdl_grab_end();
736 }
737 if (!gui_grab &&
738 (ev->motion.x > 0 && ev->motion.x < max_x &&
739 ev->motion.y > 0 && ev->motion.y < max_y)) {
740 sdl_grab_start();
741 }
742 }
743 if (gui_grab || qemu_input_is_absolute() || absolute_enabled) {
744 sdl_send_mouse_event(ev->motion.xrel, ev->motion.yrel,
745 ev->motion.x, ev->motion.y, ev->motion.state);
746 }
747 }
748
749 static void handle_mousebutton(SDL_Event *ev)
750 {
751 int buttonstate = SDL_GetMouseState(NULL, NULL);
752 SDL_MouseButtonEvent *bev;
753
754 if (!qemu_console_is_graphic(NULL)) {
755 return;
756 }
757
758 bev = &ev->button;
759 if (!gui_grab && !qemu_input_is_absolute()) {
760 if (ev->type == SDL_MOUSEBUTTONUP && bev->button == SDL_BUTTON_LEFT) {
761 /* start grabbing all events */
762 sdl_grab_start();
763 }
764 } else {
765 if (ev->type == SDL_MOUSEBUTTONDOWN) {
766 buttonstate |= SDL_BUTTON(bev->button);
767 } else {
768 buttonstate &= ~SDL_BUTTON(bev->button);
769 }
770 sdl_send_mouse_event(0, 0, bev->x, bev->y, buttonstate);
771 }
772 }
773
774 static void handle_activation(SDL_Event *ev)
775 {
776 #ifdef _WIN32
777 /* Disable grab if the window no longer has the focus
778 * (Windows-only workaround) */
779 if (gui_grab && ev->active.state == SDL_APPINPUTFOCUS &&
780 !ev->active.gain && !gui_fullscreen) {
781 sdl_grab_end();
782 }
783 #endif
784 if (!gui_grab && ev->active.gain && qemu_console_is_graphic(NULL) &&
785 (qemu_input_is_absolute() || absolute_enabled)) {
786 absolute_mouse_grab();
787 }
788 if (ev->active.state & SDL_APPACTIVE) {
789 if (ev->active.gain) {
790 /* Back to default interval */
791 update_displaychangelistener(dcl, GUI_REFRESH_INTERVAL_DEFAULT);
792 } else {
793 /* Sleeping interval. Not using the long default here as
794 * sdl_refresh does not only update the guest screen, but
795 * also checks for gui events. */
796 update_displaychangelistener(dcl, 500);
797 }
798 }
799 }
800
801 static void sdl_refresh(DisplayChangeListener *dcl)
802 {
803 SDL_Event ev1, *ev = &ev1;
804
805 if (last_vm_running != runstate_is_running()) {
806 last_vm_running = runstate_is_running();
807 sdl_update_caption();
808 }
809
810 graphic_hw_update(NULL);
811 SDL_EnableUNICODE(!qemu_console_is_graphic(NULL));
812
813 while (SDL_PollEvent(ev)) {
814 switch (ev->type) {
815 case SDL_VIDEOEXPOSE:
816 sdl_update(dcl, 0, 0, real_screen->w, real_screen->h);
817 break;
818 case SDL_KEYDOWN:
819 handle_keydown(ev);
820 break;
821 case SDL_KEYUP:
822 handle_keyup(ev);
823 break;
824 case SDL_QUIT:
825 if (!no_quit) {
826 no_shutdown = 0;
827 qemu_system_shutdown_request();
828 }
829 break;
830 case SDL_MOUSEMOTION:
831 handle_mousemotion(ev);
832 break;
833 case SDL_MOUSEBUTTONDOWN:
834 case SDL_MOUSEBUTTONUP:
835 handle_mousebutton(ev);
836 break;
837 case SDL_ACTIVEEVENT:
838 handle_activation(ev);
839 break;
840 case SDL_VIDEORESIZE:
841 sdl_scale(ev->resize.w, ev->resize.h);
842 graphic_hw_invalidate(NULL);
843 graphic_hw_update(NULL);
844 break;
845 default:
846 break;
847 }
848 }
849 }
850
851 static void sdl_mouse_warp(DisplayChangeListener *dcl,
852 int x, int y, int on)
853 {
854 if (on) {
855 if (!guest_cursor)
856 sdl_show_cursor();
857 if (gui_grab || qemu_input_is_absolute() || absolute_enabled) {
858 SDL_SetCursor(guest_sprite);
859 if (!qemu_input_is_absolute() && !absolute_enabled) {
860 SDL_WarpMouse(x, y);
861 }
862 }
863 } else if (gui_grab)
864 sdl_hide_cursor();
865 guest_cursor = on;
866 guest_x = x, guest_y = y;
867 }
868
869 static void sdl_mouse_define(DisplayChangeListener *dcl,
870 QEMUCursor *c)
871 {
872 uint8_t *image, *mask;
873 int bpl;
874
875 if (guest_sprite)
876 SDL_FreeCursor(guest_sprite);
877
878 bpl = cursor_get_mono_bpl(c);
879 image = g_malloc0(bpl * c->height);
880 mask = g_malloc0(bpl * c->height);
881 cursor_get_mono_image(c, 0x000000, image);
882 cursor_get_mono_mask(c, 0, mask);
883 guest_sprite = SDL_CreateCursor(image, mask, c->width, c->height,
884 c->hot_x, c->hot_y);
885 g_free(image);
886 g_free(mask);
887
888 if (guest_cursor &&
889 (gui_grab || qemu_input_is_absolute() || absolute_enabled))
890 SDL_SetCursor(guest_sprite);
891 }
892
893 static void sdl_cleanup(void)
894 {
895 if (guest_sprite)
896 SDL_FreeCursor(guest_sprite);
897 SDL_QuitSubSystem(SDL_INIT_VIDEO);
898 }
899
900 static const DisplayChangeListenerOps dcl_ops = {
901 .dpy_name = "sdl",
902 .dpy_gfx_update = sdl_update,
903 .dpy_gfx_switch = sdl_switch,
904 .dpy_gfx_check_format = sdl_check_format,
905 .dpy_refresh = sdl_refresh,
906 .dpy_mouse_set = sdl_mouse_warp,
907 .dpy_cursor_define = sdl_mouse_define,
908 };
909
910 void sdl_display_init(DisplayState *ds, int full_screen, int no_frame)
911 {
912 int flags;
913 uint8_t data = 0;
914 const SDL_VideoInfo *vi;
915 char *filename;
916
917 #if defined(__APPLE__)
918 /* always use generic keymaps */
919 if (!keyboard_layout)
920 keyboard_layout = "en-us";
921 #endif
922 if(keyboard_layout) {
923 kbd_layout = init_keyboard_layout(name2keysym, keyboard_layout);
924 if (!kbd_layout)
925 exit(1);
926 }
927
928 if (no_frame)
929 gui_noframe = 1;
930
931 if (!full_screen) {
932 setenv("SDL_VIDEO_ALLOW_SCREENSAVER", "1", 0);
933 }
934 #ifdef __linux__
935 /* on Linux, SDL may use fbcon|directfb|svgalib when run without
936 * accessible $DISPLAY to open X11 window. This is often the case
937 * when qemu is run using sudo. But in this case, and when actually
938 * run in X11 environment, SDL fights with X11 for the video card,
939 * making current display unavailable, often until reboot.
940 * So make x11 the default SDL video driver if this variable is unset.
941 * This is a bit hackish but saves us from bigger problem.
942 * Maybe it's a good idea to fix this in SDL instead.
943 */
944 setenv("SDL_VIDEODRIVER", "x11", 0);
945 #endif
946
947 /* Enable normal up/down events for Caps-Lock and Num-Lock keys.
948 * This requires SDL >= 1.2.14. */
949 setenv("SDL_DISABLE_LOCK_KEYS", "1", 1);
950
951 flags = SDL_INIT_VIDEO | SDL_INIT_NOPARACHUTE;
952 if (SDL_Init (flags)) {
953 fprintf(stderr, "Could not initialize SDL(%s) - exiting\n",
954 SDL_GetError());
955 exit(1);
956 }
957 vi = SDL_GetVideoInfo();
958 host_format = *(vi->vfmt);
959
960 /* Load a 32x32x4 image. White pixels are transparent. */
961 filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, "qemu-icon.bmp");
962 if (filename) {
963 SDL_Surface *image = SDL_LoadBMP(filename);
964 if (image) {
965 uint32_t colorkey = SDL_MapRGB(image->format, 255, 255, 255);
966 SDL_SetColorKey(image, SDL_SRCCOLORKEY, colorkey);
967 SDL_WM_SetIcon(image, NULL);
968 }
969 g_free(filename);
970 }
971
972 if (full_screen) {
973 gui_fullscreen = 1;
974 sdl_grab_start();
975 }
976
977 dcl = g_malloc0(sizeof(DisplayChangeListener));
978 dcl->ops = &dcl_ops;
979 register_displaychangelistener(dcl);
980
981 mouse_mode_notifier.notify = sdl_mouse_mode_change;
982 qemu_add_mouse_mode_change_notifier(&mouse_mode_notifier);
983
984 sdl_update_caption();
985 SDL_EnableKeyRepeat(250, 50);
986 gui_grab = 0;
987
988 sdl_cursor_hidden = SDL_CreateCursor(&data, &data, 8, 1, 0, 0);
989 sdl_cursor_normal = SDL_GetCursor();
990
991 atexit(sdl_cleanup);
992 }