Statistics
| Branch: | Revision:

root / sdl.c @ 8785a8dd

History | View | Annotate | Download (16.6 kB)

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

    
26
#include <SDL.h>
27

    
28
#ifndef _WIN32
29
#include <signal.h>
30
#endif
31

    
32
static SDL_Surface *screen;
33
static int gui_grab; /* if true, all keyboard/mouse events are grabbed */
34
static int last_vm_running;
35
static int gui_saved_grab;
36
static int gui_fullscreen;
37
static int gui_key_modifier_pressed;
38
static int gui_keysym;
39
static int gui_fullscreen_initial_grab;
40
static int gui_grab_code = KMOD_LALT | KMOD_LCTRL;
41
static uint8_t modifiers_state[256];
42
static int width, height;
43
static SDL_Cursor *sdl_cursor_normal;
44
static SDL_Cursor *sdl_cursor_hidden;
45
static int absolute_enabled = 0;
46

    
47
static void sdl_update(DisplayState *ds, int x, int y, int w, int h)
48
{
49
    //    printf("updating x=%d y=%d w=%d h=%d\n", x, y, w, h);
50
    SDL_UpdateRect(screen, x, y, w, h);
51
}
52

    
53
static void sdl_resize(DisplayState *ds, int w, int h)
54
{
55
    int flags;
56

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

    
59
    flags = SDL_HWSURFACE|SDL_ASYNCBLIT|SDL_HWACCEL;
60
    if (gui_fullscreen)
61
        flags |= SDL_FULLSCREEN;
62

    
63
    width = w;
64
    height = h;
65

    
66
 again:
67
    screen = SDL_SetVideoMode(w, h, 0, flags);
68
    if (!screen) {
69
        fprintf(stderr, "Could not open SDL display\n");
70
        exit(1);
71
    }
72
    if (!screen->pixels && (flags & SDL_HWSURFACE) && (flags & SDL_FULLSCREEN)) {
73
        flags &= ~SDL_HWSURFACE;
74
        goto again;
75
    }
76

    
77
    if (!screen->pixels) {
78
        fprintf(stderr, "Could not open SDL display\n");
79
        exit(1);
80
    }
81
    ds->data = screen->pixels;
82
    ds->linesize = screen->pitch;
83
    ds->depth = screen->format->BitsPerPixel;
84
    if (ds->depth == 32 && screen->format->Rshift == 0) {
85
        ds->bgr = 1;
86
    } else {
87
        ds->bgr = 0;
88
    }
89
    ds->width = w;
90
    ds->height = h;
91
}
92

    
93
/* generic keyboard conversion */
94

    
95
#include "sdl_keysym.h"
96
#include "keymaps.c"
97

    
98
static kbd_layout_t *kbd_layout = NULL;
99

    
100
static uint8_t sdl_keyevent_to_keycode_generic(const SDL_KeyboardEvent *ev)
101
{
102
    int keysym;
103
    /* workaround for X11+SDL bug with AltGR */
104
    keysym = ev->keysym.sym;
105
    if (keysym == 0 && ev->keysym.scancode == 113)
106
        keysym = SDLK_MODE;
107
    return keysym2scancode(kbd_layout, keysym);
108
}
109

    
110
/* specific keyboard conversions from scan codes */
111

    
112
#if defined(_WIN32)
113

    
114
static uint8_t sdl_keyevent_to_keycode(const SDL_KeyboardEvent *ev)
115
{
116
    return ev->keysym.scancode;
117
}
118

    
119
#else
120

    
121
static const uint8_t x_keycode_to_pc_keycode[61] = {
122
   0xc7,      /*  97  Home   */
123
   0xc8,      /*  98  Up     */
124
   0xc9,      /*  99  PgUp   */
125
   0xcb,      /* 100  Left   */
126
   0x4c,        /* 101  KP-5   */
127
   0xcd,      /* 102  Right  */
128
   0xcf,      /* 103  End    */
129
   0xd0,      /* 104  Down   */
130
   0xd1,      /* 105  PgDn   */
131
   0xd2,      /* 106  Ins    */
132
   0xd3,      /* 107  Del    */
133
   0x9c,      /* 108  Enter  */
134
   0x9d,      /* 109  Ctrl-R */
135
   0x0,       /* 110  Pause  */
136
   0xb7,      /* 111  Print  */
137
   0xb5,      /* 112  Divide */
138
   0xb8,      /* 113  Alt-R  */
139
   0xc6,      /* 114  Break  */   
140
   0x0,         /* 115 */
141
   0x0,         /* 116 */
142
   0x0,         /* 117 */
143
   0x0,         /* 118 */
144
   0x0,         /* 119 */
145
   0x70,         /* 120 Hiragana_Katakana */
146
   0x0,         /* 121 */
147
   0x0,         /* 122 */
148
   0x73,         /* 123 backslash */
149
   0x0,         /* 124 */
150
   0x0,         /* 125 */
151
   0x0,         /* 126 */
152
   0x0,         /* 127 */
153
   0x0,         /* 128 */
154
   0x79,         /* 129 Henkan */
155
   0x0,         /* 130 */
156
   0x7b,         /* 131 Muhenkan */
157
   0x0,         /* 132 */
158
   0x7d,         /* 133 Yen */
159
   0x0,         /* 134 */
160
   0x0,         /* 135 */
161
   0x47,         /* 136 KP_7 */
162
   0x48,         /* 137 KP_8 */
163
   0x49,         /* 138 KP_9 */
164
   0x4b,         /* 139 KP_4 */
165
   0x4c,         /* 140 KP_5 */
166
   0x4d,         /* 141 KP_6 */
167
   0x4f,         /* 142 KP_1 */
168
   0x50,         /* 143 KP_2 */
169
   0x51,         /* 144 KP_3 */
170
   0x52,         /* 145 KP_0 */
171
   0x53,         /* 146 KP_. */
172
   0x47,         /* 147 KP_HOME */
173
   0x48,         /* 148 KP_UP */
174
   0x49,         /* 149 KP_PgUp */
175
   0x4b,         /* 150 KP_Left */
176
   0x4c,         /* 151 KP_ */
177
   0x4d,         /* 152 KP_Right */
178
   0x4f,         /* 153 KP_End */
179
   0x50,         /* 154 KP_Down */
180
   0x51,         /* 155 KP_PgDn */
181
   0x52,         /* 156 KP_Ins */
182
   0x53,         /* 157 KP_Del */
183
};
184

    
185
static uint8_t sdl_keyevent_to_keycode(const SDL_KeyboardEvent *ev)
186
{
187
    int keycode;
188

    
189
    keycode = ev->keysym.scancode;
190

    
191
    if (keycode < 9) {
192
        keycode = 0;
193
    } else if (keycode < 97) {
194
        keycode -= 8; /* just an offset */
195
    } else if (keycode < 158) {
196
        /* use conversion table */
197
        keycode = x_keycode_to_pc_keycode[keycode - 97];
198
    } else {
199
        keycode = 0;
200
    }
201
    return keycode;
202
}
203

    
204
#endif
205

    
206
static void reset_keys(void)
207
{
208
    int i;
209
    for(i = 0; i < 256; i++) {
210
        if (modifiers_state[i]) {
211
            if (i & 0x80)
212
                kbd_put_keycode(0xe0);
213
            kbd_put_keycode(i | 0x80);
214
            modifiers_state[i] = 0;
215
        }
216
    }
217
}
218

    
219
static void sdl_process_key(SDL_KeyboardEvent *ev)
220
{
221
    int keycode, v;
222

    
223
    if (ev->keysym.sym == SDLK_PAUSE) {
224
        /* specific case */
225
        v = 0;
226
        if (ev->type == SDL_KEYUP)
227
            v |= 0x80;
228
        kbd_put_keycode(0xe1);
229
        kbd_put_keycode(0x1d | v);
230
        kbd_put_keycode(0x45 | v);
231
        return;
232
    }
233

    
234
    if (kbd_layout) {
235
        keycode = sdl_keyevent_to_keycode_generic(ev);
236
    } else {
237
        keycode = sdl_keyevent_to_keycode(ev);
238
    }
239

    
240
    switch(keycode) {
241
    case 0x00:
242
        /* sent when leaving window: reset the modifiers state */
243
        reset_keys();
244
        return;
245
    case 0x2a:                          /* Left Shift */
246
    case 0x36:                          /* Right Shift */
247
    case 0x1d:                          /* Left CTRL */
248
    case 0x9d:                          /* Right CTRL */
249
    case 0x38:                          /* Left ALT */
250
    case 0xb8:                         /* Right ALT */
251
        if (ev->type == SDL_KEYUP)
252
            modifiers_state[keycode] = 0;
253
        else
254
            modifiers_state[keycode] = 1;
255
        break;
256
    case 0x45: /* num lock */
257
    case 0x3a: /* caps lock */
258
        /* SDL does not send the key up event, so we generate it */
259
        kbd_put_keycode(keycode);
260
        kbd_put_keycode(keycode | 0x80);
261
        return;
262
    }
263

    
264
    /* now send the key code */
265
    if (keycode & 0x80)
266
        kbd_put_keycode(0xe0);
267
    if (ev->type == SDL_KEYUP)
268
        kbd_put_keycode(keycode | 0x80);
269
    else
270
        kbd_put_keycode(keycode & 0x7f);
271
}
272

    
273
static void sdl_update_caption(void)
274
{
275
    char buf[1024];
276
    strcpy(buf, "QEMU");
277
    if (!vm_running) {
278
        strcat(buf, " [Stopped]");
279
    }
280
    if (gui_grab) {
281
        strcat(buf, " - Press Ctrl-Alt to exit grab");
282
    }
283
    SDL_WM_SetCaption(buf, "QEMU");
284
}
285

    
286
static void sdl_hide_cursor(void)
287
{
288
    if (kbd_mouse_is_absolute()) {
289
        SDL_ShowCursor(1);
290
        SDL_SetCursor(sdl_cursor_hidden);
291
    } else {
292
        SDL_ShowCursor(0);
293
    }
294
}
295

    
296
static void sdl_show_cursor(void)
297
{
298
    if (!kbd_mouse_is_absolute()) {
299
        SDL_ShowCursor(1);
300
    }
301
}
302

    
303
static void sdl_grab_start(void)
304
{
305
    sdl_hide_cursor();
306
    SDL_WM_GrabInput(SDL_GRAB_ON);
307
    /* dummy read to avoid moving the mouse */
308
    SDL_GetRelativeMouseState(NULL, NULL);
309
    gui_grab = 1;
310
    sdl_update_caption();
311
}
312

    
313
static void sdl_grab_end(void)
314
{
315
    SDL_WM_GrabInput(SDL_GRAB_OFF);
316
    sdl_show_cursor();
317
    gui_grab = 0;
318
    sdl_update_caption();
319
}
320

    
321
static void sdl_send_mouse_event(int dz)
322
{
323
    int dx, dy, state, buttons;
324
    state = SDL_GetRelativeMouseState(&dx, &dy);
325
    buttons = 0;
326
    if (state & SDL_BUTTON(SDL_BUTTON_LEFT))
327
        buttons |= MOUSE_EVENT_LBUTTON;
328
    if (state & SDL_BUTTON(SDL_BUTTON_RIGHT))
329
        buttons |= MOUSE_EVENT_RBUTTON;
330
    if (state & SDL_BUTTON(SDL_BUTTON_MIDDLE))
331
        buttons |= MOUSE_EVENT_MBUTTON;
332

    
333
    if (kbd_mouse_is_absolute()) {
334
        if (!absolute_enabled) {
335
            sdl_hide_cursor();
336
            if (gui_grab) {
337
                sdl_grab_end();
338
            }
339
            absolute_enabled = 1;
340
        }
341

    
342
        SDL_GetMouseState(&dx, &dy);
343
        dx = dx * 0x7FFF / width;
344
        dy = dy * 0x7FFF / height;
345
    }
346

    
347
    kbd_mouse_event(dx, dy, dz, buttons);
348
}
349

    
350
static void toggle_full_screen(DisplayState *ds)
351
{
352
    gui_fullscreen = !gui_fullscreen;
353
    sdl_resize(ds, screen->w, screen->h);
354
    if (gui_fullscreen) {
355
        gui_saved_grab = gui_grab;
356
        sdl_grab_start();
357
    } else {
358
        if (!gui_saved_grab)
359
            sdl_grab_end();
360
    }
361
    vga_hw_invalidate();
362
    vga_hw_update();
363
}
364

    
365
static void sdl_refresh(DisplayState *ds)
366
{
367
    SDL_Event ev1, *ev = &ev1;
368
    int mod_state;
369
                     
370
    if (last_vm_running != vm_running) {
371
        last_vm_running = vm_running;
372
        sdl_update_caption();
373
    }
374

    
375
    vga_hw_update();
376

    
377
    while (SDL_PollEvent(ev)) {
378
        switch (ev->type) {
379
        case SDL_VIDEOEXPOSE:
380
            sdl_update(ds, 0, 0, screen->w, screen->h);
381
            break;
382
        case SDL_KEYDOWN:
383
        case SDL_KEYUP:
384
            if (ev->type == SDL_KEYDOWN) {
385
                mod_state = (SDL_GetModState() & gui_grab_code) ==
386
                    gui_grab_code;
387
                gui_key_modifier_pressed = mod_state;
388
                if (gui_key_modifier_pressed) {
389
                    int keycode;
390
                    keycode = sdl_keyevent_to_keycode(&ev->key);
391
                    switch(keycode) {
392
                    case 0x21: /* 'f' key on US keyboard */
393
                        toggle_full_screen(ds);
394
                        gui_keysym = 1;
395
                        break;
396
                    case 0x02 ... 0x0a: /* '1' to '9' keys */ 
397
                        console_select(keycode - 0x02);
398
                        if (!is_graphic_console()) {
399
                            /* display grab if going to a text console */
400
                            if (gui_grab)
401
                                sdl_grab_end();
402
                        }
403
                        gui_keysym = 1;
404
                        break;
405
                    default:
406
                        break;
407
                    }
408
                } else if (!is_graphic_console()) {
409
                    int keysym;
410
                    keysym = 0;
411
                    if (ev->key.keysym.mod & (KMOD_LCTRL | KMOD_RCTRL)) {
412
                        switch(ev->key.keysym.sym) {
413
                        case SDLK_UP: keysym = QEMU_KEY_CTRL_UP; break;
414
                        case SDLK_DOWN: keysym = QEMU_KEY_CTRL_DOWN; break;
415
                        case SDLK_LEFT: keysym = QEMU_KEY_CTRL_LEFT; break;
416
                        case SDLK_RIGHT: keysym = QEMU_KEY_CTRL_RIGHT; break;
417
                        case SDLK_HOME: keysym = QEMU_KEY_CTRL_HOME; break;
418
                        case SDLK_END: keysym = QEMU_KEY_CTRL_END; break;
419
                        case SDLK_PAGEUP: keysym = QEMU_KEY_CTRL_PAGEUP; break;
420
                        case SDLK_PAGEDOWN: keysym = QEMU_KEY_CTRL_PAGEDOWN; break;
421
                        default: break;
422
                        }
423
                    } else {
424
                        switch(ev->key.keysym.sym) {
425
                        case SDLK_UP: keysym = QEMU_KEY_UP; break;
426
                        case SDLK_DOWN: keysym = QEMU_KEY_DOWN; break;
427
                        case SDLK_LEFT: keysym = QEMU_KEY_LEFT; break;
428
                        case SDLK_RIGHT: keysym = QEMU_KEY_RIGHT; break;
429
                        case SDLK_HOME: keysym = QEMU_KEY_HOME; break;
430
                        case SDLK_END: keysym = QEMU_KEY_END; break;
431
                        case SDLK_PAGEUP: keysym = QEMU_KEY_PAGEUP; break;
432
                        case SDLK_PAGEDOWN: keysym = QEMU_KEY_PAGEDOWN; break;
433
                        case SDLK_BACKSPACE: keysym = QEMU_KEY_BACKSPACE; break;                        case SDLK_DELETE: keysym = QEMU_KEY_DELETE; break;
434
                        default: break;
435
                        }
436
                    }
437
                    if (keysym) {
438
                        kbd_put_keysym(keysym);
439
                    } else if (ev->key.keysym.unicode != 0) {
440
                        kbd_put_keysym(ev->key.keysym.unicode);
441
                    }
442
                }
443
            } else if (ev->type == SDL_KEYUP) {
444
                mod_state = (ev->key.keysym.mod & gui_grab_code);
445
                if (!mod_state) {
446
                    if (gui_key_modifier_pressed) {
447
                        gui_key_modifier_pressed = 0;
448
                        if (gui_keysym == 0) {
449
                            /* exit/enter grab if pressing Ctrl-Alt */
450
                            if (!gui_grab)
451
                                sdl_grab_start();
452
                            else
453
                                sdl_grab_end();
454
                            /* SDL does not send back all the
455
                               modifiers key, so we must correct it */
456
                            reset_keys();
457
                            break;
458
                        }
459
                        gui_keysym = 0;
460
                    }
461
                }
462
            }
463
            if (is_graphic_console()) 
464
                sdl_process_key(&ev->key);
465
            break;
466
        case SDL_QUIT:
467
            qemu_system_shutdown_request();
468
            break;
469
        case SDL_MOUSEMOTION:
470
            if (gui_grab || kbd_mouse_is_absolute()) {
471
                sdl_send_mouse_event(0);
472
            }
473
            break;
474
        case SDL_MOUSEBUTTONDOWN:
475
        case SDL_MOUSEBUTTONUP:
476
            {
477
                SDL_MouseButtonEvent *bev = &ev->button;
478
                if (!gui_grab && !kbd_mouse_is_absolute()) {
479
                    if (ev->type == SDL_MOUSEBUTTONDOWN &&
480
                        (bev->state & SDL_BUTTON_LMASK)) {
481
                        /* start grabbing all events */
482
                        sdl_grab_start();
483
                    }
484
                } else {
485
                    int dz;
486
                    dz = 0;
487
#ifdef SDL_BUTTON_WHEELUP
488
                    if (bev->button == SDL_BUTTON_WHEELUP && ev->type == SDL_MOUSEBUTTONDOWN) {
489
                        dz = -1;
490
                    } else if (bev->button == SDL_BUTTON_WHEELDOWN && ev->type == SDL_MOUSEBUTTONDOWN) {
491
                        dz = 1;
492
                    }
493
#endif               
494
                    sdl_send_mouse_event(dz);
495
                }
496
            }
497
            break;
498
        case SDL_ACTIVEEVENT:
499
            if (gui_grab && ev->active.state == SDL_APPINPUTFOCUS &&
500
                !ev->active.gain && !gui_fullscreen_initial_grab) {
501
                sdl_grab_end();
502
            }
503
            break;
504
        default:
505
            break;
506
        }
507
    }
508
}
509

    
510
static void sdl_cleanup(void) 
511
{
512
    SDL_Quit();
513
}
514

    
515
void sdl_display_init(DisplayState *ds, int full_screen)
516
{
517
    int flags;
518
    uint8_t data = 0;
519

    
520
#if defined(__APPLE__)
521
    /* always use generic keymaps */
522
    if (!keyboard_layout)
523
        keyboard_layout = "en-us";
524
#endif
525
    if(keyboard_layout) {
526
        kbd_layout = init_keyboard_layout(keyboard_layout);
527
        if (!kbd_layout)
528
            exit(1);
529
    }
530

    
531
    flags = SDL_INIT_VIDEO | SDL_INIT_NOPARACHUTE;
532
    if (SDL_Init (flags)) {
533
        fprintf(stderr, "Could not initialize SDL - exiting\n");
534
        exit(1);
535
    }
536
#ifndef _WIN32
537
    /* NOTE: we still want Ctrl-C to work, so we undo the SDL redirections */
538
    signal(SIGINT, SIG_DFL);
539
    signal(SIGQUIT, SIG_DFL);
540
#endif
541

    
542
    ds->dpy_update = sdl_update;
543
    ds->dpy_resize = sdl_resize;
544
    ds->dpy_refresh = sdl_refresh;
545

    
546
    sdl_resize(ds, 640, 400);
547
    sdl_update_caption();
548
    SDL_EnableKeyRepeat(250, 50);
549
    SDL_EnableUNICODE(1);
550
    gui_grab = 0;
551

    
552
    sdl_cursor_hidden = SDL_CreateCursor(&data, &data, 8, 1, 0, 0);
553
    sdl_cursor_normal = SDL_GetCursor();
554

    
555
    atexit(sdl_cleanup);
556
    if (full_screen) {
557
        gui_fullscreen = 1;
558
        gui_fullscreen_initial_grab = 1;
559
        sdl_grab_start();
560
    }
561
}