X-Git-Url: https://repo.jachan.dev/qemu.git/blobdiff_plain/7d957bd8cbcbf56f7916d375e65042d767f544b5..7ef6e71c599aebd066ab1d29be8944bd5c0eb0c2:/sdl.c diff --git a/sdl.c b/sdl.c index 73396e86ad..36fb07f22a 100644 --- a/sdl.c +++ b/sdl.c @@ -21,16 +21,19 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#include "qemu-common.h" -#include "console.h" -#include "sysemu.h" - #include +#include #ifndef _WIN32 #include #endif +#include "qemu-common.h" +#include "console.h" +#include "sysemu.h" +#include "x_keymap.h" +#include "sdl_zoom.h" + static DisplayChangeListener *dcl; static SDL_Surface *real_screen; static SDL_Surface *guest_screen = NULL; @@ -50,19 +53,31 @@ static SDL_Cursor *sdl_cursor_hidden; static int absolute_enabled = 0; static int guest_cursor = 0; static int guest_x, guest_y; -static SDL_Cursor *guest_sprite = 0; +static SDL_Cursor *guest_sprite = NULL; +static uint8_t allocator; +static SDL_PixelFormat host_format; +static int scaling_active = 0; static void sdl_update(DisplayState *ds, int x, int y, int w, int h) { + // printf("updating x=%d y=%d w=%d h=%d\n", x, y, w, h); SDL_Rect rec; rec.x = x; rec.y = y; rec.w = w; rec.h = h; - // printf("updating x=%d y=%d w=%d h=%d\n", x, y, w, h); - SDL_BlitSurface(guest_screen, &rec, real_screen, &rec); - SDL_Flip(real_screen); + if (guest_screen) { + if (!scaling_active) { + SDL_BlitSurface(guest_screen, &rec, real_screen, &rec); + } else { + if (sdl_zoom_blit(guest_screen, real_screen, SMOOTHING_ON, &rec) < 0) { + fprintf(stderr, "Zoom blit failed\n"); + exit(1); + } + } + } + SDL_UpdateRect(real_screen, rec.x, rec.y, rec.w, rec.h); } static void sdl_setdata(DisplayState *ds) @@ -81,41 +96,144 @@ static void sdl_setdata(DisplayState *ds) ds->surface->pf.bmask, ds->surface->pf.amask); } -static void sdl_resize(DisplayState *ds) +static void do_sdl_resize(int new_width, int new_height, int bpp) { int flags; // printf("resizing to %d %d\n", w, h); - flags = SDL_HWSURFACE|SDL_ASYNCBLIT|SDL_HWACCEL; + flags = SDL_HWSURFACE|SDL_ASYNCBLIT|SDL_HWACCEL|SDL_RESIZABLE; if (gui_fullscreen) flags |= SDL_FULLSCREEN; if (gui_noframe) flags |= SDL_NOFRAME; - again: - real_screen = SDL_SetVideoMode(ds_get_width(ds), ds_get_height(ds), 0, flags); + width = new_width; + height = new_height; + real_screen = SDL_SetVideoMode(width, height, bpp, flags); if (!real_screen) { fprintf(stderr, "Could not open SDL display\n"); exit(1); } - if (!real_screen->pixels && (flags & SDL_HWSURFACE) && (flags & SDL_FULLSCREEN)) { - flags &= ~SDL_HWSURFACE; - goto again; +} + +static void sdl_resize(DisplayState *ds) +{ + if (!allocator) { + if (!scaling_active) + do_sdl_resize(ds_get_width(ds), ds_get_height(ds), 0); + else if (real_screen->format->BitsPerPixel != ds_get_bits_per_pixel(ds)) + do_sdl_resize(real_screen->w, real_screen->h, ds_get_bits_per_pixel(ds)); + sdl_setdata(ds); + } else { + if (guest_screen != NULL) { + SDL_FreeSurface(guest_screen); + guest_screen = NULL; + } } +} - if (!real_screen->pixels) { - fprintf(stderr, "Could not open SDL display\n"); +static PixelFormat sdl_to_qemu_pixelformat(SDL_PixelFormat *sdl_pf) +{ + PixelFormat qemu_pf; + + memset(&qemu_pf, 0x00, sizeof(PixelFormat)); + + qemu_pf.bits_per_pixel = sdl_pf->BitsPerPixel; + qemu_pf.bytes_per_pixel = sdl_pf->BytesPerPixel; + qemu_pf.depth = (qemu_pf.bits_per_pixel) == 32 ? 24 : (qemu_pf.bits_per_pixel); + + qemu_pf.rmask = sdl_pf->Rmask; + qemu_pf.gmask = sdl_pf->Gmask; + qemu_pf.bmask = sdl_pf->Bmask; + qemu_pf.amask = sdl_pf->Amask; + + qemu_pf.rshift = sdl_pf->Rshift; + qemu_pf.gshift = sdl_pf->Gshift; + qemu_pf.bshift = sdl_pf->Bshift; + qemu_pf.ashift = sdl_pf->Ashift; + + qemu_pf.rbits = 8 - sdl_pf->Rloss; + qemu_pf.gbits = 8 - sdl_pf->Gloss; + qemu_pf.bbits = 8 - sdl_pf->Bloss; + qemu_pf.abits = 8 - sdl_pf->Aloss; + + qemu_pf.rmax = ((1 << qemu_pf.rbits) - 1); + qemu_pf.gmax = ((1 << qemu_pf.gbits) - 1); + qemu_pf.bmax = ((1 << qemu_pf.bbits) - 1); + qemu_pf.amax = ((1 << qemu_pf.abits) - 1); + + return qemu_pf; +} + +static DisplaySurface* sdl_create_displaysurface(int width, int height) +{ + DisplaySurface *surface = (DisplaySurface*) qemu_mallocz(sizeof(DisplaySurface)); + if (surface == NULL) { + fprintf(stderr, "sdl_create_displaysurface: malloc failed\n"); exit(1); } - sdl_setdata(ds); + surface->width = width; + surface->height = height; + + if (scaling_active) { + if (host_format.BytesPerPixel != 2 && host_format.BytesPerPixel != 4) { + surface->linesize = width * 4; + surface->pf = qemu_default_pixelformat(32); + } else { + surface->linesize = width * host_format.BytesPerPixel; + surface->pf = sdl_to_qemu_pixelformat(&host_format); + } +#ifdef HOST_WORDS_BIGENDIAN + surface->flags = QEMU_ALLOCATED_FLAG | QEMU_BIG_ENDIAN_FLAG; +#else + surface->flags = QEMU_ALLOCATED_FLAG; +#endif + surface->data = (uint8_t*) qemu_mallocz(surface->linesize * surface->height); + + return surface; + } + + if (host_format.BitsPerPixel == 16) + do_sdl_resize(width, height, 16); + else + do_sdl_resize(width, height, 32); + + surface->pf = sdl_to_qemu_pixelformat(real_screen->format); + surface->linesize = real_screen->pitch; + surface->data = real_screen->pixels; + +#ifdef HOST_WORDS_BIGENDIAN + surface->flags = QEMU_REALPIXELS_FLAG | QEMU_BIG_ENDIAN_FLAG; +#else + surface->flags = QEMU_REALPIXELS_FLAG; +#endif + allocator = 1; + + return surface; +} + +static void sdl_free_displaysurface(DisplaySurface *surface) +{ + allocator = 0; + if (surface == NULL) + return; + + if (surface->flags & QEMU_ALLOCATED_FLAG) + qemu_free(surface->data); + qemu_free(surface); +} + +static DisplaySurface* sdl_resize_displaysurface(DisplaySurface *surface, int width, int height) +{ + sdl_free_displaysurface(surface); + return sdl_create_displaysurface(width, height); } /* generic keyboard conversion */ #include "sdl_keysym.h" -#include "keymaps.c" static kbd_layout_t *kbd_layout = NULL; @@ -144,9 +262,57 @@ static uint8_t sdl_keyevent_to_keycode(const SDL_KeyboardEvent *ev) #else +#if defined(SDL_VIDEO_DRIVER_X11) +#include + +static int check_for_evdev(void) +{ + SDL_SysWMinfo info; + XkbDescPtr desc = NULL; + int has_evdev = 0; + char *keycodes = NULL; + + SDL_VERSION(&info.version); + if (!SDL_GetWMInfo(&info)) { + return 0; + } + desc = XkbGetKeyboard(info.info.x11.display, + XkbGBN_AllComponentsMask, + XkbUseCoreKbd); + if (desc && desc->names) { + keycodes = XGetAtomName(info.info.x11.display, desc->names->keycodes); + if (keycodes == NULL) { + fprintf(stderr, "could not lookup keycode name\n"); + } else if (strstart(keycodes, "evdev", NULL)) { + has_evdev = 1; + } else if (!strstart(keycodes, "xfree86", NULL)) { + fprintf(stderr, "unknown keycodes `%s', please report to " + "qemu-devel@nongnu.org\n", keycodes); + } + } + + if (desc) { + XkbFreeKeyboard(desc, XkbGBN_AllComponentsMask, True); + } + if (keycodes) { + XFree(keycodes); + } + return has_evdev; +} +#else +static int check_for_evdev(void) +{ + return 0; +} +#endif + static uint8_t sdl_keyevent_to_keycode(const SDL_KeyboardEvent *ev) { int keycode; + static int has_evdev = -1; + + if (has_evdev == -1) + has_evdev = check_for_evdev(); keycode = ev->keysym.scancode; @@ -154,9 +320,16 @@ static uint8_t sdl_keyevent_to_keycode(const SDL_KeyboardEvent *ev) keycode = 0; } else if (keycode < 97) { keycode -= 8; /* just an offset */ - } else if (keycode < 212) { + } else if (keycode < 158) { /* use conversion table */ - keycode = _translate_keycode(keycode - 97); + if (has_evdev) + keycode = translate_evdev_keycode(keycode - 97); + else + keycode = translate_xfree86_keycode(keycode - 97); + } else if (keycode == 208) { /* Hiragana_Katakana */ + keycode = 0x70; + } else if (keycode == 211) { /* backslash */ + keycode = 0x73; } else { keycode = 0; } @@ -286,7 +459,8 @@ static void sdl_grab_start(void) { if (guest_cursor) { SDL_SetCursor(guest_sprite); - SDL_WarpMouse(guest_x, guest_y); + if (!kbd_mouse_is_absolute() && !absolute_enabled) + SDL_WarpMouse(guest_x, guest_y); } else sdl_hide_cursor(); @@ -345,8 +519,8 @@ static void sdl_send_mouse_event(int dx, int dy, int dz, int x, int y, int state static void toggle_full_screen(DisplayState *ds) { gui_fullscreen = !gui_fullscreen; - sdl_resize(ds); if (gui_fullscreen) { + scaling_active = 0; gui_saved_grab = gui_grab; sdl_grab_start(); } else { @@ -538,6 +712,22 @@ static void sdl_refresh(DisplayState *ds) } } break; + case SDL_VIDEORESIZE: + { + SDL_ResizeEvent *rev = &ev->resize; + int bpp = real_screen->format->BitsPerPixel; + if (bpp != 16 && bpp != 32) + bpp = 32; + do_sdl_resize(rev->w, rev->h, bpp); + scaling_active = 1; + if (!is_buffer_shared(ds->surface)) { + ds->surface = qemu_resize_displaysurface(ds, ds_get_width(ds), ds_get_height(ds)); + dpy_resize(ds); + } + vga_hw_invalidate(); + vga_hw_update(); + break; + } default: break; } @@ -557,7 +747,8 @@ static void sdl_mouse_warp(int x, int y, int on) sdl_show_cursor(); if (gui_grab || kbd_mouse_is_absolute() || absolute_enabled) { SDL_SetCursor(guest_sprite); - SDL_WarpMouse(x, y); + if (!kbd_mouse_is_absolute() && !absolute_enabled) + SDL_WarpMouse(x, y); } } else if (gui_grab) sdl_hide_cursor(); @@ -615,13 +806,15 @@ static void sdl_cleanup(void) { if (guest_sprite) SDL_FreeCursor(guest_sprite); - SDL_Quit(); + SDL_QuitSubSystem(SDL_INIT_VIDEO); } void sdl_display_init(DisplayState *ds, int full_screen, int no_frame) { int flags; uint8_t data = 0; + DisplayAllocator *da; + const SDL_VideoInfo *vi; #if defined(__APPLE__) /* always use generic keymaps */ @@ -629,7 +822,7 @@ void sdl_display_init(DisplayState *ds, int full_screen, int no_frame) keyboard_layout = "en-us"; #endif if(keyboard_layout) { - kbd_layout = init_keyboard_layout(keyboard_layout); + kbd_layout = init_keyboard_layout(name2keysym, keyboard_layout); if (!kbd_layout) exit(1); } @@ -642,10 +835,10 @@ void sdl_display_init(DisplayState *ds, int full_screen, int no_frame) fprintf(stderr, "Could not initialize SDL - exiting\n"); exit(1); } + vi = SDL_GetVideoInfo(); + host_format = *(vi->vfmt); dcl = qemu_mallocz(sizeof(DisplayChangeListener)); - if (!dcl) - exit(1); dcl->dpy_update = sdl_update; dcl->dpy_resize = sdl_resize; dcl->dpy_refresh = sdl_refresh; @@ -655,6 +848,18 @@ void sdl_display_init(DisplayState *ds, int full_screen, int no_frame) ds->cursor_define = sdl_mouse_define; register_displaychangelistener(ds, dcl); + da = qemu_mallocz(sizeof(DisplayAllocator)); + da->create_displaysurface = sdl_create_displaysurface; + da->resize_displaysurface = sdl_resize_displaysurface; + da->free_displaysurface = sdl_free_displaysurface; + if (register_displayallocator(ds, da) == da) { + DisplaySurface *surf; + surf = sdl_create_displaysurface(ds_get_width(ds), ds_get_height(ds)); + defaultallocator_free_displaysurface(ds->surface); + ds->surface = surf; + dpy_resize(ds); + } + sdl_update_caption(); SDL_EnableKeyRepeat(250, 50); gui_grab = 0;