qemu/ui/sdl2.c
<<
>>
Prefs
   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
  32static int sdl2_num_outputs;
  33static struct sdl2_console *sdl2_console;
  34
  35static SDL_Surface *guest_sprite_surface;
  36static int gui_grab; /* if true, all keyboard/mouse events are grabbed */
  37
  38static int gui_saved_grab;
  39static int gui_fullscreen;
  40static int gui_noframe;
  41static int gui_key_modifier_pressed;
  42static int gui_keysym;
  43static int gui_grab_code = KMOD_LALT | KMOD_LCTRL;
  44static SDL_Cursor *sdl_cursor_normal;
  45static SDL_Cursor *sdl_cursor_hidden;
  46static int absolute_enabled;
  47static int guest_cursor;
  48static int guest_x, guest_y;
  49static SDL_Cursor *guest_sprite;
  50static Notifier mouse_mode_notifier;
  51
  52static void sdl_update_caption(struct sdl2_console *scon);
  53
  54static 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
  65void 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
  95void 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
 107void 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
 118static 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
 129static 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
 161static 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
 175static 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
 193static 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
 221static 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
 229static 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
 241static 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
 256static 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
 313static 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
 330static 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
 410static 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
 441static 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
 452static 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
 478static 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
 500static 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
 520static 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
 578void 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
 623static 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
 644static 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
 676static void sdl_cleanup(void)
 677{
 678    if (guest_sprite) {
 679        SDL_FreeCursor(guest_sprite);
 680    }
 681    SDL_QuitSubSystem(SDL_INIT_VIDEO);
 682}
 683
 684static 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
 695static 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
 706void 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
 723void 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