| /* |
| * 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. */ |
| |
| /* Avoid compiler warning because macro is redefined in SDL_syswm.h. */ |
| #undef WIN32_LEAN_AND_MEAN |
| |
| #include <SDL.h> |
| |
| #if SDL_MAJOR_VERSION == 2 |
| #include <SDL_syswm.h> |
| |
| #include "qemu-common.h" |
| #include "ui/console.h" |
| #include "ui/input.h" |
| #include "sysemu/sysemu.h" |
| #include "sdl_zoom.h" |
| |
| #include "sdl2-keymap.h" |
| |
| static int sdl2_num_outputs; |
| static struct sdl2_state { |
| DisplayChangeListener dcl; |
| DisplaySurface *surface; |
| SDL_Texture *texture; |
| SDL_Window *real_window; |
| SDL_Renderer *real_renderer; |
| int idx; |
| int last_vm_running; /* per console for caption reasons */ |
| int x, y; |
| } *sdl2_console; |
| |
| static SDL_Surface *guest_sprite_surface; |
| static int gui_grab; /* if true, all keyboard/mouse events are grabbed */ |
| |
| static bool gui_saved_scaling; |
| static int gui_saved_width; |
| static int gui_saved_height; |
| static int gui_saved_grab; |
| static int gui_fullscreen; |
| static int gui_noframe; |
| static int gui_key_modifier_pressed; |
| static int gui_keysym; |
| static int gui_grab_code = KMOD_LALT | KMOD_LCTRL; |
| static uint8_t modifiers_state[SDL_NUM_SCANCODES]; |
| static SDL_Cursor *sdl_cursor_normal; |
| static SDL_Cursor *sdl_cursor_hidden; |
| static int absolute_enabled; |
| static int guest_cursor; |
| static int guest_x, guest_y; |
| static SDL_Cursor *guest_sprite; |
| static int scaling_active; |
| static Notifier mouse_mode_notifier; |
| |
| static void sdl_update_caption(struct sdl2_state *scon); |
| |
| static struct sdl2_state *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; |
| } |
| |
| static void sdl_update(DisplayChangeListener *dcl, |
| int x, int y, int w, int h) |
| { |
| struct sdl2_state *scon = container_of(dcl, struct sdl2_state, dcl); |
| SDL_Rect rect; |
| DisplaySurface *surf = qemu_console_surface(dcl->con); |
| |
| if (!surf) { |
| return; |
| } |
| if (!scon->texture) { |
| return; |
| } |
| |
| rect.x = x; |
| rect.y = y; |
| rect.w = w; |
| rect.h = h; |
| |
| SDL_UpdateTexture(scon->texture, NULL, surface_data(surf), |
| surface_stride(surf)); |
| SDL_RenderCopy(scon->real_renderer, scon->texture, &rect, &rect); |
| SDL_RenderPresent(scon->real_renderer); |
| } |
| |
| static void do_sdl_resize(struct sdl2_state *scon, int width, int height, |
| int bpp) |
| { |
| int flags; |
| |
| if (scon->real_window && scon->real_renderer) { |
| if (width && height) { |
| SDL_RenderSetLogicalSize(scon->real_renderer, width, height); |
| SDL_SetWindowSize(scon->real_window, width, height); |
| } else { |
| SDL_DestroyRenderer(scon->real_renderer); |
| SDL_DestroyWindow(scon->real_window); |
| scon->real_renderer = NULL; |
| scon->real_window = NULL; |
| } |
| } else { |
| if (!width || !height) { |
| return; |
| } |
| flags = 0; |
| if (gui_fullscreen) { |
| flags |= SDL_WINDOW_FULLSCREEN; |
| } else { |
| flags |= SDL_WINDOW_RESIZABLE; |
| } |
| |
| scon->real_window = SDL_CreateWindow("", SDL_WINDOWPOS_UNDEFINED, |
| SDL_WINDOWPOS_UNDEFINED, |
| width, height, flags); |
| scon->real_renderer = SDL_CreateRenderer(scon->real_window, -1, 0); |
| sdl_update_caption(scon); |
| } |
| } |
| |
| static void sdl_switch(DisplayChangeListener *dcl, |
| DisplaySurface *new_surface) |
| { |
| struct sdl2_state *scon = container_of(dcl, struct sdl2_state, dcl); |
| int format = 0; |
| int idx = scon->idx; |
| DisplaySurface *old_surface = scon->surface; |
| |
| /* temporary hack: allows to call sdl_switch to handle scaling changes */ |
| if (new_surface) { |
| scon->surface = new_surface; |
| } |
| |
| if (!new_surface && idx > 0) { |
| scon->surface = NULL; |
| } |
| |
| if (new_surface == NULL) { |
| do_sdl_resize(scon, 0, 0, 0); |
| } else { |
| do_sdl_resize(scon, surface_width(scon->surface), |
| surface_height(scon->surface), 0); |
| } |
| |
| if (old_surface && scon->texture) { |
| SDL_DestroyTexture(scon->texture); |
| scon->texture = NULL; |
| } |
| |
| if (new_surface) { |
| if (!scon->texture) { |
| if (surface_bits_per_pixel(scon->surface) == 16) { |
| format = SDL_PIXELFORMAT_RGB565; |
| } else if (surface_bits_per_pixel(scon->surface) == 32) { |
| format = SDL_PIXELFORMAT_ARGB8888; |
| } |
| |
| scon->texture = SDL_CreateTexture(scon->real_renderer, format, |
| SDL_TEXTUREACCESS_STREAMING, |
| surface_width(new_surface), |
| surface_height(new_surface)); |
| } |
| } |
| } |
| |
| static void reset_keys(void) |
| { |
| int i; |
| |
| for (i = 0; i < 256; i++) { |
| if (modifiers_state[i]) { |
| int qcode = sdl2_scancode_to_qcode[i]; |
| qemu_input_event_send_key_qcode(NULL, qcode, false); |
| modifiers_state[i] = 0; |
| } |
| } |
| } |
| |
| static void sdl_process_key(SDL_KeyboardEvent *ev) |
| { |
| int qcode = sdl2_scancode_to_qcode[ev->keysym.scancode]; |
| |
| switch (ev->keysym.scancode) { |
| #if 0 |
| case SDL_SCANCODE_NUMLOCKCLEAR: |
| case SDL_SCANCODE_CAPSLOCK: |
| /* SDL does not send the key up event, so we generate it */ |
| qemu_input_event_send_key_qcode(NULL, qcode, true); |
| qemu_input_event_send_key_qcode(NULL, qcode, false); |
| return; |
| #endif |
| case SDL_SCANCODE_LCTRL: |
| case SDL_SCANCODE_LSHIFT: |
| case SDL_SCANCODE_LALT: |
| case SDL_SCANCODE_LGUI: |
| case SDL_SCANCODE_RCTRL: |
| case SDL_SCANCODE_RSHIFT: |
| case SDL_SCANCODE_RALT: |
| case SDL_SCANCODE_RGUI: |
| if (ev->type == SDL_KEYUP) { |
| modifiers_state[ev->keysym.scancode] = 0; |
| } else { |
| modifiers_state[ev->keysym.scancode] = 1; |
| } |
| /* fall though */ |
| default: |
| qemu_input_event_send_key_qcode(NULL, qcode, |
| ev->type == SDL_KEYDOWN); |
| } |
| } |
| |
| static void sdl_update_caption(struct sdl2_state *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) { |
| status = " - Press Ctrl-Alt-Shift to exit mouse grab"; |
| } else if (ctrl_grab) { |
| status = " - Press Right-Ctrl to exit mouse grab"; |
| } else { |
| status = " - Press Ctrl-Alt to exit mouse grab"; |
| } |
| } |
| |
| 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(void) |
| { |
| if (!cursor_hide) { |
| return; |
| } |
| |
| if (qemu_input_is_absolute()) { |
| SDL_ShowCursor(1); |
| SDL_SetCursor(sdl_cursor_hidden); |
| } else { |
| SDL_SetRelativeMouseMode(SDL_TRUE); |
| } |
| } |
| |
| static void sdl_show_cursor(void) |
| { |
| if (!cursor_hide) { |
| return; |
| } |
| |
| if (!qemu_input_is_absolute()) { |
| SDL_SetRelativeMouseMode(SDL_FALSE); |
| SDL_ShowCursor(1); |
| if (guest_cursor && |
| (gui_grab || qemu_input_is_absolute() || absolute_enabled)) { |
| SDL_SetCursor(guest_sprite); |
| } else { |
| SDL_SetCursor(sdl_cursor_normal); |
| } |
| } |
| } |
| |
| static void sdl_grab_start(struct sdl2_state *scon) |
| { |
| /* |
| * 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() && !absolute_enabled) { |
| SDL_WarpMouseInWindow(scon->real_window, guest_x, guest_y); |
| } |
| } else { |
| sdl_hide_cursor(); |
| } |
| SDL_SetWindowGrab(scon->real_window, SDL_TRUE); |
| gui_grab = 1; |
| sdl_update_caption(scon); |
| } |
| |
| static void sdl_grab_end(struct sdl2_state *scon) |
| { |
| SDL_SetWindowGrab(scon->real_window, SDL_FALSE); |
| gui_grab = 0; |
| sdl_show_cursor(); |
| sdl_update_caption(scon); |
| } |
| |
| static void absolute_mouse_grab(struct sdl2_state *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()) { |
| if (!absolute_enabled) { |
| absolute_enabled = 1; |
| 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_state *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), |
| }; |
| 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()) { |
| int scr_w, scr_h; |
| int max_w = 0, max_h = 0; |
| int off_x = 0, off_y = 0; |
| int cur_off_x = 0, cur_off_y = 0; |
| int i; |
| |
| for (i = 0; i < sdl2_num_outputs; i++) { |
| struct sdl2_state *thiscon = &sdl2_console[i]; |
| if (thiscon->real_window && thiscon->surface) { |
| SDL_GetWindowSize(thiscon->real_window, &scr_w, &scr_h); |
| cur_off_x = thiscon->x; |
| cur_off_y = thiscon->y; |
| if (scr_w + cur_off_x > max_w) { |
| max_w = scr_w + cur_off_x; |
| } |
| if (scr_h + cur_off_y > max_h) { |
| max_h = scr_h + cur_off_y; |
| } |
| if (i == scon->idx) { |
| off_x = cur_off_x; |
| off_y = cur_off_y; |
| } |
| } |
| } |
| qemu_input_queue_abs(scon->dcl.con, INPUT_AXIS_X, off_x + x, max_w); |
| qemu_input_queue_abs(scon->dcl.con, INPUT_AXIS_Y, off_y + y, max_h); |
| } 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 sdl_scale(struct sdl2_state *scon, int width, int height) |
| { |
| int bpp = 0; |
| do_sdl_resize(scon, width, height, bpp); |
| scaling_active = 1; |
| } |
| |
| static void toggle_full_screen(struct sdl2_state *scon) |
| { |
| int width = surface_width(scon->surface); |
| int height = surface_height(scon->surface); |
| int bpp = surface_bits_per_pixel(scon->surface); |
| |
| gui_fullscreen = !gui_fullscreen; |
| if (gui_fullscreen) { |
| SDL_GetWindowSize(scon->real_window, |
| &gui_saved_width, &gui_saved_height); |
| gui_saved_scaling = scaling_active; |
| |
| do_sdl_resize(scon, width, height, bpp); |
| scaling_active = 0; |
| |
| gui_saved_grab = gui_grab; |
| sdl_grab_start(scon); |
| } else { |
| if (gui_saved_scaling) { |
| sdl_scale(scon, gui_saved_width, gui_saved_height); |
| } else { |
| do_sdl_resize(scon, width, height, 0); |
| } |
| if (!gui_saved_grab) { |
| sdl_grab_end(scon); |
| } |
| } |
| graphic_hw_invalidate(scon->dcl.con); |
| graphic_hw_update(scon->dcl.con); |
| } |
| |
| static void handle_keydown(SDL_Event *ev) |
| { |
| int mod_state; |
| struct sdl2_state *scon = get_scon_from_window(ev->key.windowID); |
| |
| if (alt_grab) { |
| mod_state = (SDL_GetModState() & (gui_grab_code | KMOD_LSHIFT)) == |
| (gui_grab_code | KMOD_LSHIFT); |
| } else if (ctrl_grab) { |
| mod_state = (SDL_GetModState() & KMOD_RCTRL) == KMOD_RCTRL; |
| } else { |
| mod_state = (SDL_GetModState() & gui_grab_code) == gui_grab_code; |
| } |
| gui_key_modifier_pressed = mod_state; |
| |
| if (gui_key_modifier_pressed) { |
| switch (ev->key.keysym.scancode) { |
| case SDL_SCANCODE_F: |
| toggle_full_screen(scon); |
| gui_keysym = 1; |
| break; |
| case SDL_SCANCODE_U: |
| if (scaling_active) { |
| scaling_active = 0; |
| sdl_switch(&scon->dcl, NULL); |
| graphic_hw_invalidate(scon->dcl.con); |
| graphic_hw_update(scon->dcl.con); |
| } |
| gui_keysym = 1; |
| break; |
| 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); |
| |
| sdl_scale(scon, width, height); |
| graphic_hw_invalidate(NULL); |
| graphic_hw_update(NULL); |
| gui_keysym = 1; |
| } |
| default: |
| break; |
| } |
| } |
| if (!gui_keysym) { |
| sdl_process_key(&ev->key); |
| } |
| } |
| |
| static void handle_keyup(SDL_Event *ev) |
| { |
| int mod_state; |
| struct sdl2_state *scon = get_scon_from_window(ev->key.windowID); |
| |
| if (!alt_grab) { |
| mod_state = (ev->key.keysym.mod & gui_grab_code); |
| } else { |
| mod_state = (ev->key.keysym.mod & (gui_grab_code | KMOD_LSHIFT)); |
| } |
| if (!mod_state && gui_key_modifier_pressed) { |
| gui_key_modifier_pressed = 0; |
| if (gui_keysym == 0) { |
| /* exit/enter grab if pressing Ctrl-Alt */ |
| if (!gui_grab) { |
| sdl_grab_start(scon); |
| } else if (!gui_fullscreen) { |
| sdl_grab_end(scon); |
| } |
| /* SDL does not send back all the modifiers key, so we must |
| * correct it. */ |
| reset_keys(); |
| return; |
| } |
| gui_keysym = 0; |
| } |
| if (!gui_keysym) { |
| sdl_process_key(&ev->key); |
| } |
| } |
| |
| static void handle_mousemotion(SDL_Event *ev) |
| { |
| int max_x, max_y; |
| struct sdl2_state *scon = get_scon_from_window(ev->key.windowID); |
| |
| if (qemu_input_is_absolute() || 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 && (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() || 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_state *scon = get_scon_from_window(ev->key.windowID); |
| |
| bev = &ev->button; |
| if (!gui_grab && !qemu_input_is_absolute()) { |
| 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_state *scon = get_scon_from_window(ev->key.windowID); |
| SDL_MouseWheelEvent *wev = &ev->wheel; |
| InputButton btn; |
| |
| if (wev->y > 0) { |
| btn = INPUT_BUTTON_WHEEL_UP; |
| } else if (wev->y < 0) { |
| btn = INPUT_BUTTON_WHEEL_DOWN; |
| } 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(DisplayChangeListener *dcl, SDL_Event *ev) |
| { |
| int w, h; |
| struct sdl2_state *scon = get_scon_from_window(ev->key.windowID); |
| |
| switch (ev->window.event) { |
| case SDL_WINDOWEVENT_RESIZED: |
| sdl_scale(scon, ev->window.data1, ev->window.data2); |
| { |
| 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); |
| } |
| graphic_hw_invalidate(scon->dcl.con); |
| graphic_hw_update(scon->dcl.con); |
| break; |
| case SDL_WINDOWEVENT_EXPOSED: |
| SDL_GetWindowSize(SDL_GetWindowFromID(ev->window.windowID), &w, &h); |
| sdl_update(dcl, 0, 0, w, h); |
| break; |
| case SDL_WINDOWEVENT_FOCUS_GAINED: |
| case SDL_WINDOWEVENT_ENTER: |
| if (!gui_grab && (qemu_input_is_absolute() || absolute_enabled)) { |
| absolute_mouse_grab(scon); |
| } |
| break; |
| case SDL_WINDOWEVENT_FOCUS_LOST: |
| if (gui_grab && !gui_fullscreen) { |
| sdl_grab_end(scon); |
| } |
| break; |
| case SDL_WINDOWEVENT_RESTORED: |
| update_displaychangelistener(dcl, GUI_REFRESH_INTERVAL_DEFAULT); |
| break; |
| case SDL_WINDOWEVENT_MINIMIZED: |
| update_displaychangelistener(dcl, 500); |
| break; |
| case SDL_WINDOWEVENT_CLOSE: |
| if (!no_quit) { |
| no_shutdown = 0; |
| qemu_system_shutdown_request(); |
| } |
| break; |
| } |
| } |
| |
| static void sdl_refresh(DisplayChangeListener *dcl) |
| { |
| struct sdl2_state *scon = container_of(dcl, struct sdl2_state, dcl); |
| SDL_Event ev1, *ev = &ev1; |
| |
| if (scon->last_vm_running != runstate_is_running()) { |
| scon->last_vm_running = runstate_is_running(); |
| sdl_update_caption(scon); |
| } |
| |
| graphic_hw_update(dcl->con); |
| |
| while (SDL_PollEvent(ev)) { |
| switch (ev->type) { |
| case SDL_KEYDOWN: |
| handle_keydown(ev); |
| break; |
| case SDL_KEYUP: |
| handle_keyup(ev); |
| break; |
| case SDL_QUIT: |
| if (!no_quit) { |
| no_shutdown = 0; |
| qemu_system_shutdown_request(); |
| } |
| break; |
| case SDL_MOUSEMOTION: |
| handle_mousemotion(ev); |
| break; |
| case SDL_MOUSEBUTTONDOWN: |
| case SDL_MOUSEBUTTONUP: |
| handle_mousebutton(ev); |
| break; |
| case SDL_MOUSEWHEEL: |
| handle_mousewheel(ev); |
| break; |
| case SDL_WINDOWEVENT: |
| handle_windowevent(dcl, ev); |
| break; |
| default: |
| break; |
| } |
| } |
| } |
| |
| static void sdl_mouse_warp(DisplayChangeListener *dcl, |
| int x, int y, int on) |
| { |
| struct sdl2_state *scon = container_of(dcl, struct sdl2_state, dcl); |
| if (on) { |
| if (!guest_cursor) { |
| sdl_show_cursor(); |
| } |
| if (gui_grab || qemu_input_is_absolute() || absolute_enabled) { |
| SDL_SetCursor(guest_sprite); |
| if (!qemu_input_is_absolute() && !absolute_enabled) { |
| SDL_WarpMouseInWindow(scon->real_window, x, y); |
| } |
| } |
| } else if (gui_grab) { |
| sdl_hide_cursor(); |
| } |
| 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() || 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_ops = { |
| .dpy_name = "sdl", |
| .dpy_gfx_update = sdl_update, |
| .dpy_gfx_switch = sdl_switch, |
| .dpy_refresh = sdl_refresh, |
| .dpy_mouse_set = sdl_mouse_warp, |
| .dpy_cursor_define = sdl_mouse_define, |
| }; |
| |
| void sdl_display_init(DisplayState *ds, int full_screen, int no_frame) |
| { |
| int flags; |
| uint8_t data = 0; |
| char *filename; |
| int i; |
| |
| if (no_frame) { |
| gui_noframe = 1; |
| } |
| |
| #ifdef __linux__ |
| /* on Linux, SDL may use fbcon|directfb|svgalib when run without |
| * accessible $DISPLAY to open X11 window. This is often the case |
| * when qemu is run using sudo. But in this case, and when actually |
| * run in X11 environment, SDL fights with X11 for the video card, |
| * making current display unavailable, often until reboot. |
| * So make x11 the default SDL video driver if this variable is unset. |
| * This is a bit hackish but saves us from bigger problem. |
| * Maybe it's a good idea to fix this in SDL instead. |
| */ |
| setenv("SDL_VIDEODRIVER", "x11", 0); |
| #endif |
| |
| flags = SDL_INIT_VIDEO | SDL_INIT_NOPARACHUTE; |
| if (SDL_Init(flags)) { |
| fprintf(stderr, "Could not initialize SDL(%s) - exiting\n", |
| SDL_GetError()); |
| exit(1); |
| } |
| |
| for (i = 0;; i++) { |
| QemuConsole *con = qemu_console_lookup_by_index(i); |
| if (!con || !qemu_console_is_graphic(con)) { |
| break; |
| } |
| } |
| sdl2_num_outputs = i; |
| sdl2_console = g_new0(struct sdl2_state, sdl2_num_outputs); |
| for (i = 0; i < sdl2_num_outputs; i++) { |
| QemuConsole *con = qemu_console_lookup_by_index(i); |
| sdl2_console[i].dcl.ops = &dcl_ops; |
| sdl2_console[i].dcl.con = con; |
| register_displaychangelistener(&sdl2_console[i].dcl); |
| sdl2_console[i].idx = i; |
| } |
| |
| /* Load a 32x32x4 image. White pixels are transparent. */ |
| filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, "qemu-icon.bmp"); |
| if (filename) { |
| SDL_Surface *image = SDL_LoadBMP(filename); |
| if (image) { |
| uint32_t colorkey = SDL_MapRGB(image->format, 255, 255, 255); |
| SDL_SetColorKey(image, SDL_TRUE, colorkey); |
| SDL_SetWindowIcon(sdl2_console[0].real_window, image); |
| } |
| g_free(filename); |
| } |
| |
| if (full_screen) { |
| gui_fullscreen = 1; |
| sdl_grab_start(0); |
| } |
| |
| mouse_mode_notifier.notify = sdl_mouse_mode_change; |
| qemu_add_mouse_mode_change_notifier(&mouse_mode_notifier); |
| |
| gui_grab = 0; |
| |
| sdl_cursor_hidden = SDL_CreateCursor(&data, &data, 8, 1, 0, 0); |
| sdl_cursor_normal = SDL_GetCursor(); |
| |
| atexit(sdl_cleanup); |
| } |
| #endif |