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