|  | /* | 
|  | * QEMU SDL display driver | 
|  | * | 
|  | * Copyright (c) 2003 Fabrice Bellard | 
|  | * | 
|  | * Permission is hereby granted, free of charge, to any person obtaining a copy | 
|  | * of this software and associated documentation files (the "Software"), to deal | 
|  | * in the Software without restriction, including without limitation the rights | 
|  | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | 
|  | * copies of the Software, and to permit persons to whom the Software is | 
|  | * furnished to do so, subject to the following conditions: | 
|  | * | 
|  | * The above copyright notice and this permission notice shall be included in | 
|  | * all copies or substantial portions of the Software. | 
|  | * | 
|  | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | 
|  | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | 
|  | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | 
|  | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | 
|  | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | 
|  | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | 
|  | * THE SOFTWARE. | 
|  | */ | 
|  | /* Ported SDL 1.2 code to 2.0 by Dave Airlie. */ | 
|  |  | 
|  | #include "qemu/osdep.h" | 
|  | #include "qemu/module.h" | 
|  | #include "qemu/cutils.h" | 
|  | #include "ui/console.h" | 
|  | #include "ui/input.h" | 
|  | #include "ui/sdl2.h" | 
|  | #include "sysemu/runstate.h" | 
|  | #include "sysemu/runstate-action.h" | 
|  | #include "sysemu/sysemu.h" | 
|  | #include "ui/win32-kbd-hook.h" | 
|  | #include "qemu/log.h" | 
|  |  | 
|  | static int sdl2_num_outputs; | 
|  | static struct sdl2_console *sdl2_console; | 
|  |  | 
|  | static SDL_Surface *guest_sprite_surface; | 
|  | static int gui_grab; /* if true, all keyboard/mouse events are grabbed */ | 
|  | static bool alt_grab; | 
|  | static bool ctrl_grab; | 
|  |  | 
|  | static int gui_saved_grab; | 
|  | static int gui_fullscreen; | 
|  | static int gui_grab_code = KMOD_LALT | KMOD_LCTRL; | 
|  | static SDL_Cursor *sdl_cursor_normal; | 
|  | static SDL_Cursor *sdl_cursor_hidden; | 
|  | static int absolute_enabled; | 
|  | static bool guest_cursor; | 
|  | static int guest_x, guest_y; | 
|  | static SDL_Cursor *guest_sprite; | 
|  | static Notifier mouse_mode_notifier; | 
|  |  | 
|  | #define SDL2_REFRESH_INTERVAL_BUSY 10 | 
|  | #define SDL2_MAX_IDLE_COUNT (2 * GUI_REFRESH_INTERVAL_DEFAULT \ | 
|  | / SDL2_REFRESH_INTERVAL_BUSY + 1) | 
|  |  | 
|  | /* introduced in SDL 2.0.10 */ | 
|  | #ifndef SDL_HINT_RENDER_BATCHING | 
|  | #define SDL_HINT_RENDER_BATCHING "SDL_RENDER_BATCHING" | 
|  | #endif | 
|  |  | 
|  | static void sdl_update_caption(struct sdl2_console *scon); | 
|  |  | 
|  | static struct sdl2_console *get_scon_from_window(uint32_t window_id) | 
|  | { | 
|  | int i; | 
|  | for (i = 0; i < sdl2_num_outputs; i++) { | 
|  | if (sdl2_console[i].real_window == SDL_GetWindowFromID(window_id)) { | 
|  | return &sdl2_console[i]; | 
|  | } | 
|  | } | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | void sdl2_window_create(struct sdl2_console *scon) | 
|  | { | 
|  | int flags = 0; | 
|  |  | 
|  | if (!scon->surface) { | 
|  | return; | 
|  | } | 
|  | assert(!scon->real_window); | 
|  |  | 
|  | if (gui_fullscreen) { | 
|  | flags |= SDL_WINDOW_FULLSCREEN_DESKTOP; | 
|  | } else { | 
|  | flags |= SDL_WINDOW_RESIZABLE; | 
|  | } | 
|  | if (scon->hidden) { | 
|  | flags |= SDL_WINDOW_HIDDEN; | 
|  | } | 
|  | #ifdef CONFIG_OPENGL | 
|  | if (scon->opengl) { | 
|  | flags |= SDL_WINDOW_OPENGL; | 
|  | } | 
|  | #endif | 
|  |  | 
|  | scon->real_window = SDL_CreateWindow("", SDL_WINDOWPOS_UNDEFINED, | 
|  | SDL_WINDOWPOS_UNDEFINED, | 
|  | surface_width(scon->surface), | 
|  | surface_height(scon->surface), | 
|  | flags); | 
|  | if (scon->opengl) { | 
|  | const char *driver = "opengl"; | 
|  |  | 
|  | if (scon->opts->gl == DISPLAY_GL_MODE_ES) { | 
|  | driver = "opengles2"; | 
|  | } | 
|  |  | 
|  | SDL_SetHint(SDL_HINT_RENDER_DRIVER, driver); | 
|  | SDL_SetHint(SDL_HINT_RENDER_BATCHING, "1"); | 
|  |  | 
|  | scon->winctx = SDL_GL_CreateContext(scon->real_window); | 
|  | SDL_GL_SetSwapInterval(0); | 
|  | } else { | 
|  | /* The SDL renderer is only used by sdl2-2D, when OpenGL is disabled */ | 
|  | scon->real_renderer = SDL_CreateRenderer(scon->real_window, -1, 0); | 
|  | } | 
|  | sdl_update_caption(scon); | 
|  | } | 
|  |  | 
|  | void sdl2_window_destroy(struct sdl2_console *scon) | 
|  | { | 
|  | if (!scon->real_window) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | if (scon->winctx) { | 
|  | SDL_GL_DeleteContext(scon->winctx); | 
|  | scon->winctx = NULL; | 
|  | } | 
|  | if (scon->real_renderer) { | 
|  | SDL_DestroyRenderer(scon->real_renderer); | 
|  | scon->real_renderer = NULL; | 
|  | } | 
|  | SDL_DestroyWindow(scon->real_window); | 
|  | scon->real_window = NULL; | 
|  | } | 
|  |  | 
|  | void sdl2_window_resize(struct sdl2_console *scon) | 
|  | { | 
|  | if (!scon->real_window) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | SDL_SetWindowSize(scon->real_window, | 
|  | surface_width(scon->surface), | 
|  | surface_height(scon->surface)); | 
|  | } | 
|  |  | 
|  | static void sdl2_redraw(struct sdl2_console *scon) | 
|  | { | 
|  | if (scon->opengl) { | 
|  | #ifdef CONFIG_OPENGL | 
|  | sdl2_gl_redraw(scon); | 
|  | #endif | 
|  | } else { | 
|  | sdl2_2d_redraw(scon); | 
|  | } | 
|  | } | 
|  |  | 
|  | static void sdl_update_caption(struct sdl2_console *scon) | 
|  | { | 
|  | char win_title[1024]; | 
|  | char icon_title[1024]; | 
|  | const char *status = ""; | 
|  |  | 
|  | if (!runstate_is_running()) { | 
|  | status = " [Stopped]"; | 
|  | } else if (gui_grab) { | 
|  | if (alt_grab) { | 
|  | #ifdef CONFIG_DARWIN | 
|  | status = " - Press ⌃⌥⇧G to exit grab"; | 
|  | #else | 
|  | status = " - Press Ctrl-Alt-Shift-G to exit grab"; | 
|  | #endif | 
|  | } else if (ctrl_grab) { | 
|  | status = " - Press Right-Ctrl-G to exit grab"; | 
|  | } else { | 
|  | #ifdef CONFIG_DARWIN | 
|  | status = " - Press ⌃⌥G to exit grab"; | 
|  | #else | 
|  | status = " - Press Ctrl-Alt-G to exit grab"; | 
|  | #endif | 
|  | } | 
|  | } | 
|  |  | 
|  | if (qemu_name) { | 
|  | snprintf(win_title, sizeof(win_title), "QEMU (%s-%d)%s", qemu_name, | 
|  | scon->idx, status); | 
|  | snprintf(icon_title, sizeof(icon_title), "QEMU (%s)", qemu_name); | 
|  | } else { | 
|  | snprintf(win_title, sizeof(win_title), "QEMU%s", status); | 
|  | snprintf(icon_title, sizeof(icon_title), "QEMU"); | 
|  | } | 
|  |  | 
|  | if (scon->real_window) { | 
|  | SDL_SetWindowTitle(scon->real_window, win_title); | 
|  | } | 
|  | } | 
|  |  | 
|  | static void sdl_hide_cursor(struct sdl2_console *scon) | 
|  | { | 
|  | if (scon->opts->has_show_cursor && scon->opts->show_cursor) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | SDL_ShowCursor(SDL_DISABLE); | 
|  | SDL_SetCursor(sdl_cursor_hidden); | 
|  |  | 
|  | if (!qemu_input_is_absolute(scon->dcl.con)) { | 
|  | SDL_SetRelativeMouseMode(SDL_TRUE); | 
|  | } | 
|  | } | 
|  |  | 
|  | static void sdl_show_cursor(struct sdl2_console *scon) | 
|  | { | 
|  | if (scon->opts->has_show_cursor && scon->opts->show_cursor) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | if (!qemu_input_is_absolute(scon->dcl.con)) { | 
|  | SDL_SetRelativeMouseMode(SDL_FALSE); | 
|  | } | 
|  |  | 
|  | if (guest_cursor && | 
|  | (gui_grab || qemu_input_is_absolute(scon->dcl.con) || absolute_enabled)) { | 
|  | SDL_SetCursor(guest_sprite); | 
|  | } else { | 
|  | SDL_SetCursor(sdl_cursor_normal); | 
|  | } | 
|  |  | 
|  | SDL_ShowCursor(SDL_ENABLE); | 
|  | } | 
|  |  | 
|  | static void sdl_grab_start(struct sdl2_console *scon) | 
|  | { | 
|  | QemuConsole *con = scon ? scon->dcl.con : NULL; | 
|  |  | 
|  | if (!con || !qemu_console_is_graphic(con)) { | 
|  | return; | 
|  | } | 
|  | /* | 
|  | * If the application is not active, do not try to enter grab state. This | 
|  | * prevents 'SDL_WM_GrabInput(SDL_GRAB_ON)' from blocking all the | 
|  | * application (SDL bug). | 
|  | */ | 
|  | if (!(SDL_GetWindowFlags(scon->real_window) & SDL_WINDOW_INPUT_FOCUS)) { | 
|  | return; | 
|  | } | 
|  | if (guest_cursor) { | 
|  | SDL_SetCursor(guest_sprite); | 
|  | if (!qemu_input_is_absolute(scon->dcl.con) && !absolute_enabled) { | 
|  | SDL_WarpMouseInWindow(scon->real_window, guest_x, guest_y); | 
|  | } | 
|  | } else { | 
|  | sdl_hide_cursor(scon); | 
|  | } | 
|  | SDL_SetWindowGrab(scon->real_window, SDL_TRUE); | 
|  | gui_grab = 1; | 
|  | win32_kbd_set_grab(true); | 
|  | sdl_update_caption(scon); | 
|  | } | 
|  |  | 
|  | static void sdl_grab_end(struct sdl2_console *scon) | 
|  | { | 
|  | SDL_SetWindowGrab(scon->real_window, SDL_FALSE); | 
|  | gui_grab = 0; | 
|  | win32_kbd_set_grab(false); | 
|  | sdl_show_cursor(scon); | 
|  | sdl_update_caption(scon); | 
|  | } | 
|  |  | 
|  | static void absolute_mouse_grab(struct sdl2_console *scon) | 
|  | { | 
|  | int mouse_x, mouse_y; | 
|  | int scr_w, scr_h; | 
|  | SDL_GetMouseState(&mouse_x, &mouse_y); | 
|  | SDL_GetWindowSize(scon->real_window, &scr_w, &scr_h); | 
|  | if (mouse_x > 0 && mouse_x < scr_w - 1 && | 
|  | mouse_y > 0 && mouse_y < scr_h - 1) { | 
|  | sdl_grab_start(scon); | 
|  | } | 
|  | } | 
|  |  | 
|  | static void sdl_mouse_mode_change(Notifier *notify, void *data) | 
|  | { | 
|  | if (qemu_input_is_absolute(sdl2_console[0].dcl.con)) { | 
|  | if (!absolute_enabled) { | 
|  | absolute_enabled = 1; | 
|  | SDL_SetRelativeMouseMode(SDL_FALSE); | 
|  | absolute_mouse_grab(&sdl2_console[0]); | 
|  | } | 
|  | } else if (absolute_enabled) { | 
|  | if (!gui_fullscreen) { | 
|  | sdl_grab_end(&sdl2_console[0]); | 
|  | } | 
|  | absolute_enabled = 0; | 
|  | } | 
|  | } | 
|  |  | 
|  | static void sdl_send_mouse_event(struct sdl2_console *scon, int dx, int dy, | 
|  | int x, int y, int state) | 
|  | { | 
|  | static uint32_t bmap[INPUT_BUTTON__MAX] = { | 
|  | [INPUT_BUTTON_LEFT]       = SDL_BUTTON(SDL_BUTTON_LEFT), | 
|  | [INPUT_BUTTON_MIDDLE]     = SDL_BUTTON(SDL_BUTTON_MIDDLE), | 
|  | [INPUT_BUTTON_RIGHT]      = SDL_BUTTON(SDL_BUTTON_RIGHT), | 
|  | [INPUT_BUTTON_SIDE]       = SDL_BUTTON(SDL_BUTTON_X1), | 
|  | [INPUT_BUTTON_EXTRA]      = SDL_BUTTON(SDL_BUTTON_X2) | 
|  | }; | 
|  | static uint32_t prev_state; | 
|  |  | 
|  | if (prev_state != state) { | 
|  | qemu_input_update_buttons(scon->dcl.con, bmap, prev_state, state); | 
|  | prev_state = state; | 
|  | } | 
|  |  | 
|  | if (qemu_input_is_absolute(scon->dcl.con)) { | 
|  | qemu_input_queue_abs(scon->dcl.con, INPUT_AXIS_X, | 
|  | x, 0, surface_width(scon->surface)); | 
|  | qemu_input_queue_abs(scon->dcl.con, INPUT_AXIS_Y, | 
|  | y, 0, surface_height(scon->surface)); | 
|  | } else { | 
|  | if (guest_cursor) { | 
|  | x -= guest_x; | 
|  | y -= guest_y; | 
|  | guest_x += x; | 
|  | guest_y += y; | 
|  | dx = x; | 
|  | dy = y; | 
|  | } | 
|  | qemu_input_queue_rel(scon->dcl.con, INPUT_AXIS_X, dx); | 
|  | qemu_input_queue_rel(scon->dcl.con, INPUT_AXIS_Y, dy); | 
|  | } | 
|  | qemu_input_event_sync(); | 
|  | } | 
|  |  | 
|  | static void toggle_full_screen(struct sdl2_console *scon) | 
|  | { | 
|  | gui_fullscreen = !gui_fullscreen; | 
|  | if (gui_fullscreen) { | 
|  | SDL_SetWindowFullscreen(scon->real_window, | 
|  | SDL_WINDOW_FULLSCREEN_DESKTOP); | 
|  | gui_saved_grab = gui_grab; | 
|  | sdl_grab_start(scon); | 
|  | } else { | 
|  | if (!gui_saved_grab) { | 
|  | sdl_grab_end(scon); | 
|  | } | 
|  | SDL_SetWindowFullscreen(scon->real_window, 0); | 
|  | } | 
|  | sdl2_redraw(scon); | 
|  | } | 
|  |  | 
|  | static int get_mod_state(void) | 
|  | { | 
|  | SDL_Keymod mod = SDL_GetModState(); | 
|  |  | 
|  | if (alt_grab) { | 
|  | return (mod & (gui_grab_code | KMOD_LSHIFT)) == | 
|  | (gui_grab_code | KMOD_LSHIFT); | 
|  | } else if (ctrl_grab) { | 
|  | return (mod & KMOD_RCTRL) == KMOD_RCTRL; | 
|  | } else { | 
|  | return (mod & gui_grab_code) == gui_grab_code; | 
|  | } | 
|  | } | 
|  |  | 
|  | static void *sdl2_win32_get_hwnd(struct sdl2_console *scon) | 
|  | { | 
|  | #ifdef CONFIG_WIN32 | 
|  | SDL_SysWMinfo info; | 
|  |  | 
|  | SDL_VERSION(&info.version); | 
|  | if (SDL_GetWindowWMInfo(scon->real_window, &info)) { | 
|  | return info.info.win.window; | 
|  | } | 
|  | #endif | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | static void handle_keydown(SDL_Event *ev) | 
|  | { | 
|  | int win; | 
|  | struct sdl2_console *scon = get_scon_from_window(ev->key.windowID); | 
|  | int gui_key_modifier_pressed = get_mod_state(); | 
|  |  | 
|  | if (!scon) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | scon->gui_keysym = false; | 
|  |  | 
|  | if (!scon->ignore_hotkeys && gui_key_modifier_pressed && !ev->key.repeat) { | 
|  | switch (ev->key.keysym.scancode) { | 
|  | case SDL_SCANCODE_2: | 
|  | case SDL_SCANCODE_3: | 
|  | case SDL_SCANCODE_4: | 
|  | case SDL_SCANCODE_5: | 
|  | case SDL_SCANCODE_6: | 
|  | case SDL_SCANCODE_7: | 
|  | case SDL_SCANCODE_8: | 
|  | case SDL_SCANCODE_9: | 
|  | if (gui_grab) { | 
|  | sdl_grab_end(scon); | 
|  | } | 
|  |  | 
|  | win = ev->key.keysym.scancode - SDL_SCANCODE_1; | 
|  | if (win < sdl2_num_outputs) { | 
|  | sdl2_console[win].hidden = !sdl2_console[win].hidden; | 
|  | if (sdl2_console[win].real_window) { | 
|  | if (sdl2_console[win].hidden) { | 
|  | SDL_HideWindow(sdl2_console[win].real_window); | 
|  | } else { | 
|  | SDL_ShowWindow(sdl2_console[win].real_window); | 
|  | } | 
|  | } | 
|  | sdl2_release_modifiers(scon); | 
|  | scon->gui_keysym = true; | 
|  | } | 
|  | break; | 
|  | case SDL_SCANCODE_F: | 
|  | toggle_full_screen(scon); | 
|  | scon->gui_keysym = true; | 
|  | break; | 
|  | case SDL_SCANCODE_G: | 
|  | scon->gui_keysym = true; | 
|  | if (!gui_grab) { | 
|  | sdl_grab_start(scon); | 
|  | } else if (!gui_fullscreen) { | 
|  | sdl_grab_end(scon); | 
|  | } | 
|  | break; | 
|  | case SDL_SCANCODE_U: | 
|  | sdl2_window_resize(scon); | 
|  | if (!scon->opengl) { | 
|  | /* re-create scon->texture */ | 
|  | sdl2_2d_switch(&scon->dcl, scon->surface); | 
|  | } | 
|  | scon->gui_keysym = true; | 
|  | break; | 
|  | #if 0 | 
|  | case SDL_SCANCODE_KP_PLUS: | 
|  | case SDL_SCANCODE_KP_MINUS: | 
|  | if (!gui_fullscreen) { | 
|  | int scr_w, scr_h; | 
|  | int width, height; | 
|  | SDL_GetWindowSize(scon->real_window, &scr_w, &scr_h); | 
|  |  | 
|  | width = MAX(scr_w + (ev->key.keysym.scancode == | 
|  | SDL_SCANCODE_KP_PLUS ? 50 : -50), | 
|  | 160); | 
|  | height = (surface_height(scon->surface) * width) / | 
|  | surface_width(scon->surface); | 
|  | fprintf(stderr, "%s: scale to %dx%d\n", | 
|  | __func__, width, height); | 
|  | sdl_scale(scon, width, height); | 
|  | sdl2_redraw(scon); | 
|  | scon->gui_keysym = true; | 
|  | } | 
|  | #endif | 
|  | default: | 
|  | break; | 
|  | } | 
|  | } | 
|  | if (!scon->gui_keysym) { | 
|  | sdl2_process_key(scon, &ev->key); | 
|  | } | 
|  | } | 
|  |  | 
|  | static void handle_keyup(SDL_Event *ev) | 
|  | { | 
|  | struct sdl2_console *scon = get_scon_from_window(ev->key.windowID); | 
|  |  | 
|  | if (!scon) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | scon->ignore_hotkeys = false; | 
|  | sdl2_process_key(scon, &ev->key); | 
|  | } | 
|  |  | 
|  | static void handle_textinput(SDL_Event *ev) | 
|  | { | 
|  | struct sdl2_console *scon = get_scon_from_window(ev->text.windowID); | 
|  | QemuConsole *con = scon ? scon->dcl.con : NULL; | 
|  |  | 
|  | if (!con) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | if (!scon->gui_keysym && QEMU_IS_TEXT_CONSOLE(con)) { | 
|  | qemu_text_console_put_string(QEMU_TEXT_CONSOLE(con), ev->text.text, strlen(ev->text.text)); | 
|  | } | 
|  | } | 
|  |  | 
|  | static void handle_mousemotion(SDL_Event *ev) | 
|  | { | 
|  | int max_x, max_y; | 
|  | struct sdl2_console *scon = get_scon_from_window(ev->motion.windowID); | 
|  |  | 
|  | if (!scon || !qemu_console_is_graphic(scon->dcl.con)) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | if (qemu_input_is_absolute(scon->dcl.con) || absolute_enabled) { | 
|  | int scr_w, scr_h; | 
|  | SDL_GetWindowSize(scon->real_window, &scr_w, &scr_h); | 
|  | max_x = scr_w - 1; | 
|  | max_y = scr_h - 1; | 
|  | if (gui_grab && !gui_fullscreen | 
|  | && (ev->motion.x == 0 || ev->motion.y == 0 || | 
|  | ev->motion.x == max_x || ev->motion.y == max_y)) { | 
|  | sdl_grab_end(scon); | 
|  | } | 
|  | if (!gui_grab && | 
|  | (ev->motion.x > 0 && ev->motion.x < max_x && | 
|  | ev->motion.y > 0 && ev->motion.y < max_y)) { | 
|  | sdl_grab_start(scon); | 
|  | } | 
|  | } | 
|  | if (gui_grab || qemu_input_is_absolute(scon->dcl.con) || absolute_enabled) { | 
|  | sdl_send_mouse_event(scon, ev->motion.xrel, ev->motion.yrel, | 
|  | ev->motion.x, ev->motion.y, ev->motion.state); | 
|  | } | 
|  | } | 
|  |  | 
|  | static void handle_mousebutton(SDL_Event *ev) | 
|  | { | 
|  | int buttonstate = SDL_GetMouseState(NULL, NULL); | 
|  | SDL_MouseButtonEvent *bev; | 
|  | struct sdl2_console *scon = get_scon_from_window(ev->button.windowID); | 
|  |  | 
|  | if (!scon || !qemu_console_is_graphic(scon->dcl.con)) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | bev = &ev->button; | 
|  | if (!gui_grab && !qemu_input_is_absolute(scon->dcl.con)) { | 
|  | if (ev->type == SDL_MOUSEBUTTONUP && bev->button == SDL_BUTTON_LEFT) { | 
|  | /* start grabbing all events */ | 
|  | sdl_grab_start(scon); | 
|  | } | 
|  | } else { | 
|  | if (ev->type == SDL_MOUSEBUTTONDOWN) { | 
|  | buttonstate |= SDL_BUTTON(bev->button); | 
|  | } else { | 
|  | buttonstate &= ~SDL_BUTTON(bev->button); | 
|  | } | 
|  | sdl_send_mouse_event(scon, 0, 0, bev->x, bev->y, buttonstate); | 
|  | } | 
|  | } | 
|  |  | 
|  | static void handle_mousewheel(SDL_Event *ev) | 
|  | { | 
|  | struct sdl2_console *scon = get_scon_from_window(ev->wheel.windowID); | 
|  | SDL_MouseWheelEvent *wev = &ev->wheel; | 
|  | InputButton btn; | 
|  |  | 
|  | if (!scon || !qemu_console_is_graphic(scon->dcl.con)) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | if (wev->y > 0) { | 
|  | btn = INPUT_BUTTON_WHEEL_UP; | 
|  | } else if (wev->y < 0) { | 
|  | btn = INPUT_BUTTON_WHEEL_DOWN; | 
|  | } else if (wev->x < 0) { | 
|  | btn = INPUT_BUTTON_WHEEL_RIGHT; | 
|  | } else if (wev->x > 0) { | 
|  | btn = INPUT_BUTTON_WHEEL_LEFT; | 
|  | } else { | 
|  | return; | 
|  | } | 
|  |  | 
|  | qemu_input_queue_btn(scon->dcl.con, btn, true); | 
|  | qemu_input_event_sync(); | 
|  | qemu_input_queue_btn(scon->dcl.con, btn, false); | 
|  | qemu_input_event_sync(); | 
|  | } | 
|  |  | 
|  | static void handle_windowevent(SDL_Event *ev) | 
|  | { | 
|  | struct sdl2_console *scon = get_scon_from_window(ev->window.windowID); | 
|  | bool allow_close = true; | 
|  |  | 
|  | if (!scon) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | switch (ev->window.event) { | 
|  | case SDL_WINDOWEVENT_RESIZED: | 
|  | { | 
|  | QemuUIInfo info; | 
|  | memset(&info, 0, sizeof(info)); | 
|  | info.width = ev->window.data1; | 
|  | info.height = ev->window.data2; | 
|  | dpy_set_ui_info(scon->dcl.con, &info, true); | 
|  | } | 
|  | sdl2_redraw(scon); | 
|  | break; | 
|  | case SDL_WINDOWEVENT_EXPOSED: | 
|  | sdl2_redraw(scon); | 
|  | break; | 
|  | case SDL_WINDOWEVENT_FOCUS_GAINED: | 
|  | win32_kbd_set_grab(gui_grab); | 
|  | if (qemu_console_is_graphic(scon->dcl.con)) { | 
|  | win32_kbd_set_window(sdl2_win32_get_hwnd(scon)); | 
|  | } | 
|  | /* fall through */ | 
|  | case SDL_WINDOWEVENT_ENTER: | 
|  | if (!gui_grab && (qemu_input_is_absolute(scon->dcl.con) || absolute_enabled)) { | 
|  | absolute_mouse_grab(scon); | 
|  | } | 
|  | /* If a new console window opened using a hotkey receives the | 
|  | * focus, SDL sends another KEYDOWN event to the new window, | 
|  | * closing the console window immediately after. | 
|  | * | 
|  | * Work around this by ignoring further hotkey events until a | 
|  | * key is released. | 
|  | */ | 
|  | scon->ignore_hotkeys = get_mod_state(); | 
|  | break; | 
|  | case SDL_WINDOWEVENT_FOCUS_LOST: | 
|  | if (qemu_console_is_graphic(scon->dcl.con)) { | 
|  | win32_kbd_set_window(NULL); | 
|  | } | 
|  | if (gui_grab && !gui_fullscreen) { | 
|  | sdl_grab_end(scon); | 
|  | } | 
|  | break; | 
|  | case SDL_WINDOWEVENT_RESTORED: | 
|  | update_displaychangelistener(&scon->dcl, GUI_REFRESH_INTERVAL_DEFAULT); | 
|  | break; | 
|  | case SDL_WINDOWEVENT_MINIMIZED: | 
|  | update_displaychangelistener(&scon->dcl, 500); | 
|  | break; | 
|  | case SDL_WINDOWEVENT_CLOSE: | 
|  | if (qemu_console_is_graphic(scon->dcl.con)) { | 
|  | if (scon->opts->has_window_close && !scon->opts->window_close) { | 
|  | allow_close = false; | 
|  | } | 
|  | if (allow_close) { | 
|  | shutdown_action = SHUTDOWN_ACTION_POWEROFF; | 
|  | qemu_system_shutdown_request(SHUTDOWN_CAUSE_HOST_UI); | 
|  | } | 
|  | } else { | 
|  | SDL_HideWindow(scon->real_window); | 
|  | scon->hidden = true; | 
|  | } | 
|  | break; | 
|  | case SDL_WINDOWEVENT_SHOWN: | 
|  | scon->hidden = false; | 
|  | break; | 
|  | case SDL_WINDOWEVENT_HIDDEN: | 
|  | scon->hidden = true; | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | void sdl2_poll_events(struct sdl2_console *scon) | 
|  | { | 
|  | SDL_Event ev1, *ev = &ev1; | 
|  | bool allow_close = true; | 
|  | int idle = 1; | 
|  |  | 
|  | if (scon->last_vm_running != runstate_is_running()) { | 
|  | scon->last_vm_running = runstate_is_running(); | 
|  | sdl_update_caption(scon); | 
|  | } | 
|  |  | 
|  | while (SDL_PollEvent(ev)) { | 
|  | switch (ev->type) { | 
|  | case SDL_KEYDOWN: | 
|  | idle = 0; | 
|  | handle_keydown(ev); | 
|  | break; | 
|  | case SDL_KEYUP: | 
|  | idle = 0; | 
|  | handle_keyup(ev); | 
|  | break; | 
|  | case SDL_TEXTINPUT: | 
|  | idle = 0; | 
|  | handle_textinput(ev); | 
|  | break; | 
|  | case SDL_QUIT: | 
|  | if (scon->opts->has_window_close && !scon->opts->window_close) { | 
|  | allow_close = false; | 
|  | } | 
|  | if (allow_close) { | 
|  | shutdown_action = SHUTDOWN_ACTION_POWEROFF; | 
|  | qemu_system_shutdown_request(SHUTDOWN_CAUSE_HOST_UI); | 
|  | } | 
|  | break; | 
|  | case SDL_MOUSEMOTION: | 
|  | idle = 0; | 
|  | handle_mousemotion(ev); | 
|  | break; | 
|  | case SDL_MOUSEBUTTONDOWN: | 
|  | case SDL_MOUSEBUTTONUP: | 
|  | idle = 0; | 
|  | handle_mousebutton(ev); | 
|  | break; | 
|  | case SDL_MOUSEWHEEL: | 
|  | idle = 0; | 
|  | handle_mousewheel(ev); | 
|  | break; | 
|  | case SDL_WINDOWEVENT: | 
|  | handle_windowevent(ev); | 
|  | break; | 
|  | default: | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (idle) { | 
|  | if (scon->idle_counter < SDL2_MAX_IDLE_COUNT) { | 
|  | scon->idle_counter++; | 
|  | if (scon->idle_counter >= SDL2_MAX_IDLE_COUNT) { | 
|  | scon->dcl.update_interval = GUI_REFRESH_INTERVAL_DEFAULT; | 
|  | } | 
|  | } | 
|  | } else { | 
|  | scon->idle_counter = 0; | 
|  | scon->dcl.update_interval = SDL2_REFRESH_INTERVAL_BUSY; | 
|  | } | 
|  | } | 
|  |  | 
|  | static void sdl_mouse_warp(DisplayChangeListener *dcl, | 
|  | int x, int y, bool on) | 
|  | { | 
|  | struct sdl2_console *scon = container_of(dcl, struct sdl2_console, dcl); | 
|  |  | 
|  | if (!qemu_console_is_graphic(scon->dcl.con)) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | if (on) { | 
|  | if (!guest_cursor) { | 
|  | sdl_show_cursor(scon); | 
|  | } | 
|  | if (gui_grab || qemu_input_is_absolute(scon->dcl.con) || absolute_enabled) { | 
|  | SDL_SetCursor(guest_sprite); | 
|  | if (!qemu_input_is_absolute(scon->dcl.con) && !absolute_enabled) { | 
|  | SDL_WarpMouseInWindow(scon->real_window, x, y); | 
|  | } | 
|  | } | 
|  | } else if (gui_grab) { | 
|  | sdl_hide_cursor(scon); | 
|  | } | 
|  | guest_cursor = on; | 
|  | guest_x = x, guest_y = y; | 
|  | } | 
|  |  | 
|  | static void sdl_mouse_define(DisplayChangeListener *dcl, | 
|  | QEMUCursor *c) | 
|  | { | 
|  |  | 
|  | if (guest_sprite) { | 
|  | SDL_FreeCursor(guest_sprite); | 
|  | } | 
|  |  | 
|  | if (guest_sprite_surface) { | 
|  | SDL_FreeSurface(guest_sprite_surface); | 
|  | } | 
|  |  | 
|  | guest_sprite_surface = | 
|  | SDL_CreateRGBSurfaceFrom(c->data, c->width, c->height, 32, c->width * 4, | 
|  | 0xff0000, 0x00ff00, 0xff, 0xff000000); | 
|  |  | 
|  | if (!guest_sprite_surface) { | 
|  | fprintf(stderr, "Failed to make rgb surface from %p\n", c); | 
|  | return; | 
|  | } | 
|  | guest_sprite = SDL_CreateColorCursor(guest_sprite_surface, | 
|  | c->hot_x, c->hot_y); | 
|  | if (!guest_sprite) { | 
|  | fprintf(stderr, "Failed to make color cursor from %p\n", c); | 
|  | return; | 
|  | } | 
|  | if (guest_cursor && | 
|  | (gui_grab || qemu_input_is_absolute(dcl->con) || absolute_enabled)) { | 
|  | SDL_SetCursor(guest_sprite); | 
|  | } | 
|  | } | 
|  |  | 
|  | static void sdl_cleanup(void) | 
|  | { | 
|  | if (guest_sprite) { | 
|  | SDL_FreeCursor(guest_sprite); | 
|  | } | 
|  | SDL_QuitSubSystem(SDL_INIT_VIDEO); | 
|  | } | 
|  |  | 
|  | static const DisplayChangeListenerOps dcl_2d_ops = { | 
|  | .dpy_name             = "sdl2-2d", | 
|  | .dpy_gfx_update       = sdl2_2d_update, | 
|  | .dpy_gfx_switch       = sdl2_2d_switch, | 
|  | .dpy_gfx_check_format = sdl2_2d_check_format, | 
|  | .dpy_refresh          = sdl2_2d_refresh, | 
|  | .dpy_mouse_set        = sdl_mouse_warp, | 
|  | .dpy_cursor_define    = sdl_mouse_define, | 
|  | }; | 
|  |  | 
|  | #ifdef CONFIG_OPENGL | 
|  | static const DisplayChangeListenerOps dcl_gl_ops = { | 
|  | .dpy_name                = "sdl2-gl", | 
|  | .dpy_gfx_update          = sdl2_gl_update, | 
|  | .dpy_gfx_switch          = sdl2_gl_switch, | 
|  | .dpy_gfx_check_format    = console_gl_check_format, | 
|  | .dpy_refresh             = sdl2_gl_refresh, | 
|  | .dpy_mouse_set           = sdl_mouse_warp, | 
|  | .dpy_cursor_define       = sdl_mouse_define, | 
|  |  | 
|  | .dpy_gl_scanout_disable  = sdl2_gl_scanout_disable, | 
|  | .dpy_gl_scanout_texture  = sdl2_gl_scanout_texture, | 
|  | .dpy_gl_update           = sdl2_gl_scanout_flush, | 
|  | }; | 
|  |  | 
|  | static bool | 
|  | sdl2_gl_is_compatible_dcl(DisplayGLCtx *dgc, | 
|  | DisplayChangeListener *dcl) | 
|  | { | 
|  | return dcl->ops == &dcl_gl_ops; | 
|  | } | 
|  |  | 
|  | static const DisplayGLCtxOps gl_ctx_ops = { | 
|  | .dpy_gl_ctx_is_compatible_dcl = sdl2_gl_is_compatible_dcl, | 
|  | .dpy_gl_ctx_create       = sdl2_gl_create_context, | 
|  | .dpy_gl_ctx_destroy      = sdl2_gl_destroy_context, | 
|  | .dpy_gl_ctx_make_current = sdl2_gl_make_context_current, | 
|  | }; | 
|  | #endif | 
|  |  | 
|  | static void sdl2_display_early_init(DisplayOptions *o) | 
|  | { | 
|  | assert(o->type == DISPLAY_TYPE_SDL); | 
|  | if (o->has_gl && o->gl) { | 
|  | #ifdef CONFIG_OPENGL | 
|  | display_opengl = 1; | 
|  | #endif | 
|  | } | 
|  | } | 
|  |  | 
|  | static void sdl2_display_init(DisplayState *ds, DisplayOptions *o) | 
|  | { | 
|  | uint8_t data = 0; | 
|  | int i; | 
|  | SDL_SysWMinfo info; | 
|  | SDL_Surface *icon = NULL; | 
|  | char *dir; | 
|  |  | 
|  | assert(o->type == DISPLAY_TYPE_SDL); | 
|  |  | 
|  | if (SDL_GetHintBoolean("QEMU_ENABLE_SDL_LOGGING", SDL_FALSE)) { | 
|  | SDL_LogSetAllPriority(SDL_LOG_PRIORITY_VERBOSE); | 
|  | } | 
|  |  | 
|  | if (SDL_Init(SDL_INIT_VIDEO)) { | 
|  | fprintf(stderr, "Could not initialize SDL(%s) - exiting\n", | 
|  | SDL_GetError()); | 
|  | exit(1); | 
|  | } | 
|  | #ifdef SDL_HINT_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR /* only available since SDL 2.0.8 */ | 
|  | SDL_SetHint(SDL_HINT_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR, "0"); | 
|  | #endif | 
|  | #ifndef CONFIG_WIN32 | 
|  | /* QEMU uses its own low level keyboard hook procedure on Windows */ | 
|  | SDL_SetHint(SDL_HINT_GRAB_KEYBOARD, "1"); | 
|  | #endif | 
|  | #ifdef SDL_HINT_ALLOW_ALT_TAB_WHILE_GRABBED | 
|  | SDL_SetHint(SDL_HINT_ALLOW_ALT_TAB_WHILE_GRABBED, "0"); | 
|  | #endif | 
|  | SDL_SetHint(SDL_HINT_WINDOWS_NO_CLOSE_ON_ALT_F4, "1"); | 
|  | SDL_EnableScreenSaver(); | 
|  | memset(&info, 0, sizeof(info)); | 
|  | SDL_VERSION(&info.version); | 
|  |  | 
|  | gui_fullscreen = o->has_full_screen && o->full_screen; | 
|  |  | 
|  | if (o->u.sdl.has_grab_mod) { | 
|  | if (o->u.sdl.grab_mod == HOT_KEY_MOD_LSHIFT_LCTRL_LALT) { | 
|  | alt_grab = true; | 
|  | } else if (o->u.sdl.grab_mod == HOT_KEY_MOD_RCTRL) { | 
|  | ctrl_grab = true; | 
|  | } | 
|  | } | 
|  |  | 
|  | for (i = 0;; i++) { | 
|  | QemuConsole *con = qemu_console_lookup_by_index(i); | 
|  | if (!con) { | 
|  | break; | 
|  | } | 
|  | } | 
|  | sdl2_num_outputs = i; | 
|  | if (sdl2_num_outputs == 0) { | 
|  | return; | 
|  | } | 
|  | sdl2_console = g_new0(struct sdl2_console, sdl2_num_outputs); | 
|  | for (i = 0; i < sdl2_num_outputs; i++) { | 
|  | QemuConsole *con = qemu_console_lookup_by_index(i); | 
|  | assert(con != NULL); | 
|  | if (!qemu_console_is_graphic(con) && | 
|  | qemu_console_get_index(con) != 0) { | 
|  | sdl2_console[i].hidden = true; | 
|  | } | 
|  | sdl2_console[i].idx = i; | 
|  | sdl2_console[i].opts = o; | 
|  | #ifdef CONFIG_OPENGL | 
|  | sdl2_console[i].opengl = display_opengl; | 
|  | sdl2_console[i].dcl.ops = display_opengl ? &dcl_gl_ops : &dcl_2d_ops; | 
|  | sdl2_console[i].dgc.ops = display_opengl ? &gl_ctx_ops : NULL; | 
|  | #else | 
|  | sdl2_console[i].opengl = 0; | 
|  | sdl2_console[i].dcl.ops = &dcl_2d_ops; | 
|  | #endif | 
|  | sdl2_console[i].dcl.con = con; | 
|  | sdl2_console[i].kbd = qkbd_state_init(con); | 
|  | if (display_opengl) { | 
|  | qemu_console_set_display_gl_ctx(con, &sdl2_console[i].dgc); | 
|  | } | 
|  | register_displaychangelistener(&sdl2_console[i].dcl); | 
|  |  | 
|  | #if defined(SDL_VIDEO_DRIVER_WINDOWS) || defined(SDL_VIDEO_DRIVER_X11) | 
|  | if (SDL_GetWindowWMInfo(sdl2_console[i].real_window, &info)) { | 
|  | #if defined(SDL_VIDEO_DRIVER_WINDOWS) | 
|  | qemu_console_set_window_id(con, (uintptr_t)info.info.win.window); | 
|  | #elif defined(SDL_VIDEO_DRIVER_X11) | 
|  | qemu_console_set_window_id(con, info.info.x11.window); | 
|  | #endif | 
|  | } | 
|  | #endif | 
|  | } | 
|  |  | 
|  | #ifdef CONFIG_SDL_IMAGE | 
|  | dir = get_relocated_path(CONFIG_QEMU_ICONDIR "/hicolor/128x128/apps/qemu.png"); | 
|  | icon = IMG_Load(dir); | 
|  | #else | 
|  | /* Load a 32x32x4 image. White pixels are transparent. */ | 
|  | dir = get_relocated_path(CONFIG_QEMU_ICONDIR "/hicolor/32x32/apps/qemu.bmp"); | 
|  | icon = SDL_LoadBMP(dir); | 
|  | if (icon) { | 
|  | uint32_t colorkey = SDL_MapRGB(icon->format, 255, 255, 255); | 
|  | SDL_SetColorKey(icon, SDL_TRUE, colorkey); | 
|  | } | 
|  | #endif | 
|  | g_free(dir); | 
|  | if (icon) { | 
|  | SDL_SetWindowIcon(sdl2_console[0].real_window, icon); | 
|  | } | 
|  |  | 
|  | mouse_mode_notifier.notify = sdl_mouse_mode_change; | 
|  | qemu_add_mouse_mode_change_notifier(&mouse_mode_notifier); | 
|  |  | 
|  | sdl_cursor_hidden = SDL_CreateCursor(&data, &data, 8, 1, 0, 0); | 
|  | sdl_cursor_normal = SDL_GetCursor(); | 
|  |  | 
|  | if (gui_fullscreen) { | 
|  | sdl_grab_start(&sdl2_console[0]); | 
|  | } | 
|  |  | 
|  | atexit(sdl_cleanup); | 
|  | } | 
|  |  | 
|  | static QemuDisplay qemu_display_sdl2 = { | 
|  | .type       = DISPLAY_TYPE_SDL, | 
|  | .early_init = sdl2_display_early_init, | 
|  | .init       = sdl2_display_init, | 
|  | }; | 
|  |  | 
|  | static void register_sdl1(void) | 
|  | { | 
|  | qemu_display_register(&qemu_display_sdl2); | 
|  | } | 
|  |  | 
|  | type_init(register_sdl1); | 
|  |  | 
|  | #ifdef CONFIG_OPENGL | 
|  | module_dep("ui-opengl"); | 
|  | #endif |