cachepc-qemu

Fork of AMDESE/qemu with changes for cachepc side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-qemu
Log | Files | Refs | Submodules | LICENSE | sfeed.txt

sdl2.c (27547B)


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