From: Dave Airlie <airl...@redhat.com> This reworks the complete SDL2 code to support multi-head, by using DisplayChangeListeners wrapped inside a structure per-head, containing the SDL2 information along with the console info.
This also adds a hack to allow Ctrl-Alt-n to toggle the first console on/off. Signed-off-by: Dave Airlie <airl...@redhat.com> --- ui/sdl2.c | 322 ++++++++++++++++++++++++++++++++++++++++---------------------- 1 file changed, 211 insertions(+), 111 deletions(-) diff --git a/ui/sdl2.c b/ui/sdl2.c index 4ad7ce3..6f3a919 100644 --- a/ui/sdl2.c +++ b/ui/sdl2.c @@ -39,11 +39,18 @@ #include "sdl2_scancode_translate.h" -static DisplayChangeListener *dcl; -static DisplaySurface *surface; -static SDL_Window *real_window; -static SDL_Renderer *real_renderer; -static SDL_Texture *guest_texture; +#define SDL2_MAX_OUTPUT 4 + +static struct sdl2_console_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[SDL2_MAX_OUTPUT]; static SDL_Surface *guest_sprite_surface; static int gui_grab; /* if true, all keyboard/mouse events are grabbed */ @@ -67,70 +74,112 @@ static SDL_Cursor *guest_sprite = NULL; static int scaling_active = 0; static Notifier mouse_mode_notifier; -static void sdl_update_caption(void); +static void sdl_update_caption(struct sdl2_console_state *scon); + +static struct sdl2_console_state *get_scon_from_window(uint32_t window_id) +{ + int i; + for (i = 0; i < SDL2_MAX_OUTPUT; 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) { - if (!surface) + struct sdl2_console_state *scon = container_of(dcl, struct sdl2_console_state, dcl); + SDL_Rect rect; + DisplaySurface *surf = qemu_console_surface(dcl->con); + + if (!surf) + return; + if (!scon->texture) return; - SDL_UpdateTexture(guest_texture, NULL, surface_data(surface), - surface_stride(surface)); - SDL_RenderCopy(real_renderer, guest_texture, NULL, NULL); - SDL_RenderPresent(real_renderer); + 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(int width, int height, int bpp) +static void do_sdl_resize(struct sdl2_console_state *scon, int width, int height, int bpp) { int flags; - if (real_window) { - SDL_SetWindowSize(real_window, width, height); - SDL_RenderSetLogicalSize(real_renderer, width, height); + 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; - real_window = SDL_CreateWindow("", SDL_WINDOWPOS_UNDEFINED, - SDL_WINDOWPOS_UNDEFINED, - width, height, flags); - real_renderer = SDL_CreateRenderer(real_window, -1, 0); - sdl_update_caption(); + 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_console_state *scon = container_of(dcl, struct sdl2_console_state, dcl); int format = 0; + int idx = qemu_get_console_index(dcl->con); + DisplaySurface *old_surface = scon->surface; + /* temporary hack: allows to call sdl_switch to handle scaling changes */ if (new_surface) { - surface = new_surface; + scon->surface = new_surface; } - if (!scaling_active) { - do_sdl_resize(surface_width(surface), surface_height(surface), 0); - } else - do_sdl_resize(surface_width(surface), surface_height(surface), 0); + if (!new_surface && idx > 0) + scon->surface = NULL; - if (guest_texture) - SDL_DestroyTexture(guest_texture); + 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 (surface_bits_per_pixel(surface) == 16) - format = SDL_PIXELFORMAT_RGB565; - else if (surface_bits_per_pixel(surface) == 32) - format = SDL_PIXELFORMAT_ARGB8888; + if (old_surface && scon->texture) { + SDL_DestroyTexture(scon->texture); + scon->texture = NULL; + } - if (!format) - exit(1); - guest_texture = SDL_CreateTexture(real_renderer, format, - SDL_TEXTUREACCESS_STREAMING, - surface_width(surface), surface_height(surface)); - SDL_RenderSetLogicalSize(real_renderer, surface_width(surface), surface_height(surface)); + 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)); + } + } } /* generic keyboard conversion */ @@ -250,7 +299,7 @@ static void sdl_process_key(SDL_KeyboardEvent *ev) kbd_put_keycode(keycode & SCANCODE_KEYCODEMASK); } -static void sdl_update_caption(void) +static void sdl_update_caption(struct sdl2_console_state *scon) { char win_title[1024]; char icon_title[1024]; @@ -268,15 +317,15 @@ static void sdl_update_caption(void) } if (qemu_name) { - snprintf(win_title, sizeof(win_title), "QEMU (%s)%s", qemu_name, status); + 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 (real_window) - SDL_SetWindowTitle(real_window, win_title); + if (scon->real_window) + SDL_SetWindowTitle(scon->real_window, win_title); } static void sdl_hide_cursor(void) @@ -300,52 +349,52 @@ static void sdl_show_cursor(void) if (!kbd_mouse_is_absolute() || !qemu_console_is_graphic(NULL)) { SDL_ShowCursor(1); if (guest_cursor && - (gui_grab || kbd_mouse_is_absolute() || absolute_enabled)) + (gui_grab || kbd_mouse_is_absolute() || absolute_enabled)) SDL_SetCursor(guest_sprite); else SDL_SetCursor(sdl_cursor_normal); } } -static void sdl_grab_start(void) +static void sdl_grab_start(struct sdl2_console_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(real_window) & SDL_WINDOW_INPUT_FOCUS)) { + if (!(SDL_GetWindowFlags(scon->real_window) & SDL_WINDOW_INPUT_FOCUS)) { return; } if (guest_cursor) { SDL_SetCursor(guest_sprite); if (!kbd_mouse_is_absolute() && !absolute_enabled) { - SDL_WarpMouseInWindow(real_window, guest_x, guest_y); + SDL_WarpMouseInWindow(scon->real_window, guest_x, guest_y); } } else sdl_hide_cursor(); - SDL_SetWindowGrab(real_window, SDL_TRUE); + SDL_SetWindowGrab(scon->real_window, SDL_TRUE); gui_grab = 1; - sdl_update_caption(); + sdl_update_caption(scon); } -static void sdl_grab_end(void) +static void sdl_grab_end(struct sdl2_console_state *scon) { - SDL_SetWindowGrab(real_window, SDL_FALSE); + SDL_SetWindowGrab(scon->real_window, SDL_FALSE); gui_grab = 0; sdl_show_cursor(); - sdl_update_caption(); + sdl_update_caption(scon); } -static void absolute_mouse_grab(void) +static void absolute_mouse_grab(struct sdl2_console_state *scon) { int mouse_x, mouse_y; int scr_w, scr_h; SDL_GetMouseState(&mouse_x, &mouse_y); - SDL_GetWindowSize(real_window, &scr_w, &scr_h); + 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(); + sdl_grab_start(scon); } } @@ -355,18 +404,18 @@ static void sdl_mouse_mode_change(Notifier *notify, void *data) if (!absolute_enabled) { absolute_enabled = 1; if (qemu_console_is_graphic(NULL)) { - absolute_mouse_grab(); + absolute_mouse_grab(&sdl2_console[0]); } } } else if (absolute_enabled) { if (!gui_fullscreen) { - sdl_grab_end(); + sdl_grab_end(&sdl2_console[0]); } absolute_enabled = 0; } } -static void sdl_send_mouse_event(int dx, int dy, int dz, int x, int y, int state) +static void sdl_send_mouse_event(struct sdl2_console_state *scon, int dx, int dy, int dz, int x, int y, int state) { int buttons = 0; @@ -382,9 +431,30 @@ static void sdl_send_mouse_event(int dx, int dy, int dz, int x, int y, int state if (kbd_mouse_is_absolute()) { int scr_w, scr_h; - SDL_GetWindowSize(real_window, &scr_w, &scr_h); - dx = x * 0x7FFF / (scr_w - 1); - dy = y * 0x7FFF / (scr_h - 1); + 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_MAX_OUTPUT; i++) { + struct sdl2_console_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; + } + } + } + + dx = (off_x + x) * 0x7FFF / (max_w - 1); + dy = (off_y + y) * 0x7FFF / (max_h - 1); } else if (guest_cursor) { x -= guest_x; y -= guest_y; @@ -397,51 +467,52 @@ static void sdl_send_mouse_event(int dx, int dy, int dz, int x, int y, int state kbd_mouse_event(dx, dy, dz, buttons); } -static void sdl_scale(int width, int height) +static void sdl_scale(struct sdl2_console_state *scon, int width, int height) { int bpp = 0; - do_sdl_resize(width, height, bpp); + do_sdl_resize(scon, width, height, bpp); scaling_active = 1; } -static void toggle_full_screen(void) +static void toggle_full_screen(struct sdl2_console_state *scon) { - int width = surface_width(surface); - int height = surface_height(surface); - int bpp = surface_bits_per_pixel(surface); + 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(real_window, &gui_saved_width, &gui_saved_height); + SDL_GetWindowSize(scon->real_window, &gui_saved_width, &gui_saved_height); gui_saved_scaling = scaling_active; - do_sdl_resize(width, height, bpp); + do_sdl_resize(scon, width, height, bpp); scaling_active = 0; gui_saved_grab = gui_grab; - sdl_grab_start(); + sdl_grab_start(scon); } else { if (gui_saved_scaling) { - sdl_scale(gui_saved_width, gui_saved_height); + sdl_scale(scon, gui_saved_width, gui_saved_height); } else { - do_sdl_resize(width, height, 0); + do_sdl_resize(scon, width, height, 0); } if (!gui_saved_grab || !qemu_console_is_graphic(NULL)) { - sdl_grab_end(); + sdl_grab_end(scon); } } - graphic_hw_invalidate(NULL); - graphic_hw_update(NULL); + graphic_hw_invalidate(scon->dcl.con); + graphic_hw_update(scon->dcl.con); } static void handle_keydown(SDL_Event *ev) { int mod_state; int keycode; + struct sdl2_console_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); + (gui_grab_code | KMOD_LSHIFT); } else if (ctrl_grab) { mod_state = (SDL_GetModState() & KMOD_RCTRL) == KMOD_RCTRL; } else { @@ -452,16 +523,23 @@ static void handle_keydown(SDL_Event *ev) if (gui_key_modifier_pressed) { keycode = sdl_keyevent_to_keycode(&ev->key); switch (keycode) { + case 0x31: + /* spawn a new connected monitor if we have one */ + if (sdl2_console[1].surface) + graphic_hw_notify_state(sdl2_console[1].dcl.con, 0, 0, 0, 0); + else + graphic_hw_notify_state(sdl2_console[1].dcl.con, 0, 0, 1024, 768); + break; case 0x21: /* 'f' key on US keyboard */ - toggle_full_screen(); + toggle_full_screen(scon); gui_keysym = 1; break; case 0x16: /* 'u' key on US keyboard */ if (scaling_active) { scaling_active = 0; - sdl_switch(dcl, NULL); - graphic_hw_invalidate(NULL); - graphic_hw_update(NULL); + sdl_switch(&scon->dcl, NULL); + graphic_hw_invalidate(scon->dcl.con); + graphic_hw_update(scon->dcl.con); } gui_keysym = 1; break; @@ -476,13 +554,13 @@ static void handle_keydown(SDL_Event *ev) if (!qemu_console_is_graphic(NULL)) { /* release grab if going to a text console */ if (gui_grab) { - sdl_grab_end(); + sdl_grab_end(scon); } else if (absolute_enabled) { sdl_show_cursor(); } } else if (absolute_enabled) { sdl_hide_cursor(); - absolute_mouse_grab(); + absolute_mouse_grab(scon); } break; case 0x1b: /* '+' */ @@ -490,14 +568,14 @@ static void handle_keydown(SDL_Event *ev) if (!gui_fullscreen) { int scr_w, scr_h; int width, height; - SDL_GetWindowSize(real_window, &scr_w, &scr_h); + SDL_GetWindowSize(scon->real_window, &scr_w, &scr_h); width = MAX(scr_w + (keycode == 0x1b ? 50 : -50), - 160); - height = (surface_height(surface) * width) / - surface_width(surface); + 160); + height = (surface_height(scon->surface) * width) / + surface_width(scon->surface); - sdl_scale(width, height); + sdl_scale(scon, width, height); graphic_hw_invalidate(NULL); graphic_hw_update(NULL); gui_keysym = 1; @@ -585,6 +663,7 @@ static void handle_keydown(SDL_Event *ev) static void handle_keyup(SDL_Event *ev) { int mod_state; + struct sdl2_console_state *scon = get_scon_from_window(ev->key.windowID); if (!alt_grab) { mod_state = (ev->key.keysym.mod & gui_grab_code); @@ -597,10 +676,10 @@ static void handle_keyup(SDL_Event *ev) /* exit/enter grab if pressing Ctrl-Alt */ if (!gui_grab) { if (qemu_console_is_graphic(NULL)) { - sdl_grab_start(); + sdl_grab_start(scon); } } else if (!gui_fullscreen) { - sdl_grab_end(); + sdl_grab_end(scon); } /* SDL does not send back all the modifiers key, so we must * correct it. */ @@ -617,25 +696,26 @@ static void handle_keyup(SDL_Event *ev) static void handle_mousemotion(SDL_Event *ev) { int max_x, max_y; + struct sdl2_console_state *scon = get_scon_from_window(ev->key.windowID); if (qemu_console_is_graphic(NULL) && (kbd_mouse_is_absolute() || absolute_enabled)) { int scr_w, scr_h; - SDL_GetWindowSize(real_window, &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(); + 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(); + ev->motion.y > 0 && ev->motion.y < max_y)) { + sdl_grab_start(scon); } } if (gui_grab || kbd_mouse_is_absolute() || absolute_enabled) { - sdl_send_mouse_event(ev->motion.xrel, ev->motion.yrel, 0, + sdl_send_mouse_event(scon, ev->motion.xrel, ev->motion.yrel, 0, ev->motion.x, ev->motion.y, ev->motion.state); } } @@ -644,6 +724,7 @@ static void handle_mousebutton(SDL_Event *ev) { int buttonstate = SDL_GetMouseState(NULL, NULL); SDL_MouseButtonEvent *bev; + struct sdl2_console_state *scon = get_scon_from_window(ev->key.windowID); int dz; if (!qemu_console_is_graphic(NULL)) { @@ -654,7 +735,7 @@ static void handle_mousebutton(SDL_Event *ev) if (!gui_grab && !kbd_mouse_is_absolute()) { if (ev->type == SDL_MOUSEBUTTONUP && bev->button == SDL_BUTTON_LEFT) { /* start grabbing all events */ - sdl_grab_start(); + sdl_grab_start(scon); } } else { dz = 0; @@ -672,19 +753,20 @@ static void handle_mousebutton(SDL_Event *ev) dz = 1; } #endif - sdl_send_mouse_event(0, 0, dz, bev->x, bev->y, buttonstate); + sdl_send_mouse_event(scon, 0, 0, dz, bev->x, bev->y, buttonstate); } } static void handle_windowevent(DisplayChangeListener *dcl, SDL_Event *ev) { int w, h; + struct sdl2_console_state *scon = get_scon_from_window(ev->key.windowID); switch (ev->window.event) { case SDL_WINDOWEVENT_RESIZED: - sdl_scale(ev->window.data1, ev->window.data2); - graphic_hw_invalidate(NULL); - graphic_hw_update(NULL); + sdl_scale(scon, ev->window.data1, ev->window.data2); + 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); @@ -694,12 +776,12 @@ static void handle_windowevent(DisplayChangeListener *dcl, SDL_Event *ev) case SDL_WINDOWEVENT_ENTER: if (!gui_grab && qemu_console_is_graphic(NULL) && (kbd_mouse_is_absolute() || absolute_enabled)) { - absolute_mouse_grab(); + absolute_mouse_grab(scon); } break; case SDL_WINDOWEVENT_FOCUS_LOST: if (gui_grab && !gui_fullscreen) - sdl_grab_end(); + sdl_grab_end(scon); break; case SDL_WINDOWEVENT_RESTORED: update_displaychangelistener(dcl, GUI_REFRESH_INTERVAL_DEFAULT); @@ -718,14 +800,15 @@ static void handle_windowevent(DisplayChangeListener *dcl, SDL_Event *ev) static void sdl_refresh(DisplayChangeListener *dcl) { + struct sdl2_console_state *scon = container_of(dcl, struct sdl2_console_state, dcl); SDL_Event ev1, *ev = &ev1; - if (last_vm_running != runstate_is_running()) { - last_vm_running = runstate_is_running(); - sdl_update_caption(); + if (scon->last_vm_running != runstate_is_running()) { + scon->last_vm_running = runstate_is_running(); + sdl_update_caption(scon); } - graphic_hw_update(NULL); + graphic_hw_update(dcl->con); while (SDL_PollEvent(ev)) { switch (ev->type) { @@ -760,13 +843,14 @@ static void sdl_refresh(DisplayChangeListener *dcl) static void sdl_mouse_warp(DisplayChangeListener *dcl, int x, int y, int on) { + struct sdl2_console_state *scon = container_of(dcl, struct sdl2_console_state, dcl); if (on) { if (!guest_cursor) sdl_show_cursor(); if (gui_grab || kbd_mouse_is_absolute() || absolute_enabled) { SDL_SetCursor(guest_sprite); if (!kbd_mouse_is_absolute() && !absolute_enabled) { - SDL_WarpMouseInWindow(real_window, x, y); + SDL_WarpMouseInWindow(scon->real_window, x, y); } } } else if (gui_grab) @@ -804,6 +888,15 @@ static void sdl_mouse_define(DisplayChangeListener *dcl, SDL_SetCursor(guest_sprite); } +static void sdl_notify_state(DisplayChangeListener *dcl, + int x, int y, uint32_t width, uint32_t height) +{ + struct sdl2_console_state *scon = container_of(dcl, struct sdl2_console_state, dcl); + + scon->x = x; + scon->y = y; +} + static void sdl_cleanup(void) { if (guest_sprite) @@ -818,6 +911,7 @@ static const DisplayChangeListenerOps dcl_ops = { .dpy_refresh = sdl_refresh, .dpy_mouse_set = sdl_mouse_warp, .dpy_cursor_define = sdl_mouse_define, + .dpy_notify_state = sdl_notify_state, }; void sdl_display_init(DisplayState *ds, int full_screen, int no_frame) @@ -825,7 +919,8 @@ void sdl_display_init(DisplayState *ds, int full_screen, int no_frame) int flags; uint8_t data = 0; char *filename; - + int nconsoles; + int i; #if defined(__APPLE__) /* always use generic keymaps */ if (!keyboard_layout) @@ -866,19 +961,24 @@ void sdl_display_init(DisplayState *ds, int full_screen, int no_frame) if (image) { uint32_t colorkey = SDL_MapRGB(image->format, 255, 255, 255); SDL_SetColorKey(image, SDL_TRUE, colorkey); - SDL_SetWindowIcon(real_window, image); + SDL_SetWindowIcon(sdl2_console[0].real_window, image); } g_free(filename); } if (full_screen) { gui_fullscreen = 1; - sdl_grab_start(); + sdl_grab_start(0); } - dcl = g_malloc0(sizeof(DisplayChangeListener)); - dcl->ops = &dcl_ops; - register_displaychangelistener(dcl); + nconsoles = qemu_get_number_graphical_consoles(); + + for (i = 0; i < nconsoles; i++) { + sdl2_console[i].dcl.ops = &dcl_ops; + sdl2_console[i].dcl.con = qemu_console_lookup_by_index(i); + register_displaychangelistener(&sdl2_console[i].dcl); + sdl2_console[i].idx = i; + } mouse_mode_notifier.notify = sdl_mouse_mode_change; qemu_add_mouse_mode_change_notifier(&mouse_mode_notifier); -- 1.8.3.1