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