sdl.c 28.8 KB
Newer Older
1 2
/*
 * QEMU SDL display driver
3
 *
4
 * Copyright (c) 2003 Fabrice Bellard
5
 *
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
24 25 26 27

/* Avoid compiler warning because macro is redefined in SDL_syswm.h. */
#undef WIN32_LEAN_AND_MEAN

28
#include <SDL.h>
29
#include <SDL_syswm.h>
30

31 32 33 34
#include "qemu-common.h"
#include "console.h"
#include "sysemu.h"
#include "x_keymap.h"
S
Stefano Stabellini 已提交
35
#include "sdl_zoom.h"
36

37 38 39
static DisplayChangeListener *dcl;
static SDL_Surface *real_screen;
static SDL_Surface *guest_screen = NULL;
40
static int gui_grab; /* if true, all keyboard/mouse events are grabbed */
41
static int last_vm_running;
42 43 44
static bool gui_saved_scaling;
static int gui_saved_width;
static int gui_saved_height;
45 46
static int gui_saved_grab;
static int gui_fullscreen;
47
static int gui_noframe;
48 49
static int gui_key_modifier_pressed;
static int gui_keysym;
50 51
static int gui_grab_code = KMOD_LALT | KMOD_LCTRL;
static uint8_t modifiers_state[256];
52 53 54 55
static int width, height;
static SDL_Cursor *sdl_cursor_normal;
static SDL_Cursor *sdl_cursor_hidden;
static int absolute_enabled = 0;
56 57
static int guest_cursor = 0;
static int guest_x, guest_y;
58
static SDL_Cursor *guest_sprite = NULL;
59
static uint8_t allocator;
S
Stefano Stabellini 已提交
60 61
static SDL_PixelFormat host_format;
static int scaling_active = 0;
A
Anthony Liguori 已提交
62
static Notifier mouse_mode_notifier;
63 64 65

static void sdl_update(DisplayState *ds, int x, int y, int w, int h)
{
B
bellard 已提交
66
    //    printf("updating x=%d y=%d w=%d h=%d\n", x, y, w, h);
S
Stefano Stabellini 已提交
67 68 69 70 71 72
    SDL_Rect rec;
    rec.x = x;
    rec.y = y;
    rec.w = w;
    rec.h = h;

73
    if (guest_screen) {
S
Stefano Stabellini 已提交
74 75 76 77 78 79 80 81 82 83
        if (!scaling_active) {
            SDL_BlitSurface(guest_screen, &rec, real_screen, &rec);
        } else {
            if (sdl_zoom_blit(guest_screen, real_screen, SMOOTHING_ON, &rec) < 0) {
                fprintf(stderr, "Zoom blit failed\n");
                exit(1);
            }
        }
    } 
    SDL_UpdateRect(real_screen, rec.x, rec.y, rec.w, rec.h);
84 85 86 87 88 89 90 91 92 93
}

static void sdl_setdata(DisplayState *ds)
{
    if (guest_screen != NULL) SDL_FreeSurface(guest_screen);

    guest_screen = SDL_CreateRGBSurfaceFrom(ds_get_data(ds), ds_get_width(ds), ds_get_height(ds),
                                            ds_get_bits_per_pixel(ds), ds_get_linesize(ds),
                                            ds->surface->pf.rmask, ds->surface->pf.gmask,
                                            ds->surface->pf.bmask, ds->surface->pf.amask);
94 95
}

96
static void do_sdl_resize(int new_width, int new_height, int bpp)
97 98 99 100 101
{
    int flags;

    //    printf("resizing to %d %d\n", w, h);

102 103
    flags = SDL_HWSURFACE | SDL_ASYNCBLIT | SDL_HWACCEL;
    if (gui_fullscreen) {
104
        flags |= SDL_FULLSCREEN;
105 106 107
    } else {
        flags |= SDL_RESIZABLE;
    }
108 109
    if (gui_noframe)
        flags |= SDL_NOFRAME;
B
bellard 已提交
110

111 112
    width = new_width;
    height = new_height;
113
    real_screen = SDL_SetVideoMode(width, height, bpp, flags);
114
    if (!real_screen) {
115 116
	fprintf(stderr, "Could not open SDL display (%dx%dx%d): %s\n", width, 
		height, bpp, SDL_GetError());
117 118
        exit(1);
    }
119 120 121 122 123
}

static void sdl_resize(DisplayState *ds)
{
    if  (!allocator) {
S
Stefano Stabellini 已提交
124 125 126 127
        if (!scaling_active)
            do_sdl_resize(ds_get_width(ds), ds_get_height(ds), 0);
        else if (real_screen->format->BitsPerPixel != ds_get_bits_per_pixel(ds))
            do_sdl_resize(real_screen->w, real_screen->h, ds_get_bits_per_pixel(ds));
128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179
        sdl_setdata(ds);
    } else {
        if (guest_screen != NULL) {
            SDL_FreeSurface(guest_screen);
            guest_screen = NULL;
        }
    }
}

static PixelFormat sdl_to_qemu_pixelformat(SDL_PixelFormat *sdl_pf)
{
    PixelFormat qemu_pf;

    memset(&qemu_pf, 0x00, sizeof(PixelFormat));

    qemu_pf.bits_per_pixel = sdl_pf->BitsPerPixel;
    qemu_pf.bytes_per_pixel = sdl_pf->BytesPerPixel;
    qemu_pf.depth = (qemu_pf.bits_per_pixel) == 32 ? 24 : (qemu_pf.bits_per_pixel);

    qemu_pf.rmask = sdl_pf->Rmask;
    qemu_pf.gmask = sdl_pf->Gmask;
    qemu_pf.bmask = sdl_pf->Bmask;
    qemu_pf.amask = sdl_pf->Amask;

    qemu_pf.rshift = sdl_pf->Rshift;
    qemu_pf.gshift = sdl_pf->Gshift;
    qemu_pf.bshift = sdl_pf->Bshift;
    qemu_pf.ashift = sdl_pf->Ashift;

    qemu_pf.rbits = 8 - sdl_pf->Rloss;
    qemu_pf.gbits = 8 - sdl_pf->Gloss;
    qemu_pf.bbits = 8 - sdl_pf->Bloss;
    qemu_pf.abits = 8 - sdl_pf->Aloss;

    qemu_pf.rmax = ((1 << qemu_pf.rbits) - 1);
    qemu_pf.gmax = ((1 << qemu_pf.gbits) - 1);
    qemu_pf.bmax = ((1 << qemu_pf.bbits) - 1);
    qemu_pf.amax = ((1 << qemu_pf.abits) - 1);

    return qemu_pf;
}

static DisplaySurface* sdl_create_displaysurface(int width, int height)
{
    DisplaySurface *surface = (DisplaySurface*) qemu_mallocz(sizeof(DisplaySurface));
    if (surface == NULL) {
        fprintf(stderr, "sdl_create_displaysurface: malloc failed\n");
        exit(1);
    }

    surface->width = width;
    surface->height = height;
180

S
Stefano Stabellini 已提交
181
    if (scaling_active) {
182 183
        int linesize;
        PixelFormat pf;
S
Stefano Stabellini 已提交
184
        if (host_format.BytesPerPixel != 2 && host_format.BytesPerPixel != 4) {
185 186
            linesize = width * 4;
            pf = qemu_default_pixelformat(32);
S
Stefano Stabellini 已提交
187
        } else {
188 189
            linesize = width * host_format.BytesPerPixel;
            pf = sdl_to_qemu_pixelformat(&host_format);
S
Stefano Stabellini 已提交
190
        }
191
        qemu_alloc_display(surface, width, height, linesize, pf, 0);
S
Stefano Stabellini 已提交
192 193
        return surface;
    }
194

S
Stefano Stabellini 已提交
195
    if (host_format.BitsPerPixel == 16)
196 197 198 199 200 201 202 203
        do_sdl_resize(width, height, 16);
    else
        do_sdl_resize(width, height, 32);

    surface->pf = sdl_to_qemu_pixelformat(real_screen->format);
    surface->linesize = real_screen->pitch;
    surface->data = real_screen->pixels;

204
#ifdef HOST_WORDS_BIGENDIAN
S
Stefano Stabellini 已提交
205
    surface->flags = QEMU_REALPIXELS_FLAG | QEMU_BIG_ENDIAN_FLAG;
206
#else
S
Stefano Stabellini 已提交
207
    surface->flags = QEMU_REALPIXELS_FLAG;
208 209 210 211 212 213 214 215 216 217 218
#endif
    allocator = 1;

    return surface;
}

static void sdl_free_displaysurface(DisplaySurface *surface)
{
    allocator = 0;
    if (surface == NULL)
        return;
S
Stefano Stabellini 已提交
219 220 221

    if (surface->flags & QEMU_ALLOCATED_FLAG)
        qemu_free(surface->data);
222 223 224 225 226 227 228
    qemu_free(surface);
}

static DisplaySurface* sdl_resize_displaysurface(DisplaySurface *surface, int width, int height)
{
    sdl_free_displaysurface(surface);
    return sdl_create_displaysurface(width, height);
229 230
}

231
/* generic keyboard conversion */
B
bellard 已提交
232

233 234
#include "sdl_keysym.h"

A
Anthony Liguori 已提交
235
static kbd_layout_t *kbd_layout = NULL;
236 237

static uint8_t sdl_keyevent_to_keycode_generic(const SDL_KeyboardEvent *ev)
B
bellard 已提交
238
{
239 240 241 242 243
    int keysym;
    /* workaround for X11+SDL bug with AltGR */
    keysym = ev->keysym.sym;
    if (keysym == 0 && ev->keysym.scancode == 113)
        keysym = SDLK_MODE;
B
bellard 已提交
244 245 246 247
    /* For Japanese key '\' and '|' */
    if (keysym == 92 && ev->keysym.scancode == 133) {
        keysym = 0xa5;
    }
248
    return keysym2scancode(kbd_layout, keysym) & SCANCODE_KEYMASK;
B
bellard 已提交
249 250
}

251 252 253
/* specific keyboard conversions from scan codes */

#if defined(_WIN32)
B
bellard 已提交
254 255 256 257 258 259 260 261

static uint8_t sdl_keyevent_to_keycode(const SDL_KeyboardEvent *ev)
{
    return ev->keysym.scancode;
}

#else

262 263 264 265 266 267
#if defined(SDL_VIDEO_DRIVER_X11)
#include <X11/XKBlib.h>

static int check_for_evdev(void)
{
    SDL_SysWMinfo info;
J
Jan Kiszka 已提交
268
    XkbDescPtr desc = NULL;
269
    int has_evdev = 0;
J
Jan Kiszka 已提交
270
    char *keycodes = NULL;
271 272

    SDL_VERSION(&info.version);
J
Jan Kiszka 已提交
273
    if (!SDL_GetWMInfo(&info)) {
274
        return 0;
J
Jan Kiszka 已提交
275
    }
276 277 278
    desc = XkbGetKeyboard(info.info.x11.display,
                          XkbGBN_AllComponentsMask,
                          XkbUseCoreKbd);
J
Jan Kiszka 已提交
279 280 281 282 283 284 285 286 287 288 289
    if (desc && desc->names) {
        keycodes = XGetAtomName(info.info.x11.display, desc->names->keycodes);
        if (keycodes == NULL) {
            fprintf(stderr, "could not lookup keycode name\n");
        } else if (strstart(keycodes, "evdev", NULL)) {
            has_evdev = 1;
        } else if (!strstart(keycodes, "xfree86", NULL)) {
            fprintf(stderr, "unknown keycodes `%s', please report to "
                    "qemu-devel@nongnu.org\n", keycodes);
        }
    }
290

J
Jan Kiszka 已提交
291 292 293 294 295 296
    if (desc) {
        XkbFreeKeyboard(desc, XkbGBN_AllComponentsMask, True);
    }
    if (keycodes) {
        XFree(keycodes);
    }
297 298 299 300 301 302 303 304 305
    return has_evdev;
}
#else
static int check_for_evdev(void)
{
	return 0;
}
#endif

B
bellard 已提交
306 307 308
static uint8_t sdl_keyevent_to_keycode(const SDL_KeyboardEvent *ev)
{
    int keycode;
309 310 311 312
    static int has_evdev = -1;

    if (has_evdev == -1)
        has_evdev = check_for_evdev();
B
bellard 已提交
313 314 315 316 317 318 319

    keycode = ev->keysym.scancode;

    if (keycode < 9) {
        keycode = 0;
    } else if (keycode < 97) {
        keycode -= 8; /* just an offset */
320
    } else if (keycode < 158) {
B
bellard 已提交
321
        /* use conversion table */
322 323 324 325 326 327 328 329
        if (has_evdev)
            keycode = translate_evdev_keycode(keycode - 97);
        else
            keycode = translate_xfree86_keycode(keycode - 97);
    } else if (keycode == 208) { /* Hiragana_Katakana */
        keycode = 0x70;
    } else if (keycode == 211) { /* backslash */
        keycode = 0x73;
B
bellard 已提交
330 331 332 333 334 335 336 337
    } else {
        keycode = 0;
    }
    return keycode;
}

#endif

338 339 340 341 342
static void reset_keys(void)
{
    int i;
    for(i = 0; i < 256; i++) {
        if (modifiers_state[i]) {
343 344 345
            if (i & SCANCODE_GREY)
                kbd_put_keycode(SCANCODE_EMUL0);
            kbd_put_keycode(i | SCANCODE_UP);
346 347 348 349 350
            modifiers_state[i] = 0;
        }
    }
}

351 352
static void sdl_process_key(SDL_KeyboardEvent *ev)
{
353
    int keycode, v;
354 355 356 357 358

    if (ev->keysym.sym == SDLK_PAUSE) {
        /* specific case */
        v = 0;
        if (ev->type == SDL_KEYUP)
359
            v |= SCANCODE_UP;
360 361 362 363 364 365
        kbd_put_keycode(0xe1);
        kbd_put_keycode(0x1d | v);
        kbd_put_keycode(0x45 | v);
        return;
    }

366 367 368 369 370
    if (kbd_layout) {
        keycode = sdl_keyevent_to_keycode_generic(ev);
    } else {
        keycode = sdl_keyevent_to_keycode(ev);
    }
371 372 373 374

    switch(keycode) {
    case 0x00:
        /* sent when leaving window: reset the modifiers state */
375
        reset_keys();
376 377 378 379 380 381 382
        return;
    case 0x2a:                          /* Left Shift */
    case 0x36:                          /* Right Shift */
    case 0x1d:                          /* Left CTRL */
    case 0x9d:                          /* Right CTRL */
    case 0x38:                          /* Left ALT */
    case 0xb8:                         /* Right ALT */
383
        if (ev->type == SDL_KEYUP)
384 385 386 387
            modifiers_state[keycode] = 0;
        else
            modifiers_state[keycode] = 1;
        break;
388 389 390
#define QEMU_SDL_VERSION ((SDL_MAJOR_VERSION << 8) + SDL_MINOR_VERSION)
#if QEMU_SDL_VERSION < 0x102 || QEMU_SDL_VERSION == 0x102 && SDL_PATCHLEVEL < 14
        /* SDL versions before 1.2.14 don't support key up for caps/num lock. */
391 392 393 394
    case 0x45: /* num lock */
    case 0x3a: /* caps lock */
        /* SDL does not send the key up event, so we generate it */
        kbd_put_keycode(keycode);
395
        kbd_put_keycode(keycode | SCANCODE_UP);
396
        return;
397
#endif
398
    }
399 400

    /* now send the key code */
401 402
    if (keycode & SCANCODE_GREY)
        kbd_put_keycode(SCANCODE_EMUL0);
403
    if (ev->type == SDL_KEYUP)
404
        kbd_put_keycode(keycode | SCANCODE_UP);
405
    else
406
        kbd_put_keycode(keycode & SCANCODE_KEYCODEMASK);
407 408
}

409 410
static void sdl_update_caption(void)
{
411 412
    char win_title[1024];
    char icon_title[1024];
T
ths 已提交
413 414 415 416
    const char *status = "";

    if (!vm_running)
        status = " [Stopped]";
T
ths 已提交
417
    else if (gui_grab) {
418
        if (alt_grab)
419
            status = " - Press Ctrl-Alt-Shift to exit mouse grab";
420
        else if (ctrl_grab)
421
            status = " - Press Right-Ctrl to exit mouse grab";
422
        else
423
            status = " - Press Ctrl-Alt to exit mouse grab";
T
ths 已提交
424
    }
T
ths 已提交
425

426 427 428 429 430 431 432
    if (qemu_name) {
        snprintf(win_title, sizeof(win_title), "QEMU (%s)%s", qemu_name, status);
        snprintf(icon_title, sizeof(icon_title), "QEMU (%s)", qemu_name);
    } else {
        snprintf(win_title, sizeof(win_title), "QEMU%s", status);
        snprintf(icon_title, sizeof(icon_title), "QEMU");
    }
T
ths 已提交
433

434
    SDL_WM_SetCaption(win_title, icon_title);
435 436
}

437 438
static void sdl_hide_cursor(void)
{
439 440 441
    if (!cursor_hide)
        return;

442 443 444 445 446 447
    if (kbd_mouse_is_absolute()) {
        SDL_ShowCursor(1);
        SDL_SetCursor(sdl_cursor_hidden);
    } else {
        SDL_ShowCursor(0);
    }
448 449 450 451
}

static void sdl_show_cursor(void)
{
452 453 454
    if (!cursor_hide)
        return;

455
    if (!kbd_mouse_is_absolute()) {
456
        SDL_ShowCursor(1);
457 458 459 460 461
        if (guest_cursor &&
                (gui_grab || kbd_mouse_is_absolute() || absolute_enabled))
            SDL_SetCursor(guest_sprite);
        else
            SDL_SetCursor(sdl_cursor_normal);
462 463 464
    }
}

465 466
static void sdl_grab_start(void)
{
467 468
    if (guest_cursor) {
        SDL_SetCursor(guest_sprite);
469 470
        if (!kbd_mouse_is_absolute() && !absolute_enabled)
            SDL_WarpMouse(guest_x, guest_y);
471 472
    } else
        sdl_hide_cursor();
473 474 475 476 477 478

    if (SDL_WM_GrabInput(SDL_GRAB_ON) == SDL_GRAB_ON) {
        gui_grab = 1;
        sdl_update_caption();
    } else
        sdl_show_cursor();
479 480 481 482 483 484
}

static void sdl_grab_end(void)
{
    SDL_WM_GrabInput(SDL_GRAB_OFF);
    gui_grab = 0;
485
    sdl_show_cursor();
486
    sdl_update_caption();
487 488
}

489
static void sdl_mouse_mode_change(Notifier *notify, void *data)
A
Anthony Liguori 已提交
490 491 492 493 494 495 496 497 498 499 500 501 502 503 504
{
    if (kbd_mouse_is_absolute()) {
        if (!absolute_enabled) {
            sdl_hide_cursor();
            if (gui_grab) {
                sdl_grab_end();
            }
            absolute_enabled = 1;
        }
    } else if (absolute_enabled) {
	sdl_show_cursor();
	absolute_enabled = 0;
    }
}

A
aurel32 已提交
505
static void sdl_send_mouse_event(int dx, int dy, int dz, int x, int y, int state)
506
{
A
aurel32 已提交
507
    int buttons;
508 509 510 511 512 513 514
    buttons = 0;
    if (state & SDL_BUTTON(SDL_BUTTON_LEFT))
        buttons |= MOUSE_EVENT_LBUTTON;
    if (state & SDL_BUTTON(SDL_BUTTON_RIGHT))
        buttons |= MOUSE_EVENT_RBUTTON;
    if (state & SDL_BUTTON(SDL_BUTTON_MIDDLE))
        buttons |= MOUSE_EVENT_MBUTTON;
515 516

    if (kbd_mouse_is_absolute()) {
A
aurel32 已提交
517 518
       dx = x * 0x7FFF / (width - 1);
       dy = y * 0x7FFF / (height - 1);
519
    } else if (guest_cursor) {
A
aurel32 已提交
520 521 522 523 524 525
        x -= guest_x;
        y -= guest_y;
        guest_x += x;
        guest_y += y;
        dx = x;
        dy = y;
526 527
    }

528 529 530
    kbd_mouse_event(dx, dy, dz, buttons);
}

531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546
static void sdl_scale(DisplayState *ds, int width, int height)
{
    int bpp = real_screen->format->BitsPerPixel;

    if (bpp != 16 && bpp != 32) {
        bpp = 32;
    }
    do_sdl_resize(width, height, bpp);
    scaling_active = 1;
    if (!is_buffer_shared(ds->surface)) {
        ds->surface = qemu_resize_displaysurface(ds, ds_get_width(ds),
                                                 ds_get_height(ds));
        dpy_resize(ds);
    }
}

547 548 549 550
static void toggle_full_screen(DisplayState *ds)
{
    gui_fullscreen = !gui_fullscreen;
    if (gui_fullscreen) {
551 552 553 554 555 556
        gui_saved_width = real_screen->w;
        gui_saved_height = real_screen->h;
        gui_saved_scaling = scaling_active;

        do_sdl_resize(ds_get_width(ds), ds_get_height(ds),
                      ds_get_bits_per_pixel(ds));
S
Stefano Stabellini 已提交
557
        scaling_active = 0;
558

559 560 561
        gui_saved_grab = gui_grab;
        sdl_grab_start();
    } else {
562 563 564 565 566
        if (gui_saved_scaling) {
            sdl_scale(ds, gui_saved_width, gui_saved_height);
        } else {
            do_sdl_resize(ds_get_width(ds), ds_get_height(ds), 0);
        }
567 568 569
        if (!gui_saved_grab)
            sdl_grab_end();
    }
P
pbrook 已提交
570 571
    vga_hw_invalidate();
    vga_hw_update();
572 573
}

574 575 576
static void sdl_refresh(DisplayState *ds)
{
    SDL_Event ev1, *ev = &ev1;
577
    int mod_state;
A
aurel32 已提交
578
    int buttonstate = SDL_GetMouseState(NULL, NULL);
579

580 581 582 583 584
    if (last_vm_running != vm_running) {
        last_vm_running = vm_running;
        sdl_update_caption();
    }

P
pbrook 已提交
585
    vga_hw_update();
A
aurel32 已提交
586
    SDL_EnableUNICODE(!is_graphic_console());
B
bellard 已提交
587

588 589 590
    while (SDL_PollEvent(ev)) {
        switch (ev->type) {
        case SDL_VIDEOEXPOSE:
591
            sdl_update(ds, 0, 0, real_screen->w, real_screen->h);
592 593 594 595
            break;
        case SDL_KEYDOWN:
        case SDL_KEYUP:
            if (ev->type == SDL_KEYDOWN) {
596
                if (alt_grab) {
T
ths 已提交
597 598
                    mod_state = (SDL_GetModState() & (gui_grab_code | KMOD_LSHIFT)) ==
                                (gui_grab_code | KMOD_LSHIFT);
599 600 601 602 603
                } else if (ctrl_grab) {
                    mod_state = (SDL_GetModState() & KMOD_RCTRL) == KMOD_RCTRL;
                } else {
                    mod_state = (SDL_GetModState() & gui_grab_code) ==
                                gui_grab_code;
T
ths 已提交
604
                }
605
                gui_key_modifier_pressed = mod_state;
B
bellard 已提交
606
                if (gui_key_modifier_pressed) {
607 608 609 610
                    int keycode;
                    keycode = sdl_keyevent_to_keycode(&ev->key);
                    switch(keycode) {
                    case 0x21: /* 'f' key on US keyboard */
B
bellard 已提交
611 612 613
                        toggle_full_screen(ds);
                        gui_keysym = 1;
                        break;
614
                    case 0x16: /* 'u' key on US keyboard */
615 616 617 618 619 620
                        if (scaling_active) {
                            scaling_active = 0;
                            sdl_resize(ds);
                            vga_hw_invalidate();
                            vga_hw_update();
                        }
621
                        gui_keysym = 1;
622
                        break;
623
                    case 0x02 ... 0x0a: /* '1' to '9' keys */
624 625
                        /* Reset the modifiers sent to the current console */
                        reset_keys();
626
                        console_select(keycode - 0x02);
P
pbrook 已提交
627
                        if (!is_graphic_console()) {
B
bellard 已提交
628 629 630 631 632 633 634 635 636
                            /* display grab if going to a text console */
                            if (gui_grab)
                                sdl_grab_end();
                        }
                        gui_keysym = 1;
                        break;
                    default:
                        break;
                    }
P
pbrook 已提交
637
                } else if (!is_graphic_console()) {
B
bellard 已提交
638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661
                    int keysym;
                    keysym = 0;
                    if (ev->key.keysym.mod & (KMOD_LCTRL | KMOD_RCTRL)) {
                        switch(ev->key.keysym.sym) {
                        case SDLK_UP: keysym = QEMU_KEY_CTRL_UP; break;
                        case SDLK_DOWN: keysym = QEMU_KEY_CTRL_DOWN; break;
                        case SDLK_LEFT: keysym = QEMU_KEY_CTRL_LEFT; break;
                        case SDLK_RIGHT: keysym = QEMU_KEY_CTRL_RIGHT; break;
                        case SDLK_HOME: keysym = QEMU_KEY_CTRL_HOME; break;
                        case SDLK_END: keysym = QEMU_KEY_CTRL_END; break;
                        case SDLK_PAGEUP: keysym = QEMU_KEY_CTRL_PAGEUP; break;
                        case SDLK_PAGEDOWN: keysym = QEMU_KEY_CTRL_PAGEDOWN; break;
                        default: break;
                        }
                    } else {
                        switch(ev->key.keysym.sym) {
                        case SDLK_UP: keysym = QEMU_KEY_UP; break;
                        case SDLK_DOWN: keysym = QEMU_KEY_DOWN; break;
                        case SDLK_LEFT: keysym = QEMU_KEY_LEFT; break;
                        case SDLK_RIGHT: keysym = QEMU_KEY_RIGHT; break;
                        case SDLK_HOME: keysym = QEMU_KEY_HOME; break;
                        case SDLK_END: keysym = QEMU_KEY_END; break;
                        case SDLK_PAGEUP: keysym = QEMU_KEY_PAGEUP; break;
                        case SDLK_PAGEDOWN: keysym = QEMU_KEY_PAGEDOWN; break;
T
ths 已提交
662 663
                        case SDLK_BACKSPACE: keysym = QEMU_KEY_BACKSPACE; break;
                        case SDLK_DELETE: keysym = QEMU_KEY_DELETE; break;
B
bellard 已提交
664 665 666 667 668 669 670 671
                        default: break;
                        }
                    }
                    if (keysym) {
                        kbd_put_keysym(keysym);
                    } else if (ev->key.keysym.unicode != 0) {
                        kbd_put_keysym(ev->key.keysym.unicode);
                    }
672 673
                }
            } else if (ev->type == SDL_KEYUP) {
T
ths 已提交
674 675 676 677 678 679
                if (!alt_grab) {
                    mod_state = (ev->key.keysym.mod & gui_grab_code);
                } else {
                    mod_state = (ev->key.keysym.mod &
                                 (gui_grab_code | KMOD_LSHIFT));
                }
680 681
                if (!mod_state) {
                    if (gui_key_modifier_pressed) {
682
                        gui_key_modifier_pressed = 0;
B
bellard 已提交
683
                        if (gui_keysym == 0) {
684
                            /* exit/enter grab if pressing Ctrl-Alt */
685 686 687 688 689 690 691 692 693 694
                            if (!gui_grab) {
                                /* if the application is not active,
                                   do not try to enter grab state. It
                                   prevents
                                   'SDL_WM_GrabInput(SDL_GRAB_ON)'
                                   from blocking all the application
                                   (SDL bug). */
                                if (SDL_GetAppState() & SDL_APPACTIVE)
                                    sdl_grab_start();
                            } else {
695
                                sdl_grab_end();
696
                            }
697 698 699
                            /* SDL does not send back all the
                               modifiers key, so we must correct it */
                            reset_keys();
700 701 702 703
                            break;
                        }
                        gui_keysym = 0;
                    }
704 705
                }
            }
706
            if (is_graphic_console() && !gui_keysym)
B
bellard 已提交
707
                sdl_process_key(&ev->key);
708 709
            break;
        case SDL_QUIT:
710 711
            if (!no_quit) {
                no_shutdown = 0;
712
                qemu_system_shutdown_request();
713
            }
714 715
            break;
        case SDL_MOUSEMOTION:
716 717
            if (gui_grab || kbd_mouse_is_absolute() ||
                absolute_enabled) {
A
aurel32 已提交
718 719
                sdl_send_mouse_event(ev->motion.xrel, ev->motion.yrel, 0,
                       ev->motion.x, ev->motion.y, ev->motion.state);
720 721 722 723 724 725
            }
            break;
        case SDL_MOUSEBUTTONDOWN:
        case SDL_MOUSEBUTTONUP:
            {
                SDL_MouseButtonEvent *bev = &ev->button;
726
                if (!gui_grab && !kbd_mouse_is_absolute()) {
727
                    if (ev->type == SDL_MOUSEBUTTONDOWN &&
A
aurel32 已提交
728
                        (bev->button == SDL_BUTTON_LEFT)) {
729 730 731 732
                        /* start grabbing all events */
                        sdl_grab_start();
                    }
                } else {
733 734
                    int dz;
                    dz = 0;
A
aurel32 已提交
735 736 737 738 739
                    if (ev->type == SDL_MOUSEBUTTONDOWN) {
                        buttonstate |= SDL_BUTTON(bev->button);
                    } else {
                        buttonstate &= ~SDL_BUTTON(bev->button);
                    }
740
#ifdef SDL_BUTTON_WHEELUP
741
                    if (bev->button == SDL_BUTTON_WHEELUP && ev->type == SDL_MOUSEBUTTONDOWN) {
742
                        dz = -1;
743
                    } else if (bev->button == SDL_BUTTON_WHEELDOWN && ev->type == SDL_MOUSEBUTTONDOWN) {
744 745
                        dz = 1;
                    }
746
#endif
A
aurel32 已提交
747
                    sdl_send_mouse_event(0, 0, dz, bev->x, bev->y, buttonstate);
748 749 750
                }
            }
            break;
751
        case SDL_ACTIVEEVENT:
752
            if (gui_grab && ev->active.state == SDL_APPINPUTFOCUS &&
753
                !ev->active.gain && !gui_fullscreen) {
754 755
                sdl_grab_end();
            }
A
aurel32 已提交
756 757 758
            if (ev->active.state & SDL_APPACTIVE) {
                if (ev->active.gain) {
                    /* Back to default interval */
759 760
                    dcl->gui_timer_interval = 0;
                    dcl->idle = 0;
A
aurel32 已提交
761 762
                } else {
                    /* Sleeping interval */
763 764
                    dcl->gui_timer_interval = 500;
                    dcl->idle = 1;
A
aurel32 已提交
765 766
                }
            }
767
            break;
768 769
        case SDL_VIDEORESIZE:
            sdl_scale(ds, ev->resize.w, ev->resize.h);
S
Stefano Stabellini 已提交
770 771 772
            vga_hw_invalidate();
            vga_hw_update();
            break;
773 774 775 776 777 778
        default:
            break;
        }
    }
}

779 780 781
static void sdl_fill(DisplayState *ds, int x, int y, int w, int h, uint32_t c)
{
    SDL_Rect dst = { x, y, w, h };
782
    SDL_FillRect(real_screen, &dst, c);
783 784 785 786 787 788 789 790 791
}

static void sdl_mouse_warp(int x, int y, int on)
{
    if (on) {
        if (!guest_cursor)
            sdl_show_cursor();
        if (gui_grab || kbd_mouse_is_absolute() || absolute_enabled) {
            SDL_SetCursor(guest_sprite);
792 793
            if (!kbd_mouse_is_absolute() && !absolute_enabled)
                SDL_WarpMouse(x, y);
794 795 796 797 798 799 800
        }
    } else if (gui_grab)
        sdl_hide_cursor();
    guest_cursor = on;
    guest_x = x, guest_y = y;
}

801
static void sdl_mouse_define(QEMUCursor *c)
802
{
803 804 805
    uint8_t *image, *mask;
    int bpl;

806 807 808
    if (guest_sprite)
        SDL_FreeCursor(guest_sprite);

809 810 811 812 813 814 815 816 817
    bpl = cursor_get_mono_bpl(c);
    image = qemu_mallocz(bpl * c->height);
    mask  = qemu_mallocz(bpl * c->height);
    cursor_get_mono_image(c, 0x000000, image);
    cursor_get_mono_mask(c, 0, mask);
    guest_sprite = SDL_CreateCursor(image, mask, c->width, c->height,
                                    c->hot_x, c->hot_y);
    qemu_free(image);
    qemu_free(mask);
818 819 820 821 822 823

    if (guest_cursor &&
            (gui_grab || kbd_mouse_is_absolute() || absolute_enabled))
        SDL_SetCursor(guest_sprite);
}

824
static void sdl_cleanup(void)
B
bellard 已提交
825
{
826 827
    if (guest_sprite)
        SDL_FreeCursor(guest_sprite);
828
    SDL_QuitSubSystem(SDL_INIT_VIDEO);
B
bellard 已提交
829 830
}

831
void sdl_display_init(DisplayState *ds, int full_screen, int no_frame)
832 833
{
    int flags;
834
    uint8_t data = 0;
835 836
    DisplayAllocator *da;
    const SDL_VideoInfo *vi;
S
Stefan Weil 已提交
837
    char *filename;
838

839 840 841 842 843 844
#if defined(__APPLE__)
    /* always use generic keymaps */
    if (!keyboard_layout)
        keyboard_layout = "en-us";
#endif
    if(keyboard_layout) {
845
        kbd_layout = init_keyboard_layout(name2keysym, keyboard_layout);
846 847 848 849
        if (!kbd_layout)
            exit(1);
    }

850 851 852
    if (no_frame)
        gui_noframe = 1;

853 854 855
    if (!full_screen) {
        setenv("SDL_VIDEO_ALLOW_SCREENSAVER", "1", 0);
    }
856 857 858 859 860 861 862 863 864 865 866 867
#ifdef __linux__
    /* on Linux, SDL may use fbcon|directfb|svgalib when run without
     * accessible $DISPLAY to open X11 window.  This is often the case
     * when qemu is run using sudo.  But in this case, and when actually
     * run in X11 environment, SDL fights with X11 for the video card,
     * making current display unavailable, often until reboot.
     * So make x11 the default SDL video driver if this variable is unset.
     * This is a bit hackish but saves us from bigger problem.
     * Maybe it's a good idea to fix this in SDL instead.
     */
    setenv("SDL_VIDEODRIVER", "x11", 0);
#endif
868

869 870 871 872
    /* Enable normal up/down events for Caps-Lock and Num-Lock keys.
     * This requires SDL >= 1.2.14. */
    setenv("SDL_DISABLE_LOCK_KEYS", "1", 1);

873 874
    flags = SDL_INIT_VIDEO | SDL_INIT_NOPARACHUTE;
    if (SDL_Init (flags)) {
875 876
        fprintf(stderr, "Could not initialize SDL(%s) - exiting\n",
                SDL_GetError());
877 878
        exit(1);
    }
879
    vi = SDL_GetVideoInfo();
S
Stefano Stabellini 已提交
880
    host_format = *(vi->vfmt);
881

S
Stefan Weil 已提交
882 883 884 885 886 887 888 889 890 891 892 893
    /* Load a 32x32x4 image. White pixels are transparent. */
    filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, "qemu-icon.bmp");
    if (filename) {
        SDL_Surface *image = SDL_LoadBMP(filename);
        if (image) {
            uint32_t colorkey = SDL_MapRGB(image->format, 255, 255, 255);
            SDL_SetColorKey(image, SDL_SRCCOLORKEY, colorkey);
            SDL_WM_SetIcon(image, NULL);
        }
        qemu_free(filename);
    }

894 895 896 897 898 899
    dcl = qemu_mallocz(sizeof(DisplayChangeListener));
    dcl->dpy_update = sdl_update;
    dcl->dpy_resize = sdl_resize;
    dcl->dpy_refresh = sdl_refresh;
    dcl->dpy_setdata = sdl_setdata;
    dcl->dpy_fill = sdl_fill;
900 901
    ds->mouse_set = sdl_mouse_warp;
    ds->cursor_define = sdl_mouse_define;
902
    register_displaychangelistener(ds, dcl);
903

904 905 906 907 908 909 910 911
    da = qemu_mallocz(sizeof(DisplayAllocator));
    da->create_displaysurface = sdl_create_displaysurface;
    da->resize_displaysurface = sdl_resize_displaysurface;
    da->free_displaysurface = sdl_free_displaysurface;
    if (register_displayallocator(ds, da) == da) {
        dpy_resize(ds);
    }

A
Anthony Liguori 已提交
912 913 914
    mouse_mode_notifier.notify = sdl_mouse_mode_change;
    qemu_add_mouse_mode_change_notifier(&mouse_mode_notifier);

915
    sdl_update_caption();
916 917
    SDL_EnableKeyRepeat(250, 50);
    gui_grab = 0;
B
bellard 已提交
918

919 920 921
    sdl_cursor_hidden = SDL_CreateCursor(&data, &data, 8, 1, 0, 0);
    sdl_cursor_normal = SDL_GetCursor();

922
    atexit(sdl_cleanup);
B
bellard 已提交
923 924 925 926
    if (full_screen) {
        gui_fullscreen = 1;
        sdl_grab_start();
    }
927
}