sdl.c 29.9 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
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;
58
static uint8_t allocator;
S
Stefano Stabellini 已提交
59 60
static SDL_PixelFormat host_format;
static int scaling_active = 0;
A
Anthony Liguori 已提交
61
static Notifier mouse_mode_notifier;
62 63 64

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

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

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);
93 94
}

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

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

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

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

static void sdl_resize(DisplayState *ds)
{
    if  (!allocator) {
S
Stefano Stabellini 已提交
121 122 123 124
        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));
125 126 127 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
        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)
{
169
    DisplaySurface *surface = (DisplaySurface*) g_malloc0(sizeof(DisplaySurface));
170 171 172

    surface->width = width;
    surface->height = height;
173

S
Stefano Stabellini 已提交
174
    if (scaling_active) {
175 176
        int linesize;
        PixelFormat pf;
S
Stefano Stabellini 已提交
177
        if (host_format.BytesPerPixel != 2 && host_format.BytesPerPixel != 4) {
178 179
            linesize = width * 4;
            pf = qemu_default_pixelformat(32);
S
Stefano Stabellini 已提交
180
        } else {
181 182
            linesize = width * host_format.BytesPerPixel;
            pf = sdl_to_qemu_pixelformat(&host_format);
S
Stefano Stabellini 已提交
183
        }
184
        qemu_alloc_display(surface, width, height, linesize, pf, 0);
S
Stefano Stabellini 已提交
185 186
        return surface;
    }
187

S
Stefano Stabellini 已提交
188
    if (host_format.BitsPerPixel == 16)
189 190 191 192 193 194 195 196
        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;

197
#ifdef HOST_WORDS_BIGENDIAN
S
Stefano Stabellini 已提交
198
    surface->flags = QEMU_REALPIXELS_FLAG | QEMU_BIG_ENDIAN_FLAG;
199
#else
S
Stefano Stabellini 已提交
200
    surface->flags = QEMU_REALPIXELS_FLAG;
201 202 203 204 205 206 207 208 209 210 211
#endif
    allocator = 1;

    return surface;
}

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

    if (surface->flags & QEMU_ALLOCATED_FLAG)
214 215
        g_free(surface->data);
    g_free(surface);
216 217 218 219 220 221
}

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

224
/* generic keyboard conversion */
B
bellard 已提交
225

226 227
#include "sdl_keysym.h"

A
Anthony Liguori 已提交
228
static kbd_layout_t *kbd_layout = NULL;
229 230

static uint8_t sdl_keyevent_to_keycode_generic(const SDL_KeyboardEvent *ev)
B
bellard 已提交
231
{
232 233 234 235 236
    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 已提交
237 238 239 240
    /* For Japanese key '\' and '|' */
    if (keysym == 92 && ev->keysym.scancode == 133) {
        keysym = 0xa5;
    }
241
    return keysym2scancode(kbd_layout, keysym) & SCANCODE_KEYMASK;
B
bellard 已提交
242 243
}

244 245 246
/* specific keyboard conversions from scan codes */

#if defined(_WIN32)
B
bellard 已提交
247 248 249 250 251 252 253 254

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

#else

255 256 257 258 259 260
#if defined(SDL_VIDEO_DRIVER_X11)
#include <X11/XKBlib.h>

static int check_for_evdev(void)
{
    SDL_SysWMinfo info;
J
Jan Kiszka 已提交
261
    XkbDescPtr desc = NULL;
262
    int has_evdev = 0;
J
Jan Kiszka 已提交
263
    char *keycodes = NULL;
264 265

    SDL_VERSION(&info.version);
J
Jan Kiszka 已提交
266
    if (!SDL_GetWMInfo(&info)) {
267
        return 0;
J
Jan Kiszka 已提交
268
    }
269 270 271
    desc = XkbGetKeyboard(info.info.x11.display,
                          XkbGBN_AllComponentsMask,
                          XkbUseCoreKbd);
J
Jan Kiszka 已提交
272 273 274 275 276 277 278 279 280 281 282
    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);
        }
    }
283

J
Jan Kiszka 已提交
284 285 286 287 288 289
    if (desc) {
        XkbFreeKeyboard(desc, XkbGBN_AllComponentsMask, True);
    }
    if (keycodes) {
        XFree(keycodes);
    }
290 291 292 293 294 295 296 297 298
    return has_evdev;
}
#else
static int check_for_evdev(void)
{
	return 0;
}
#endif

B
bellard 已提交
299 300 301
static uint8_t sdl_keyevent_to_keycode(const SDL_KeyboardEvent *ev)
{
    int keycode;
302 303 304 305
    static int has_evdev = -1;

    if (has_evdev == -1)
        has_evdev = check_for_evdev();
B
bellard 已提交
306 307 308 309 310 311 312

    keycode = ev->keysym.scancode;

    if (keycode < 9) {
        keycode = 0;
    } else if (keycode < 97) {
        keycode -= 8; /* just an offset */
313
    } else if (keycode < 158) {
B
bellard 已提交
314
        /* use conversion table */
315 316 317 318 319 320 321 322
        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 已提交
323 324 325 326 327 328 329 330
    } else {
        keycode = 0;
    }
    return keycode;
}

#endif

331 332 333 334 335
static void reset_keys(void)
{
    int i;
    for(i = 0; i < 256; i++) {
        if (modifiers_state[i]) {
336 337 338
            if (i & SCANCODE_GREY)
                kbd_put_keycode(SCANCODE_EMUL0);
            kbd_put_keycode(i | SCANCODE_UP);
339 340 341 342 343
            modifiers_state[i] = 0;
        }
    }
}

344 345
static void sdl_process_key(SDL_KeyboardEvent *ev)
{
346
    int keycode, v;
347 348 349 350 351

    if (ev->keysym.sym == SDLK_PAUSE) {
        /* specific case */
        v = 0;
        if (ev->type == SDL_KEYUP)
352
            v |= SCANCODE_UP;
353 354 355 356 357 358
        kbd_put_keycode(0xe1);
        kbd_put_keycode(0x1d | v);
        kbd_put_keycode(0x45 | v);
        return;
    }

359 360 361 362 363
    if (kbd_layout) {
        keycode = sdl_keyevent_to_keycode_generic(ev);
    } else {
        keycode = sdl_keyevent_to_keycode(ev);
    }
364 365 366 367

    switch(keycode) {
    case 0x00:
        /* sent when leaving window: reset the modifiers state */
368
        reset_keys();
369 370 371 372 373 374 375
        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 */
376
        if (ev->type == SDL_KEYUP)
377 378 379 380
            modifiers_state[keycode] = 0;
        else
            modifiers_state[keycode] = 1;
        break;
381 382 383
#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. */
384 385 386 387
    case 0x45: /* num lock */
    case 0x3a: /* caps lock */
        /* SDL does not send the key up event, so we generate it */
        kbd_put_keycode(keycode);
388
        kbd_put_keycode(keycode | SCANCODE_UP);
389
        return;
390
#endif
391
    }
392 393

    /* now send the key code */
394 395
    if (keycode & SCANCODE_GREY)
        kbd_put_keycode(SCANCODE_EMUL0);
396
    if (ev->type == SDL_KEYUP)
397
        kbd_put_keycode(keycode | SCANCODE_UP);
398
    else
399
        kbd_put_keycode(keycode & SCANCODE_KEYCODEMASK);
400 401
}

402 403
static void sdl_update_caption(void)
{
404 405
    char win_title[1024];
    char icon_title[1024];
T
ths 已提交
406 407
    const char *status = "";

408
    if (!runstate_is_running())
T
ths 已提交
409
        status = " [Stopped]";
T
ths 已提交
410
    else if (gui_grab) {
411
        if (alt_grab)
412
            status = " - Press Ctrl-Alt-Shift to exit mouse grab";
413
        else if (ctrl_grab)
414
            status = " - Press Right-Ctrl to exit mouse grab";
415
        else
416
            status = " - Press Ctrl-Alt to exit mouse grab";
T
ths 已提交
417
    }
T
ths 已提交
418

419 420 421 422 423 424 425
    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 已提交
426

427
    SDL_WM_SetCaption(win_title, icon_title);
428 429
}

430 431
static void sdl_hide_cursor(void)
{
432 433 434
    if (!cursor_hide)
        return;

435 436 437 438 439 440
    if (kbd_mouse_is_absolute()) {
        SDL_ShowCursor(1);
        SDL_SetCursor(sdl_cursor_hidden);
    } else {
        SDL_ShowCursor(0);
    }
441 442 443 444
}

static void sdl_show_cursor(void)
{
445 446 447
    if (!cursor_hide)
        return;

448
    if (!kbd_mouse_is_absolute() || !is_graphic_console()) {
449
        SDL_ShowCursor(1);
450 451 452 453 454
        if (guest_cursor &&
                (gui_grab || kbd_mouse_is_absolute() || absolute_enabled))
            SDL_SetCursor(guest_sprite);
        else
            SDL_SetCursor(sdl_cursor_normal);
455 456 457
    }
}

458 459
static void sdl_grab_start(void)
{
460 461 462 463 464 465 466 467
    /*
     * 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;
    }
468 469
    if (guest_cursor) {
        SDL_SetCursor(guest_sprite);
470 471
        if (!kbd_mouse_is_absolute() && !absolute_enabled)
            SDL_WarpMouse(guest_x, guest_y);
472 473
    } else
        sdl_hide_cursor();
474 475 476
    SDL_WM_GrabInput(SDL_GRAB_ON);
    gui_grab = 1;
    sdl_update_caption();
477 478 479 480 481 482
}

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

487 488 489 490
static void absolute_mouse_grab(void)
{
    int mouse_x, mouse_y;

491 492 493 494
    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();
495 496 497
    }
}

498
static void sdl_mouse_mode_change(Notifier *notify, void *data)
A
Anthony Liguori 已提交
499 500 501 502
{
    if (kbd_mouse_is_absolute()) {
        if (!absolute_enabled) {
            absolute_enabled = 1;
503 504 505
            if (is_graphic_console()) {
                absolute_mouse_grab();
            }
A
Anthony Liguori 已提交
506 507
        }
    } else if (absolute_enabled) {
508 509 510
        if (!gui_fullscreen) {
            sdl_grab_end();
        }
511
        absolute_enabled = 0;
A
Anthony Liguori 已提交
512 513 514
    }
}

A
aurel32 已提交
515
static void sdl_send_mouse_event(int dx, int dy, int dz, int x, int y, int state)
516
{
J
Jan Kiszka 已提交
517 518 519
    int buttons = 0;

    if (state & SDL_BUTTON(SDL_BUTTON_LEFT)) {
520
        buttons |= MOUSE_EVENT_LBUTTON;
J
Jan Kiszka 已提交
521 522
    }
    if (state & SDL_BUTTON(SDL_BUTTON_RIGHT)) {
523
        buttons |= MOUSE_EVENT_RBUTTON;
J
Jan Kiszka 已提交
524 525
    }
    if (state & SDL_BUTTON(SDL_BUTTON_MIDDLE)) {
526
        buttons |= MOUSE_EVENT_MBUTTON;
J
Jan Kiszka 已提交
527
    }
528 529

    if (kbd_mouse_is_absolute()) {
J
Jan Kiszka 已提交
530 531
        dx = x * 0x7FFF / (real_screen->w - 1);
        dy = y * 0x7FFF / (real_screen->h - 1);
532
    } else if (guest_cursor) {
A
aurel32 已提交
533 534 535 536 537 538
        x -= guest_x;
        y -= guest_y;
        guest_x += x;
        guest_y += y;
        dx = x;
        dy = y;
539 540
    }

541 542 543
    kbd_mouse_event(dx, dy, dz, buttons);
}

544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559
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);
    }
}

560 561 562 563
static void toggle_full_screen(DisplayState *ds)
{
    gui_fullscreen = !gui_fullscreen;
    if (gui_fullscreen) {
564 565 566 567 568 569
        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 已提交
570
        scaling_active = 0;
571

572 573 574
        gui_saved_grab = gui_grab;
        sdl_grab_start();
    } else {
575 576 577 578 579
        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);
        }
580
        if (!gui_saved_grab || !is_graphic_console()) {
581
            sdl_grab_end();
582
        }
583
    }
P
pbrook 已提交
584 585
    vga_hw_invalidate();
    vga_hw_update();
586 587
}

588
static void handle_keydown(DisplayState *ds, SDL_Event *ev)
589
{
590
    int mod_state;
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 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 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 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746
    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;
                sdl_resize(ds);
                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) {
747
                if (is_graphic_console()) {
748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776
                    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();
        }
777
        if (!gui_grab &&
778 779 780 781 782 783 784 785 786 787 788 789 790
            (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 已提交
791
    int buttonstate = SDL_GetMouseState(NULL, NULL);
792 793 794 795 796 797 798 799 800
    SDL_MouseButtonEvent *bev;
    int dz;

    if (!is_graphic_console()) {
        return;
    }

    bev = &ev->button;
    if (!gui_grab && !kbd_mouse_is_absolute()) {
801
        if (ev->type == SDL_MOUSEBUTTONUP && bev->button == SDL_BUTTON_LEFT) {
802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826
            /* 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)
{
827 828 829
#ifdef _WIN32
    /* Disable grab if the window no longer has the focus
     * (Windows-only workaround) */
830 831 832 833
    if (gui_grab && ev->active.state == SDL_APPINPUTFOCUS &&
        !ev->active.gain && !gui_fullscreen) {
        sdl_grab_end();
    }
834
#endif
835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854
    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;
        }
    }
}

static void sdl_refresh(DisplayState *ds)
{
    SDL_Event ev1, *ev = &ev1;
855

856 857
    if (last_vm_running != runstate_is_running()) {
        last_vm_running = runstate_is_running();
858 859 860
        sdl_update_caption();
    }

P
pbrook 已提交
861
    vga_hw_update();
A
aurel32 已提交
862
    SDL_EnableUNICODE(!is_graphic_console());
B
bellard 已提交
863

864 865 866
    while (SDL_PollEvent(ev)) {
        switch (ev->type) {
        case SDL_VIDEOEXPOSE:
867
            sdl_update(ds, 0, 0, real_screen->w, real_screen->h);
868 869
            break;
        case SDL_KEYDOWN:
870 871
            handle_keydown(ds, ev);
            break;
872
        case SDL_KEYUP:
873
            handle_keyup(ds, ev);
874 875
            break;
        case SDL_QUIT:
876 877
            if (!no_quit) {
                no_shutdown = 0;
878
                qemu_system_shutdown_request();
879
            }
880 881
            break;
        case SDL_MOUSEMOTION:
882
            handle_mousemotion(ds, ev);
883 884 885
            break;
        case SDL_MOUSEBUTTONDOWN:
        case SDL_MOUSEBUTTONUP:
886
            handle_mousebutton(ds, ev);
887
            break;
888
        case SDL_ACTIVEEVENT:
889
            handle_activation(ds, ev);
890
            break;
891 892
        case SDL_VIDEORESIZE:
            sdl_scale(ds, ev->resize.w, ev->resize.h);
S
Stefano Stabellini 已提交
893 894 895
            vga_hw_invalidate();
            vga_hw_update();
            break;
896 897 898 899 900 901
        default:
            break;
        }
    }
}

902 903 904
static void sdl_fill(DisplayState *ds, int x, int y, int w, int h, uint32_t c)
{
    SDL_Rect dst = { x, y, w, h };
905
    SDL_FillRect(real_screen, &dst, c);
906 907
}

908
static void sdl_mouse_warp(DisplayState *ds, int x, int y, int on)
909 910 911 912 913 914
{
    if (on) {
        if (!guest_cursor)
            sdl_show_cursor();
        if (gui_grab || kbd_mouse_is_absolute() || absolute_enabled) {
            SDL_SetCursor(guest_sprite);
915 916
            if (!kbd_mouse_is_absolute() && !absolute_enabled)
                SDL_WarpMouse(x, y);
917 918 919 920 921 922 923
        }
    } else if (gui_grab)
        sdl_hide_cursor();
    guest_cursor = on;
    guest_x = x, guest_y = y;
}

924
static void sdl_mouse_define(DisplayState *ds, QEMUCursor *c)
925
{
926 927 928
    uint8_t *image, *mask;
    int bpl;

929 930 931
    if (guest_sprite)
        SDL_FreeCursor(guest_sprite);

932
    bpl = cursor_get_mono_bpl(c);
933 934
    image = g_malloc0(bpl * c->height);
    mask  = g_malloc0(bpl * c->height);
935 936 937 938
    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);
939 940
    g_free(image);
    g_free(mask);
941 942 943 944 945 946

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

947
static void sdl_cleanup(void)
B
bellard 已提交
948
{
949 950
    if (guest_sprite)
        SDL_FreeCursor(guest_sprite);
951
    SDL_QuitSubSystem(SDL_INIT_VIDEO);
B
bellard 已提交
952 953
}

954
void sdl_display_init(DisplayState *ds, int full_screen, int no_frame)
955 956
{
    int flags;
957
    uint8_t data = 0;
958 959
    DisplayAllocator *da;
    const SDL_VideoInfo *vi;
S
Stefan Weil 已提交
960
    char *filename;
961

962 963 964 965 966 967
#if defined(__APPLE__)
    /* always use generic keymaps */
    if (!keyboard_layout)
        keyboard_layout = "en-us";
#endif
    if(keyboard_layout) {
968
        kbd_layout = init_keyboard_layout(name2keysym, keyboard_layout);
969 970 971 972
        if (!kbd_layout)
            exit(1);
    }

973 974 975
    if (no_frame)
        gui_noframe = 1;

976 977 978
    if (!full_screen) {
        setenv("SDL_VIDEO_ALLOW_SCREENSAVER", "1", 0);
    }
979 980 981 982 983 984 985 986 987 988 989 990
#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
991

992 993 994 995
    /* 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);

996 997
    flags = SDL_INIT_VIDEO | SDL_INIT_NOPARACHUTE;
    if (SDL_Init (flags)) {
998 999
        fprintf(stderr, "Could not initialize SDL(%s) - exiting\n",
                SDL_GetError());
1000 1001
        exit(1);
    }
1002
    vi = SDL_GetVideoInfo();
S
Stefano Stabellini 已提交
1003
    host_format = *(vi->vfmt);
1004

S
Stefan Weil 已提交
1005 1006 1007 1008 1009 1010 1011 1012 1013
    /* 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);
        }
1014
        g_free(filename);
S
Stefan Weil 已提交
1015 1016
    }

1017 1018 1019 1020 1021
    if (full_screen) {
        gui_fullscreen = 1;
        sdl_grab_start();
    }

1022
    dcl = g_malloc0(sizeof(DisplayChangeListener));
1023 1024 1025 1026 1027
    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;
1028 1029
    dcl->dpy_mouse_set = sdl_mouse_warp;
    dcl->dpy_cursor_define = sdl_mouse_define;
1030
    register_displaychangelistener(ds, dcl);
1031

1032
    da = g_malloc0(sizeof(DisplayAllocator));
1033 1034 1035 1036 1037 1038 1039
    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 已提交
1040 1041 1042
    mouse_mode_notifier.notify = sdl_mouse_mode_change;
    qemu_add_mouse_mode_change_notifier(&mouse_mode_notifier);

1043
    sdl_update_caption();
1044 1045
    SDL_EnableKeyRepeat(250, 50);
    gui_grab = 0;
B
bellard 已提交
1046

1047 1048 1049
    sdl_cursor_hidden = SDL_CreateCursor(&data, &data, 8, 1, 0, 0);
    sdl_cursor_normal = SDL_GetCursor();

1050
    atexit(sdl_cleanup);
1051
}