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;
* 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(
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;
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);
* 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;
# 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)
# 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)
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);
// 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);
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();
}
}
}
}
+#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)
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
{
* (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(
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;
|| 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)
{
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
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;
}
G_CALLBACK(gtk_settings_xft_dpi_changed_cb), NULL);
}
+#ifdef FEAT_IMAGE
+ gui.scale = gtk_widget_get_scale_factor(gui.formwin);
+#endif
+
return OK;
}
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
}
}
+#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,
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;
*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
// 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,
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 : */
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);
static int included_patches[] =
{ /* Add new patch number below this line */
+/**/
+ 853,
/**/
852,
/**/