ui/gtk: Add scale option

Allow user to set a preferred scale (defaulting to 1) of the virtual
display. Along with zoom-to-fix=false, this would be helpful for users
running QEMU on hi-dpi host desktop to achieve pixel to pixel display --
e.g., if the scale factor of a user's host desktop is set to 200%, then
they can set a 0.5 scale for the virtual display to avoid magnification
that might cause blurriness.

Signed-off-by: Weifeng Liu <weifeng.liu.z@gmail.com>
Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Tested-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Message-Id: <20250601045245.36778-3-weifeng.liu.z@gmail.com>
This commit is contained in:
Weifeng Liu
2025-06-01 12:52:33 +08:00
committed by Marc-André Lureau
parent c65680a76c
commit 0ba45b7945
3 changed files with 34 additions and 18 deletions

View File

@ -41,6 +41,7 @@ typedef struct VirtualGfxConsole {
DisplaySurface *ds;
pixman_image_t *convert;
cairo_surface_t *surface;
double preferred_scale;
double scale_x;
double scale_y;
#if defined(CONFIG_OPENGL)

View File

@ -1338,6 +1338,8 @@
# @keep-aspect-ratio: Keep width/height aspect ratio of guest content when
# resizing host window. Defaults to "on". (Since 10.1)
#
# @scale: Set preferred scale of the display. Defaults to 1.0. (Since 10.1)
#
# Since: 2.12
##
{ 'struct' : 'DisplayGTK',
@ -1345,7 +1347,8 @@
'*zoom-to-fit' : 'bool',
'*show-tabs' : 'bool',
'*show-menubar' : 'bool',
'*keep-aspect-ratio' : 'bool' } }
'*keep-aspect-ratio' : 'bool',
'*scale' : 'number' } }
##
# @DisplayEGLHeadless:

View File

@ -67,6 +67,7 @@
#define VC_TERM_X_MIN 80
#define VC_TERM_Y_MIN 25
#define VC_SCALE_MIN 0.25
#define VC_SCALE_MAX 4
#define VC_SCALE_STEP 0.25
#ifdef GDK_WINDOWING_X11
@ -272,15 +273,11 @@ static void gd_update_geometry_hints(VirtualConsole *vc)
if (!vc->gfx.ds) {
return;
}
if (s->free_scale) {
geo.min_width = surface_width(vc->gfx.ds) * VC_SCALE_MIN;
geo.min_height = surface_height(vc->gfx.ds) * VC_SCALE_MIN;
mask |= GDK_HINT_MIN_SIZE;
} else {
geo.min_width = surface_width(vc->gfx.ds) * vc->gfx.scale_x;
geo.min_height = surface_height(vc->gfx.ds) * vc->gfx.scale_y;
mask |= GDK_HINT_MIN_SIZE;
}
double scale_x = s->free_scale ? VC_SCALE_MIN : vc->gfx.scale_x;
double scale_y = s->free_scale ? VC_SCALE_MIN : vc->gfx.scale_y;
geo.min_width = surface_width(vc->gfx.ds) * scale_x;
geo.min_height = surface_height(vc->gfx.ds) * scale_y;
mask |= GDK_HINT_MIN_SIZE;
geo_widget = vc->gfx.drawing_area;
gtk_widget_set_size_request(geo_widget, geo.min_width, geo.min_height);
@ -1579,8 +1576,8 @@ static void gd_menu_full_screen(GtkMenuItem *item, void *opaque)
}
s->full_screen = FALSE;
if (vc->type == GD_VC_GFX) {
vc->gfx.scale_x = 1.0;
vc->gfx.scale_y = 1.0;
vc->gfx.scale_x = vc->gfx.preferred_scale;
vc->gfx.scale_y = vc->gfx.preferred_scale;
gd_update_windowsize(vc);
}
}
@ -1636,8 +1633,8 @@ static void gd_menu_zoom_fixed(GtkMenuItem *item, void *opaque)
GtkDisplayState *s = opaque;
VirtualConsole *vc = gd_vc_find_current(s);
vc->gfx.scale_x = 1.0;
vc->gfx.scale_y = 1.0;
vc->gfx.scale_x = vc->gfx.preferred_scale;
vc->gfx.scale_y = vc->gfx.preferred_scale;
gd_update_windowsize(vc);
}
@ -1651,8 +1648,8 @@ static void gd_menu_zoom_fit(GtkMenuItem *item, void *opaque)
s->free_scale = TRUE;
} else {
s->free_scale = FALSE;
vc->gfx.scale_x = 1.0;
vc->gfx.scale_y = 1.0;
vc->gfx.scale_x = vc->gfx.preferred_scale;
vc->gfx.scale_y = vc->gfx.preferred_scale;
}
gd_update_windowsize(vc);
@ -2243,6 +2240,11 @@ static void gl_area_realize(GtkGLArea *area, VirtualConsole *vc)
}
#endif
static bool gd_scale_valid(double scale)
{
return scale >= VC_SCALE_MIN && scale <= VC_SCALE_MAX;
}
static GSList *gd_vc_gfx_init(GtkDisplayState *s, VirtualConsole *vc,
QemuConsole *con, int idx,
GSList *group, GtkWidget *view_menu)
@ -2252,8 +2254,18 @@ static GSList *gd_vc_gfx_init(GtkDisplayState *s, VirtualConsole *vc,
vc->label = qemu_console_get_label(con);
vc->s = s;
vc->gfx.scale_x = 1.0;
vc->gfx.scale_y = 1.0;
vc->gfx.preferred_scale = 1.0;
if (s->opts->u.gtk.has_scale) {
if (gd_scale_valid(s->opts->u.gtk.scale)) {
vc->gfx.preferred_scale = s->opts->u.gtk.scale;
} else {
error_report("Invalid scale value %lf given, being ignored",
s->opts->u.gtk.scale);
s->opts->u.gtk.has_scale = false;
}
}
vc->gfx.scale_x = vc->gfx.preferred_scale;
vc->gfx.scale_y = vc->gfx.preferred_scale;
#if defined(CONFIG_OPENGL)
if (display_opengl) {