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