From: Foxe Chen Date: Fri, 24 Jul 2026 21:56:37 +0000 (+0000) Subject: patch 9.2.0853: popup: popup images do not support scaling X-Git-Tag: v9.2.0853^0 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=2e2747ad5fa3b151884112be51264c20fa19abd7;p=thirdparty%2Fvim.git patch 9.2.0853: popup: popup images do not support scaling Problem: popup: popup images do not support scaling Solution: Add popup scaling support for GTK4 UI (Foxe Chen). closes: #20611 Signed-off-by: Foxe Chen Signed-off-by: Christian Brabandt --- diff --git a/src/cairo.c b/src/cairo.c index 7543a33d9b..7f1df2b2d0 100644 --- a/src/cairo.c +++ b/src/cairo.c @@ -122,6 +122,7 @@ cairo_popup_image_ensure(win_T *wp) cairo_surface_destroy(surf); return false; } + cairo_surface_set_device_scale(surf, gui.scale, gui.scale); cairo_popup_image_fill_surface(wp, surf); wp->w_popup_image_surface = surf; return true; @@ -163,7 +164,8 @@ cairo_popup_image_free(win_T *wp) * non-zero offsets to crop the portion that falls outside the host window. * Builds the cache on first call. Caller is responsible for scheduling a * redraw of the target's owning widget if needed (e.g. - * gtk_widget_queue_draw_area on GTK). + * gtk_widget_queue_draw_area on GTK). "x" and "y" are assumed to be in logical + * pixels, the rest are in physical pixels. */ void cairo_popup_image_paint( @@ -171,10 +173,10 @@ cairo_popup_image_paint( void *target, int x, int y, - int src_x, - int src_y, - int draw_w, - int draw_h) + double src_x, + double src_y, + double draw_w, + double draw_h) { cairo_t *cr; @@ -184,6 +186,10 @@ cairo_popup_image_paint( return; cairo_surface_t *surface = (cairo_surface_t *)wp->w_popup_image_surface; + src_x = PHY2LOG(src_x); + src_y = PHY2LOG(src_y); + draw_w = PHY2LOG(draw_w); + draw_h = PHY2LOG(draw_h); # if !GTK_CHECK_VERSION(3,0,0) cr = gdk_cairo_create((GdkDrawable *)target); diff --git a/src/gui.c b/src/gui.c index f3ba3705a9..0c7b39be0b 100644 --- a/src/gui.c +++ b/src/gui.c @@ -680,6 +680,9 @@ gui_init(void) * Create the GUI shell. */ gui.in_use = true; // Must be set after menus have been set up +#if defined(FEAT_GUI_GTK) && defined(FEAT_IMAGE) + gui.scale = 1.0; // Default value +#endif if (gui_mch_init() == FAIL) goto error; diff --git a/src/gui.h b/src/gui.h index 1fadca0f01..16f13e4772 100644 --- a/src/gui.h +++ b/src/gui.h @@ -103,6 +103,15 @@ typedef GdkEvent GdkEventKey; // GTK4: GdkEventKey merged into GdkEvent # define FILL_Y(row) ((row) * gui.char_height + gui.border_offset) # define Y_2_ROW(y) (((y) - gui.border_offset) / gui.char_height) #endif +#if defined(FEAT_GUI_GTK) && defined(FEAT_IMAGE) +// Logical pixels to physical pixels +# define LOG2PHY(l) (gui.in_use ? (double)(l) * gui.scale : (l)) + // Physical pixels to logical pixels +# define PHY2LOG(p) (gui.in_use ? (double)(p) / gui.scale : (p)) +#else +# define LOG2PHY(l) (l) +# define PHY2LOG(p) (p) +#endif // Indices for arrays of scrollbars #define SBAR_NONE (-1) @@ -423,6 +432,9 @@ typedef struct Gui # ifdef GDK_WINDOWING_WAYLAND bool is_wayland; // active gdk backend in gtk is wayland # endif +# ifdef FEAT_IMAGE + double scale; // Current scaling (may be fractional) +# endif #endif // FEAT_GUI_GTK #if defined(FEAT_GUI_GTK) || defined(FEAT_GUI_MSWIN) diff --git a/src/gui_gtk4.c b/src/gui_gtk4.c index b29b180d63..486ed38a0e 100644 --- a/src/gui_gtk4.c +++ b/src/gui_gtk4.c @@ -300,9 +300,9 @@ static void mainwin_fullscreened_cb(GObject *obj, GParamSpec *pspec, gpointer us static void drawarea_realize_cb(GtkWidget *widget, gpointer data); static void drawarea_unrealize_cb(GtkWidget *widget, gpointer data); #ifndef USE_GTK4_SNAPSHOT -static void drawarea_scale_factor_cb(GObject *object, GParamSpec *pspec, gpointer data); static cairo_surface_t *create_backing_surface(int width, int height); #endif +static void scale_factor_cb(GdkSurface *surface, GParamSpec *pspec, void *udata); static void clipboard_changed_cb(GdkClipboard *clipboard, gpointer user_data); #ifdef FEAT_MENU static void show_menubar_popover(void); @@ -569,9 +569,6 @@ gui_mch_init(void) // Set up drawing. gtk_drawing_area_set_draw_func(GTK_DRAWING_AREA(gui.drawarea), (GtkDrawingAreaDrawFunc)draw_event, NULL, NULL); - - g_signal_connect(G_OBJECT(gui.drawarea), "notify::scale-factor", - G_CALLBACK(drawarea_scale_factor_cb), NULL); #endif g_signal_connect(G_OBJECT(gui.drawarea), "realize", G_CALLBACK(drawarea_realize_cb), NULL); @@ -2343,6 +2340,17 @@ drawarea_realize_cb(GtkWidget *widget UNUSED, gpointer data UNUSED) cairo_surface_destroy(gui.surface); gui.surface = create_backing_surface(w, h); #endif + { + // Use GdkSurface, as that handles fractional scale values. + GdkSurface *surface = gtk_native_get_surface( + gtk_widget_get_native(gui.drawarea)); + double old = gui.scale; + + gui.scale = gdk_surface_get_scale(surface); + popup_update_scale(old); + g_signal_connect(G_OBJECT(surface), "notify::scale", + G_CALLBACK(scale_factor_cb), NULL); + } gui_mch_new_colors(); } @@ -2408,16 +2416,16 @@ gui_gtk4_resize(int width, int height) } } } +#endif static void -drawarea_scale_factor_cb(GObject *object UNUSED, - GParamSpec *pspec UNUSED, gpointer data UNUSED) +scale_factor_cb(GdkSurface *surface, + GParamSpec *pspec UNUSED, + void *udata UNUSED) { +#ifndef USE_GTK4_SNAPSHOT int w, h; - if (gui.drawarea == NULL) - return; - w = gtk_widget_get_width(gui.drawarea); h = gtk_widget_get_height(gui.drawarea); if (w <= 0 || h <= 0) @@ -2437,8 +2445,16 @@ drawarea_scale_factor_cb(GObject *object UNUSED, gtk_widget_queue_draw(gui.drawarea); if (gui.in_use) redraw_all_later(UPD_CLEAR); -} #endif +#if defined(FEAT_IMAGE) + { + double old = gui.scale; + + gui.scale = gdk_surface_get_scale(surface); + popup_update_scale(old); + } +#endif +} typedef enum { diff --git a/src/gui_gtk4_da.c b/src/gui_gtk4_da.c index 4a7211fd70..739f2e0aed 100644 --- a/src/gui_gtk4_da.c +++ b/src/gui_gtk4_da.c @@ -1752,7 +1752,8 @@ vim_draw_area_queue_image(VimDrawArea *self, GList *link) * (src_x, src_y, draw_w, draw_h) describe which pixel sub-rect of the source * texture should be drawn. If there is an image that has the same id, then it * is re-rendered with the new texture. If zindex of an image changed, then the - * queue will be updated accordingly. + * queue will be updated accordingly. Note that the dimensions/positions are to + * be in physical pixels!!! */ void vim_draw_area_add_image( @@ -1760,15 +1761,15 @@ vim_draw_area_add_image( GdkTexture *image, int row, int col, - int src_x, - int src_y, - int draw_w, - int draw_h, + double src_x, + double src_y, + double draw_w, + double draw_h, int zindex, int id) { GskRenderNode *node, *old; - int w, h; + double w, h; graphene_rect_t clip; GList *link; DrawImage *dimg; @@ -1778,12 +1779,16 @@ vim_draw_area_add_image( || col >= self->n_cols)) return; - w = gdk_texture_get_width(image); - h = gdk_texture_get_height(image); + w = PHY2LOG(gdk_texture_get_width(image)); + h = PHY2LOG(gdk_texture_get_height(image)); + src_x = PHY2LOG(src_x); + src_y = PHY2LOG(src_y); + draw_w = PHY2LOG(draw_w); + draw_h = PHY2LOG(draw_h); - node = gsk_texture_node_new(image, + node = gsk_texture_scale_node_new(image, &GRAPHENE_RECT_INIT(FILL_X(col) - src_x, FILL_Y(row) - src_y, - w, h)); + w, h), GSK_SCALING_FILTER_TRILINEAR); if (node != NULL) { diff --git a/src/gui_gtk4_da.h b/src/gui_gtk4_da.h index 5cd1cdc552..a214729a87 100644 --- a/src/gui_gtk4_da.h +++ b/src/gui_gtk4_da.h @@ -34,7 +34,7 @@ void vim_draw_area_add_sign(VimDrawArea *self, GdkTexture *sign, int row, int co void vim_draw_area_add_multisign(VimDrawArea *self, cairo_surface_t *surf, int row, int col, int width, int height); # endif # ifdef FEAT_IMAGE_GDK -void vim_draw_area_add_image(VimDrawArea *self, GdkTexture *image, int row, int col, int src_x, int src_y, int draw_w, int draw_h, int zindex, int id); +void vim_draw_area_add_image(VimDrawArea *self, GdkTexture *image, int row, int col, double src_x, double src_y, double draw_w, double draw_h, int zindex, int id); void vim_draw_area_remove_image(VimDrawArea *self, int id); # endif diff --git a/src/gui_gtk_x11.c b/src/gui_gtk_x11.c index 098892d5c1..1291919b29 100644 --- a/src/gui_gtk_x11.c +++ b/src/gui_gtk_x11.c @@ -836,6 +836,14 @@ scale_factor_event(GtkWidget *widget, gui.force_redraw = 1; gui_resize_shell(w, usable_height); gui_gtk_form_thaw(GTK_FORM(gui.formwin)); +# ifdef FEAT_IMAGE + { + double old = gui.scale; + + gui.scale = gtk_widget_get_scale_factor(widget); + popup_update_scale(old); + } +# endif return TRUE; } @@ -4291,6 +4299,10 @@ gui_mch_init(void) G_CALLBACK(gtk_settings_xft_dpi_changed_cb), NULL); } +#ifdef FEAT_IMAGE + gui.scale = gtk_widget_get_scale_factor(gui.formwin); +#endif + return OK; } diff --git a/src/popupwin.c b/src/popupwin.c index 293305ed92..bbbfa300c6 100644 --- a/src/popupwin.c +++ b/src/popupwin.c @@ -1110,9 +1110,9 @@ apply_general_options(win_T *wp, dict_T *dict) if (gui.in_use) { if (gui.char_width > 0) - cx = gui.char_width; + cx = LOG2PHY(gui.char_width); if (gui.char_height > 0) - cy = gui.char_height; + cy = LOG2PHY(gui.char_height); } else # endif @@ -3013,6 +3013,35 @@ popup_adjust_position(win_T *wp) } } +#if defined(FEAT_GUI_GTK) && defined(FEAT_IMAGE) +/* + * Should be called when scale factor changes. + */ + void +popup_update_scale(double old_scale) +{ + win_T *wp; + double ratio = old_scale / gui.scale; + + FOR_ALL_POPUPWINS_IN_TAB(curtab, wp) + { + // If the popup has an image, recalculate its bounding box in cells + if (wp->w_popup_image_data != NULL) + { + // Add +0.5 so that it rounds to nearest whole value + wp->w_minwidth = wp->w_minwidth * ratio + 0.5; + wp->w_maxwidth = wp->w_maxwidth * ratio + 0.5; + wp->w_minheight = wp->w_minheight * ratio + 0.5; + wp->w_maxheight = wp->w_maxheight * ratio + 0.5; + } + + // Reflow the popup layout with the newly calculated limits + popup_adjust_position(wp); + } + redraw_later(UPD_CLEAR); +} +#endif + typedef enum { TYPE_NORMAL, @@ -6825,9 +6854,13 @@ popup_image_gui_clip( int *draw_h) { popup_clip_T cl; - int cell_x = gui.char_width > 0 ? gui.char_width : 8; - int cell_y = gui.char_height > 0 ? gui.char_height : 16; + // "gui.char_*" are in logical pixels, must convert to physical first, + // because all the other dimensions are in physical pixels. + int cell_x = LOG2PHY(gui.char_width > 0 ? gui.char_width : 8); + int cell_y = LOG2PHY(gui.char_height > 0 ? gui.char_height : 16); win_T *cw; + int visible_w; + int visible_h; popup_compute_clip(wp, &cl); *row += cl.clip_top_content; @@ -6857,6 +6890,18 @@ popup_image_gui_clip( *draw_w = 0; if (*draw_h < 0) *draw_h = 0; + + // If image is larger than the actual screen size, then clip it. Maybe best + // solution would be to somehow make popup win bigger than the screen size + // (as if 'nowrap' is on). However that would require a lot of changes so + // just clip the image for now. + visible_w = wp->w_width - cl.clip_left_content - cl.clip_right_content; + visible_h = wp->w_height - cl.clip_top_content - cl.clip_bot_content; + + if (visible_w > 0) + *draw_w = MIN(*draw_w, visible_w * cell_x); + if (visible_h > 0) + *draw_h = MIN(*draw_h, visible_h * cell_y); } # endif @@ -7085,12 +7130,12 @@ popup_emit_image(win_T *wp) // vim still thinks it is at the image origin, so the relative-move // optimisation would otherwise place the cursor on the line just // after the image. - out_str((char_u *)"\033[?25l"); + cursor_off(); term_windgoto(row, col); out_str(wp->w_popup_image_seq); screen_start(); setcursor_mayforce(TRUE); - out_str((char_u *)"\033[?25h"); + cursor_on(); out_flush(); // The sixel bytes just painted over every cell of the emitted rectangle, diff --git a/src/proto/cairo.pro b/src/proto/cairo.pro index a297371042..1aad52c2bd 100644 --- a/src/proto/cairo.pro +++ b/src/proto/cairo.pro @@ -2,5 +2,5 @@ bool cairo_popup_image_ensure(win_T *wp); bool cairo_popup_image_update(win_T *wp); void cairo_popup_image_free(win_T *wp); -void cairo_popup_image_paint(win_T *wp, void *target, int x, int y, int src_x, int src_y, int draw_w, int draw_h); +void cairo_popup_image_paint(win_T *wp, void *target, int x, int y, double src_x, double src_y, double draw_w, double draw_h); /* vim: set ft=c : */ diff --git a/src/proto/popupwin.pro b/src/proto/popupwin.pro index 98e3a5f592..758695c35f 100644 --- a/src/proto/popupwin.pro +++ b/src/proto/popupwin.pro @@ -11,6 +11,7 @@ int popup_left_extra(win_T *wp); int popup_height(win_T *wp); int popup_width(win_T *wp); int popup_extra_width(win_T *wp); +void popup_update_scale(double old_scale); int parse_previewpopup(win_T *wp); int parse_completepopup(win_T *wp); void popup_set_wantpos_cursor(win_T *wp, int width, dict_T *d); diff --git a/src/version.c b/src/version.c index c857374a93..7601e516d0 100644 --- a/src/version.c +++ b/src/version.c @@ -758,6 +758,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 853, /**/ 852, /**/