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