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