sdl.c 27.6 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 30

#if SDL_MAJOR_VERSION == 1
31
#include <SDL_syswm.h>
32

33
#include "qemu-common.h"
34
#include "ui/console.h"
35
#include "ui/input.h"
36
#include "sysemu/sysemu.h"
37
#include "x_keymap.h"
S
Stefano Stabellini 已提交
38
#include "sdl_zoom.h"
39

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

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

76
    if (guest_screen) {
S
Stefano Stabellini 已提交
77 78 79 80 81 82 83 84 85 86
        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);
87 88
}

J
Jan Kiszka 已提交
89
static void do_sdl_resize(int width, int height, int bpp)
90 91
{
    int flags;
92
    SDL_Surface *tmp_screen;
93 94 95

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

96 97
    flags = SDL_HWSURFACE | SDL_ASYNCBLIT | SDL_HWACCEL;
    if (gui_fullscreen) {
98
        flags |= SDL_FULLSCREEN;
99 100 101
    } else {
        flags |= SDL_RESIZABLE;
    }
102 103
    if (gui_noframe)
        flags |= SDL_NOFRAME;
B
bellard 已提交
104

105
    tmp_screen = SDL_SetVideoMode(width, height, bpp, flags);
106
    if (!real_screen) {
107 108 109 110 111 112 113 114 115 116 117 118 119 120 121
        if (!tmp_screen) {
            fprintf(stderr, "Could not open SDL display (%dx%dx%d): %s\n",
                    width, height, bpp, SDL_GetError());
            exit(1);
        }
    } else {
        /*
         * Revert to the previous video mode if the change of resizing or
         * resolution failed.
         */
        if (!tmp_screen) {
            fprintf(stderr, "Failed to set SDL display (%dx%dx%d): %s\n",
                    width, height, bpp, SDL_GetError());
            return;
        }
122
    }
123 124

    real_screen = tmp_screen;
125 126
}

127
static void sdl_switch(DisplayChangeListener *dcl,
G
Gerd Hoffmann 已提交
128
                       DisplaySurface *new_surface)
129
{
G
Gerd Hoffmann 已提交
130 131 132 133 134 135

    /* temporary hack: allows to call sdl_switch to handle scaling changes */
    if (new_surface) {
        surface = new_surface;
    }

G
Gerd Hoffmann 已提交
136
    if (!scaling_active) {
G
Gerd Hoffmann 已提交
137 138 139
        do_sdl_resize(surface_width(surface), surface_height(surface), 0);
    } else if (real_screen->format->BitsPerPixel !=
               surface_bits_per_pixel(surface)) {
G
Gerd Hoffmann 已提交
140
        do_sdl_resize(real_screen->w, real_screen->h,
G
Gerd Hoffmann 已提交
141
                      surface_bits_per_pixel(surface));
S
Stefano Stabellini 已提交
142
    }
G
Gerd Hoffmann 已提交
143 144 145 146 147 148 149 150 151 152

    if (guest_screen != NULL) {
        SDL_FreeSurface(guest_screen);
    }
    guest_screen = SDL_CreateRGBSurfaceFrom
        (surface_data(surface),
         surface_width(surface), surface_height(surface),
         surface_bits_per_pixel(surface), surface_stride(surface),
         surface->pf.rmask, surface->pf.gmask,
         surface->pf.bmask, surface->pf.amask);
153 154
}

155
/* generic keyboard conversion */
B
bellard 已提交
156

157 158
#include "sdl_keysym.h"

A
Anthony Liguori 已提交
159
static kbd_layout_t *kbd_layout = NULL;
160 161

static uint8_t sdl_keyevent_to_keycode_generic(const SDL_KeyboardEvent *ev)
B
bellard 已提交
162
{
163 164 165 166 167
    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 已提交
168 169 170 171
    /* For Japanese key '\' and '|' */
    if (keysym == 92 && ev->keysym.scancode == 133) {
        keysym = 0xa5;
    }
172
    return keysym2scancode(kbd_layout, keysym) & SCANCODE_KEYMASK;
B
bellard 已提交
173 174
}

175 176 177
/* specific keyboard conversions from scan codes */

#if defined(_WIN32)
B
bellard 已提交
178 179 180 181 182 183 184 185

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

#else

186 187 188 189 190 191
#if defined(SDL_VIDEO_DRIVER_X11)
#include <X11/XKBlib.h>

static int check_for_evdev(void)
{
    SDL_SysWMinfo info;
J
Jan Kiszka 已提交
192
    XkbDescPtr desc = NULL;
193
    int has_evdev = 0;
J
Jan Kiszka 已提交
194
    char *keycodes = NULL;
195 196

    SDL_VERSION(&info.version);
J
Jan Kiszka 已提交
197
    if (!SDL_GetWMInfo(&info)) {
198
        return 0;
J
Jan Kiszka 已提交
199
    }
200 201 202
    desc = XkbGetKeyboard(info.info.x11.display,
                          XkbGBN_AllComponentsMask,
                          XkbUseCoreKbd);
J
Jan Kiszka 已提交
203 204 205 206 207 208 209 210 211 212 213
    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);
        }
    }
214

J
Jan Kiszka 已提交
215 216 217 218 219 220
    if (desc) {
        XkbFreeKeyboard(desc, XkbGBN_AllComponentsMask, True);
    }
    if (keycodes) {
        XFree(keycodes);
    }
221 222 223 224 225 226 227 228 229
    return has_evdev;
}
#else
static int check_for_evdev(void)
{
	return 0;
}
#endif

B
bellard 已提交
230 231 232
static uint8_t sdl_keyevent_to_keycode(const SDL_KeyboardEvent *ev)
{
    int keycode;
233 234 235 236
    static int has_evdev = -1;

    if (has_evdev == -1)
        has_evdev = check_for_evdev();
B
bellard 已提交
237 238 239 240 241 242 243

    keycode = ev->keysym.scancode;

    if (keycode < 9) {
        keycode = 0;
    } else if (keycode < 97) {
        keycode -= 8; /* just an offset */
244
    } else if (keycode < 158) {
B
bellard 已提交
245
        /* use conversion table */
246 247 248 249 250 251 252 253
        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 已提交
254 255 256 257 258 259 260 261
    } else {
        keycode = 0;
    }
    return keycode;
}

#endif

262 263 264 265 266
static void reset_keys(void)
{
    int i;
    for(i = 0; i < 256; i++) {
        if (modifiers_state[i]) {
267
            qemu_input_event_send_key_number(dcl->con, i, false);
268 269 270 271 272
            modifiers_state[i] = 0;
        }
    }
}

273 274
static void sdl_process_key(SDL_KeyboardEvent *ev)
{
275
    int keycode;
276 277 278

    if (ev->keysym.sym == SDLK_PAUSE) {
        /* specific case */
279 280
        qemu_input_event_send_key_qcode(dcl->con, Q_KEY_CODE_PAUSE,
                                        ev->type == SDL_KEYDOWN);
281 282 283
        return;
    }

284 285 286 287 288
    if (kbd_layout) {
        keycode = sdl_keyevent_to_keycode_generic(ev);
    } else {
        keycode = sdl_keyevent_to_keycode(ev);
    }
289 290 291 292

    switch(keycode) {
    case 0x00:
        /* sent when leaving window: reset the modifiers state */
293
        reset_keys();
294 295 296 297 298 299 300
        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 */
301
        if (ev->type == SDL_KEYUP)
302 303 304 305
            modifiers_state[keycode] = 0;
        else
            modifiers_state[keycode] = 1;
        break;
306 307 308
#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. */
309 310 311
    case 0x45: /* num lock */
    case 0x3a: /* caps lock */
        /* SDL does not send the key up event, so we generate it */
312 313
        qemu_input_event_send_key_number(dcl->con, keycode, true);
        qemu_input_event_send_key_number(dcl->con, keycode, false);
314
        return;
315
#endif
316
    }
317 318

    /* now send the key code */
319 320
    qemu_input_event_send_key_number(dcl->con, keycode,
                                     ev->type == SDL_KEYDOWN);
321 322
}

323 324
static void sdl_update_caption(void)
{
325 326
    char win_title[1024];
    char icon_title[1024];
T
ths 已提交
327 328
    const char *status = "";

329
    if (!runstate_is_running())
T
ths 已提交
330
        status = " [Stopped]";
T
ths 已提交
331
    else if (gui_grab) {
332
        if (alt_grab)
333
            status = " - Press Ctrl-Alt-Shift to exit mouse grab";
334
        else if (ctrl_grab)
335
            status = " - Press Right-Ctrl to exit mouse grab";
336
        else
337
            status = " - Press Ctrl-Alt to exit mouse grab";
T
ths 已提交
338
    }
T
ths 已提交
339

340 341 342 343 344 345 346
    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 已提交
347

348
    SDL_WM_SetCaption(win_title, icon_title);
349 350
}

351 352
static void sdl_hide_cursor(void)
{
353 354 355
    if (!cursor_hide)
        return;

356
    if (qemu_input_is_absolute()) {
357 358 359 360 361
        SDL_ShowCursor(1);
        SDL_SetCursor(sdl_cursor_hidden);
    } else {
        SDL_ShowCursor(0);
    }
362 363 364 365
}

static void sdl_show_cursor(void)
{
366 367 368
    if (!cursor_hide)
        return;

369
    if (!qemu_input_is_absolute() || !qemu_console_is_graphic(NULL)) {
370
        SDL_ShowCursor(1);
371
        if (guest_cursor &&
372
                (gui_grab || qemu_input_is_absolute() || absolute_enabled))
373 374 375
            SDL_SetCursor(guest_sprite);
        else
            SDL_SetCursor(sdl_cursor_normal);
376 377 378
    }
}

379 380
static void sdl_grab_start(void)
{
381 382 383 384 385 386 387 388
    /*
     * 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;
    }
389 390
    if (guest_cursor) {
        SDL_SetCursor(guest_sprite);
391
        if (!qemu_input_is_absolute() && !absolute_enabled) {
392
            SDL_WarpMouse(guest_x, guest_y);
393
        }
394 395
    } else
        sdl_hide_cursor();
396 397 398
    SDL_WM_GrabInput(SDL_GRAB_ON);
    gui_grab = 1;
    sdl_update_caption();
399 400 401 402 403 404
}

static void sdl_grab_end(void)
{
    SDL_WM_GrabInput(SDL_GRAB_OFF);
    gui_grab = 0;
405
    sdl_show_cursor();
406
    sdl_update_caption();
407 408
}

409 410 411 412
static void absolute_mouse_grab(void)
{
    int mouse_x, mouse_y;

413 414 415 416
    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();
417 418 419
    }
}

420
static void sdl_mouse_mode_change(Notifier *notify, void *data)
A
Anthony Liguori 已提交
421
{
422
    if (qemu_input_is_absolute()) {
A
Anthony Liguori 已提交
423 424
        if (!absolute_enabled) {
            absolute_enabled = 1;
G
Gerd Hoffmann 已提交
425
            if (qemu_console_is_graphic(NULL)) {
426 427
                absolute_mouse_grab();
            }
A
Anthony Liguori 已提交
428 429
        }
    } else if (absolute_enabled) {
430 431 432
        if (!gui_fullscreen) {
            sdl_grab_end();
        }
433
        absolute_enabled = 0;
A
Anthony Liguori 已提交
434 435 436
    }
}

437
static void sdl_send_mouse_event(int dx, int dy, int x, int y, int state)
438
{
439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457
    static uint32_t bmap[INPUT_BUTTON_MAX] = {
        [INPUT_BUTTON_LEFT]       = SDL_BUTTON(SDL_BUTTON_LEFT),
        [INPUT_BUTTON_MIDDLE]     = SDL_BUTTON(SDL_BUTTON_MIDDLE),
        [INPUT_BUTTON_RIGHT]      = SDL_BUTTON(SDL_BUTTON_RIGHT),
        [INPUT_BUTTON_WHEEL_UP]   = SDL_BUTTON(SDL_BUTTON_WHEELUP),
        [INPUT_BUTTON_WHEEL_DOWN] = SDL_BUTTON(SDL_BUTTON_WHEELDOWN),
    };
    static uint32_t prev_state;

    if (prev_state != state) {
        qemu_input_update_buttons(dcl->con, bmap, prev_state, state);
        prev_state = state;
    }

    if (qemu_input_is_absolute()) {
        qemu_input_queue_abs(dcl->con, INPUT_AXIS_X, x,
                             real_screen->w);
        qemu_input_queue_abs(dcl->con, INPUT_AXIS_Y, y,
                             real_screen->h);
458 459 460 461 462 463 464 465 466 467 468
    } else {
        if (guest_cursor) {
            x -= guest_x;
            y -= guest_y;
            guest_x += x;
            guest_y += y;
            dx = x;
            dy = y;
        }
        qemu_input_queue_rel(dcl->con, INPUT_AXIS_X, dx);
        qemu_input_queue_rel(dcl->con, INPUT_AXIS_Y, dy);
469
    }
470
    qemu_input_event_sync();
471 472
}

G
Gerd Hoffmann 已提交
473
static void sdl_scale(int width, int height)
474 475 476 477 478 479 480 481 482 483
{
    int bpp = real_screen->format->BitsPerPixel;

    if (bpp != 16 && bpp != 32) {
        bpp = 32;
    }
    do_sdl_resize(width, height, bpp);
    scaling_active = 1;
}

G
Gerd Hoffmann 已提交
484
static void toggle_full_screen(void)
485
{
G
Gerd Hoffmann 已提交
486 487 488 489
    int width = surface_width(surface);
    int height = surface_height(surface);
    int bpp = surface_bits_per_pixel(surface);

490 491
    gui_fullscreen = !gui_fullscreen;
    if (gui_fullscreen) {
492 493 494 495
        gui_saved_width = real_screen->w;
        gui_saved_height = real_screen->h;
        gui_saved_scaling = scaling_active;

G
Gerd Hoffmann 已提交
496
        do_sdl_resize(width, height, bpp);
S
Stefano Stabellini 已提交
497
        scaling_active = 0;
498

499 500 501
        gui_saved_grab = gui_grab;
        sdl_grab_start();
    } else {
502
        if (gui_saved_scaling) {
G
Gerd Hoffmann 已提交
503
            sdl_scale(gui_saved_width, gui_saved_height);
504
        } else {
G
Gerd Hoffmann 已提交
505
            do_sdl_resize(width, height, 0);
506
        }
G
Gerd Hoffmann 已提交
507
        if (!gui_saved_grab || !qemu_console_is_graphic(NULL)) {
508
            sdl_grab_end();
509
        }
510
    }
511 512
    graphic_hw_invalidate(NULL);
    graphic_hw_update(NULL);
513 514
}

G
Gerd Hoffmann 已提交
515
static void handle_keydown(SDL_Event *ev)
516
{
517
    int mod_state;
518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533
    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 */
G
Gerd Hoffmann 已提交
534
            toggle_full_screen();
535 536 537 538 539
            gui_keysym = 1;
            break;
        case 0x16: /* 'u' key on US keyboard */
            if (scaling_active) {
                scaling_active = 0;
540
                sdl_switch(dcl, NULL);
541 542
                graphic_hw_invalidate(NULL);
                graphic_hw_update(NULL);
543 544 545 546 547 548 549 550 551 552 553
            }
            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;
            }
G
Gerd Hoffmann 已提交
554
            if (!qemu_console_is_graphic(NULL)) {
555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570
                /* 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);
G
Gerd Hoffmann 已提交
571 572
                int height = (surface_height(surface) * width) /
                    surface_width(surface);
573

G
Gerd Hoffmann 已提交
574
                sdl_scale(width, height);
575 576
                graphic_hw_invalidate(NULL);
                graphic_hw_update(NULL);
577 578 579 580 581
                gui_keysym = 1;
            }
        default:
            break;
        }
G
Gerd Hoffmann 已提交
582
    } else if (!qemu_console_is_graphic(NULL)) {
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 650 651 652 653 654 655
        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);
        }
    }
G
Gerd Hoffmann 已提交
656
    if (qemu_console_is_graphic(NULL) && !gui_keysym) {
657 658 659 660
        sdl_process_key(&ev->key);
    }
}

G
Gerd Hoffmann 已提交
661
static void handle_keyup(SDL_Event *ev)
662 663 664 665 666 667 668 669 670 671 672 673 674
{
    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) {
G
Gerd Hoffmann 已提交
675
                if (qemu_console_is_graphic(NULL)) {
676 677 678 679 680 681 682 683 684 685 686 687
                    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;
    }
G
Gerd Hoffmann 已提交
688
    if (qemu_console_is_graphic(NULL) && !gui_keysym) {
689 690 691 692
        sdl_process_key(&ev->key);
    }
}

G
Gerd Hoffmann 已提交
693
static void handle_mousemotion(SDL_Event *ev)
694 695 696
{
    int max_x, max_y;

G
Gerd Hoffmann 已提交
697
    if (qemu_console_is_graphic(NULL) &&
698
        (qemu_input_is_absolute() || absolute_enabled)) {
699 700 701 702 703 704
        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();
        }
705
        if (!gui_grab &&
706 707 708 709 710
            (ev->motion.x > 0 && ev->motion.x < max_x &&
            ev->motion.y > 0 && ev->motion.y < max_y)) {
            sdl_grab_start();
        }
    }
711 712
    if (gui_grab || qemu_input_is_absolute() || absolute_enabled) {
        sdl_send_mouse_event(ev->motion.xrel, ev->motion.yrel,
713 714 715 716
                             ev->motion.x, ev->motion.y, ev->motion.state);
    }
}

G
Gerd Hoffmann 已提交
717
static void handle_mousebutton(SDL_Event *ev)
718
{
A
aurel32 已提交
719
    int buttonstate = SDL_GetMouseState(NULL, NULL);
720 721
    SDL_MouseButtonEvent *bev;

G
Gerd Hoffmann 已提交
722
    if (!qemu_console_is_graphic(NULL)) {
723 724 725 726
        return;
    }

    bev = &ev->button;
727
    if (!gui_grab && !qemu_input_is_absolute()) {
728
        if (ev->type == SDL_MOUSEBUTTONUP && bev->button == SDL_BUTTON_LEFT) {
729 730 731 732 733 734 735 736 737
            /* start grabbing all events */
            sdl_grab_start();
        }
    } else {
        if (ev->type == SDL_MOUSEBUTTONDOWN) {
            buttonstate |= SDL_BUTTON(bev->button);
        } else {
            buttonstate &= ~SDL_BUTTON(bev->button);
        }
738
        sdl_send_mouse_event(0, 0, bev->x, bev->y, buttonstate);
739 740 741
    }
}

G
Gerd Hoffmann 已提交
742
static void handle_activation(SDL_Event *ev)
743
{
744 745 746
#ifdef _WIN32
    /* Disable grab if the window no longer has the focus
     * (Windows-only workaround) */
747 748 749 750
    if (gui_grab && ev->active.state == SDL_APPINPUTFOCUS &&
        !ev->active.gain && !gui_fullscreen) {
        sdl_grab_end();
    }
751
#endif
G
Gerd Hoffmann 已提交
752
    if (!gui_grab && ev->active.gain && qemu_console_is_graphic(NULL) &&
753
        (qemu_input_is_absolute() || absolute_enabled)) {
754 755 756 757 758
        absolute_mouse_grab();
    }
    if (ev->active.state & SDL_APPACTIVE) {
        if (ev->active.gain) {
            /* Back to default interval */
G
Gerd Hoffmann 已提交
759
            update_displaychangelistener(dcl, GUI_REFRESH_INTERVAL_DEFAULT);
760
        } else {
G
Gerd Hoffmann 已提交
761 762 763 764
            /* Sleeping interval.  Not using the long default here as
             * sdl_refresh does not only update the guest screen, but
             * also checks for gui events. */
            update_displaychangelistener(dcl, 500);
765 766 767 768
        }
    }
}

769
static void sdl_refresh(DisplayChangeListener *dcl)
770 771
{
    SDL_Event ev1, *ev = &ev1;
772

773 774
    if (last_vm_running != runstate_is_running()) {
        last_vm_running = runstate_is_running();
775 776 777
        sdl_update_caption();
    }

778
    graphic_hw_update(NULL);
G
Gerd Hoffmann 已提交
779
    SDL_EnableUNICODE(!qemu_console_is_graphic(NULL));
B
bellard 已提交
780

781 782 783
    while (SDL_PollEvent(ev)) {
        switch (ev->type) {
        case SDL_VIDEOEXPOSE:
784
            sdl_update(dcl, 0, 0, real_screen->w, real_screen->h);
785 786
            break;
        case SDL_KEYDOWN:
G
Gerd Hoffmann 已提交
787
            handle_keydown(ev);
788
            break;
789
        case SDL_KEYUP:
G
Gerd Hoffmann 已提交
790
            handle_keyup(ev);
791 792
            break;
        case SDL_QUIT:
793 794
            if (!no_quit) {
                no_shutdown = 0;
795
                qemu_system_shutdown_request();
796
            }
797 798
            break;
        case SDL_MOUSEMOTION:
G
Gerd Hoffmann 已提交
799
            handle_mousemotion(ev);
800 801 802
            break;
        case SDL_MOUSEBUTTONDOWN:
        case SDL_MOUSEBUTTONUP:
G
Gerd Hoffmann 已提交
803
            handle_mousebutton(ev);
804
            break;
805
        case SDL_ACTIVEEVENT:
G
Gerd Hoffmann 已提交
806
            handle_activation(ev);
807
            break;
808
        case SDL_VIDEORESIZE:
G
Gerd Hoffmann 已提交
809
            sdl_scale(ev->resize.w, ev->resize.h);
810 811
            graphic_hw_invalidate(NULL);
            graphic_hw_update(NULL);
S
Stefano Stabellini 已提交
812
            break;
813 814 815 816 817 818
        default:
            break;
        }
    }
}

819 820
static void sdl_mouse_warp(DisplayChangeListener *dcl,
                           int x, int y, int on)
821 822 823 824
{
    if (on) {
        if (!guest_cursor)
            sdl_show_cursor();
825
        if (gui_grab || qemu_input_is_absolute() || absolute_enabled) {
826
            SDL_SetCursor(guest_sprite);
827
            if (!qemu_input_is_absolute() && !absolute_enabled) {
828
                SDL_WarpMouse(x, y);
829
            }
830 831 832 833 834 835 836
        }
    } else if (gui_grab)
        sdl_hide_cursor();
    guest_cursor = on;
    guest_x = x, guest_y = y;
}

837 838
static void sdl_mouse_define(DisplayChangeListener *dcl,
                             QEMUCursor *c)
839
{
840 841 842
    uint8_t *image, *mask;
    int bpl;

843 844 845
    if (guest_sprite)
        SDL_FreeCursor(guest_sprite);

846
    bpl = cursor_get_mono_bpl(c);
847 848
    image = g_malloc0(bpl * c->height);
    mask  = g_malloc0(bpl * c->height);
849 850 851 852
    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);
853 854
    g_free(image);
    g_free(mask);
855 856

    if (guest_cursor &&
857
            (gui_grab || qemu_input_is_absolute() || absolute_enabled))
858 859 860
        SDL_SetCursor(guest_sprite);
}

861
static void sdl_cleanup(void)
B
bellard 已提交
862
{
863 864
    if (guest_sprite)
        SDL_FreeCursor(guest_sprite);
865
    SDL_QuitSubSystem(SDL_INIT_VIDEO);
B
bellard 已提交
866 867
}

868 869 870
static const DisplayChangeListenerOps dcl_ops = {
    .dpy_name          = "sdl",
    .dpy_gfx_update    = sdl_update,
871
    .dpy_gfx_switch    = sdl_switch,
872 873 874 875 876
    .dpy_refresh       = sdl_refresh,
    .dpy_mouse_set     = sdl_mouse_warp,
    .dpy_cursor_define = sdl_mouse_define,
};

877
void sdl_display_init(DisplayState *ds, int full_screen, int no_frame)
878 879
{
    int flags;
880
    uint8_t data = 0;
881
    const SDL_VideoInfo *vi;
S
Stefan Weil 已提交
882
    char *filename;
883

884 885 886 887 888 889
#if defined(__APPLE__)
    /* always use generic keymaps */
    if (!keyboard_layout)
        keyboard_layout = "en-us";
#endif
    if(keyboard_layout) {
890
        kbd_layout = init_keyboard_layout(name2keysym, keyboard_layout);
891 892 893 894
        if (!kbd_layout)
            exit(1);
    }

895 896 897
    if (no_frame)
        gui_noframe = 1;

898 899 900
    if (!full_screen) {
        setenv("SDL_VIDEO_ALLOW_SCREENSAVER", "1", 0);
    }
901 902 903 904 905 906 907 908 909 910 911 912
#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
913

914 915 916 917
    /* 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);

918 919
    flags = SDL_INIT_VIDEO | SDL_INIT_NOPARACHUTE;
    if (SDL_Init (flags)) {
920 921
        fprintf(stderr, "Could not initialize SDL(%s) - exiting\n",
                SDL_GetError());
922 923
        exit(1);
    }
924
    vi = SDL_GetVideoInfo();
S
Stefano Stabellini 已提交
925
    host_format = *(vi->vfmt);
926

S
Stefan Weil 已提交
927 928 929 930 931 932 933 934 935
    /* 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);
        }
936
        g_free(filename);
S
Stefan Weil 已提交
937 938
    }

939 940 941 942 943
    if (full_screen) {
        gui_fullscreen = 1;
        sdl_grab_start();
    }

944
    dcl = g_malloc0(sizeof(DisplayChangeListener));
945
    dcl->ops = &dcl_ops;
946
    register_displaychangelistener(dcl);
947

A
Anthony Liguori 已提交
948 949 950
    mouse_mode_notifier.notify = sdl_mouse_mode_change;
    qemu_add_mouse_mode_change_notifier(&mouse_mode_notifier);

951
    sdl_update_caption();
952 953
    SDL_EnableKeyRepeat(250, 50);
    gui_grab = 0;
B
bellard 已提交
954

955 956 957
    sdl_cursor_hidden = SDL_CreateCursor(&data, &data, 8, 1, 0, 0);
    sdl_cursor_normal = SDL_GetCursor();

958
    atexit(sdl_cleanup);
959
}
960
#endif