sdl.c 28.2 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
static int gui_saved_grab;
static int gui_fullscreen;
44
static int gui_noframe;
45 46
static int gui_key_modifier_pressed;
static int gui_keysym;
B
bellard 已提交
47
static int gui_fullscreen_initial_grab;
48 49
static int gui_grab_code = KMOD_LALT | KMOD_LCTRL;
static uint8_t modifiers_state[256];
50 51 52 53
static int width, height;
static SDL_Cursor *sdl_cursor_normal;
static SDL_Cursor *sdl_cursor_hidden;
static int absolute_enabled = 0;
54 55
static int guest_cursor = 0;
static int guest_x, guest_y;
56
static SDL_Cursor *guest_sprite = NULL;
57
static uint8_t allocator;
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

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

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

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

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

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

S
Stefano Stabellini 已提交
100
    flags = SDL_HWSURFACE|SDL_ASYNCBLIT|SDL_HWACCEL|SDL_RESIZABLE;
101 102
    if (gui_fullscreen)
        flags |= SDL_FULLSCREEN;
103 104
    if (gui_noframe)
        flags |= SDL_NOFRAME;
B
bellard 已提交
105

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

static void sdl_resize(DisplayState *ds)
{
    if  (!allocator) {
S
Stefano Stabellini 已提交
119 120 121 122
        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));
123 124 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 169 170 171 172 173 174
        sdl_setdata(ds);
    } else {
        if (guest_screen != NULL) {
            SDL_FreeSurface(guest_screen);
            guest_screen = NULL;
        }
    }
}

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

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

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

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

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

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

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

    return qemu_pf;
}

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

    surface->width = width;
    surface->height = height;
175

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

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

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

    return surface;
}

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

    if (surface->flags & QEMU_ALLOCATED_FLAG)
        qemu_free(surface->data);
217 218 219 220 221 222 223
    qemu_free(surface);
}

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

226
/* generic keyboard conversion */
B
bellard 已提交
227

228 229
#include "sdl_keysym.h"

A
Anthony Liguori 已提交
230
static kbd_layout_t *kbd_layout = NULL;
231 232

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

246 247 248
/* specific keyboard conversions from scan codes */

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

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

#else

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

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

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

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

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

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

    keycode = ev->keysym.scancode;

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

#endif

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

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

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

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

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

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

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

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

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

429
    SDL_WM_SetCaption(win_title, icon_title);
430 431
}

432 433
static void sdl_hide_cursor(void)
{
434 435 436
    if (!cursor_hide)
        return;

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

static void sdl_show_cursor(void)
{
447 448 449
    if (!cursor_hide)
        return;

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

460 461
static void sdl_grab_start(void)
{
462 463
    if (guest_cursor) {
        SDL_SetCursor(guest_sprite);
464 465
        if (!kbd_mouse_is_absolute() && !absolute_enabled)
            SDL_WarpMouse(guest_x, guest_y);
466 467
    } else
        sdl_hide_cursor();
468 469 470 471 472 473

    if (SDL_WM_GrabInput(SDL_GRAB_ON) == SDL_GRAB_ON) {
        gui_grab = 1;
        sdl_update_caption();
    } else
        sdl_show_cursor();
474 475 476 477 478 479
}

static void sdl_grab_end(void)
{
    SDL_WM_GrabInput(SDL_GRAB_OFF);
    gui_grab = 0;
480
    sdl_show_cursor();
481
    sdl_update_caption();
482 483
}

A
Anthony Liguori 已提交
484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499
static void sdl_mouse_mode_change(Notifier *notify)
{
    if (kbd_mouse_is_absolute()) {
        if (!absolute_enabled) {
            sdl_hide_cursor();
            if (gui_grab) {
                sdl_grab_end();
            }
            absolute_enabled = 1;
        }
    } else if (absolute_enabled) {
	sdl_show_cursor();
	absolute_enabled = 0;
    }
}

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

    if (kbd_mouse_is_absolute()) {
A
aurel32 已提交
512 513
       dx = x * 0x7FFF / (width - 1);
       dy = y * 0x7FFF / (height - 1);
514
    } else if (guest_cursor) {
A
aurel32 已提交
515 516 517 518 519 520
        x -= guest_x;
        y -= guest_y;
        guest_x += x;
        guest_y += y;
        dx = x;
        dy = y;
521 522
    }

523 524 525
    kbd_mouse_event(dx, dy, dz, buttons);
}

526 527 528
static void toggle_full_screen(DisplayState *ds)
{
    gui_fullscreen = !gui_fullscreen;
529
    do_sdl_resize(real_screen->w, real_screen->h, real_screen->format->BitsPerPixel);
530
    if (gui_fullscreen) {
S
Stefano Stabellini 已提交
531
        scaling_active = 0;
532 533 534 535 536 537
        gui_saved_grab = gui_grab;
        sdl_grab_start();
    } else {
        if (!gui_saved_grab)
            sdl_grab_end();
    }
P
pbrook 已提交
538 539
    vga_hw_invalidate();
    vga_hw_update();
540 541
}

542 543 544
static void sdl_refresh(DisplayState *ds)
{
    SDL_Event ev1, *ev = &ev1;
545
    int mod_state;
A
aurel32 已提交
546
    int buttonstate = SDL_GetMouseState(NULL, NULL);
547

548 549 550 551 552
    if (last_vm_running != vm_running) {
        last_vm_running = vm_running;
        sdl_update_caption();
    }

P
pbrook 已提交
553
    vga_hw_update();
A
aurel32 已提交
554
    SDL_EnableUNICODE(!is_graphic_console());
B
bellard 已提交
555

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

753 754 755
static void sdl_fill(DisplayState *ds, int x, int y, int w, int h, uint32_t c)
{
    SDL_Rect dst = { x, y, w, h };
756
    SDL_FillRect(real_screen, &dst, c);
757 758 759 760 761 762 763 764 765
}

static void sdl_mouse_warp(int x, int y, int on)
{
    if (on) {
        if (!guest_cursor)
            sdl_show_cursor();
        if (gui_grab || kbd_mouse_is_absolute() || absolute_enabled) {
            SDL_SetCursor(guest_sprite);
766 767
            if (!kbd_mouse_is_absolute() && !absolute_enabled)
                SDL_WarpMouse(x, y);
768 769 770 771 772 773 774
        }
    } else if (gui_grab)
        sdl_hide_cursor();
    guest_cursor = on;
    guest_x = x, guest_y = y;
}

775
static void sdl_mouse_define(QEMUCursor *c)
776
{
777 778 779
    uint8_t *image, *mask;
    int bpl;

780 781 782
    if (guest_sprite)
        SDL_FreeCursor(guest_sprite);

783 784 785 786 787 788 789 790 791
    bpl = cursor_get_mono_bpl(c);
    image = qemu_mallocz(bpl * c->height);
    mask  = qemu_mallocz(bpl * c->height);
    cursor_get_mono_image(c, 0x000000, image);
    cursor_get_mono_mask(c, 0, mask);
    guest_sprite = SDL_CreateCursor(image, mask, c->width, c->height,
                                    c->hot_x, c->hot_y);
    qemu_free(image);
    qemu_free(mask);
792 793 794 795 796 797

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

798
static void sdl_cleanup(void)
B
bellard 已提交
799
{
800 801
    if (guest_sprite)
        SDL_FreeCursor(guest_sprite);
802
    SDL_QuitSubSystem(SDL_INIT_VIDEO);
B
bellard 已提交
803 804
}

805
void sdl_display_init(DisplayState *ds, int full_screen, int no_frame)
806 807
{
    int flags;
808
    uint8_t data = 0;
809 810
    DisplayAllocator *da;
    const SDL_VideoInfo *vi;
S
Stefan Weil 已提交
811
    char *filename;
812

813 814 815 816 817 818
#if defined(__APPLE__)
    /* always use generic keymaps */
    if (!keyboard_layout)
        keyboard_layout = "en-us";
#endif
    if(keyboard_layout) {
819
        kbd_layout = init_keyboard_layout(name2keysym, keyboard_layout);
820 821 822 823
        if (!kbd_layout)
            exit(1);
    }

824 825 826
    if (no_frame)
        gui_noframe = 1;

827 828 829
    if (!full_screen) {
        setenv("SDL_VIDEO_ALLOW_SCREENSAVER", "1", 0);
    }
830 831 832 833 834 835 836 837 838 839 840 841
#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
842

843 844 845 846
    /* 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);

847 848
    flags = SDL_INIT_VIDEO | SDL_INIT_NOPARACHUTE;
    if (SDL_Init (flags)) {
849 850
        fprintf(stderr, "Could not initialize SDL(%s) - exiting\n",
                SDL_GetError());
851 852
        exit(1);
    }
853
    vi = SDL_GetVideoInfo();
S
Stefano Stabellini 已提交
854
    host_format = *(vi->vfmt);
855

S
Stefan Weil 已提交
856 857 858 859 860 861 862 863 864 865 866 867
    /* Load a 32x32x4 image. White pixels are transparent. */
    filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, "qemu-icon.bmp");
    if (filename) {
        SDL_Surface *image = SDL_LoadBMP(filename);
        if (image) {
            uint32_t colorkey = SDL_MapRGB(image->format, 255, 255, 255);
            SDL_SetColorKey(image, SDL_SRCCOLORKEY, colorkey);
            SDL_WM_SetIcon(image, NULL);
        }
        qemu_free(filename);
    }

868 869 870 871 872 873
    dcl = qemu_mallocz(sizeof(DisplayChangeListener));
    dcl->dpy_update = sdl_update;
    dcl->dpy_resize = sdl_resize;
    dcl->dpy_refresh = sdl_refresh;
    dcl->dpy_setdata = sdl_setdata;
    dcl->dpy_fill = sdl_fill;
874 875
    ds->mouse_set = sdl_mouse_warp;
    ds->cursor_define = sdl_mouse_define;
876
    register_displaychangelistener(ds, dcl);
877

878 879 880 881 882 883 884 885
    da = qemu_mallocz(sizeof(DisplayAllocator));
    da->create_displaysurface = sdl_create_displaysurface;
    da->resize_displaysurface = sdl_resize_displaysurface;
    da->free_displaysurface = sdl_free_displaysurface;
    if (register_displayallocator(ds, da) == da) {
        dpy_resize(ds);
    }

A
Anthony Liguori 已提交
886 887 888
    mouse_mode_notifier.notify = sdl_mouse_mode_change;
    qemu_add_mouse_mode_change_notifier(&mouse_mode_notifier);

889
    sdl_update_caption();
890 891
    SDL_EnableKeyRepeat(250, 50);
    gui_grab = 0;
B
bellard 已提交
892

893 894 895
    sdl_cursor_hidden = SDL_CreateCursor(&data, &data, 8, 1, 0, 0);
    sdl_cursor_normal = SDL_GetCursor();

896
    atexit(sdl_cleanup);
B
bellard 已提交
897 898 899 900 901
    if (full_screen) {
        gui_fullscreen = 1;
        gui_fullscreen_initial_grab = 1;
        sdl_grab_start();
    }
902
}