#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;
#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)
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);
}
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;
}
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
}
}
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);
}
}
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);
}
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_SetWindowGrab(scon->real_window, SDL_TRUE);
gui_grab = 1;
+ win32_kbd_set_grab(true);
sdl_update_caption(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 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);
[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;
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,
}
}
+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;
return;
}
- if (qemu_console_is_graphic(con)) {
- return;
+ if (QEMU_IS_TEXT_CONSOLE(con)) {
+ qemu_text_console_put_string(QEMU_TEXT_CONSOLE(con), ev->text.text, strlen(ev->text.text));
}
- kbd_put_string_console(con, ev->text.text, strlen(ev->text.text));
}
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;
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);
}
}
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);
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;
}
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;
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
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);
}
allow_close = false;
}
if (allow_close) {
- no_shutdown = 0;
+ shutdown_action = SHUTDOWN_ACTION_POWEROFF;
qemu_system_shutdown_request(SHUTDOWN_CAUSE_HOST_UI);
}
} else {
allow_close = false;
}
if (allow_close) {
- no_shutdown = 0;
+ shutdown_action = SHUTDOWN_ACTION_POWEROFF;
qemu_system_shutdown_request(SHUTDOWN_CAUSE_HOST_UI);
}
break;
if (!guest_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);
}
}
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);
}
}
.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)
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.
- */
- g_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",
#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) {
#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)
}
#ifdef CONFIG_SDL_IMAGE
- icon = IMG_Load(CONFIG_QEMU_ICONDIR "/hicolor/128x128/apps/qemu.png");
+ 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. */
- icon = SDL_LoadBMP(CONFIG_QEMU_ICONDIR "/hicolor/32x32/apps/qemu.bmp");
+ 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);
}
- gui_grab = 0;
- if (gui_fullscreen) {
- sdl_grab_start(0);
- }
-
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);
}
}
type_init(register_sdl1);
+
+#ifdef CONFIG_OPENGL
+module_dep("ui-opengl");
+#endif