sdl.c 27.0 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
#include "qemu-common.h"
32
#include "ui/console.h"
33
#include "sysemu/sysemu.h"
34
#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
static SDL_Cursor *sdl_cursor_normal;
static SDL_Cursor *sdl_cursor_hidden;
static int absolute_enabled = 0;
55 56
static int guest_cursor = 0;
static int guest_x, guest_y;
57
static SDL_Cursor *guest_sprite = NULL;
S
Stefano Stabellini 已提交
58 59
static SDL_PixelFormat host_format;
static int scaling_active = 0;
A
Anthony Liguori 已提交
60
static Notifier mouse_mode_notifier;
61

62 63 64
static void sdl_update(DisplayChangeListener *dcl,
                       DisplayState *ds,
                       int x, int y, int w, int h)
65
{
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
static void sdl_setdata(DisplayChangeListener *dcl,
                        DisplayState *ds)
88 89 90 91 92 93 94
{
    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);
95 96
}

J
Jan Kiszka 已提交
97
static void do_sdl_resize(int width, int height, int bpp)
98 99 100 101 102
{
    int flags;

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

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

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

120 121
static void sdl_resize(DisplayChangeListener *dcl,
                       DisplayState *ds)
122
{
G
Gerd Hoffmann 已提交
123 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));
S
Stefano Stabellini 已提交
128
    }
129
    sdl_setdata(dcl, ds);
130 131
}

132
/* generic keyboard conversion */
B
bellard 已提交
133

134 135
#include "sdl_keysym.h"

A
Anthony Liguori 已提交
136
static kbd_layout_t *kbd_layout = NULL;
137 138

static uint8_t sdl_keyevent_to_keycode_generic(const SDL_KeyboardEvent *ev)
B
bellard 已提交
139
{
140 141 142 143 144
    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 已提交
145 146 147 148
    /* For Japanese key '\' and '|' */
    if (keysym == 92 && ev->keysym.scancode == 133) {
        keysym = 0xa5;
    }
149
    return keysym2scancode(kbd_layout, keysym) & SCANCODE_KEYMASK;
B
bellard 已提交
150 151
}

152 153 154
/* specific keyboard conversions from scan codes */

#if defined(_WIN32)
B
bellard 已提交
155 156 157 158 159 160 161 162

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

#else

163 164 165 166 167 168
#if defined(SDL_VIDEO_DRIVER_X11)
#include <X11/XKBlib.h>

static int check_for_evdev(void)
{
    SDL_SysWMinfo info;
J
Jan Kiszka 已提交
169
    XkbDescPtr desc = NULL;
170
    int has_evdev = 0;
J
Jan Kiszka 已提交
171
    char *keycodes = NULL;
172 173

    SDL_VERSION(&info.version);
J
Jan Kiszka 已提交
174
    if (!SDL_GetWMInfo(&info)) {
175
        return 0;
J
Jan Kiszka 已提交
176
    }
177 178 179
    desc = XkbGetKeyboard(info.info.x11.display,
                          XkbGBN_AllComponentsMask,
                          XkbUseCoreKbd);
J
Jan Kiszka 已提交
180 181 182 183 184 185 186 187 188 189 190
    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);
        }
    }
191

J
Jan Kiszka 已提交
192 193 194 195 196 197
    if (desc) {
        XkbFreeKeyboard(desc, XkbGBN_AllComponentsMask, True);
    }
    if (keycodes) {
        XFree(keycodes);
    }
198 199 200 201 202 203 204 205 206
    return has_evdev;
}
#else
static int check_for_evdev(void)
{
	return 0;
}
#endif

B
bellard 已提交
207 208 209
static uint8_t sdl_keyevent_to_keycode(const SDL_KeyboardEvent *ev)
{
    int keycode;
210 211 212 213
    static int has_evdev = -1;

    if (has_evdev == -1)
        has_evdev = check_for_evdev();
B
bellard 已提交
214 215 216 217 218 219 220

    keycode = ev->keysym.scancode;

    if (keycode < 9) {
        keycode = 0;
    } else if (keycode < 97) {
        keycode -= 8; /* just an offset */
221
    } else if (keycode < 158) {
B
bellard 已提交
222
        /* use conversion table */
223 224 225 226 227 228 229 230
        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 已提交
231 232 233 234 235 236 237 238
    } else {
        keycode = 0;
    }
    return keycode;
}

#endif

239 240 241 242 243
static void reset_keys(void)
{
    int i;
    for(i = 0; i < 256; i++) {
        if (modifiers_state[i]) {
244 245 246
            if (i & SCANCODE_GREY)
                kbd_put_keycode(SCANCODE_EMUL0);
            kbd_put_keycode(i | SCANCODE_UP);
247 248 249 250 251
            modifiers_state[i] = 0;
        }
    }
}

252 253
static void sdl_process_key(SDL_KeyboardEvent *ev)
{
254
    int keycode, v;
255 256 257 258 259

    if (ev->keysym.sym == SDLK_PAUSE) {
        /* specific case */
        v = 0;
        if (ev->type == SDL_KEYUP)
260
            v |= SCANCODE_UP;
261 262 263 264 265 266
        kbd_put_keycode(0xe1);
        kbd_put_keycode(0x1d | v);
        kbd_put_keycode(0x45 | v);
        return;
    }

267 268 269 270 271
    if (kbd_layout) {
        keycode = sdl_keyevent_to_keycode_generic(ev);
    } else {
        keycode = sdl_keyevent_to_keycode(ev);
    }
272 273 274 275

    switch(keycode) {
    case 0x00:
        /* sent when leaving window: reset the modifiers state */
276
        reset_keys();
277 278 279 280 281 282 283
        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 */
284
        if (ev->type == SDL_KEYUP)
285 286 287 288
            modifiers_state[keycode] = 0;
        else
            modifiers_state[keycode] = 1;
        break;
289 290 291
#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. */
292 293 294 295
    case 0x45: /* num lock */
    case 0x3a: /* caps lock */
        /* SDL does not send the key up event, so we generate it */
        kbd_put_keycode(keycode);
296
        kbd_put_keycode(keycode | SCANCODE_UP);
297
        return;
298
#endif
299
    }
300 301

    /* now send the key code */
302 303
    if (keycode & SCANCODE_GREY)
        kbd_put_keycode(SCANCODE_EMUL0);
304
    if (ev->type == SDL_KEYUP)
305
        kbd_put_keycode(keycode | SCANCODE_UP);
306
    else
307
        kbd_put_keycode(keycode & SCANCODE_KEYCODEMASK);
308 309
}

310 311
static void sdl_update_caption(void)
{
312 313
    char win_title[1024];
    char icon_title[1024];
T
ths 已提交
314 315
    const char *status = "";

316
    if (!runstate_is_running())
T
ths 已提交
317
        status = " [Stopped]";
T
ths 已提交
318
    else if (gui_grab) {
319
        if (alt_grab)
320
            status = " - Press Ctrl-Alt-Shift to exit mouse grab";
321
        else if (ctrl_grab)
322
            status = " - Press Right-Ctrl to exit mouse grab";
323
        else
324
            status = " - Press Ctrl-Alt to exit mouse grab";
T
ths 已提交
325
    }
T
ths 已提交
326

327 328 329 330 331 332 333
    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 已提交
334

335
    SDL_WM_SetCaption(win_title, icon_title);
336 337
}

338 339
static void sdl_hide_cursor(void)
{
340 341 342
    if (!cursor_hide)
        return;

343 344 345 346 347 348
    if (kbd_mouse_is_absolute()) {
        SDL_ShowCursor(1);
        SDL_SetCursor(sdl_cursor_hidden);
    } else {
        SDL_ShowCursor(0);
    }
349 350 351 352
}

static void sdl_show_cursor(void)
{
353 354 355
    if (!cursor_hide)
        return;

356
    if (!kbd_mouse_is_absolute() || !is_graphic_console()) {
357
        SDL_ShowCursor(1);
358 359 360 361 362
        if (guest_cursor &&
                (gui_grab || kbd_mouse_is_absolute() || absolute_enabled))
            SDL_SetCursor(guest_sprite);
        else
            SDL_SetCursor(sdl_cursor_normal);
363 364 365
    }
}

366 367
static void sdl_grab_start(void)
{
368 369 370 371 372 373 374 375
    /*
     * If the application is not active, do not try to enter grab state. This
     * prevents 'SDL_WM_GrabInput(SDL_GRAB_ON)' from blocking all the
     * application (SDL bug).
     */
    if (!(SDL_GetAppState() & SDL_APPINPUTFOCUS)) {
        return;
    }
376 377
    if (guest_cursor) {
        SDL_SetCursor(guest_sprite);
378 379
        if (!kbd_mouse_is_absolute() && !absolute_enabled)
            SDL_WarpMouse(guest_x, guest_y);
380 381
    } else
        sdl_hide_cursor();
382 383 384
    SDL_WM_GrabInput(SDL_GRAB_ON);
    gui_grab = 1;
    sdl_update_caption();
385 386 387 388 389 390
}

static void sdl_grab_end(void)
{
    SDL_WM_GrabInput(SDL_GRAB_OFF);
    gui_grab = 0;
391
    sdl_show_cursor();
392
    sdl_update_caption();
393 394
}

395 396 397 398
static void absolute_mouse_grab(void)
{
    int mouse_x, mouse_y;

399 400 401 402
    SDL_GetMouseState(&mouse_x, &mouse_y);
    if (mouse_x > 0 && mouse_x < real_screen->w - 1 &&
        mouse_y > 0 && mouse_y < real_screen->h - 1) {
        sdl_grab_start();
403 404 405
    }
}

406
static void sdl_mouse_mode_change(Notifier *notify, void *data)
A
Anthony Liguori 已提交
407 408 409 410
{
    if (kbd_mouse_is_absolute()) {
        if (!absolute_enabled) {
            absolute_enabled = 1;
411 412 413
            if (is_graphic_console()) {
                absolute_mouse_grab();
            }
A
Anthony Liguori 已提交
414 415
        }
    } else if (absolute_enabled) {
416 417 418
        if (!gui_fullscreen) {
            sdl_grab_end();
        }
419
        absolute_enabled = 0;
A
Anthony Liguori 已提交
420 421 422
    }
}

A
aurel32 已提交
423
static void sdl_send_mouse_event(int dx, int dy, int dz, int x, int y, int state)
424
{
J
Jan Kiszka 已提交
425 426 427
    int buttons = 0;

    if (state & SDL_BUTTON(SDL_BUTTON_LEFT)) {
428
        buttons |= MOUSE_EVENT_LBUTTON;
J
Jan Kiszka 已提交
429 430
    }
    if (state & SDL_BUTTON(SDL_BUTTON_RIGHT)) {
431
        buttons |= MOUSE_EVENT_RBUTTON;
J
Jan Kiszka 已提交
432 433
    }
    if (state & SDL_BUTTON(SDL_BUTTON_MIDDLE)) {
434
        buttons |= MOUSE_EVENT_MBUTTON;
J
Jan Kiszka 已提交
435
    }
436 437

    if (kbd_mouse_is_absolute()) {
J
Jan Kiszka 已提交
438 439
        dx = x * 0x7FFF / (real_screen->w - 1);
        dy = y * 0x7FFF / (real_screen->h - 1);
440
    } else if (guest_cursor) {
A
aurel32 已提交
441 442 443 444 445 446
        x -= guest_x;
        y -= guest_y;
        guest_x += x;
        guest_y += y;
        dx = x;
        dy = y;
447 448
    }

449 450 451
    kbd_mouse_event(dx, dy, dz, buttons);
}

452 453 454 455 456 457 458 459 460 461 462
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;
}

463 464 465 466
static void toggle_full_screen(DisplayState *ds)
{
    gui_fullscreen = !gui_fullscreen;
    if (gui_fullscreen) {
467 468 469 470 471 472
        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 已提交
473
        scaling_active = 0;
474

475 476 477
        gui_saved_grab = gui_grab;
        sdl_grab_start();
    } else {
478 479 480 481 482
        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);
        }
483
        if (!gui_saved_grab || !is_graphic_console()) {
484
            sdl_grab_end();
485
        }
486
    }
P
pbrook 已提交
487 488
    vga_hw_invalidate();
    vga_hw_update();
489 490
}

491
static void handle_keydown(DisplayState *ds, SDL_Event *ev)
492
{
493
    int mod_state;
494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515
    int keycode;

    if (alt_grab) {
        mod_state = (SDL_GetModState() & (gui_grab_code | KMOD_LSHIFT)) ==
                    (gui_grab_code | KMOD_LSHIFT);
    } else if (ctrl_grab) {
        mod_state = (SDL_GetModState() & KMOD_RCTRL) == KMOD_RCTRL;
    } else {
        mod_state = (SDL_GetModState() & gui_grab_code) == gui_grab_code;
    }
    gui_key_modifier_pressed = mod_state;

    if (gui_key_modifier_pressed) {
        keycode = sdl_keyevent_to_keycode(&ev->key);
        switch (keycode) {
        case 0x21: /* 'f' key on US keyboard */
            toggle_full_screen(ds);
            gui_keysym = 1;
            break;
        case 0x16: /* 'u' key on US keyboard */
            if (scaling_active) {
                scaling_active = 0;
516
                sdl_resize(dcl, ds);
517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649
                vga_hw_invalidate();
                vga_hw_update();
            }
            gui_keysym = 1;
            break;
        case 0x02 ... 0x0a: /* '1' to '9' keys */
            /* Reset the modifiers sent to the current console */
            reset_keys();
            console_select(keycode - 0x02);
            gui_keysym = 1;
            if (gui_fullscreen) {
                break;
            }
            if (!is_graphic_console()) {
                /* release grab if going to a text console */
                if (gui_grab) {
                    sdl_grab_end();
                } else if (absolute_enabled) {
                    sdl_show_cursor();
                }
            } else if (absolute_enabled) {
                sdl_hide_cursor();
                absolute_mouse_grab();
            }
            break;
        case 0x1b: /* '+' */
        case 0x35: /* '-' */
            if (!gui_fullscreen) {
                int width = MAX(real_screen->w + (keycode == 0x1b ? 50 : -50),
                                160);
                int height = (ds_get_height(ds) * width) / ds_get_width(ds);

                sdl_scale(ds, width, height);
                vga_hw_invalidate();
                vga_hw_update();
                gui_keysym = 1;
            }
        default:
            break;
        }
    } else if (!is_graphic_console()) {
        int 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;
            case SDLK_BACKSPACE:
                keysym = QEMU_KEY_BACKSPACE;
                break;
            case SDLK_DELETE:
                keysym = QEMU_KEY_DELETE;
                break;
            default:
                break;
            }
        }
        if (keysym) {
            kbd_put_keysym(keysym);
        } else if (ev->key.keysym.unicode != 0) {
            kbd_put_keysym(ev->key.keysym.unicode);
        }
    }
    if (is_graphic_console() && !gui_keysym) {
        sdl_process_key(&ev->key);
    }
}

static void handle_keyup(DisplayState *ds, SDL_Event *ev)
{
    int mod_state;

    if (!alt_grab) {
        mod_state = (ev->key.keysym.mod & gui_grab_code);
    } else {
        mod_state = (ev->key.keysym.mod & (gui_grab_code | KMOD_LSHIFT));
    }
    if (!mod_state && gui_key_modifier_pressed) {
        gui_key_modifier_pressed = 0;
        if (gui_keysym == 0) {
            /* exit/enter grab if pressing Ctrl-Alt */
            if (!gui_grab) {
650
                if (is_graphic_console()) {
651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679
                    sdl_grab_start();
                }
            } else if (!gui_fullscreen) {
                sdl_grab_end();
            }
            /* SDL does not send back all the modifiers key, so we must
             * correct it. */
            reset_keys();
            return;
        }
        gui_keysym = 0;
    }
    if (is_graphic_console() && !gui_keysym) {
        sdl_process_key(&ev->key);
    }
}

static void handle_mousemotion(DisplayState *ds, SDL_Event *ev)
{
    int max_x, max_y;

    if (is_graphic_console() &&
        (kbd_mouse_is_absolute() || absolute_enabled)) {
        max_x = real_screen->w - 1;
        max_y = real_screen->h - 1;
        if (gui_grab && (ev->motion.x == 0 || ev->motion.y == 0 ||
            ev->motion.x == max_x || ev->motion.y == max_y)) {
            sdl_grab_end();
        }
680
        if (!gui_grab &&
681 682 683 684 685 686 687 688 689 690 691 692 693
            (ev->motion.x > 0 && ev->motion.x < max_x &&
            ev->motion.y > 0 && ev->motion.y < max_y)) {
            sdl_grab_start();
        }
    }
    if (gui_grab || kbd_mouse_is_absolute() || absolute_enabled) {
        sdl_send_mouse_event(ev->motion.xrel, ev->motion.yrel, 0,
                             ev->motion.x, ev->motion.y, ev->motion.state);
    }
}

static void handle_mousebutton(DisplayState *ds, SDL_Event *ev)
{
A
aurel32 已提交
694
    int buttonstate = SDL_GetMouseState(NULL, NULL);
695 696 697 698 699 700 701 702 703
    SDL_MouseButtonEvent *bev;
    int dz;

    if (!is_graphic_console()) {
        return;
    }

    bev = &ev->button;
    if (!gui_grab && !kbd_mouse_is_absolute()) {
704
        if (ev->type == SDL_MOUSEBUTTONUP && bev->button == SDL_BUTTON_LEFT) {
705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729
            /* start grabbing all events */
            sdl_grab_start();
        }
    } else {
        dz = 0;
        if (ev->type == SDL_MOUSEBUTTONDOWN) {
            buttonstate |= SDL_BUTTON(bev->button);
        } else {
            buttonstate &= ~SDL_BUTTON(bev->button);
        }
#ifdef SDL_BUTTON_WHEELUP
        if (bev->button == SDL_BUTTON_WHEELUP &&
            ev->type == SDL_MOUSEBUTTONDOWN) {
            dz = -1;
        } else if (bev->button == SDL_BUTTON_WHEELDOWN &&
                   ev->type == SDL_MOUSEBUTTONDOWN) {
            dz = 1;
        }
#endif
        sdl_send_mouse_event(0, 0, dz, bev->x, bev->y, buttonstate);
    }
}

static void handle_activation(DisplayState *ds, SDL_Event *ev)
{
730 731 732
#ifdef _WIN32
    /* Disable grab if the window no longer has the focus
     * (Windows-only workaround) */
733 734 735 736
    if (gui_grab && ev->active.state == SDL_APPINPUTFOCUS &&
        !ev->active.gain && !gui_fullscreen) {
        sdl_grab_end();
    }
737
#endif
738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754
    if (!gui_grab && ev->active.gain && is_graphic_console() &&
        (kbd_mouse_is_absolute() || absolute_enabled)) {
        absolute_mouse_grab();
    }
    if (ev->active.state & SDL_APPACTIVE) {
        if (ev->active.gain) {
            /* Back to default interval */
            dcl->gui_timer_interval = 0;
            dcl->idle = 0;
        } else {
            /* Sleeping interval */
            dcl->gui_timer_interval = 500;
            dcl->idle = 1;
        }
    }
}

755 756
static void sdl_refresh(DisplayChangeListener *dcl,
                        DisplayState *ds)
757 758
{
    SDL_Event ev1, *ev = &ev1;
759

760 761
    if (last_vm_running != runstate_is_running()) {
        last_vm_running = runstate_is_running();
762 763 764
        sdl_update_caption();
    }

P
pbrook 已提交
765
    vga_hw_update();
A
aurel32 已提交
766
    SDL_EnableUNICODE(!is_graphic_console());
B
bellard 已提交
767

768 769 770
    while (SDL_PollEvent(ev)) {
        switch (ev->type) {
        case SDL_VIDEOEXPOSE:
771
            sdl_update(dcl, ds, 0, 0, real_screen->w, real_screen->h);
772 773
            break;
        case SDL_KEYDOWN:
774 775
            handle_keydown(ds, ev);
            break;
776
        case SDL_KEYUP:
777
            handle_keyup(ds, ev);
778 779
            break;
        case SDL_QUIT:
780 781
            if (!no_quit) {
                no_shutdown = 0;
782
                qemu_system_shutdown_request();
783
            }
784 785
            break;
        case SDL_MOUSEMOTION:
786
            handle_mousemotion(ds, ev);
787 788 789
            break;
        case SDL_MOUSEBUTTONDOWN:
        case SDL_MOUSEBUTTONUP:
790
            handle_mousebutton(ds, ev);
791
            break;
792
        case SDL_ACTIVEEVENT:
793
            handle_activation(ds, ev);
794
            break;
795 796
        case SDL_VIDEORESIZE:
            sdl_scale(ds, ev->resize.w, ev->resize.h);
S
Stefano Stabellini 已提交
797 798 799
            vga_hw_invalidate();
            vga_hw_update();
            break;
800 801 802 803 804 805
        default:
            break;
        }
    }
}

806 807 808
static void sdl_mouse_warp(DisplayChangeListener *dcl,
                           DisplayState *ds,
                           int x, int y, int on)
809 810 811 812 813 814
{
    if (on) {
        if (!guest_cursor)
            sdl_show_cursor();
        if (gui_grab || kbd_mouse_is_absolute() || absolute_enabled) {
            SDL_SetCursor(guest_sprite);
815 816
            if (!kbd_mouse_is_absolute() && !absolute_enabled)
                SDL_WarpMouse(x, y);
817 818 819 820 821 822 823
        }
    } else if (gui_grab)
        sdl_hide_cursor();
    guest_cursor = on;
    guest_x = x, guest_y = y;
}

824 825 826
static void sdl_mouse_define(DisplayChangeListener *dcl,
                             DisplayState *ds,
                             QEMUCursor *c)
827
{
828 829 830
    uint8_t *image, *mask;
    int bpl;

831 832 833
    if (guest_sprite)
        SDL_FreeCursor(guest_sprite);

834
    bpl = cursor_get_mono_bpl(c);
835 836
    image = g_malloc0(bpl * c->height);
    mask  = g_malloc0(bpl * c->height);
837 838 839 840
    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);
841 842
    g_free(image);
    g_free(mask);
843 844 845 846 847 848

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

849
static void sdl_cleanup(void)
B
bellard 已提交
850
{
851 852
    if (guest_sprite)
        SDL_FreeCursor(guest_sprite);
853
    SDL_QuitSubSystem(SDL_INIT_VIDEO);
B
bellard 已提交
854 855
}

856 857 858 859 860 861 862 863 864 865
static const DisplayChangeListenerOps dcl_ops = {
    .dpy_name          = "sdl",
    .dpy_gfx_update    = sdl_update,
    .dpy_gfx_resize    = sdl_resize,
    .dpy_refresh       = sdl_refresh,
    .dpy_gfx_setdata   = sdl_setdata,
    .dpy_mouse_set     = sdl_mouse_warp,
    .dpy_cursor_define = sdl_mouse_define,
};

866
void sdl_display_init(DisplayState *ds, int full_screen, int no_frame)
867 868
{
    int flags;
869
    uint8_t data = 0;
870
    const SDL_VideoInfo *vi;
S
Stefan Weil 已提交
871
    char *filename;
872

873 874 875 876 877 878
#if defined(__APPLE__)
    /* always use generic keymaps */
    if (!keyboard_layout)
        keyboard_layout = "en-us";
#endif
    if(keyboard_layout) {
879
        kbd_layout = init_keyboard_layout(name2keysym, keyboard_layout);
880 881 882 883
        if (!kbd_layout)
            exit(1);
    }

884 885 886
    if (no_frame)
        gui_noframe = 1;

887 888 889
    if (!full_screen) {
        setenv("SDL_VIDEO_ALLOW_SCREENSAVER", "1", 0);
    }
890 891 892 893 894 895 896 897 898 899 900 901
#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
902

903 904 905 906
    /* 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);

907 908
    flags = SDL_INIT_VIDEO | SDL_INIT_NOPARACHUTE;
    if (SDL_Init (flags)) {
909 910
        fprintf(stderr, "Could not initialize SDL(%s) - exiting\n",
                SDL_GetError());
911 912
        exit(1);
    }
913
    vi = SDL_GetVideoInfo();
S
Stefano Stabellini 已提交
914
    host_format = *(vi->vfmt);
915

S
Stefan Weil 已提交
916 917 918 919 920 921 922 923 924
    /* 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);
        }
925
        g_free(filename);
S
Stefan Weil 已提交
926 927
    }

928 929 930 931 932
    if (full_screen) {
        gui_fullscreen = 1;
        sdl_grab_start();
    }

933
    dcl = g_malloc0(sizeof(DisplayChangeListener));
934
    dcl->ops = &dcl_ops;
935
    register_displaychangelistener(ds, dcl);
936

A
Anthony Liguori 已提交
937 938 939
    mouse_mode_notifier.notify = sdl_mouse_mode_change;
    qemu_add_mouse_mode_change_notifier(&mouse_mode_notifier);

940
    sdl_update_caption();
941 942
    SDL_EnableKeyRepeat(250, 50);
    gui_grab = 0;
B
bellard 已提交
943

944 945 946
    sdl_cursor_hidden = SDL_CreateCursor(&data, &data, 8, 1, 0, 0);
    sdl_cursor_normal = SDL_GetCursor();

947
    atexit(sdl_cleanup);
948
}