]> Git Repo - qemu.git/blob - ui/sdl2.c
Merge remote-tracking branch 'remotes/kraxel/tags/pull-gtk-6' into staging
[qemu.git] / ui / sdl2.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 /* Ported SDL 1.2 code to 2.0 by Dave Airlie. */
25
26 /* Avoid compiler warning because macro is redefined in SDL_syswm.h. */
27 #undef WIN32_LEAN_AND_MEAN
28
29 #include <SDL.h>
30
31 #if SDL_MAJOR_VERSION == 2
32 #include <SDL_syswm.h>
33
34 #include "qemu-common.h"
35 #include "ui/console.h"
36 #include "ui/input.h"
37 #include "sysemu/sysemu.h"
38 #include "sdl_zoom.h"
39
40 #include "sdl2-keymap.h"
41
42 static int sdl2_num_outputs;
43 static struct sdl2_state {
44     DisplayChangeListener dcl;
45     DisplaySurface *surface;
46     SDL_Texture *texture;
47     SDL_Window *real_window;
48     SDL_Renderer *real_renderer;
49     int idx;
50     int last_vm_running; /* per console for caption reasons */
51     int x, y;
52 } *sdl2_console;
53
54 static SDL_Surface *guest_sprite_surface;
55 static int gui_grab; /* if true, all keyboard/mouse events are grabbed */
56
57 static bool gui_saved_scaling;
58 static int gui_saved_width;
59 static int gui_saved_height;
60 static int gui_saved_grab;
61 static int gui_fullscreen;
62 static int gui_noframe;
63 static int gui_key_modifier_pressed;
64 static int gui_keysym;
65 static int gui_grab_code = KMOD_LALT | KMOD_LCTRL;
66 static uint8_t modifiers_state[SDL_NUM_SCANCODES];
67 static SDL_Cursor *sdl_cursor_normal;
68 static SDL_Cursor *sdl_cursor_hidden;
69 static int absolute_enabled;
70 static int guest_cursor;
71 static int guest_x, guest_y;
72 static SDL_Cursor *guest_sprite;
73 static int scaling_active;
74 static Notifier mouse_mode_notifier;
75
76 static void sdl_update_caption(struct sdl2_state *scon);
77
78 static struct sdl2_state *get_scon_from_window(uint32_t window_id)
79 {
80     int i;
81     for (i = 0; i < sdl2_num_outputs; i++) {
82         if (sdl2_console[i].real_window == SDL_GetWindowFromID(window_id)) {
83             return &sdl2_console[i];
84         }
85     }
86     return NULL;
87 }
88
89 static void sdl_update(DisplayChangeListener *dcl,
90                        int x, int y, int w, int h)
91 {
92     struct sdl2_state *scon = container_of(dcl, struct sdl2_state, dcl);
93     SDL_Rect rect;
94     DisplaySurface *surf = qemu_console_surface(dcl->con);
95
96     if (!surf) {
97         return;
98     }
99     if (!scon->texture) {
100         return;
101     }
102
103     rect.x = x;
104     rect.y = y;
105     rect.w = w;
106     rect.h = h;
107
108     SDL_UpdateTexture(scon->texture, NULL, surface_data(surf),
109                       surface_stride(surf));
110     SDL_RenderCopy(scon->real_renderer, scon->texture, &rect, &rect);
111     SDL_RenderPresent(scon->real_renderer);
112 }
113
114 static void do_sdl_resize(struct sdl2_state *scon, int width, int height,
115                           int bpp)
116 {
117     int flags;
118
119     if (scon->real_window && scon->real_renderer) {
120         if (width && height) {
121             SDL_RenderSetLogicalSize(scon->real_renderer, width, height);
122             SDL_SetWindowSize(scon->real_window, width, height);
123         } else {
124             SDL_DestroyRenderer(scon->real_renderer);
125             SDL_DestroyWindow(scon->real_window);
126             scon->real_renderer = NULL;
127             scon->real_window = NULL;
128         }
129     } else {
130         if (!width || !height) {
131             return;
132         }
133         flags = 0;
134         if (gui_fullscreen) {
135             flags |= SDL_WINDOW_FULLSCREEN;
136         } else {
137             flags |= SDL_WINDOW_RESIZABLE;
138         }
139
140         scon->real_window = SDL_CreateWindow("", SDL_WINDOWPOS_UNDEFINED,
141                                              SDL_WINDOWPOS_UNDEFINED,
142                                              width, height, flags);
143         scon->real_renderer = SDL_CreateRenderer(scon->real_window, -1, 0);
144         sdl_update_caption(scon);
145     }
146 }
147
148 static void sdl_switch(DisplayChangeListener *dcl,
149                        DisplaySurface *new_surface)
150 {
151     struct sdl2_state *scon = container_of(dcl, struct sdl2_state, dcl);
152     int format = 0;
153     int idx = scon->idx;
154     DisplaySurface *old_surface = scon->surface;
155
156     /* temporary hack: allows to call sdl_switch to handle scaling changes */
157     if (new_surface) {
158         scon->surface = new_surface;
159     }
160
161     if (!new_surface && idx > 0) {
162         scon->surface = NULL;
163     }
164
165     if (new_surface == NULL) {
166         do_sdl_resize(scon, 0, 0, 0);
167     } else {
168         do_sdl_resize(scon, surface_width(scon->surface),
169                       surface_height(scon->surface), 0);
170     }
171
172     if (old_surface && scon->texture) {
173         SDL_DestroyTexture(scon->texture);
174         scon->texture = NULL;
175     }
176
177     if (new_surface) {
178         if (!scon->texture) {
179             if (surface_bits_per_pixel(scon->surface) == 16) {
180                 format = SDL_PIXELFORMAT_RGB565;
181             } else if (surface_bits_per_pixel(scon->surface) == 32) {
182                 format = SDL_PIXELFORMAT_ARGB8888;
183             }
184
185             scon->texture = SDL_CreateTexture(scon->real_renderer, format,
186                                               SDL_TEXTUREACCESS_STREAMING,
187                                               surface_width(new_surface),
188                                               surface_height(new_surface));
189         }
190     }
191 }
192
193 static void reset_keys(void)
194 {
195     int i;
196
197     for (i = 0; i < 256; i++) {
198         if (modifiers_state[i]) {
199             int qcode = sdl2_scancode_to_qcode[i];
200             qemu_input_event_send_key_qcode(NULL, qcode, false);
201             modifiers_state[i] = 0;
202         }
203     }
204 }
205
206 static void sdl_process_key(SDL_KeyboardEvent *ev)
207 {
208     int qcode = sdl2_scancode_to_qcode[ev->keysym.scancode];
209
210     switch (ev->keysym.scancode) {
211 #if 0
212     case SDL_SCANCODE_NUMLOCKCLEAR:
213     case SDL_SCANCODE_CAPSLOCK:
214         /* SDL does not send the key up event, so we generate it */
215         qemu_input_event_send_key_qcode(NULL, qcode, true);
216         qemu_input_event_send_key_qcode(NULL, qcode, false);
217         return;
218 #endif
219     case SDL_SCANCODE_LCTRL:
220     case SDL_SCANCODE_LSHIFT:
221     case SDL_SCANCODE_LALT:
222     case SDL_SCANCODE_LGUI:
223     case SDL_SCANCODE_RCTRL:
224     case SDL_SCANCODE_RSHIFT:
225     case SDL_SCANCODE_RALT:
226     case SDL_SCANCODE_RGUI:
227         if (ev->type == SDL_KEYUP) {
228             modifiers_state[ev->keysym.scancode] = 0;
229         } else {
230             modifiers_state[ev->keysym.scancode] = 1;
231         }
232         /* fall though */
233     default:
234         qemu_input_event_send_key_qcode(NULL, qcode,
235                                         ev->type == SDL_KEYDOWN);
236     }
237 }
238
239 static void sdl_update_caption(struct sdl2_state *scon)
240 {
241     char win_title[1024];
242     char icon_title[1024];
243     const char *status = "";
244
245     if (!runstate_is_running()) {
246         status = " [Stopped]";
247     } else if (gui_grab) {
248         if (alt_grab) {
249             status = " - Press Ctrl-Alt-Shift to exit mouse grab";
250         } else if (ctrl_grab) {
251             status = " - Press Right-Ctrl to exit mouse grab";
252         } else {
253             status = " - Press Ctrl-Alt to exit mouse grab";
254         }
255     }
256
257     if (qemu_name) {
258         snprintf(win_title, sizeof(win_title), "QEMU (%s-%d)%s", qemu_name,
259                  scon->idx, status);
260         snprintf(icon_title, sizeof(icon_title), "QEMU (%s)", qemu_name);
261     } else {
262         snprintf(win_title, sizeof(win_title), "QEMU%s", status);
263         snprintf(icon_title, sizeof(icon_title), "QEMU");
264     }
265
266     if (scon->real_window) {
267         SDL_SetWindowTitle(scon->real_window, win_title);
268     }
269 }
270
271 static void sdl_hide_cursor(void)
272 {
273     if (!cursor_hide) {
274         return;
275     }
276
277     if (qemu_input_is_absolute()) {
278         SDL_ShowCursor(1);
279         SDL_SetCursor(sdl_cursor_hidden);
280     } else {
281         SDL_SetRelativeMouseMode(SDL_TRUE);
282     }
283 }
284
285 static void sdl_show_cursor(void)
286 {
287     if (!cursor_hide) {
288         return;
289     }
290
291     if (!qemu_input_is_absolute()) {
292         SDL_SetRelativeMouseMode(SDL_FALSE);
293         SDL_ShowCursor(1);
294         if (guest_cursor &&
295             (gui_grab || qemu_input_is_absolute() || absolute_enabled)) {
296             SDL_SetCursor(guest_sprite);
297         } else {
298             SDL_SetCursor(sdl_cursor_normal);
299         }
300     }
301 }
302
303 static void sdl_grab_start(struct sdl2_state *scon)
304 {
305     /*
306      * If the application is not active, do not try to enter grab state. This
307      * prevents 'SDL_WM_GrabInput(SDL_GRAB_ON)' from blocking all the
308      * application (SDL bug).
309      */
310     if (!(SDL_GetWindowFlags(scon->real_window) & SDL_WINDOW_INPUT_FOCUS)) {
311         return;
312     }
313     if (guest_cursor) {
314         SDL_SetCursor(guest_sprite);
315         if (!qemu_input_is_absolute() && !absolute_enabled) {
316             SDL_WarpMouseInWindow(scon->real_window, guest_x, guest_y);
317         }
318     } else {
319         sdl_hide_cursor();
320     }
321     SDL_SetWindowGrab(scon->real_window, SDL_TRUE);
322     gui_grab = 1;
323     sdl_update_caption(scon);
324 }
325
326 static void sdl_grab_end(struct sdl2_state *scon)
327 {
328     SDL_SetWindowGrab(scon->real_window, SDL_FALSE);
329     gui_grab = 0;
330     sdl_show_cursor();
331     sdl_update_caption(scon);
332 }
333
334 static void absolute_mouse_grab(struct sdl2_state *scon)
335 {
336     int mouse_x, mouse_y;
337     int scr_w, scr_h;
338     SDL_GetMouseState(&mouse_x, &mouse_y);
339     SDL_GetWindowSize(scon->real_window, &scr_w, &scr_h);
340     if (mouse_x > 0 && mouse_x < scr_w - 1 &&
341         mouse_y > 0 && mouse_y < scr_h - 1) {
342         sdl_grab_start(scon);
343     }
344 }
345
346 static void sdl_mouse_mode_change(Notifier *notify, void *data)
347 {
348     if (qemu_input_is_absolute()) {
349         if (!absolute_enabled) {
350             absolute_enabled = 1;
351             absolute_mouse_grab(&sdl2_console[0]);
352         }
353     } else if (absolute_enabled) {
354         if (!gui_fullscreen) {
355             sdl_grab_end(&sdl2_console[0]);
356         }
357         absolute_enabled = 0;
358     }
359 }
360
361 static void sdl_send_mouse_event(struct sdl2_state *scon, int dx, int dy,
362                                  int x, int y, int state)
363 {
364     static uint32_t bmap[INPUT_BUTTON_MAX] = {
365         [INPUT_BUTTON_LEFT]       = SDL_BUTTON(SDL_BUTTON_LEFT),
366         [INPUT_BUTTON_MIDDLE]     = SDL_BUTTON(SDL_BUTTON_MIDDLE),
367         [INPUT_BUTTON_RIGHT]      = SDL_BUTTON(SDL_BUTTON_RIGHT),
368     };
369     static uint32_t prev_state;
370
371     if (prev_state != state) {
372         qemu_input_update_buttons(scon->dcl.con, bmap, prev_state, state);
373         prev_state = state;
374     }
375
376     if (qemu_input_is_absolute()) {
377         int scr_w, scr_h;
378         int max_w = 0, max_h = 0;
379         int off_x = 0, off_y = 0;
380         int cur_off_x = 0, cur_off_y = 0;
381         int i;
382
383         for (i = 0; i < sdl2_num_outputs; i++) {
384             struct sdl2_state *thiscon = &sdl2_console[i];
385             if (thiscon->real_window && thiscon->surface) {
386                 SDL_GetWindowSize(thiscon->real_window, &scr_w, &scr_h);
387                 cur_off_x = thiscon->x;
388                 cur_off_y = thiscon->y;
389                 if (scr_w + cur_off_x > max_w) {
390                     max_w = scr_w + cur_off_x;
391                 }
392                 if (scr_h + cur_off_y > max_h) {
393                     max_h = scr_h + cur_off_y;
394                 }
395                 if (i == scon->idx) {
396                     off_x = cur_off_x;
397                     off_y = cur_off_y;
398                 }
399             }
400         }
401         qemu_input_queue_abs(scon->dcl.con, INPUT_AXIS_X, off_x + x, max_w);
402         qemu_input_queue_abs(scon->dcl.con, INPUT_AXIS_Y, off_y + y, max_h);
403     } else {
404         if (guest_cursor) {
405             x -= guest_x;
406             y -= guest_y;
407             guest_x += x;
408             guest_y += y;
409             dx = x;
410             dy = y;
411         }
412         qemu_input_queue_rel(scon->dcl.con, INPUT_AXIS_X, dx);
413         qemu_input_queue_rel(scon->dcl.con, INPUT_AXIS_Y, dy);
414     }
415     qemu_input_event_sync();
416 }
417
418 static void sdl_scale(struct sdl2_state *scon, int width, int height)
419 {
420     int bpp = 0;
421     do_sdl_resize(scon, width, height, bpp);
422     scaling_active = 1;
423 }
424
425 static void toggle_full_screen(struct sdl2_state *scon)
426 {
427     int width = surface_width(scon->surface);
428     int height = surface_height(scon->surface);
429     int bpp = surface_bits_per_pixel(scon->surface);
430
431     gui_fullscreen = !gui_fullscreen;
432     if (gui_fullscreen) {
433         SDL_GetWindowSize(scon->real_window,
434                           &gui_saved_width, &gui_saved_height);
435         gui_saved_scaling = scaling_active;
436
437         do_sdl_resize(scon, width, height, bpp);
438         scaling_active = 0;
439
440         gui_saved_grab = gui_grab;
441         sdl_grab_start(scon);
442     } else {
443         if (gui_saved_scaling) {
444             sdl_scale(scon, gui_saved_width, gui_saved_height);
445         } else {
446             do_sdl_resize(scon, width, height, 0);
447         }
448         if (!gui_saved_grab) {
449             sdl_grab_end(scon);
450         }
451     }
452     graphic_hw_invalidate(scon->dcl.con);
453     graphic_hw_update(scon->dcl.con);
454 }
455
456 static void handle_keydown(SDL_Event *ev)
457 {
458     int mod_state;
459     struct sdl2_state *scon = get_scon_from_window(ev->key.windowID);
460
461     if (alt_grab) {
462         mod_state = (SDL_GetModState() & (gui_grab_code | KMOD_LSHIFT)) ==
463             (gui_grab_code | KMOD_LSHIFT);
464     } else if (ctrl_grab) {
465         mod_state = (SDL_GetModState() & KMOD_RCTRL) == KMOD_RCTRL;
466     } else {
467         mod_state = (SDL_GetModState() & gui_grab_code) == gui_grab_code;
468     }
469     gui_key_modifier_pressed = mod_state;
470
471     if (gui_key_modifier_pressed) {
472         switch (ev->key.keysym.scancode) {
473         case SDL_SCANCODE_F:
474             toggle_full_screen(scon);
475             gui_keysym = 1;
476             break;
477         case SDL_SCANCODE_U:
478             if (scaling_active) {
479                 scaling_active = 0;
480                 sdl_switch(&scon->dcl, NULL);
481                 graphic_hw_invalidate(scon->dcl.con);
482                 graphic_hw_update(scon->dcl.con);
483             }
484             gui_keysym = 1;
485             break;
486         case SDL_SCANCODE_KP_PLUS:
487         case SDL_SCANCODE_KP_MINUS:
488             if (!gui_fullscreen) {
489                 int scr_w, scr_h;
490                 int width, height;
491                 SDL_GetWindowSize(scon->real_window, &scr_w, &scr_h);
492
493                 width = MAX(scr_w + (ev->key.keysym.scancode ==
494                                      SDL_SCANCODE_KP_PLUS ? 50 : -50),
495                             160);
496                 height = (surface_height(scon->surface) * width) /
497                     surface_width(scon->surface);
498
499                 sdl_scale(scon, width, height);
500                 graphic_hw_invalidate(NULL);
501                 graphic_hw_update(NULL);
502                 gui_keysym = 1;
503             }
504         default:
505             break;
506         }
507     }
508     if (!gui_keysym) {
509         sdl_process_key(&ev->key);
510     }
511 }
512
513 static void handle_keyup(SDL_Event *ev)
514 {
515     int mod_state;
516     struct sdl2_state *scon = get_scon_from_window(ev->key.windowID);
517
518     if (!alt_grab) {
519         mod_state = (ev->key.keysym.mod & gui_grab_code);
520     } else {
521         mod_state = (ev->key.keysym.mod & (gui_grab_code | KMOD_LSHIFT));
522     }
523     if (!mod_state && gui_key_modifier_pressed) {
524         gui_key_modifier_pressed = 0;
525         if (gui_keysym == 0) {
526             /* exit/enter grab if pressing Ctrl-Alt */
527             if (!gui_grab) {
528                 sdl_grab_start(scon);
529             } else if (!gui_fullscreen) {
530                 sdl_grab_end(scon);
531             }
532             /* SDL does not send back all the modifiers key, so we must
533              * correct it. */
534             reset_keys();
535             return;
536         }
537         gui_keysym = 0;
538     }
539     if (!gui_keysym) {
540         sdl_process_key(&ev->key);
541     }
542 }
543
544 static void handle_mousemotion(SDL_Event *ev)
545 {
546     int max_x, max_y;
547     struct sdl2_state *scon = get_scon_from_window(ev->key.windowID);
548
549     if (qemu_input_is_absolute() || absolute_enabled) {
550         int scr_w, scr_h;
551         SDL_GetWindowSize(scon->real_window, &scr_w, &scr_h);
552         max_x = scr_w - 1;
553         max_y = scr_h - 1;
554         if (gui_grab && (ev->motion.x == 0 || ev->motion.y == 0 ||
555                          ev->motion.x == max_x || ev->motion.y == max_y)) {
556             sdl_grab_end(scon);
557         }
558         if (!gui_grab &&
559             (ev->motion.x > 0 && ev->motion.x < max_x &&
560              ev->motion.y > 0 && ev->motion.y < max_y)) {
561             sdl_grab_start(scon);
562         }
563     }
564     if (gui_grab || qemu_input_is_absolute() || absolute_enabled) {
565         sdl_send_mouse_event(scon, ev->motion.xrel, ev->motion.yrel,
566                              ev->motion.x, ev->motion.y, ev->motion.state);
567     }
568 }
569
570 static void handle_mousebutton(SDL_Event *ev)
571 {
572     int buttonstate = SDL_GetMouseState(NULL, NULL);
573     SDL_MouseButtonEvent *bev;
574     struct sdl2_state *scon = get_scon_from_window(ev->key.windowID);
575
576     bev = &ev->button;
577     if (!gui_grab && !qemu_input_is_absolute()) {
578         if (ev->type == SDL_MOUSEBUTTONUP && bev->button == SDL_BUTTON_LEFT) {
579             /* start grabbing all events */
580             sdl_grab_start(scon);
581         }
582     } else {
583         if (ev->type == SDL_MOUSEBUTTONDOWN) {
584             buttonstate |= SDL_BUTTON(bev->button);
585         } else {
586             buttonstate &= ~SDL_BUTTON(bev->button);
587         }
588         sdl_send_mouse_event(scon, 0, 0, bev->x, bev->y, buttonstate);
589     }
590 }
591
592 static void handle_mousewheel(SDL_Event *ev)
593 {
594     struct sdl2_state *scon = get_scon_from_window(ev->key.windowID);
595     SDL_MouseWheelEvent *wev = &ev->wheel;
596     InputButton btn;
597
598     if (wev->y > 0) {
599         btn = INPUT_BUTTON_WHEEL_UP;
600     } else if (wev->y < 0) {
601         btn = INPUT_BUTTON_WHEEL_DOWN;
602     } else {
603         return;
604     }
605
606     qemu_input_queue_btn(scon->dcl.con, btn, true);
607     qemu_input_event_sync();
608     qemu_input_queue_btn(scon->dcl.con, btn, false);
609     qemu_input_event_sync();
610 }
611
612 static void handle_windowevent(DisplayChangeListener *dcl, SDL_Event *ev)
613 {
614     int w, h;
615     struct sdl2_state *scon = get_scon_from_window(ev->key.windowID);
616
617     switch (ev->window.event) {
618     case SDL_WINDOWEVENT_RESIZED:
619         sdl_scale(scon, ev->window.data1, ev->window.data2);
620         {
621             QemuUIInfo info;
622             memset(&info, 0, sizeof(info));
623             info.width = ev->window.data1;
624             info.height = ev->window.data2;
625             dpy_set_ui_info(scon->dcl.con, &info);
626         }
627         graphic_hw_invalidate(scon->dcl.con);
628         graphic_hw_update(scon->dcl.con);
629         break;
630     case SDL_WINDOWEVENT_EXPOSED:
631         SDL_GetWindowSize(SDL_GetWindowFromID(ev->window.windowID), &w, &h);
632         sdl_update(dcl, 0, 0, w, h);
633         break;
634     case SDL_WINDOWEVENT_FOCUS_GAINED:
635     case SDL_WINDOWEVENT_ENTER:
636         if (!gui_grab && (qemu_input_is_absolute() || absolute_enabled)) {
637             absolute_mouse_grab(scon);
638         }
639         break;
640     case SDL_WINDOWEVENT_FOCUS_LOST:
641         if (gui_grab && !gui_fullscreen) {
642             sdl_grab_end(scon);
643         }
644         break;
645     case SDL_WINDOWEVENT_RESTORED:
646         update_displaychangelistener(dcl, GUI_REFRESH_INTERVAL_DEFAULT);
647         break;
648     case SDL_WINDOWEVENT_MINIMIZED:
649         update_displaychangelistener(dcl, 500);
650         break;
651     case SDL_WINDOWEVENT_CLOSE:
652         if (!no_quit) {
653             no_shutdown = 0;
654             qemu_system_shutdown_request();
655         }
656         break;
657     }
658 }
659
660 static void sdl_refresh(DisplayChangeListener *dcl)
661 {
662     struct sdl2_state *scon = container_of(dcl, struct sdl2_state, dcl);
663     SDL_Event ev1, *ev = &ev1;
664
665     if (scon->last_vm_running != runstate_is_running()) {
666         scon->last_vm_running = runstate_is_running();
667         sdl_update_caption(scon);
668     }
669
670     graphic_hw_update(dcl->con);
671
672     while (SDL_PollEvent(ev)) {
673         switch (ev->type) {
674         case SDL_KEYDOWN:
675             handle_keydown(ev);
676             break;
677         case SDL_KEYUP:
678             handle_keyup(ev);
679             break;
680         case SDL_QUIT:
681             if (!no_quit) {
682                 no_shutdown = 0;
683                 qemu_system_shutdown_request();
684             }
685             break;
686         case SDL_MOUSEMOTION:
687             handle_mousemotion(ev);
688             break;
689         case SDL_MOUSEBUTTONDOWN:
690         case SDL_MOUSEBUTTONUP:
691             handle_mousebutton(ev);
692             break;
693         case SDL_MOUSEWHEEL:
694             handle_mousewheel(ev);
695             break;
696         case SDL_WINDOWEVENT:
697             handle_windowevent(dcl, ev);
698             break;
699         default:
700             break;
701         }
702     }
703 }
704
705 static void sdl_mouse_warp(DisplayChangeListener *dcl,
706                            int x, int y, int on)
707 {
708     struct sdl2_state *scon = container_of(dcl, struct sdl2_state, dcl);
709     if (on) {
710         if (!guest_cursor) {
711             sdl_show_cursor();
712         }
713         if (gui_grab || qemu_input_is_absolute() || absolute_enabled) {
714             SDL_SetCursor(guest_sprite);
715             if (!qemu_input_is_absolute() && !absolute_enabled) {
716                 SDL_WarpMouseInWindow(scon->real_window, x, y);
717             }
718         }
719     } else if (gui_grab) {
720         sdl_hide_cursor();
721     }
722     guest_cursor = on;
723     guest_x = x, guest_y = y;
724 }
725
726 static void sdl_mouse_define(DisplayChangeListener *dcl,
727                              QEMUCursor *c)
728 {
729
730     if (guest_sprite) {
731         SDL_FreeCursor(guest_sprite);
732     }
733
734     if (guest_sprite_surface) {
735         SDL_FreeSurface(guest_sprite_surface);
736     }
737
738     guest_sprite_surface =
739         SDL_CreateRGBSurfaceFrom(c->data, c->width, c->height, 32, c->width * 4,
740                                  0xff0000, 0x00ff00, 0xff, 0xff000000);
741
742     if (!guest_sprite_surface) {
743         fprintf(stderr, "Failed to make rgb surface from %p\n", c);
744         return;
745     }
746     guest_sprite = SDL_CreateColorCursor(guest_sprite_surface,
747                                          c->hot_x, c->hot_y);
748     if (!guest_sprite) {
749         fprintf(stderr, "Failed to make color cursor from %p\n", c);
750         return;
751     }
752     if (guest_cursor &&
753         (gui_grab || qemu_input_is_absolute() || absolute_enabled)) {
754         SDL_SetCursor(guest_sprite);
755     }
756 }
757
758 static void sdl_cleanup(void)
759 {
760     if (guest_sprite) {
761         SDL_FreeCursor(guest_sprite);
762     }
763     SDL_QuitSubSystem(SDL_INIT_VIDEO);
764 }
765
766 static const DisplayChangeListenerOps dcl_ops = {
767     .dpy_name          = "sdl",
768     .dpy_gfx_update    = sdl_update,
769     .dpy_gfx_switch    = sdl_switch,
770     .dpy_refresh       = sdl_refresh,
771     .dpy_mouse_set     = sdl_mouse_warp,
772     .dpy_cursor_define = sdl_mouse_define,
773 };
774
775 void sdl_display_init(DisplayState *ds, int full_screen, int no_frame)
776 {
777     int flags;
778     uint8_t data = 0;
779     char *filename;
780     int i;
781
782     if (no_frame) {
783         gui_noframe = 1;
784     }
785
786 #ifdef __linux__
787     /* on Linux, SDL may use fbcon|directfb|svgalib when run without
788      * accessible $DISPLAY to open X11 window.  This is often the case
789      * when qemu is run using sudo.  But in this case, and when actually
790      * run in X11 environment, SDL fights with X11 for the video card,
791      * making current display unavailable, often until reboot.
792      * So make x11 the default SDL video driver if this variable is unset.
793      * This is a bit hackish but saves us from bigger problem.
794      * Maybe it's a good idea to fix this in SDL instead.
795      */
796     setenv("SDL_VIDEODRIVER", "x11", 0);
797 #endif
798
799     flags = SDL_INIT_VIDEO | SDL_INIT_NOPARACHUTE;
800     if (SDL_Init(flags)) {
801         fprintf(stderr, "Could not initialize SDL(%s) - exiting\n",
802                 SDL_GetError());
803         exit(1);
804     }
805
806     for (i = 0;; i++) {
807         QemuConsole *con = qemu_console_lookup_by_index(i);
808         if (!con || !qemu_console_is_graphic(con)) {
809             break;
810         }
811     }
812     sdl2_num_outputs = i;
813     sdl2_console = g_new0(struct sdl2_state, sdl2_num_outputs);
814     for (i = 0; i < sdl2_num_outputs; i++) {
815         QemuConsole *con = qemu_console_lookup_by_index(i);
816         sdl2_console[i].dcl.ops = &dcl_ops;
817         sdl2_console[i].dcl.con = con;
818         register_displaychangelistener(&sdl2_console[i].dcl);
819         sdl2_console[i].idx = i;
820     }
821
822     /* Load a 32x32x4 image. White pixels are transparent. */
823     filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, "qemu-icon.bmp");
824     if (filename) {
825         SDL_Surface *image = SDL_LoadBMP(filename);
826         if (image) {
827             uint32_t colorkey = SDL_MapRGB(image->format, 255, 255, 255);
828             SDL_SetColorKey(image, SDL_TRUE, colorkey);
829             SDL_SetWindowIcon(sdl2_console[0].real_window, image);
830         }
831         g_free(filename);
832     }
833
834     if (full_screen) {
835         gui_fullscreen = 1;
836         sdl_grab_start(0);
837     }
838
839     mouse_mode_notifier.notify = sdl_mouse_mode_change;
840     qemu_add_mouse_mode_change_notifier(&mouse_mode_notifier);
841
842     gui_grab = 0;
843
844     sdl_cursor_hidden = SDL_CreateCursor(&data, &data, 8, 1, 0, 0);
845     sdl_cursor_normal = SDL_GetCursor();
846
847     atexit(sdl_cleanup);
848 }
849 #endif
This page took 0.071516 seconds and 4 git commands to generate.