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