qemu/ui/sdl.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
  25/* Avoid compiler warning because macro is redefined in SDL_syswm.h. */
  26#undef WIN32_LEAN_AND_MEAN
  27
  28#include "qemu/osdep.h"
  29#include <SDL.h>
  30#include <SDL_syswm.h>
  31
  32#include "qemu-common.h"
  33#include "qemu/cutils.h"
  34#include "ui/console.h"
  35#include "ui/input.h"
  36#include "sysemu/sysemu.h"
  37#ifndef WIN32
  38#include "x_keymap.h"
  39#endif
  40#include "sdl_zoom.h"
  41
  42static DisplayChangeListener *dcl;
  43static DisplaySurface *surface;
  44static DisplayOptions *opts;
  45static SDL_Surface *real_screen;
  46static SDL_Surface *guest_screen = NULL;
  47static int gui_grab; /* if true, all keyboard/mouse events are grabbed */
  48static int last_vm_running;
  49static bool gui_saved_scaling;
  50static int gui_saved_width;
  51static int gui_saved_height;
  52static int gui_saved_grab;
  53static int gui_fullscreen;
  54static int gui_key_modifier_pressed;
  55static int gui_keysym;
  56static int gui_grab_code = KMOD_LALT | KMOD_LCTRL;
  57static uint8_t modifiers_state[256];
  58static SDL_Cursor *sdl_cursor_normal;
  59static SDL_Cursor *sdl_cursor_hidden;
  60static int absolute_enabled = 0;
  61static int guest_cursor = 0;
  62static int guest_x, guest_y;
  63static SDL_Cursor *guest_sprite = NULL;
  64static SDL_PixelFormat host_format;
  65static int scaling_active = 0;
  66static Notifier mouse_mode_notifier;
  67static int idle_counter;
  68static const guint16 *keycode_map;
  69static size_t keycode_maplen;
  70
  71#define SDL_REFRESH_INTERVAL_BUSY 10
  72#define SDL_MAX_IDLE_COUNT (2 * GUI_REFRESH_INTERVAL_DEFAULT \
  73                            / SDL_REFRESH_INTERVAL_BUSY + 1)
  74
  75#if 0
  76#define DEBUG_SDL
  77#endif
  78
  79static void sdl_update(DisplayChangeListener *dcl,
  80                       int x, int y, int w, int h)
  81{
  82    SDL_Rect rec;
  83    rec.x = x;
  84    rec.y = y;
  85    rec.w = w;
  86    rec.h = h;
  87
  88#ifdef DEBUG_SDL
  89    printf("SDL: Updating x=%d y=%d w=%d h=%d (scaling: %d)\n",
  90           x, y, w, h, scaling_active);
  91#endif
  92
  93    if (guest_screen) {
  94        if (!scaling_active) {
  95            SDL_BlitSurface(guest_screen, &rec, real_screen, &rec);
  96        } else {
  97            if (sdl_zoom_blit(guest_screen, real_screen, SMOOTHING_ON, &rec) < 0) {
  98                fprintf(stderr, "Zoom blit failed\n");
  99                exit(1);
 100            }
 101        }
 102    } 
 103    SDL_UpdateRect(real_screen, rec.x, rec.y, rec.w, rec.h);
 104}
 105
 106static void do_sdl_resize(int width, int height, int bpp)
 107{
 108    int flags;
 109    SDL_Surface *tmp_screen;
 110
 111#ifdef DEBUG_SDL
 112    printf("SDL: Resizing to %dx%d bpp %d\n", width, height, bpp);
 113#endif
 114
 115    flags = SDL_HWSURFACE | SDL_ASYNCBLIT | SDL_HWACCEL;
 116    if (gui_fullscreen) {
 117        flags |= SDL_FULLSCREEN;
 118    } else {
 119        flags |= SDL_RESIZABLE;
 120    }
 121    if (no_frame) {
 122        flags |= SDL_NOFRAME;
 123    }
 124
 125    tmp_screen = SDL_SetVideoMode(width, height, bpp, flags);
 126    if (!real_screen) {
 127        if (!tmp_screen) {
 128            fprintf(stderr, "Could not open SDL display (%dx%dx%d): %s\n",
 129                    width, height, bpp, SDL_GetError());
 130            exit(1);
 131        }
 132    } else {
 133        /*
 134         * Revert to the previous video mode if the change of resizing or
 135         * resolution failed.
 136         */
 137        if (!tmp_screen) {
 138            fprintf(stderr, "Failed to set SDL display (%dx%dx%d): %s\n",
 139                    width, height, bpp, SDL_GetError());
 140            return;
 141        }
 142    }
 143
 144    real_screen = tmp_screen;
 145}
 146
 147static void sdl_switch(DisplayChangeListener *dcl,
 148                       DisplaySurface *new_surface)
 149{
 150    PixelFormat pf;
 151
 152    /* temporary hack: allows to call sdl_switch to handle scaling changes */
 153    if (new_surface) {
 154        surface = new_surface;
 155    }
 156    pf = qemu_pixelformat_from_pixman(surface->format);
 157
 158    if (!scaling_active) {
 159        do_sdl_resize(surface_width(surface), surface_height(surface), 0);
 160    } else if (real_screen->format->BitsPerPixel !=
 161               surface_bits_per_pixel(surface)) {
 162        do_sdl_resize(real_screen->w, real_screen->h,
 163                      surface_bits_per_pixel(surface));
 164    }
 165
 166    if (guest_screen != NULL) {
 167        SDL_FreeSurface(guest_screen);
 168    }
 169
 170#ifdef DEBUG_SDL
 171    printf("SDL: Creating surface with masks: %08x %08x %08x %08x\n",
 172           pf.rmask, pf.gmask, pf.bmask, pf.amask);
 173#endif
 174
 175    guest_screen = SDL_CreateRGBSurfaceFrom
 176        (surface_data(surface),
 177         surface_width(surface), surface_height(surface),
 178         surface_bits_per_pixel(surface), surface_stride(surface),
 179         pf.rmask, pf.gmask,
 180         pf.bmask, pf.amask);
 181}
 182
 183static bool sdl_check_format(DisplayChangeListener *dcl,
 184                             pixman_format_code_t format)
 185{
 186    /*
 187     * We let SDL convert for us a few more formats than,
 188     * the native ones. Thes are the ones I have tested.
 189     */
 190    return (format == PIXMAN_x8r8g8b8 ||
 191            format == PIXMAN_b8g8r8x8 ||
 192            format == PIXMAN_x1r5g5b5 ||
 193            format == PIXMAN_r5g6b5);
 194}
 195
 196/* generic keyboard conversion */
 197
 198#include "sdl_keysym.h"
 199
 200static kbd_layout_t *kbd_layout = NULL;
 201
 202static uint8_t sdl_keyevent_to_keycode_generic(const SDL_KeyboardEvent *ev)
 203{
 204    bool shift = modifiers_state[0x2a] || modifiers_state[0x36];
 205    bool altgr = modifiers_state[0xb8];
 206    bool ctrl  = modifiers_state[0x1d] || modifiers_state[0x9d];
 207    int keysym;
 208    /* workaround for X11+SDL bug with AltGR */
 209    keysym = ev->keysym.sym;
 210    if (keysym == 0 && ev->keysym.scancode == 113)
 211        keysym = SDLK_MODE;
 212    /* For Japanese key '\' and '|' */
 213    if (keysym == 92 && ev->keysym.scancode == 133) {
 214        keysym = 0xa5;
 215    }
 216    return keysym2scancode(kbd_layout, keysym,
 217                           shift, altgr, ctrl) & SCANCODE_KEYMASK;
 218}
 219
 220
 221static const guint16 *sdl_get_keymap(size_t *maplen)
 222{
 223#if defined(WIN32)
 224    *maplen = qemu_input_map_atset1_to_qcode_len;
 225    return qemu_input_map_atset1_to_qcode;
 226#else
 227#if defined(SDL_VIDEO_DRIVER_X11)
 228    SDL_SysWMinfo info;
 229
 230    SDL_VERSION(&info.version);
 231    if (SDL_GetWMInfo(&info) > 0) {
 232        return qemu_xkeymap_mapping_table(
 233            info.info.x11.display, maplen);
 234    }
 235#endif
 236    g_warning("Unsupported SDL video driver / platform.\n"
 237              "Assuming Linux KBD scancodes, but probably wrong.\n"
 238              "Please report to qemu-devel@nongnu.org\n"
 239              "including the following information:\n"
 240              "\n"
 241              "  - Operating system\n"
 242              "  - SDL video driver\n");
 243    *maplen = qemu_input_map_xorgkbd_to_qcode_len;
 244    return qemu_input_map_xorgkbd_to_qcode;
 245#endif
 246}
 247
 248static uint8_t sdl_keyevent_to_keycode(const SDL_KeyboardEvent *ev)
 249{
 250    int qcode;
 251    if (!keycode_map) {
 252        return 0;
 253    }
 254    if (ev->keysym.scancode > keycode_maplen) {
 255        return 0;
 256    }
 257
 258    qcode = keycode_map[ev->keysym.scancode];
 259
 260    if (qcode > qemu_input_map_qcode_to_qnum_len) {
 261        return 0;
 262    }
 263
 264    return qemu_input_map_qcode_to_qnum[qcode];
 265}
 266
 267static void reset_keys(void)
 268{
 269    int i;
 270    for(i = 0; i < 256; i++) {
 271        if (modifiers_state[i]) {
 272            qemu_input_event_send_key_number(dcl->con, i, false);
 273            modifiers_state[i] = 0;
 274        }
 275    }
 276}
 277
 278static void sdl_process_key(SDL_KeyboardEvent *ev)
 279{
 280    int keycode;
 281
 282    if (ev->keysym.sym == SDLK_PAUSE) {
 283        /* specific case */
 284        qemu_input_event_send_key_qcode(dcl->con, Q_KEY_CODE_PAUSE,
 285                                        ev->type == SDL_KEYDOWN);
 286        return;
 287    }
 288
 289    if (kbd_layout) {
 290        keycode = sdl_keyevent_to_keycode_generic(ev);
 291    } else {
 292        keycode = sdl_keyevent_to_keycode(ev);
 293    }
 294
 295    switch(keycode) {
 296    case 0x00:
 297        /* sent when leaving window: reset the modifiers state */
 298        reset_keys();
 299        return;
 300    case 0x2a:                          /* Left Shift */
 301    case 0x36:                          /* Right Shift */
 302    case 0x1d:                          /* Left CTRL */
 303    case 0x9d:                          /* Right CTRL */
 304    case 0x38:                          /* Left ALT */
 305    case 0xb8:                         /* Right ALT */
 306        if (ev->type == SDL_KEYUP)
 307            modifiers_state[keycode] = 0;
 308        else
 309            modifiers_state[keycode] = 1;
 310        break;
 311#define QEMU_SDL_VERSION ((SDL_MAJOR_VERSION << 8) + SDL_MINOR_VERSION)
 312#if QEMU_SDL_VERSION < 0x102 || QEMU_SDL_VERSION == 0x102 && SDL_PATCHLEVEL < 14
 313        /* SDL versions before 1.2.14 don't support key up for caps/num lock. */
 314    case 0x45: /* num lock */
 315    case 0x3a: /* caps lock */
 316        /* SDL does not send the key up event, so we generate it */
 317        qemu_input_event_send_key_number(dcl->con, keycode, true);
 318        qemu_input_event_send_key_number(dcl->con, keycode, false);
 319        return;
 320#endif
 321    }
 322
 323    /* now send the key code */
 324    qemu_input_event_send_key_number(dcl->con, keycode,
 325                                     ev->type == SDL_KEYDOWN);
 326}
 327
 328static void sdl_update_caption(void)
 329{
 330    char win_title[1024];
 331    char icon_title[1024];
 332    const char *status = "";
 333
 334    if (!runstate_is_running())
 335        status = " [Stopped]";
 336    else if (gui_grab) {
 337        if (alt_grab)
 338            status = " - Press Ctrl-Alt-Shift-G to exit mouse grab";
 339        else if (ctrl_grab)
 340            status = " - Press Right-Ctrl-G to exit mouse grab";
 341        else
 342            status = " - Press Ctrl-Alt-G to exit mouse grab";
 343    }
 344
 345    if (qemu_name) {
 346        snprintf(win_title, sizeof(win_title), "QEMU (%s)%s", qemu_name, status);
 347        snprintf(icon_title, sizeof(icon_title), "QEMU (%s)", qemu_name);
 348    } else {
 349        snprintf(win_title, sizeof(win_title), "QEMU%s", status);
 350        snprintf(icon_title, sizeof(icon_title), "QEMU");
 351    }
 352
 353    SDL_WM_SetCaption(win_title, icon_title);
 354}
 355
 356static void sdl_hide_cursor(void)
 357{
 358    if (!cursor_hide)
 359        return;
 360
 361    if (qemu_input_is_absolute()) {
 362        SDL_ShowCursor(1);
 363        SDL_SetCursor(sdl_cursor_hidden);
 364    } else {
 365        SDL_ShowCursor(0);
 366    }
 367}
 368
 369static void sdl_show_cursor(void)
 370{
 371    if (!cursor_hide)
 372        return;
 373
 374    if (!qemu_input_is_absolute() || !qemu_console_is_graphic(NULL)) {
 375        SDL_ShowCursor(1);
 376        if (guest_cursor &&
 377                (gui_grab || qemu_input_is_absolute() || absolute_enabled))
 378            SDL_SetCursor(guest_sprite);
 379        else
 380            SDL_SetCursor(sdl_cursor_normal);
 381    }
 382}
 383
 384static void sdl_grab_start(void)
 385{
 386    /*
 387     * If the application is not active, do not try to enter grab state. This
 388     * prevents 'SDL_WM_GrabInput(SDL_GRAB_ON)' from blocking all the
 389     * application (SDL bug).
 390     */
 391    if (!(SDL_GetAppState() & SDL_APPINPUTFOCUS)) {
 392        return;
 393    }
 394    if (guest_cursor) {
 395        SDL_SetCursor(guest_sprite);
 396        if (!qemu_input_is_absolute() && !absolute_enabled) {
 397            SDL_WarpMouse(guest_x, guest_y);
 398        }
 399    } else
 400        sdl_hide_cursor();
 401    SDL_WM_GrabInput(SDL_GRAB_ON);
 402    gui_grab = 1;
 403    sdl_update_caption();
 404}
 405
 406static void sdl_grab_end(void)
 407{
 408    SDL_WM_GrabInput(SDL_GRAB_OFF);
 409    gui_grab = 0;
 410    sdl_show_cursor();
 411    sdl_update_caption();
 412}
 413
 414static void absolute_mouse_grab(void)
 415{
 416    int mouse_x, mouse_y;
 417
 418    SDL_GetMouseState(&mouse_x, &mouse_y);
 419    if (mouse_x > 0 && mouse_x < real_screen->w - 1 &&
 420        mouse_y > 0 && mouse_y < real_screen->h - 1) {
 421        sdl_grab_start();
 422    }
 423}
 424
 425static void sdl_mouse_mode_change(Notifier *notify, void *data)
 426{
 427    if (qemu_input_is_absolute()) {
 428        if (!absolute_enabled) {
 429            absolute_enabled = 1;
 430            if (qemu_console_is_graphic(NULL)) {
 431                absolute_mouse_grab();
 432            }
 433        }
 434    } else if (absolute_enabled) {
 435        if (!gui_fullscreen) {
 436            sdl_grab_end();
 437        }
 438        absolute_enabled = 0;
 439    }
 440}
 441
 442static void sdl_send_mouse_event(int dx, int dy, int x, int y, int state)
 443{
 444    static uint32_t bmap[INPUT_BUTTON__MAX] = {
 445        [INPUT_BUTTON_LEFT]       = SDL_BUTTON(SDL_BUTTON_LEFT),
 446        [INPUT_BUTTON_MIDDLE]     = SDL_BUTTON(SDL_BUTTON_MIDDLE),
 447        [INPUT_BUTTON_RIGHT]      = SDL_BUTTON(SDL_BUTTON_RIGHT),
 448        [INPUT_BUTTON_WHEEL_UP]   = SDL_BUTTON(SDL_BUTTON_WHEELUP),
 449        [INPUT_BUTTON_WHEEL_DOWN] = SDL_BUTTON(SDL_BUTTON_WHEELDOWN),
 450    };
 451    static uint32_t prev_state;
 452
 453    if (prev_state != state) {
 454        qemu_input_update_buttons(dcl->con, bmap, prev_state, state);
 455        prev_state = state;
 456    }
 457
 458    if (qemu_input_is_absolute()) {
 459        qemu_input_queue_abs(dcl->con, INPUT_AXIS_X, x,
 460                             0, real_screen->w);
 461        qemu_input_queue_abs(dcl->con, INPUT_AXIS_Y, y,
 462                             0, real_screen->h);
 463    } else {
 464        if (guest_cursor) {
 465            x -= guest_x;
 466            y -= guest_y;
 467            guest_x += x;
 468            guest_y += y;
 469            dx = x;
 470            dy = y;
 471        }
 472        qemu_input_queue_rel(dcl->con, INPUT_AXIS_X, dx);
 473        qemu_input_queue_rel(dcl->con, INPUT_AXIS_Y, dy);
 474    }
 475    qemu_input_event_sync();
 476}
 477
 478static void sdl_scale(int width, int height)
 479{
 480    int bpp = real_screen->format->BitsPerPixel;
 481
 482#ifdef DEBUG_SDL
 483    printf("SDL: Scaling to %dx%d bpp %d\n", width, height, bpp);
 484#endif
 485
 486    if (bpp != 16 && bpp != 32) {
 487        bpp = 32;
 488    }
 489    do_sdl_resize(width, height, bpp);
 490    scaling_active = 1;
 491}
 492
 493static void toggle_full_screen(void)
 494{
 495    int width = surface_width(surface);
 496    int height = surface_height(surface);
 497    int bpp = surface_bits_per_pixel(surface);
 498
 499    gui_fullscreen = !gui_fullscreen;
 500    if (gui_fullscreen) {
 501        gui_saved_width = real_screen->w;
 502        gui_saved_height = real_screen->h;
 503        gui_saved_scaling = scaling_active;
 504
 505        do_sdl_resize(width, height, bpp);
 506        scaling_active = 0;
 507
 508        gui_saved_grab = gui_grab;
 509        sdl_grab_start();
 510    } else {
 511        if (gui_saved_scaling) {
 512            sdl_scale(gui_saved_width, gui_saved_height);
 513        } else {
 514            do_sdl_resize(width, height, 0);
 515        }
 516        if (!gui_saved_grab || !qemu_console_is_graphic(NULL)) {
 517            sdl_grab_end();
 518        }
 519    }
 520    graphic_hw_invalidate(NULL);
 521    graphic_hw_update(NULL);
 522}
 523
 524static void handle_keydown(SDL_Event *ev)
 525{
 526    int mod_state;
 527    int keycode;
 528
 529    if (alt_grab) {
 530        mod_state = (SDL_GetModState() & (gui_grab_code | KMOD_LSHIFT)) ==
 531                    (gui_grab_code | KMOD_LSHIFT);
 532    } else if (ctrl_grab) {
 533        mod_state = (SDL_GetModState() & KMOD_RCTRL) == KMOD_RCTRL;
 534    } else {
 535        mod_state = (SDL_GetModState() & gui_grab_code) == gui_grab_code;
 536    }
 537    gui_key_modifier_pressed = mod_state;
 538
 539    if (gui_key_modifier_pressed) {
 540        keycode = sdl_keyevent_to_keycode(&ev->key);
 541        switch (keycode) {
 542        case 0x21: /* 'f' key on US keyboard */
 543            toggle_full_screen();
 544            gui_keysym = 1;
 545            break;
 546        case 0x22: /* 'g' key */
 547            if (!gui_grab) {
 548                if (qemu_console_is_graphic(NULL)) {
 549                    sdl_grab_start();
 550                }
 551            } else if (!gui_fullscreen) {
 552                sdl_grab_end();
 553            }
 554            gui_keysym = 1;
 555            break;
 556        case 0x16: /* 'u' key on US keyboard */
 557            if (scaling_active) {
 558                scaling_active = 0;
 559                sdl_switch(dcl, NULL);
 560                graphic_hw_invalidate(NULL);
 561                graphic_hw_update(NULL);
 562            }
 563            gui_keysym = 1;
 564            break;
 565        case 0x02 ... 0x0a: /* '1' to '9' keys */
 566            /* Reset the modifiers sent to the current console */
 567            reset_keys();
 568            console_select(keycode - 0x02);
 569            gui_keysym = 1;
 570            if (gui_fullscreen) {
 571                break;
 572            }
 573            if (!qemu_console_is_graphic(NULL)) {
 574                /* release grab if going to a text console */
 575                if (gui_grab) {
 576                    sdl_grab_end();
 577                } else if (absolute_enabled) {
 578                    sdl_show_cursor();
 579                }
 580            } else if (absolute_enabled) {
 581                sdl_hide_cursor();
 582                absolute_mouse_grab();
 583            }
 584            break;
 585        case 0x1b: /* '+' */
 586        case 0x35: /* '-' */
 587            if (!gui_fullscreen) {
 588                int width = MAX(real_screen->w + (keycode == 0x1b ? 50 : -50),
 589                                160);
 590                int height = (surface_height(surface) * width) /
 591                    surface_width(surface);
 592
 593                sdl_scale(width, height);
 594                graphic_hw_invalidate(NULL);
 595                graphic_hw_update(NULL);
 596                gui_keysym = 1;
 597            }
 598        default:
 599            break;
 600        }
 601    } else if (!qemu_console_is_graphic(NULL)) {
 602        int keysym = 0;
 603
 604        if (ev->key.keysym.mod & (KMOD_LCTRL | KMOD_RCTRL)) {
 605            switch (ev->key.keysym.sym) {
 606            case SDLK_UP:
 607                keysym = QEMU_KEY_CTRL_UP;
 608                break;
 609            case SDLK_DOWN:
 610                keysym = QEMU_KEY_CTRL_DOWN;
 611                break;
 612            case SDLK_LEFT:
 613                keysym = QEMU_KEY_CTRL_LEFT;
 614                break;
 615            case SDLK_RIGHT:
 616                keysym = QEMU_KEY_CTRL_RIGHT;
 617                break;
 618            case SDLK_HOME:
 619                keysym = QEMU_KEY_CTRL_HOME;
 620                break;
 621            case SDLK_END:
 622                keysym = QEMU_KEY_CTRL_END;
 623                break;
 624            case SDLK_PAGEUP:
 625                keysym = QEMU_KEY_CTRL_PAGEUP;
 626                break;
 627            case SDLK_PAGEDOWN:
 628                keysym = QEMU_KEY_CTRL_PAGEDOWN;
 629                break;
 630            default:
 631                break;
 632            }
 633        } else {
 634            switch (ev->key.keysym.sym) {
 635            case SDLK_UP:
 636                keysym = QEMU_KEY_UP;
 637                break;
 638            case SDLK_DOWN:
 639                keysym = QEMU_KEY_DOWN;
 640                break;
 641            case SDLK_LEFT:
 642                keysym = QEMU_KEY_LEFT;
 643                break;
 644            case SDLK_RIGHT:
 645                keysym = QEMU_KEY_RIGHT;
 646                break;
 647            case SDLK_HOME:
 648                keysym = QEMU_KEY_HOME;
 649                break;
 650            case SDLK_END:
 651                keysym = QEMU_KEY_END;
 652                break;
 653            case SDLK_PAGEUP:
 654                keysym = QEMU_KEY_PAGEUP;
 655                break;
 656            case SDLK_PAGEDOWN:
 657                keysym = QEMU_KEY_PAGEDOWN;
 658                break;
 659            case SDLK_BACKSPACE:
 660                keysym = QEMU_KEY_BACKSPACE;
 661                break;
 662            case SDLK_DELETE:
 663                keysym = QEMU_KEY_DELETE;
 664                break;
 665            default:
 666                break;
 667            }
 668        }
 669        if (keysym) {
 670            kbd_put_keysym(keysym);
 671        } else if (ev->key.keysym.unicode != 0) {
 672            kbd_put_keysym(ev->key.keysym.unicode);
 673        }
 674    }
 675    if (qemu_console_is_graphic(NULL) && !gui_keysym) {
 676        sdl_process_key(&ev->key);
 677    }
 678}
 679
 680static void handle_keyup(SDL_Event *ev)
 681{
 682    int mod_state;
 683
 684    if (!alt_grab) {
 685        mod_state = (ev->key.keysym.mod & gui_grab_code);
 686    } else {
 687        mod_state = (ev->key.keysym.mod & (gui_grab_code | KMOD_LSHIFT));
 688    }
 689    if (!mod_state && gui_key_modifier_pressed) {
 690        gui_key_modifier_pressed = 0;
 691        gui_keysym = 0;
 692    }
 693    if (qemu_console_is_graphic(NULL) && !gui_keysym) {
 694        sdl_process_key(&ev->key);
 695    }
 696}
 697
 698static void handle_mousemotion(SDL_Event *ev)
 699{
 700    int max_x, max_y;
 701
 702    if (qemu_console_is_graphic(NULL) &&
 703        (qemu_input_is_absolute() || absolute_enabled)) {
 704        max_x = real_screen->w - 1;
 705        max_y = real_screen->h - 1;
 706        if (gui_grab && (ev->motion.x == 0 || ev->motion.y == 0 ||
 707            ev->motion.x == max_x || ev->motion.y == max_y)) {
 708            sdl_grab_end();
 709        }
 710        if (!gui_grab &&
 711            (ev->motion.x > 0 && ev->motion.x < max_x &&
 712            ev->motion.y > 0 && ev->motion.y < max_y)) {
 713            sdl_grab_start();
 714        }
 715    }
 716    if (gui_grab || qemu_input_is_absolute() || absolute_enabled) {
 717        sdl_send_mouse_event(ev->motion.xrel, ev->motion.yrel,
 718                             ev->motion.x, ev->motion.y, ev->motion.state);
 719    }
 720}
 721
 722static void handle_mousebutton(SDL_Event *ev)
 723{
 724    int buttonstate = SDL_GetMouseState(NULL, NULL);
 725    SDL_MouseButtonEvent *bev;
 726
 727    if (!qemu_console_is_graphic(NULL)) {
 728        return;
 729    }
 730
 731    bev = &ev->button;
 732    if (!gui_grab && !qemu_input_is_absolute()) {
 733        if (ev->type == SDL_MOUSEBUTTONUP && bev->button == SDL_BUTTON_LEFT) {
 734            /* start grabbing all events */
 735            sdl_grab_start();
 736        }
 737    } else {
 738        if (ev->type == SDL_MOUSEBUTTONDOWN) {
 739            buttonstate |= SDL_BUTTON(bev->button);
 740        } else {
 741            buttonstate &= ~SDL_BUTTON(bev->button);
 742        }
 743        sdl_send_mouse_event(0, 0, bev->x, bev->y, buttonstate);
 744    }
 745}
 746
 747static void handle_activation(SDL_Event *ev)
 748{
 749#ifdef _WIN32
 750    /* Disable grab if the window no longer has the focus
 751     * (Windows-only workaround) */
 752    if (gui_grab && ev->active.state == SDL_APPINPUTFOCUS &&
 753        !ev->active.gain && !gui_fullscreen) {
 754        sdl_grab_end();
 755    }
 756#endif
 757    if (!gui_grab && ev->active.gain && qemu_console_is_graphic(NULL) &&
 758        (qemu_input_is_absolute() || absolute_enabled)) {
 759        absolute_mouse_grab();
 760    }
 761    if (ev->active.state & SDL_APPACTIVE) {
 762        if (ev->active.gain) {
 763            /* Back to default interval */
 764            update_displaychangelistener(dcl, GUI_REFRESH_INTERVAL_DEFAULT);
 765        } else {
 766            /* Sleeping interval.  Not using the long default here as
 767             * sdl_refresh does not only update the guest screen, but
 768             * also checks for gui events. */
 769            update_displaychangelistener(dcl, 500);
 770        }
 771    }
 772}
 773
 774static void sdl_refresh(DisplayChangeListener *dcl)
 775{
 776    SDL_Event ev1, *ev = &ev1;
 777    bool allow_close = true;
 778    int idle = 1;
 779
 780    if (last_vm_running != runstate_is_running()) {
 781        last_vm_running = runstate_is_running();
 782        sdl_update_caption();
 783    }
 784
 785    graphic_hw_update(NULL);
 786    SDL_EnableUNICODE(!qemu_console_is_graphic(NULL));
 787
 788    while (SDL_PollEvent(ev)) {
 789        switch (ev->type) {
 790        case SDL_VIDEOEXPOSE:
 791            sdl_update(dcl, 0, 0, real_screen->w, real_screen->h);
 792            break;
 793        case SDL_KEYDOWN:
 794            idle = 0;
 795            handle_keydown(ev);
 796            break;
 797        case SDL_KEYUP:
 798            idle = 0;
 799            handle_keyup(ev);
 800            break;
 801        case SDL_QUIT:
 802            if (opts->has_window_close && !opts->window_close) {
 803                allow_close = false;
 804            }
 805            if (allow_close) {
 806                no_shutdown = 0;
 807                qemu_system_shutdown_request(SHUTDOWN_CAUSE_HOST_UI);
 808            }
 809            break;
 810        case SDL_MOUSEMOTION:
 811            idle = 0;
 812            handle_mousemotion(ev);
 813            break;
 814        case SDL_MOUSEBUTTONDOWN:
 815        case SDL_MOUSEBUTTONUP:
 816            idle = 0;
 817            handle_mousebutton(ev);
 818            break;
 819        case SDL_ACTIVEEVENT:
 820            handle_activation(ev);
 821            break;
 822        case SDL_VIDEORESIZE:
 823            sdl_scale(ev->resize.w, ev->resize.h);
 824            graphic_hw_invalidate(NULL);
 825            graphic_hw_update(NULL);
 826            break;
 827        default:
 828            break;
 829        }
 830    }
 831
 832    if (idle) {
 833        if (idle_counter < SDL_MAX_IDLE_COUNT) {
 834            idle_counter++;
 835            if (idle_counter >= SDL_MAX_IDLE_COUNT) {
 836                dcl->update_interval = GUI_REFRESH_INTERVAL_DEFAULT;
 837            }
 838        }
 839    } else {
 840        idle_counter = 0;
 841        dcl->update_interval = SDL_REFRESH_INTERVAL_BUSY;
 842    }
 843}
 844
 845static void sdl_mouse_warp(DisplayChangeListener *dcl,
 846                           int x, int y, int on)
 847{
 848    if (on) {
 849        if (!guest_cursor)
 850            sdl_show_cursor();
 851        if (gui_grab || qemu_input_is_absolute() || absolute_enabled) {
 852            SDL_SetCursor(guest_sprite);
 853            if (!qemu_input_is_absolute() && !absolute_enabled) {
 854                SDL_WarpMouse(x, y);
 855            }
 856        }
 857    } else if (gui_grab)
 858        sdl_hide_cursor();
 859    guest_cursor = on;
 860    guest_x = x, guest_y = y;
 861}
 862
 863static void sdl_mouse_define(DisplayChangeListener *dcl,
 864                             QEMUCursor *c)
 865{
 866    uint8_t *image, *mask;
 867    int bpl;
 868
 869    if (guest_sprite)
 870        SDL_FreeCursor(guest_sprite);
 871
 872    bpl = cursor_get_mono_bpl(c);
 873    image = g_malloc0(bpl * c->height);
 874    mask  = g_malloc0(bpl * c->height);
 875    cursor_get_mono_image(c, 0x000000, image);
 876    cursor_get_mono_mask(c, 0, mask);
 877    guest_sprite = SDL_CreateCursor(image, mask, c->width, c->height,
 878                                    c->hot_x, c->hot_y);
 879    g_free(image);
 880    g_free(mask);
 881
 882    if (guest_cursor &&
 883            (gui_grab || qemu_input_is_absolute() || absolute_enabled))
 884        SDL_SetCursor(guest_sprite);
 885}
 886
 887static void sdl_cleanup(void)
 888{
 889    if (guest_sprite)
 890        SDL_FreeCursor(guest_sprite);
 891    SDL_QuitSubSystem(SDL_INIT_VIDEO);
 892}
 893
 894static const DisplayChangeListenerOps dcl_ops = {
 895    .dpy_name             = "sdl",
 896    .dpy_gfx_update       = sdl_update,
 897    .dpy_gfx_switch       = sdl_switch,
 898    .dpy_gfx_check_format = sdl_check_format,
 899    .dpy_refresh          = sdl_refresh,
 900    .dpy_mouse_set        = sdl_mouse_warp,
 901    .dpy_cursor_define    = sdl_mouse_define,
 902};
 903
 904static void sdl1_display_init(DisplayState *ds, DisplayOptions *o)
 905{
 906    int flags;
 907    uint8_t data = 0;
 908    const SDL_VideoInfo *vi;
 909    SDL_SysWMinfo info;
 910    char *filename;
 911
 912    assert(o->type == DISPLAY_TYPE_SDL);
 913    opts = o;
 914#if defined(__APPLE__)
 915    /* always use generic keymaps */
 916    if (!keyboard_layout)
 917        keyboard_layout = "en-us";
 918#endif
 919    if(keyboard_layout) {
 920        kbd_layout = init_keyboard_layout(name2keysym, keyboard_layout);
 921        if (!kbd_layout)
 922            exit(1);
 923    }
 924
 925    g_printerr("Running QEMU with SDL 1.2 is deprecated, and will be removed\n"
 926               "in a future release. Please switch to SDL 2.0 instead\n");
 927
 928    if (opts->has_full_screen && opts->full_screen) {
 929        setenv("SDL_VIDEO_ALLOW_SCREENSAVER", "1", 0);
 930    }
 931#ifdef __linux__
 932    /* on Linux, SDL may use fbcon|directfb|svgalib when run without
 933     * accessible $DISPLAY to open X11 window.  This is often the case
 934     * when qemu is run using sudo.  But in this case, and when actually
 935     * run in X11 environment, SDL fights with X11 for the video card,
 936     * making current display unavailable, often until reboot.
 937     * So make x11 the default SDL video driver if this variable is unset.
 938     * This is a bit hackish but saves us from bigger problem.
 939     * Maybe it's a good idea to fix this in SDL instead.
 940     */
 941    setenv("SDL_VIDEODRIVER", "x11", 0);
 942#endif
 943
 944    /* Enable normal up/down events for Caps-Lock and Num-Lock keys.
 945     * This requires SDL >= 1.2.14. */
 946    setenv("SDL_DISABLE_LOCK_KEYS", "1", 1);
 947
 948    flags = SDL_INIT_VIDEO | SDL_INIT_NOPARACHUTE;
 949    if (SDL_Init (flags)) {
 950        fprintf(stderr, "Could not initialize SDL(%s) - exiting\n",
 951                SDL_GetError());
 952        exit(1);
 953    }
 954    vi = SDL_GetVideoInfo();
 955    host_format = *(vi->vfmt);
 956
 957    keycode_map = sdl_get_keymap(&keycode_maplen);
 958
 959    /* Load a 32x32x4 image. White pixels are transparent. */
 960    filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, "qemu-icon.bmp");
 961    if (filename) {
 962        SDL_Surface *image = SDL_LoadBMP(filename);
 963        if (image) {
 964            uint32_t colorkey = SDL_MapRGB(image->format, 255, 255, 255);
 965            SDL_SetColorKey(image, SDL_SRCCOLORKEY, colorkey);
 966            SDL_WM_SetIcon(image, NULL);
 967        }
 968        g_free(filename);
 969    }
 970
 971    if (opts->has_full_screen && opts->full_screen) {
 972        gui_fullscreen = 1;
 973        sdl_grab_start();
 974    }
 975
 976    dcl = g_new0(DisplayChangeListener, 1);
 977    dcl->ops = &dcl_ops;
 978    register_displaychangelistener(dcl);
 979
 980    mouse_mode_notifier.notify = sdl_mouse_mode_change;
 981    qemu_add_mouse_mode_change_notifier(&mouse_mode_notifier);
 982
 983    sdl_update_caption();
 984    SDL_EnableKeyRepeat(250, 50);
 985    gui_grab = 0;
 986
 987    sdl_cursor_hidden = SDL_CreateCursor(&data, &data, 8, 1, 0, 0);
 988    sdl_cursor_normal = SDL_GetCursor();
 989
 990    memset(&info, 0, sizeof(info));
 991    SDL_VERSION(&info.version);
 992    if (SDL_GetWMInfo(&info)) {
 993        int i;
 994        for (i = 0; ; i++) {
 995            /* All consoles share the same window */
 996            QemuConsole *con = qemu_console_lookup_by_index(i);
 997            if (con) {
 998#if defined(SDL_VIDEO_DRIVER_X11)
 999                qemu_console_set_window_id(con, info.info.x11.wmwindow);
1000#elif defined(SDL_VIDEO_DRIVER_NANOX) || \
1001      defined(SDL_VIDEO_DRIVER_WINDIB) || defined(SDL_VIDEO_DRIVER_DDRAW) || \
1002      defined(SDL_VIDEO_DRIVER_GAPI) || \
1003      defined(SDL_VIDEO_DRIVER_RISCOS)
1004                qemu_console_set_window_id(con, (int) (uintptr_t) info.window);
1005#else
1006                qemu_console_set_window_id(con, info.data);
1007#endif
1008            } else {
1009                break;
1010            }
1011        }
1012    }
1013
1014    atexit(sdl_cleanup);
1015}
1016
1017static QemuDisplay qemu_display_sdl1 = {
1018    .type       = DISPLAY_TYPE_SDL,
1019    .init       = sdl1_display_init,
1020};
1021
1022static void register_sdl1(void)
1023{
1024    qemu_display_register(&qemu_display_sdl1);
1025}
1026
1027type_init(register_sdl1);
1028