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