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