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 /* Ported SDL 1.2 code to 2.0 by Dave Airlie. */ 25 26 /* Avoid compiler warning because macro is redefined in SDL_syswm.h. */ 27 #undef WIN32_LEAN_AND_MEAN 28 29 #include <SDL.h> 30 31 #if SDL_MAJOR_VERSION == 2 32 #include <SDL_syswm.h> 33 34 #include "qemu-common.h" 35 #include "ui/console.h" 36 #include "ui/input.h" 37 #include "sysemu/sysemu.h" 38 #include "sdl_zoom.h" 39 40 #include "sdl2-keymap.h" 41 42 static int sdl2_num_outputs; 43 static struct sdl2_state { 44 DisplayChangeListener dcl; 45 DisplaySurface *surface; 46 SDL_Texture *texture; 47 SDL_Window *real_window; 48 SDL_Renderer *real_renderer; 49 int idx; 50 int last_vm_running; /* per console for caption reasons */ 51 int x, y; 52 int hidden; 53 } *sdl2_console; 54 55 static SDL_Surface *guest_sprite_surface; 56 static int gui_grab; /* if true, all keyboard/mouse events are grabbed */ 57 58 static bool gui_saved_scaling; 59 static int gui_saved_width; 60 static int gui_saved_height; 61 static int gui_saved_grab; 62 static int gui_fullscreen; 63 static int gui_noframe; 64 static int gui_key_modifier_pressed; 65 static int gui_keysym; 66 static int gui_grab_code = KMOD_LALT | KMOD_LCTRL; 67 static uint8_t modifiers_state[SDL_NUM_SCANCODES]; 68 static SDL_Cursor *sdl_cursor_normal; 69 static SDL_Cursor *sdl_cursor_hidden; 70 static int absolute_enabled; 71 static int guest_cursor; 72 static int guest_x, guest_y; 73 static SDL_Cursor *guest_sprite; 74 static int scaling_active; 75 static Notifier mouse_mode_notifier; 76 77 static void sdl_update_caption(struct sdl2_state *scon); 78 79 static struct sdl2_state *get_scon_from_window(uint32_t window_id) 80 { 81 int i; 82 for (i = 0; i < sdl2_num_outputs; i++) { 83 if (sdl2_console[i].real_window == SDL_GetWindowFromID(window_id)) { 84 return &sdl2_console[i]; 85 } 86 } 87 return NULL; 88 } 89 90 static void sdl_update(DisplayChangeListener *dcl, 91 int x, int y, int w, int h) 92 { 93 struct sdl2_state *scon = container_of(dcl, struct sdl2_state, dcl); 94 SDL_Rect rect; 95 DisplaySurface *surf = qemu_console_surface(dcl->con); 96 97 if (!surf) { 98 return; 99 } 100 if (!scon->texture) { 101 return; 102 } 103 104 rect.x = x; 105 rect.y = y; 106 rect.w = w; 107 rect.h = h; 108 109 SDL_UpdateTexture(scon->texture, NULL, surface_data(surf), 110 surface_stride(surf)); 111 SDL_RenderCopy(scon->real_renderer, scon->texture, &rect, &rect); 112 SDL_RenderPresent(scon->real_renderer); 113 } 114 115 static void do_sdl_resize(struct sdl2_state *scon, int width, int height, 116 int bpp) 117 { 118 int flags; 119 120 if (scon->real_window && scon->real_renderer) { 121 if (width && height) { 122 SDL_RenderSetLogicalSize(scon->real_renderer, width, height); 123 SDL_SetWindowSize(scon->real_window, width, height); 124 } else { 125 SDL_DestroyRenderer(scon->real_renderer); 126 SDL_DestroyWindow(scon->real_window); 127 scon->real_renderer = NULL; 128 scon->real_window = NULL; 129 } 130 } else { 131 if (!width || !height) { 132 return; 133 } 134 flags = 0; 135 if (gui_fullscreen) { 136 flags |= SDL_WINDOW_FULLSCREEN; 137 } else { 138 flags |= SDL_WINDOW_RESIZABLE; 139 } 140 if (scon->hidden) { 141 flags |= SDL_WINDOW_HIDDEN; 142 } 143 144 scon->real_window = SDL_CreateWindow("", SDL_WINDOWPOS_UNDEFINED, 145 SDL_WINDOWPOS_UNDEFINED, 146 width, height, flags); 147 scon->real_renderer = SDL_CreateRenderer(scon->real_window, -1, 0); 148 sdl_update_caption(scon); 149 } 150 } 151 152 static void sdl_switch(DisplayChangeListener *dcl, 153 DisplaySurface *new_surface) 154 { 155 struct sdl2_state *scon = container_of(dcl, struct sdl2_state, dcl); 156 int format = 0; 157 int idx = scon->idx; 158 DisplaySurface *old_surface = scon->surface; 159 160 /* temporary hack: allows to call sdl_switch to handle scaling changes */ 161 if (new_surface) { 162 scon->surface = new_surface; 163 } 164 165 if (!new_surface && idx > 0) { 166 scon->surface = NULL; 167 } 168 169 if (new_surface == NULL) { 170 do_sdl_resize(scon, 0, 0, 0); 171 } else { 172 do_sdl_resize(scon, surface_width(scon->surface), 173 surface_height(scon->surface), 0); 174 } 175 176 if (old_surface && scon->texture) { 177 SDL_DestroyTexture(scon->texture); 178 scon->texture = NULL; 179 } 180 181 if (new_surface) { 182 if (!scon->texture) { 183 if (surface_bits_per_pixel(scon->surface) == 16) { 184 format = SDL_PIXELFORMAT_RGB565; 185 } else if (surface_bits_per_pixel(scon->surface) == 32) { 186 format = SDL_PIXELFORMAT_ARGB8888; 187 } 188 189 scon->texture = SDL_CreateTexture(scon->real_renderer, format, 190 SDL_TEXTUREACCESS_STREAMING, 191 surface_width(new_surface), 192 surface_height(new_surface)); 193 } 194 } 195 } 196 197 static void reset_keys(struct sdl2_state *scon) 198 { 199 QemuConsole *con = scon ? scon->dcl.con : NULL; 200 int i; 201 202 for (i = 0; i < 256; i++) { 203 if (modifiers_state[i]) { 204 int qcode = sdl2_scancode_to_qcode[i]; 205 qemu_input_event_send_key_qcode(con, qcode, false); 206 modifiers_state[i] = 0; 207 } 208 } 209 } 210 211 static void sdl_process_key(struct sdl2_state *scon, 212 SDL_KeyboardEvent *ev) 213 { 214 int qcode = sdl2_scancode_to_qcode[ev->keysym.scancode]; 215 QemuConsole *con = scon ? scon->dcl.con : NULL; 216 217 if (!qemu_console_is_graphic(con)) { 218 if (ev->type == SDL_KEYDOWN) { 219 switch (ev->keysym.scancode) { 220 case SDL_SCANCODE_RETURN: 221 kbd_put_keysym_console(con, '\n'); 222 break; 223 case SDL_SCANCODE_BACKSPACE: 224 kbd_put_keysym_console(con, QEMU_KEY_BACKSPACE); 225 break; 226 default: 227 kbd_put_qcode_console(con, qcode); 228 break; 229 } 230 } 231 return; 232 } 233 234 switch (ev->keysym.scancode) { 235 #if 0 236 case SDL_SCANCODE_NUMLOCKCLEAR: 237 case SDL_SCANCODE_CAPSLOCK: 238 /* SDL does not send the key up event, so we generate it */ 239 qemu_input_event_send_key_qcode(con, qcode, true); 240 qemu_input_event_send_key_qcode(con, qcode, false); 241 return; 242 #endif 243 case SDL_SCANCODE_LCTRL: 244 case SDL_SCANCODE_LSHIFT: 245 case SDL_SCANCODE_LALT: 246 case SDL_SCANCODE_LGUI: 247 case SDL_SCANCODE_RCTRL: 248 case SDL_SCANCODE_RSHIFT: 249 case SDL_SCANCODE_RALT: 250 case SDL_SCANCODE_RGUI: 251 if (ev->type == SDL_KEYUP) { 252 modifiers_state[ev->keysym.scancode] = 0; 253 } else { 254 modifiers_state[ev->keysym.scancode] = 1; 255 } 256 /* fall though */ 257 default: 258 qemu_input_event_send_key_qcode(con, qcode, 259 ev->type == SDL_KEYDOWN); 260 } 261 } 262 263 static void sdl_update_caption(struct sdl2_state *scon) 264 { 265 char win_title[1024]; 266 char icon_title[1024]; 267 const char *status = ""; 268 269 if (!runstate_is_running()) { 270 status = " [Stopped]"; 271 } else if (gui_grab) { 272 if (alt_grab) { 273 status = " - Press Ctrl-Alt-Shift to exit mouse grab"; 274 } else if (ctrl_grab) { 275 status = " - Press Right-Ctrl to exit mouse grab"; 276 } else { 277 status = " - Press Ctrl-Alt to exit mouse grab"; 278 } 279 } 280 281 if (qemu_name) { 282 snprintf(win_title, sizeof(win_title), "QEMU (%s-%d)%s", qemu_name, 283 scon->idx, status); 284 snprintf(icon_title, sizeof(icon_title), "QEMU (%s)", qemu_name); 285 } else { 286 snprintf(win_title, sizeof(win_title), "QEMU%s", status); 287 snprintf(icon_title, sizeof(icon_title), "QEMU"); 288 } 289 290 if (scon->real_window) { 291 SDL_SetWindowTitle(scon->real_window, win_title); 292 } 293 } 294 295 static void sdl_hide_cursor(void) 296 { 297 if (!cursor_hide) { 298 return; 299 } 300 301 if (qemu_input_is_absolute()) { 302 SDL_ShowCursor(1); 303 SDL_SetCursor(sdl_cursor_hidden); 304 } else { 305 SDL_SetRelativeMouseMode(SDL_TRUE); 306 } 307 } 308 309 static void sdl_show_cursor(void) 310 { 311 if (!cursor_hide) { 312 return; 313 } 314 315 if (!qemu_input_is_absolute()) { 316 SDL_SetRelativeMouseMode(SDL_FALSE); 317 SDL_ShowCursor(1); 318 if (guest_cursor && 319 (gui_grab || qemu_input_is_absolute() || absolute_enabled)) { 320 SDL_SetCursor(guest_sprite); 321 } else { 322 SDL_SetCursor(sdl_cursor_normal); 323 } 324 } 325 } 326 327 static void sdl_grab_start(struct sdl2_state *scon) 328 { 329 QemuConsole *con = scon ? scon->dcl.con : NULL; 330 331 if (!con || !qemu_console_is_graphic(con)) { 332 return; 333 } 334 /* 335 * If the application is not active, do not try to enter grab state. This 336 * prevents 'SDL_WM_GrabInput(SDL_GRAB_ON)' from blocking all the 337 * application (SDL bug). 338 */ 339 if (!(SDL_GetWindowFlags(scon->real_window) & SDL_WINDOW_INPUT_FOCUS)) { 340 return; 341 } 342 if (guest_cursor) { 343 SDL_SetCursor(guest_sprite); 344 if (!qemu_input_is_absolute() && !absolute_enabled) { 345 SDL_WarpMouseInWindow(scon->real_window, guest_x, guest_y); 346 } 347 } else { 348 sdl_hide_cursor(); 349 } 350 SDL_SetWindowGrab(scon->real_window, SDL_TRUE); 351 gui_grab = 1; 352 sdl_update_caption(scon); 353 } 354 355 static void sdl_grab_end(struct sdl2_state *scon) 356 { 357 SDL_SetWindowGrab(scon->real_window, SDL_FALSE); 358 gui_grab = 0; 359 sdl_show_cursor(); 360 sdl_update_caption(scon); 361 } 362 363 static void absolute_mouse_grab(struct sdl2_state *scon) 364 { 365 int mouse_x, mouse_y; 366 int scr_w, scr_h; 367 SDL_GetMouseState(&mouse_x, &mouse_y); 368 SDL_GetWindowSize(scon->real_window, &scr_w, &scr_h); 369 if (mouse_x > 0 && mouse_x < scr_w - 1 && 370 mouse_y > 0 && mouse_y < scr_h - 1) { 371 sdl_grab_start(scon); 372 } 373 } 374 375 static void sdl_mouse_mode_change(Notifier *notify, void *data) 376 { 377 if (qemu_input_is_absolute()) { 378 if (!absolute_enabled) { 379 absolute_enabled = 1; 380 absolute_mouse_grab(&sdl2_console[0]); 381 } 382 } else if (absolute_enabled) { 383 if (!gui_fullscreen) { 384 sdl_grab_end(&sdl2_console[0]); 385 } 386 absolute_enabled = 0; 387 } 388 } 389 390 static void sdl_send_mouse_event(struct sdl2_state *scon, int dx, int dy, 391 int x, int y, int state) 392 { 393 static uint32_t bmap[INPUT_BUTTON_MAX] = { 394 [INPUT_BUTTON_LEFT] = SDL_BUTTON(SDL_BUTTON_LEFT), 395 [INPUT_BUTTON_MIDDLE] = SDL_BUTTON(SDL_BUTTON_MIDDLE), 396 [INPUT_BUTTON_RIGHT] = SDL_BUTTON(SDL_BUTTON_RIGHT), 397 }; 398 static uint32_t prev_state; 399 400 if (prev_state != state) { 401 qemu_input_update_buttons(scon->dcl.con, bmap, prev_state, state); 402 prev_state = state; 403 } 404 405 if (qemu_input_is_absolute()) { 406 int scr_w, scr_h; 407 int max_w = 0, max_h = 0; 408 int off_x = 0, off_y = 0; 409 int cur_off_x = 0, cur_off_y = 0; 410 int i; 411 412 for (i = 0; i < sdl2_num_outputs; i++) { 413 struct sdl2_state *thiscon = &sdl2_console[i]; 414 if (thiscon->real_window && thiscon->surface) { 415 SDL_GetWindowSize(thiscon->real_window, &scr_w, &scr_h); 416 cur_off_x = thiscon->x; 417 cur_off_y = thiscon->y; 418 if (scr_w + cur_off_x > max_w) { 419 max_w = scr_w + cur_off_x; 420 } 421 if (scr_h + cur_off_y > max_h) { 422 max_h = scr_h + cur_off_y; 423 } 424 if (i == scon->idx) { 425 off_x = cur_off_x; 426 off_y = cur_off_y; 427 } 428 } 429 } 430 qemu_input_queue_abs(scon->dcl.con, INPUT_AXIS_X, off_x + x, max_w); 431 qemu_input_queue_abs(scon->dcl.con, INPUT_AXIS_Y, off_y + y, max_h); 432 } else { 433 if (guest_cursor) { 434 x -= guest_x; 435 y -= guest_y; 436 guest_x += x; 437 guest_y += y; 438 dx = x; 439 dy = y; 440 } 441 qemu_input_queue_rel(scon->dcl.con, INPUT_AXIS_X, dx); 442 qemu_input_queue_rel(scon->dcl.con, INPUT_AXIS_Y, dy); 443 } 444 qemu_input_event_sync(); 445 } 446 447 static void sdl_scale(struct sdl2_state *scon, int width, int height) 448 { 449 int bpp = 0; 450 do_sdl_resize(scon, width, height, bpp); 451 scaling_active = 1; 452 } 453 454 static void toggle_full_screen(struct sdl2_state *scon) 455 { 456 int width = surface_width(scon->surface); 457 int height = surface_height(scon->surface); 458 int bpp = surface_bits_per_pixel(scon->surface); 459 460 gui_fullscreen = !gui_fullscreen; 461 if (gui_fullscreen) { 462 SDL_GetWindowSize(scon->real_window, 463 &gui_saved_width, &gui_saved_height); 464 gui_saved_scaling = scaling_active; 465 466 do_sdl_resize(scon, width, height, bpp); 467 scaling_active = 0; 468 469 gui_saved_grab = gui_grab; 470 sdl_grab_start(scon); 471 } else { 472 if (gui_saved_scaling) { 473 sdl_scale(scon, gui_saved_width, gui_saved_height); 474 } else { 475 do_sdl_resize(scon, width, height, 0); 476 } 477 if (!gui_saved_grab) { 478 sdl_grab_end(scon); 479 } 480 } 481 graphic_hw_invalidate(scon->dcl.con); 482 graphic_hw_update(scon->dcl.con); 483 } 484 485 static void handle_keydown(SDL_Event *ev) 486 { 487 int mod_state, win; 488 struct sdl2_state *scon = get_scon_from_window(ev->key.windowID); 489 490 if (alt_grab) { 491 mod_state = (SDL_GetModState() & (gui_grab_code | KMOD_LSHIFT)) == 492 (gui_grab_code | KMOD_LSHIFT); 493 } else if (ctrl_grab) { 494 mod_state = (SDL_GetModState() & KMOD_RCTRL) == KMOD_RCTRL; 495 } else { 496 mod_state = (SDL_GetModState() & gui_grab_code) == gui_grab_code; 497 } 498 gui_key_modifier_pressed = mod_state; 499 500 if (gui_key_modifier_pressed) { 501 switch (ev->key.keysym.scancode) { 502 case SDL_SCANCODE_2: 503 case SDL_SCANCODE_3: 504 case SDL_SCANCODE_4: 505 case SDL_SCANCODE_5: 506 case SDL_SCANCODE_6: 507 case SDL_SCANCODE_7: 508 case SDL_SCANCODE_8: 509 case SDL_SCANCODE_9: 510 win = ev->key.keysym.scancode - SDL_SCANCODE_1; 511 if (win < sdl2_num_outputs) { 512 sdl2_console[win].hidden = !sdl2_console[win].hidden; 513 if (sdl2_console[win].real_window) { 514 if (sdl2_console[win].hidden) { 515 SDL_HideWindow(sdl2_console[win].real_window); 516 } else { 517 SDL_ShowWindow(sdl2_console[win].real_window); 518 } 519 } 520 gui_keysym = 1; 521 } 522 break; 523 case SDL_SCANCODE_F: 524 toggle_full_screen(scon); 525 gui_keysym = 1; 526 break; 527 case SDL_SCANCODE_U: 528 if (scaling_active) { 529 scaling_active = 0; 530 sdl_switch(&scon->dcl, NULL); 531 graphic_hw_invalidate(scon->dcl.con); 532 graphic_hw_update(scon->dcl.con); 533 } 534 gui_keysym = 1; 535 break; 536 case SDL_SCANCODE_KP_PLUS: 537 case SDL_SCANCODE_KP_MINUS: 538 if (!gui_fullscreen) { 539 int scr_w, scr_h; 540 int width, height; 541 SDL_GetWindowSize(scon->real_window, &scr_w, &scr_h); 542 543 width = MAX(scr_w + (ev->key.keysym.scancode == 544 SDL_SCANCODE_KP_PLUS ? 50 : -50), 545 160); 546 height = (surface_height(scon->surface) * width) / 547 surface_width(scon->surface); 548 549 sdl_scale(scon, width, height); 550 graphic_hw_invalidate(NULL); 551 graphic_hw_update(NULL); 552 gui_keysym = 1; 553 } 554 default: 555 break; 556 } 557 } 558 if (!gui_keysym) { 559 sdl_process_key(scon, &ev->key); 560 } 561 } 562 563 static void handle_keyup(SDL_Event *ev) 564 { 565 int mod_state; 566 struct sdl2_state *scon = get_scon_from_window(ev->key.windowID); 567 568 if (!alt_grab) { 569 mod_state = (ev->key.keysym.mod & gui_grab_code); 570 } else { 571 mod_state = (ev->key.keysym.mod & (gui_grab_code | KMOD_LSHIFT)); 572 } 573 if (!mod_state && gui_key_modifier_pressed) { 574 gui_key_modifier_pressed = 0; 575 if (gui_keysym == 0) { 576 /* exit/enter grab if pressing Ctrl-Alt */ 577 if (!gui_grab) { 578 sdl_grab_start(scon); 579 } else if (!gui_fullscreen) { 580 sdl_grab_end(scon); 581 } 582 /* SDL does not send back all the modifiers key, so we must 583 * correct it. */ 584 reset_keys(scon); 585 return; 586 } 587 gui_keysym = 0; 588 } 589 if (!gui_keysym) { 590 sdl_process_key(scon, &ev->key); 591 } 592 } 593 594 static void handle_textinput(SDL_Event *ev) 595 { 596 struct sdl2_state *scon = get_scon_from_window(ev->key.windowID); 597 QemuConsole *con = scon ? scon->dcl.con : NULL; 598 599 if (qemu_console_is_graphic(con)) { 600 return; 601 } 602 kbd_put_string_console(con, ev->text.text, strlen(ev->text.text)); 603 } 604 605 static void handle_mousemotion(SDL_Event *ev) 606 { 607 int max_x, max_y; 608 struct sdl2_state *scon = get_scon_from_window(ev->key.windowID); 609 610 if (qemu_input_is_absolute() || absolute_enabled) { 611 int scr_w, scr_h; 612 SDL_GetWindowSize(scon->real_window, &scr_w, &scr_h); 613 max_x = scr_w - 1; 614 max_y = scr_h - 1; 615 if (gui_grab && (ev->motion.x == 0 || ev->motion.y == 0 || 616 ev->motion.x == max_x || ev->motion.y == max_y)) { 617 sdl_grab_end(scon); 618 } 619 if (!gui_grab && 620 (ev->motion.x > 0 && ev->motion.x < max_x && 621 ev->motion.y > 0 && ev->motion.y < max_y)) { 622 sdl_grab_start(scon); 623 } 624 } 625 if (gui_grab || qemu_input_is_absolute() || absolute_enabled) { 626 sdl_send_mouse_event(scon, ev->motion.xrel, ev->motion.yrel, 627 ev->motion.x, ev->motion.y, ev->motion.state); 628 } 629 } 630 631 static void handle_mousebutton(SDL_Event *ev) 632 { 633 int buttonstate = SDL_GetMouseState(NULL, NULL); 634 SDL_MouseButtonEvent *bev; 635 struct sdl2_state *scon = get_scon_from_window(ev->key.windowID); 636 637 bev = &ev->button; 638 if (!gui_grab && !qemu_input_is_absolute()) { 639 if (ev->type == SDL_MOUSEBUTTONUP && bev->button == SDL_BUTTON_LEFT) { 640 /* start grabbing all events */ 641 sdl_grab_start(scon); 642 } 643 } else { 644 if (ev->type == SDL_MOUSEBUTTONDOWN) { 645 buttonstate |= SDL_BUTTON(bev->button); 646 } else { 647 buttonstate &= ~SDL_BUTTON(bev->button); 648 } 649 sdl_send_mouse_event(scon, 0, 0, bev->x, bev->y, buttonstate); 650 } 651 } 652 653 static void handle_mousewheel(SDL_Event *ev) 654 { 655 struct sdl2_state *scon = get_scon_from_window(ev->key.windowID); 656 SDL_MouseWheelEvent *wev = &ev->wheel; 657 InputButton btn; 658 659 if (wev->y > 0) { 660 btn = INPUT_BUTTON_WHEEL_UP; 661 } else if (wev->y < 0) { 662 btn = INPUT_BUTTON_WHEEL_DOWN; 663 } else { 664 return; 665 } 666 667 qemu_input_queue_btn(scon->dcl.con, btn, true); 668 qemu_input_event_sync(); 669 qemu_input_queue_btn(scon->dcl.con, btn, false); 670 qemu_input_event_sync(); 671 } 672 673 static void handle_windowevent(DisplayChangeListener *dcl, SDL_Event *ev) 674 { 675 int w, h; 676 struct sdl2_state *scon = get_scon_from_window(ev->key.windowID); 677 678 switch (ev->window.event) { 679 case SDL_WINDOWEVENT_RESIZED: 680 sdl_scale(scon, ev->window.data1, ev->window.data2); 681 { 682 QemuUIInfo info; 683 memset(&info, 0, sizeof(info)); 684 info.width = ev->window.data1; 685 info.height = ev->window.data2; 686 dpy_set_ui_info(scon->dcl.con, &info); 687 } 688 graphic_hw_invalidate(scon->dcl.con); 689 graphic_hw_update(scon->dcl.con); 690 break; 691 case SDL_WINDOWEVENT_EXPOSED: 692 SDL_GetWindowSize(SDL_GetWindowFromID(ev->window.windowID), &w, &h); 693 sdl_update(dcl, 0, 0, w, h); 694 break; 695 case SDL_WINDOWEVENT_FOCUS_GAINED: 696 case SDL_WINDOWEVENT_ENTER: 697 if (!gui_grab && (qemu_input_is_absolute() || absolute_enabled)) { 698 absolute_mouse_grab(scon); 699 } 700 break; 701 case SDL_WINDOWEVENT_FOCUS_LOST: 702 if (gui_grab && !gui_fullscreen) { 703 sdl_grab_end(scon); 704 } 705 break; 706 case SDL_WINDOWEVENT_RESTORED: 707 update_displaychangelistener(dcl, GUI_REFRESH_INTERVAL_DEFAULT); 708 break; 709 case SDL_WINDOWEVENT_MINIMIZED: 710 update_displaychangelistener(dcl, 500); 711 break; 712 case SDL_WINDOWEVENT_CLOSE: 713 if (!no_quit) { 714 no_shutdown = 0; 715 qemu_system_shutdown_request(); 716 } 717 break; 718 } 719 } 720 721 static void sdl_refresh(DisplayChangeListener *dcl) 722 { 723 struct sdl2_state *scon = container_of(dcl, struct sdl2_state, dcl); 724 SDL_Event ev1, *ev = &ev1; 725 726 if (scon->last_vm_running != runstate_is_running()) { 727 scon->last_vm_running = runstate_is_running(); 728 sdl_update_caption(scon); 729 } 730 731 graphic_hw_update(dcl->con); 732 733 while (SDL_PollEvent(ev)) { 734 switch (ev->type) { 735 case SDL_KEYDOWN: 736 handle_keydown(ev); 737 break; 738 case SDL_KEYUP: 739 handle_keyup(ev); 740 break; 741 case SDL_TEXTINPUT: 742 handle_textinput(ev); 743 break; 744 case SDL_QUIT: 745 if (!no_quit) { 746 no_shutdown = 0; 747 qemu_system_shutdown_request(); 748 } 749 break; 750 case SDL_MOUSEMOTION: 751 handle_mousemotion(ev); 752 break; 753 case SDL_MOUSEBUTTONDOWN: 754 case SDL_MOUSEBUTTONUP: 755 handle_mousebutton(ev); 756 break; 757 case SDL_MOUSEWHEEL: 758 handle_mousewheel(ev); 759 break; 760 case SDL_WINDOWEVENT: 761 handle_windowevent(dcl, ev); 762 break; 763 default: 764 break; 765 } 766 } 767 } 768 769 static void sdl_mouse_warp(DisplayChangeListener *dcl, 770 int x, int y, int on) 771 { 772 struct sdl2_state *scon = container_of(dcl, struct sdl2_state, dcl); 773 if (on) { 774 if (!guest_cursor) { 775 sdl_show_cursor(); 776 } 777 if (gui_grab || qemu_input_is_absolute() || absolute_enabled) { 778 SDL_SetCursor(guest_sprite); 779 if (!qemu_input_is_absolute() && !absolute_enabled) { 780 SDL_WarpMouseInWindow(scon->real_window, x, y); 781 } 782 } 783 } else if (gui_grab) { 784 sdl_hide_cursor(); 785 } 786 guest_cursor = on; 787 guest_x = x, guest_y = y; 788 } 789 790 static void sdl_mouse_define(DisplayChangeListener *dcl, 791 QEMUCursor *c) 792 { 793 794 if (guest_sprite) { 795 SDL_FreeCursor(guest_sprite); 796 } 797 798 if (guest_sprite_surface) { 799 SDL_FreeSurface(guest_sprite_surface); 800 } 801 802 guest_sprite_surface = 803 SDL_CreateRGBSurfaceFrom(c->data, c->width, c->height, 32, c->width * 4, 804 0xff0000, 0x00ff00, 0xff, 0xff000000); 805 806 if (!guest_sprite_surface) { 807 fprintf(stderr, "Failed to make rgb surface from %p\n", c); 808 return; 809 } 810 guest_sprite = SDL_CreateColorCursor(guest_sprite_surface, 811 c->hot_x, c->hot_y); 812 if (!guest_sprite) { 813 fprintf(stderr, "Failed to make color cursor from %p\n", c); 814 return; 815 } 816 if (guest_cursor && 817 (gui_grab || qemu_input_is_absolute() || absolute_enabled)) { 818 SDL_SetCursor(guest_sprite); 819 } 820 } 821 822 static void sdl_cleanup(void) 823 { 824 if (guest_sprite) { 825 SDL_FreeCursor(guest_sprite); 826 } 827 SDL_QuitSubSystem(SDL_INIT_VIDEO); 828 } 829 830 static const DisplayChangeListenerOps dcl_ops = { 831 .dpy_name = "sdl", 832 .dpy_gfx_update = sdl_update, 833 .dpy_gfx_switch = sdl_switch, 834 .dpy_refresh = sdl_refresh, 835 .dpy_mouse_set = sdl_mouse_warp, 836 .dpy_cursor_define = sdl_mouse_define, 837 }; 838 839 void sdl_display_init(DisplayState *ds, int full_screen, int no_frame) 840 { 841 int flags; 842 uint8_t data = 0; 843 char *filename; 844 int i; 845 846 if (no_frame) { 847 gui_noframe = 1; 848 } 849 850 #ifdef __linux__ 851 /* on Linux, SDL may use fbcon|directfb|svgalib when run without 852 * accessible $DISPLAY to open X11 window. This is often the case 853 * when qemu is run using sudo. But in this case, and when actually 854 * run in X11 environment, SDL fights with X11 for the video card, 855 * making current display unavailable, often until reboot. 856 * So make x11 the default SDL video driver if this variable is unset. 857 * This is a bit hackish but saves us from bigger problem. 858 * Maybe it's a good idea to fix this in SDL instead. 859 */ 860 setenv("SDL_VIDEODRIVER", "x11", 0); 861 #endif 862 863 flags = SDL_INIT_VIDEO | SDL_INIT_NOPARACHUTE; 864 if (SDL_Init(flags)) { 865 fprintf(stderr, "Could not initialize SDL(%s) - exiting\n", 866 SDL_GetError()); 867 exit(1); 868 } 869 870 for (i = 0;; i++) { 871 QemuConsole *con = qemu_console_lookup_by_index(i); 872 if (!con) { 873 break; 874 } 875 } 876 sdl2_num_outputs = i; 877 sdl2_console = g_new0(struct sdl2_state, sdl2_num_outputs); 878 for (i = 0; i < sdl2_num_outputs; i++) { 879 QemuConsole *con = qemu_console_lookup_by_index(i); 880 if (!qemu_console_is_graphic(con)) { 881 sdl2_console[i].hidden = true; 882 } 883 sdl2_console[i].dcl.ops = &dcl_ops; 884 sdl2_console[i].dcl.con = con; 885 register_displaychangelistener(&sdl2_console[i].dcl); 886 sdl2_console[i].idx = i; 887 } 888 889 /* Load a 32x32x4 image. White pixels are transparent. */ 890 filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, "qemu-icon.bmp"); 891 if (filename) { 892 SDL_Surface *image = SDL_LoadBMP(filename); 893 if (image) { 894 uint32_t colorkey = SDL_MapRGB(image->format, 255, 255, 255); 895 SDL_SetColorKey(image, SDL_TRUE, colorkey); 896 SDL_SetWindowIcon(sdl2_console[0].real_window, image); 897 } 898 g_free(filename); 899 } 900 901 if (full_screen) { 902 gui_fullscreen = 1; 903 sdl_grab_start(0); 904 } 905 906 mouse_mode_notifier.notify = sdl_mouse_mode_change; 907 qemu_add_mouse_mode_change_notifier(&mouse_mode_notifier); 908 909 gui_grab = 0; 910 911 sdl_cursor_hidden = SDL_CreateCursor(&data, &data, 8, 1, 0, 0); 912 sdl_cursor_normal = SDL_GetCursor(); 913 914 atexit(sdl_cleanup); 915 } 916 #endif 917