qemu

FORK: QEMU emulator
git clone https://git.neptards.moe/neptards/qemu.git
Log | Files | Refs | Submodules | LICENSE

sdl2.c (28439B)


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