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 dz, 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 #if 0 369 [INPUT_BUTTON_WHEEL_UP] = SDL_BUTTON(SDL_BUTTON_WHEELUP), 370 [INPUT_BUTTON_WHEEL_DOWN] = SDL_BUTTON(SDL_BUTTON_WHEELDOWN), 371 #endif 372 }; 373 static uint32_t prev_state; 374 375 if (prev_state != state) { 376 qemu_input_update_buttons(scon->dcl.con, bmap, prev_state, state); 377 prev_state = state; 378 } 379 380 if (qemu_input_is_absolute()) { 381 int scr_w, scr_h; 382 int max_w = 0, max_h = 0; 383 int off_x = 0, off_y = 0; 384 int cur_off_x = 0, cur_off_y = 0; 385 int i; 386 387 for (i = 0; i < sdl2_num_outputs; i++) { 388 struct sdl2_state *thiscon = &sdl2_console[i]; 389 if (thiscon->real_window && thiscon->surface) { 390 SDL_GetWindowSize(thiscon->real_window, &scr_w, &scr_h); 391 cur_off_x = thiscon->x; 392 cur_off_y = thiscon->y; 393 if (scr_w + cur_off_x > max_w) { 394 max_w = scr_w + cur_off_x; 395 } 396 if (scr_h + cur_off_y > max_h) { 397 max_h = scr_h + cur_off_y; 398 } 399 if (i == scon->idx) { 400 off_x = cur_off_x; 401 off_y = cur_off_y; 402 } 403 } 404 } 405 qemu_input_queue_abs(scon->dcl.con, INPUT_AXIS_X, off_x + x, max_w); 406 qemu_input_queue_abs(scon->dcl.con, INPUT_AXIS_Y, off_y + y, max_h); 407 } else { 408 if (guest_cursor) { 409 x -= guest_x; 410 y -= guest_y; 411 guest_x += x; 412 guest_y += y; 413 dx = x; 414 dy = y; 415 } 416 qemu_input_queue_rel(scon->dcl.con, INPUT_AXIS_X, dx); 417 qemu_input_queue_rel(scon->dcl.con, INPUT_AXIS_Y, dy); 418 } 419 qemu_input_event_sync(); 420 } 421 422 static void sdl_scale(struct sdl2_state *scon, int width, int height) 423 { 424 int bpp = 0; 425 do_sdl_resize(scon, width, height, bpp); 426 scaling_active = 1; 427 } 428 429 static void toggle_full_screen(struct sdl2_state *scon) 430 { 431 int width = surface_width(scon->surface); 432 int height = surface_height(scon->surface); 433 int bpp = surface_bits_per_pixel(scon->surface); 434 435 gui_fullscreen = !gui_fullscreen; 436 if (gui_fullscreen) { 437 SDL_GetWindowSize(scon->real_window, 438 &gui_saved_width, &gui_saved_height); 439 gui_saved_scaling = scaling_active; 440 441 do_sdl_resize(scon, width, height, bpp); 442 scaling_active = 0; 443 444 gui_saved_grab = gui_grab; 445 sdl_grab_start(scon); 446 } else { 447 if (gui_saved_scaling) { 448 sdl_scale(scon, gui_saved_width, gui_saved_height); 449 } else { 450 do_sdl_resize(scon, width, height, 0); 451 } 452 if (!gui_saved_grab) { 453 sdl_grab_end(scon); 454 } 455 } 456 graphic_hw_invalidate(scon->dcl.con); 457 graphic_hw_update(scon->dcl.con); 458 } 459 460 static void handle_keydown(SDL_Event *ev) 461 { 462 int mod_state; 463 struct sdl2_state *scon = get_scon_from_window(ev->key.windowID); 464 465 if (alt_grab) { 466 mod_state = (SDL_GetModState() & (gui_grab_code | KMOD_LSHIFT)) == 467 (gui_grab_code | KMOD_LSHIFT); 468 } else if (ctrl_grab) { 469 mod_state = (SDL_GetModState() & KMOD_RCTRL) == KMOD_RCTRL; 470 } else { 471 mod_state = (SDL_GetModState() & gui_grab_code) == gui_grab_code; 472 } 473 gui_key_modifier_pressed = mod_state; 474 475 if (gui_key_modifier_pressed) { 476 switch (ev->key.keysym.scancode) { 477 case SDL_SCANCODE_F: 478 toggle_full_screen(scon); 479 gui_keysym = 1; 480 break; 481 case SDL_SCANCODE_U: 482 if (scaling_active) { 483 scaling_active = 0; 484 sdl_switch(&scon->dcl, NULL); 485 graphic_hw_invalidate(scon->dcl.con); 486 graphic_hw_update(scon->dcl.con); 487 } 488 gui_keysym = 1; 489 break; 490 case SDL_SCANCODE_KP_PLUS: 491 case SDL_SCANCODE_KP_MINUS: 492 if (!gui_fullscreen) { 493 int scr_w, scr_h; 494 int width, height; 495 SDL_GetWindowSize(scon->real_window, &scr_w, &scr_h); 496 497 width = MAX(scr_w + (ev->key.keysym.scancode == 498 SDL_SCANCODE_KP_PLUS ? 50 : -50), 499 160); 500 height = (surface_height(scon->surface) * width) / 501 surface_width(scon->surface); 502 503 sdl_scale(scon, width, height); 504 graphic_hw_invalidate(NULL); 505 graphic_hw_update(NULL); 506 gui_keysym = 1; 507 } 508 default: 509 break; 510 } 511 } 512 if (!gui_keysym) { 513 sdl_process_key(&ev->key); 514 } 515 } 516 517 static void handle_keyup(SDL_Event *ev) 518 { 519 int mod_state; 520 struct sdl2_state *scon = get_scon_from_window(ev->key.windowID); 521 522 if (!alt_grab) { 523 mod_state = (ev->key.keysym.mod & gui_grab_code); 524 } else { 525 mod_state = (ev->key.keysym.mod & (gui_grab_code | KMOD_LSHIFT)); 526 } 527 if (!mod_state && gui_key_modifier_pressed) { 528 gui_key_modifier_pressed = 0; 529 if (gui_keysym == 0) { 530 /* exit/enter grab if pressing Ctrl-Alt */ 531 if (!gui_grab) { 532 sdl_grab_start(scon); 533 } else if (!gui_fullscreen) { 534 sdl_grab_end(scon); 535 } 536 /* SDL does not send back all the modifiers key, so we must 537 * correct it. */ 538 reset_keys(); 539 return; 540 } 541 gui_keysym = 0; 542 } 543 if (!gui_keysym) { 544 sdl_process_key(&ev->key); 545 } 546 } 547 548 static void handle_mousemotion(SDL_Event *ev) 549 { 550 int max_x, max_y; 551 struct sdl2_state *scon = get_scon_from_window(ev->key.windowID); 552 553 if (qemu_input_is_absolute() || absolute_enabled) { 554 int scr_w, scr_h; 555 SDL_GetWindowSize(scon->real_window, &scr_w, &scr_h); 556 max_x = scr_w - 1; 557 max_y = scr_h - 1; 558 if (gui_grab && (ev->motion.x == 0 || ev->motion.y == 0 || 559 ev->motion.x == max_x || ev->motion.y == max_y)) { 560 sdl_grab_end(scon); 561 } 562 if (!gui_grab && 563 (ev->motion.x > 0 && ev->motion.x < max_x && 564 ev->motion.y > 0 && ev->motion.y < max_y)) { 565 sdl_grab_start(scon); 566 } 567 } 568 if (gui_grab || qemu_input_is_absolute() || absolute_enabled) { 569 sdl_send_mouse_event(scon, ev->motion.xrel, ev->motion.yrel, 0, 570 ev->motion.x, ev->motion.y, ev->motion.state); 571 } 572 } 573 574 static void handle_mousebutton(SDL_Event *ev) 575 { 576 int buttonstate = SDL_GetMouseState(NULL, NULL); 577 SDL_MouseButtonEvent *bev; 578 struct sdl2_state *scon = get_scon_from_window(ev->key.windowID); 579 int dz; 580 581 bev = &ev->button; 582 if (!gui_grab && !qemu_input_is_absolute()) { 583 if (ev->type == SDL_MOUSEBUTTONUP && bev->button == SDL_BUTTON_LEFT) { 584 /* start grabbing all events */ 585 sdl_grab_start(scon); 586 } 587 } else { 588 dz = 0; 589 if (ev->type == SDL_MOUSEBUTTONDOWN) { 590 buttonstate |= SDL_BUTTON(bev->button); 591 } else { 592 buttonstate &= ~SDL_BUTTON(bev->button); 593 } 594 #ifdef SDL_BUTTON_WHEELUP 595 if (bev->button == SDL_BUTTON_WHEELUP && 596 ev->type == SDL_MOUSEBUTTONDOWN) { 597 dz = -1; 598 } else if (bev->button == SDL_BUTTON_WHEELDOWN && 599 ev->type == SDL_MOUSEBUTTONDOWN) { 600 dz = 1; 601 } 602 #endif 603 sdl_send_mouse_event(scon, 0, 0, dz, bev->x, bev->y, buttonstate); 604 } 605 } 606 607 static void handle_windowevent(DisplayChangeListener *dcl, SDL_Event *ev) 608 { 609 int w, h; 610 struct sdl2_state *scon = get_scon_from_window(ev->key.windowID); 611 612 switch (ev->window.event) { 613 case SDL_WINDOWEVENT_RESIZED: 614 sdl_scale(scon, ev->window.data1, ev->window.data2); 615 graphic_hw_invalidate(scon->dcl.con); 616 graphic_hw_update(scon->dcl.con); 617 break; 618 case SDL_WINDOWEVENT_EXPOSED: 619 SDL_GetWindowSize(SDL_GetWindowFromID(ev->window.windowID), &w, &h); 620 sdl_update(dcl, 0, 0, w, h); 621 break; 622 case SDL_WINDOWEVENT_FOCUS_GAINED: 623 case SDL_WINDOWEVENT_ENTER: 624 if (!gui_grab && (qemu_input_is_absolute() || absolute_enabled)) { 625 absolute_mouse_grab(scon); 626 } 627 break; 628 case SDL_WINDOWEVENT_FOCUS_LOST: 629 if (gui_grab && !gui_fullscreen) { 630 sdl_grab_end(scon); 631 } 632 break; 633 case SDL_WINDOWEVENT_RESTORED: 634 update_displaychangelistener(dcl, GUI_REFRESH_INTERVAL_DEFAULT); 635 break; 636 case SDL_WINDOWEVENT_MINIMIZED: 637 update_displaychangelistener(dcl, 500); 638 break; 639 case SDL_WINDOWEVENT_CLOSE: 640 if (!no_quit) { 641 no_shutdown = 0; 642 qemu_system_shutdown_request(); 643 } 644 break; 645 } 646 } 647 648 static void sdl_refresh(DisplayChangeListener *dcl) 649 { 650 struct sdl2_state *scon = container_of(dcl, struct sdl2_state, dcl); 651 SDL_Event ev1, *ev = &ev1; 652 653 if (scon->last_vm_running != runstate_is_running()) { 654 scon->last_vm_running = runstate_is_running(); 655 sdl_update_caption(scon); 656 } 657 658 graphic_hw_update(dcl->con); 659 660 while (SDL_PollEvent(ev)) { 661 switch (ev->type) { 662 case SDL_KEYDOWN: 663 handle_keydown(ev); 664 break; 665 case SDL_KEYUP: 666 handle_keyup(ev); 667 break; 668 case SDL_QUIT: 669 if (!no_quit) { 670 no_shutdown = 0; 671 qemu_system_shutdown_request(); 672 } 673 break; 674 case SDL_MOUSEMOTION: 675 handle_mousemotion(ev); 676 break; 677 case SDL_MOUSEBUTTONDOWN: 678 case SDL_MOUSEBUTTONUP: 679 handle_mousebutton(ev); 680 break; 681 case SDL_WINDOWEVENT: 682 handle_windowevent(dcl, ev); 683 break; 684 default: 685 break; 686 } 687 } 688 } 689 690 static void sdl_mouse_warp(DisplayChangeListener *dcl, 691 int x, int y, int on) 692 { 693 struct sdl2_state *scon = container_of(dcl, struct sdl2_state, dcl); 694 if (on) { 695 if (!guest_cursor) { 696 sdl_show_cursor(); 697 } 698 if (gui_grab || qemu_input_is_absolute() || absolute_enabled) { 699 SDL_SetCursor(guest_sprite); 700 if (!qemu_input_is_absolute() && !absolute_enabled) { 701 SDL_WarpMouseInWindow(scon->real_window, x, y); 702 } 703 } 704 } else if (gui_grab) { 705 sdl_hide_cursor(); 706 } 707 guest_cursor = on; 708 guest_x = x, guest_y = y; 709 } 710 711 static void sdl_mouse_define(DisplayChangeListener *dcl, 712 QEMUCursor *c) 713 { 714 715 if (guest_sprite) { 716 SDL_FreeCursor(guest_sprite); 717 } 718 719 if (guest_sprite_surface) { 720 SDL_FreeSurface(guest_sprite_surface); 721 } 722 723 guest_sprite_surface = 724 SDL_CreateRGBSurfaceFrom(c->data, c->width, c->height, 32, c->width * 4, 725 0xff0000, 0x00ff00, 0xff, 0xff000000); 726 727 if (!guest_sprite_surface) { 728 fprintf(stderr, "Failed to make rgb surface from %p\n", c); 729 return; 730 } 731 guest_sprite = SDL_CreateColorCursor(guest_sprite_surface, 732 c->hot_x, c->hot_y); 733 if (!guest_sprite) { 734 fprintf(stderr, "Failed to make color cursor from %p\n", c); 735 return; 736 } 737 if (guest_cursor && 738 (gui_grab || qemu_input_is_absolute() || absolute_enabled)) { 739 SDL_SetCursor(guest_sprite); 740 } 741 } 742 743 static void sdl_cleanup(void) 744 { 745 if (guest_sprite) { 746 SDL_FreeCursor(guest_sprite); 747 } 748 SDL_QuitSubSystem(SDL_INIT_VIDEO); 749 } 750 751 static const DisplayChangeListenerOps dcl_ops = { 752 .dpy_name = "sdl", 753 .dpy_gfx_update = sdl_update, 754 .dpy_gfx_switch = sdl_switch, 755 .dpy_refresh = sdl_refresh, 756 .dpy_mouse_set = sdl_mouse_warp, 757 .dpy_cursor_define = sdl_mouse_define, 758 }; 759 760 void sdl_display_init(DisplayState *ds, int full_screen, int no_frame) 761 { 762 int flags; 763 uint8_t data = 0; 764 char *filename; 765 int i; 766 767 if (no_frame) { 768 gui_noframe = 1; 769 } 770 771 #ifdef __linux__ 772 /* on Linux, SDL may use fbcon|directfb|svgalib when run without 773 * accessible $DISPLAY to open X11 window. This is often the case 774 * when qemu is run using sudo. But in this case, and when actually 775 * run in X11 environment, SDL fights with X11 for the video card, 776 * making current display unavailable, often until reboot. 777 * So make x11 the default SDL video driver if this variable is unset. 778 * This is a bit hackish but saves us from bigger problem. 779 * Maybe it's a good idea to fix this in SDL instead. 780 */ 781 setenv("SDL_VIDEODRIVER", "x11", 0); 782 #endif 783 784 flags = SDL_INIT_VIDEO | SDL_INIT_NOPARACHUTE; 785 if (SDL_Init(flags)) { 786 fprintf(stderr, "Could not initialize SDL(%s) - exiting\n", 787 SDL_GetError()); 788 exit(1); 789 } 790 791 for (i = 0;; i++) { 792 QemuConsole *con = qemu_console_lookup_by_index(i); 793 if (!con || !qemu_console_is_graphic(con)) { 794 break; 795 } 796 } 797 sdl2_num_outputs = i; 798 sdl2_console = g_new0(struct sdl2_state, sdl2_num_outputs); 799 for (i = 0; i < sdl2_num_outputs; i++) { 800 QemuConsole *con = qemu_console_lookup_by_index(i); 801 sdl2_console[i].dcl.ops = &dcl_ops; 802 sdl2_console[i].dcl.con = con; 803 register_displaychangelistener(&sdl2_console[i].dcl); 804 sdl2_console[i].idx = i; 805 } 806 807 /* Load a 32x32x4 image. White pixels are transparent. */ 808 filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, "qemu-icon.bmp"); 809 if (filename) { 810 SDL_Surface *image = SDL_LoadBMP(filename); 811 if (image) { 812 uint32_t colorkey = SDL_MapRGB(image->format, 255, 255, 255); 813 SDL_SetColorKey(image, SDL_TRUE, colorkey); 814 SDL_SetWindowIcon(sdl2_console[0].real_window, image); 815 } 816 g_free(filename); 817 } 818 819 if (full_screen) { 820 gui_fullscreen = 1; 821 sdl_grab_start(0); 822 } 823 824 mouse_mode_notifier.notify = sdl_mouse_mode_change; 825 qemu_add_mouse_mode_change_notifier(&mouse_mode_notifier); 826 827 gui_grab = 0; 828 829 sdl_cursor_hidden = SDL_CreateCursor(&data, &data, 8, 1, 0, 0); 830 sdl_cursor_normal = SDL_GetCursor(); 831 832 atexit(sdl_cleanup); 833 } 834 #endif 835