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