diff options
Diffstat (limited to 'ui/sdl2.c')
-rw-r--r-- | ui/sdl2.c | 248 |
1 files changed, 178 insertions, 70 deletions
@@ -24,21 +24,27 @@ /* Ported SDL 1.2 code to 2.0 by Dave Airlie. */ #include "qemu/osdep.h" -#include "qemu-common.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_keysym; static int gui_grab_code = KMOD_LALT | KMOD_LCTRL; static SDL_Cursor *sdl_cursor_normal; static SDL_Cursor *sdl_cursor_hidden; @@ -52,6 +58,11 @@ static Notifier mouse_mode_notifier; #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) @@ -82,15 +93,31 @@ void sdl2_window_create(struct sdl2_console *scon) 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); - scon->real_renderer = SDL_CreateRenderer(scon->real_window, -1, 0); if (scon->opengl) { - scon->winctx = SDL_GL_GetCurrentContext(); + const char *driver = "opengl"; + + if (scon->opts->gl == DISPLAYGL_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); + } 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); } @@ -101,8 +128,14 @@ void sdl2_window_destroy(struct sdl2_console *scon) return; } - SDL_DestroyRenderer(scon->real_renderer); - scon->real_renderer = NULL; + 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; } @@ -139,11 +172,19 @@ static void sdl_update_caption(struct sdl2_console *scon) 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 } } @@ -161,32 +202,32 @@ static void sdl_update_caption(struct sdl2_console *scon) } } -static void sdl_hide_cursor(void) +static void sdl_hide_cursor(struct sdl2_console *scon) { - if (!cursor_hide) { + 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()) { + if (!qemu_input_is_absolute(scon->dcl.con)) { SDL_SetRelativeMouseMode(SDL_TRUE); } } -static void sdl_show_cursor(void) +static void sdl_show_cursor(struct sdl2_console *scon) { - if (!cursor_hide) { + if (scon->opts->has_show_cursor && scon->opts->show_cursor) { return; } - if (!qemu_input_is_absolute()) { + if (!qemu_input_is_absolute(scon->dcl.con)) { SDL_SetRelativeMouseMode(SDL_FALSE); } if (guest_cursor && - (gui_grab || qemu_input_is_absolute() || absolute_enabled)) { + (gui_grab || qemu_input_is_absolute(scon->dcl.con) || absolute_enabled)) { SDL_SetCursor(guest_sprite); } else { SDL_SetCursor(sdl_cursor_normal); @@ -212,14 +253,15 @@ static void sdl_grab_start(struct sdl2_console *scon) } if (guest_cursor) { SDL_SetCursor(guest_sprite); - if (!qemu_input_is_absolute() && !absolute_enabled) { + if (!qemu_input_is_absolute(scon->dcl.con) && !absolute_enabled) { SDL_WarpMouseInWindow(scon->real_window, guest_x, guest_y); } } else { - sdl_hide_cursor(); + sdl_hide_cursor(scon); } SDL_SetWindowGrab(scon->real_window, SDL_TRUE); gui_grab = 1; + win32_kbd_set_grab(true); sdl_update_caption(scon); } @@ -227,7 +269,8 @@ static void sdl_grab_end(struct sdl2_console *scon) { SDL_SetWindowGrab(scon->real_window, SDL_FALSE); gui_grab = 0; - sdl_show_cursor(); + win32_kbd_set_grab(false); + sdl_show_cursor(scon); sdl_update_caption(scon); } @@ -245,7 +288,7 @@ static void absolute_mouse_grab(struct sdl2_console *scon) static void sdl_mouse_mode_change(Notifier *notify, void *data) { - if (qemu_input_is_absolute()) { + if (qemu_input_is_absolute(sdl2_console[0].dcl.con)) { if (!absolute_enabled) { absolute_enabled = 1; SDL_SetRelativeMouseMode(SDL_FALSE); @@ -266,6 +309,8 @@ static void sdl_send_mouse_event(struct sdl2_console *scon, int dx, int dy, [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; @@ -274,7 +319,7 @@ static void sdl_send_mouse_event(struct sdl2_console *scon, int dx, int dy, prev_state = state; } - if (qemu_input_is_absolute()) { + 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, @@ -325,11 +370,29 @@ static int get_mod_state(void) } } +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(); + int gui_keysym = 0; + + if (!scon) { + return; + } if (!scon->ignore_hotkeys && gui_key_modifier_pressed && !ev->key.repeat) { switch (ev->key.keysym.scancode) { @@ -410,16 +473,13 @@ static void handle_keydown(SDL_Event *ev) static void handle_keyup(SDL_Event *ev) { struct sdl2_console *scon = get_scon_from_window(ev->key.windowID); - int gui_key_modifier_pressed = get_mod_state(); - - scon->ignore_hotkeys = false; - if (!gui_key_modifier_pressed) { - gui_keysym = 0; - } - if (!gui_keysym) { - sdl2_process_key(scon, &ev->key); + if (!scon) { + return; } + + scon->ignore_hotkeys = false; + sdl2_process_key(scon, &ev->key); } static void handle_textinput(SDL_Event *ev) @@ -427,10 +487,13 @@ 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 (qemu_console_is_graphic(con)) { + if (!con) { return; } - kbd_put_string_console(con, ev->text.text, strlen(ev->text.text)); + + if (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) @@ -442,7 +505,7 @@ static void handle_mousemotion(SDL_Event *ev) return; } - if (qemu_input_is_absolute() || absolute_enabled) { + 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; @@ -458,7 +521,7 @@ static void handle_mousemotion(SDL_Event *ev) sdl_grab_start(scon); } } - if (gui_grab || qemu_input_is_absolute() || absolute_enabled) { + 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); } @@ -475,7 +538,7 @@ static void handle_mousebutton(SDL_Event *ev) } bev = &ev->button; - if (!gui_grab && !qemu_input_is_absolute()) { + 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); @@ -504,6 +567,10 @@ static void handle_mousewheel(SDL_Event *ev) 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; } @@ -530,7 +597,7 @@ static void handle_windowevent(SDL_Event *ev) memset(&info, 0, sizeof(info)); info.width = ev->window.data1; info.height = ev->window.data2; - dpy_set_ui_info(scon->dcl.con, &info); + dpy_set_ui_info(scon->dcl.con, &info, true); } sdl2_redraw(scon); break; @@ -538,8 +605,13 @@ static void handle_windowevent(SDL_Event *ev) 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() || absolute_enabled)) { + 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 @@ -552,6 +624,9 @@ static void handle_windowevent(SDL_Event *ev) 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); } @@ -568,7 +643,7 @@ static void handle_windowevent(SDL_Event *ev) allow_close = false; } if (allow_close) { - no_shutdown = 0; + shutdown_action = SHUTDOWN_ACTION_POWEROFF; qemu_system_shutdown_request(SHUTDOWN_CAUSE_HOST_UI); } } else { @@ -615,7 +690,7 @@ void sdl2_poll_events(struct sdl2_console *scon) allow_close = false; } if (allow_close) { - no_shutdown = 0; + shutdown_action = SHUTDOWN_ACTION_POWEROFF; qemu_system_shutdown_request(SHUTDOWN_CAUSE_HOST_UI); } break; @@ -664,16 +739,16 @@ static void sdl_mouse_warp(DisplayChangeListener *dcl, if (on) { if (!guest_cursor) { - sdl_show_cursor(); + sdl_show_cursor(scon); } - if (gui_grab || qemu_input_is_absolute() || absolute_enabled) { + if (gui_grab || qemu_input_is_absolute(scon->dcl.con) || absolute_enabled) { SDL_SetCursor(guest_sprite); - if (!qemu_input_is_absolute() && !absolute_enabled) { + if (!qemu_input_is_absolute(scon->dcl.con) && !absolute_enabled) { SDL_WarpMouseInWindow(scon->real_window, x, y); } } } else if (gui_grab) { - sdl_hide_cursor(); + sdl_hide_cursor(scon); } guest_cursor = on; guest_x = x, guest_y = y; @@ -706,7 +781,7 @@ static void sdl_mouse_define(DisplayChangeListener *dcl, return; } if (guest_cursor && - (gui_grab || qemu_input_is_absolute() || absolute_enabled)) { + (gui_grab || qemu_input_is_absolute(dcl->con) || absolute_enabled)) { SDL_SetCursor(guest_sprite); } } @@ -739,14 +814,24 @@ static const DisplayChangeListenerOps dcl_gl_ops = { .dpy_mouse_set = sdl_mouse_warp, .dpy_cursor_define = sdl_mouse_define, - .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, - .dpy_gl_ctx_get_current = sdl2_gl_get_current_context, .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) @@ -762,36 +847,46 @@ static void sdl2_display_early_init(DisplayOptions *o) static void sdl2_display_init(DisplayState *ds, DisplayOptions *o) { uint8_t data = 0; - char *filename; int i; SDL_SysWMinfo info; + SDL_Surface *icon = NULL; + char *dir; assert(o->type == DISPLAY_TYPE_SDL); -#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 + 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"); 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) { @@ -815,11 +910,16 @@ static void sdl2_display_init(DisplayState *ds, DisplayOptions *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) @@ -833,21 +933,21 @@ static void sdl2_display_init(DisplayState *ds, DisplayOptions *o) #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. */ - 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); + 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); } - - gui_grab = 0; - if (gui_fullscreen) { - sdl_grab_start(0); +#endif + g_free(dir); + if (icon) { + SDL_SetWindowIcon(sdl2_console[0].real_window, icon); } mouse_mode_notifier.notify = sdl_mouse_mode_change; @@ -856,6 +956,10 @@ static void sdl2_display_init(DisplayState *ds, DisplayOptions *o) 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); } @@ -871,3 +975,7 @@ static void register_sdl1(void) } type_init(register_sdl1); + +#ifdef CONFIG_OPENGL +module_dep("ui-opengl"); +#endif |