[gtk-vnc] src: introduce helper for calculating scaling/offset info



commit e8dbb63ef3ca8fa6686366e07e5b5eb5f9878fec
Author: Daniel P. Berrangé <dan berrange com>
Date:   Tue Mar 23 11:36:25 2021 +0000

    src: introduce helper for calculating scaling/offset info
    
    Currently the widget either scales the rendering, or offsets it.
    In the future it needs to be able to do both. There are multiple
    methods which need to know this info, so introduce a helper to
    calculate it.
    
    Signed-off-by: Daniel P. Berrangé <berrange redhat com>

 src/vncdisplay.c | 249 ++++++++++++++++++++++++++++++-------------------------
 1 file changed, 136 insertions(+), 113 deletions(-)
---
diff --git a/src/vncdisplay.c b/src/vncdisplay.c
index 0042faa..0bac3af 100644
--- a/src/vncdisplay.c
+++ b/src/vncdisplay.c
@@ -378,66 +378,114 @@ static void setup_surface_cache(VncDisplay *dpy, cairo_t *crWin, int w, int h)
     cairo_destroy(crCache);
 }
 
-static gboolean draw_event(GtkWidget *widget, cairo_t *cr)
+static void
+get_render_region_info(GtkWidget *widget,
+                       int *offsetx, int *offsety,
+                       int *width, int *height,
+                       double *scalex, double *scaley,
+                       int *fbwidth, int *fbheight,
+                       int *winwidth, int *winheight)
 {
     VncDisplay *obj = VNC_DISPLAY(widget);
     VncDisplayPrivate *priv = obj->priv;
-    int ww, wh;
-    int mx = 0, my = 0;
-    int fbw = 0, fbh = 0;
 
-    if (priv->fb) {
-        fbw = vnc_framebuffer_get_width(VNC_FRAMEBUFFER(priv->fb));
-        fbh = vnc_framebuffer_get_height(VNC_FRAMEBUFFER(priv->fb));
+    *winwidth = gdk_window_get_width(gtk_widget_get_window(widget));
+    *winheight = gdk_window_get_height(gtk_widget_get_window(widget));
+
+    if (!priv->fb) {
+        *offsetx = 0;
+        *offsety = 0;
+        *width = 0;
+        *height = 0;
+        *fbwidth = 0;
+        *fbheight = 0;
+        *scalex = 1;
+        *scaley = 1;
+        return;
+    }
 
-        setup_surface_cache(obj, cr, fbw, fbh);
+    *fbwidth = vnc_framebuffer_get_width(VNC_FRAMEBUFFER(priv->fb));
+    *fbheight = vnc_framebuffer_get_height(VNC_FRAMEBUFFER(priv->fb));
+
+    if (priv->allow_scaling) {
+        *offsetx = 0;
+        *offsety = 0;
+        *width = *winwidth;
+        *height = *winheight;
+        *scalex = (double)*winwidth / (double)*fbwidth;
+        *scaley = (double)*winheight / (double)*fbheight;
+    } else {
+        if (*winwidth > *fbwidth) {
+            *offsetx = (*winwidth - *fbwidth) / 2;
+            *width = *fbwidth;
+        } else {
+            *offsetx = 0;
+            *width = *winwidth;
+        }
+        if (*winheight > *fbheight) {
+            *offsety = (*winheight - *fbheight) / 2;
+            *height = *fbheight;
+        } else {
+            *offsety = 0;
+            *height = *winheight;
+        }
+        *scalex = 1;
+        *scaley = 1;
     }
 
-    ww = gdk_window_get_width(gtk_widget_get_window(widget));
-    wh = gdk_window_get_height(gtk_widget_get_window(widget));
+    VNC_DEBUG("win %dx%d fb %dx%d render %dx%d @ %d,%d scale %f,%f",
+              *winwidth, *winheight,
+              *fbwidth, *fbheight,
+              *width, *height,
+              *offsetx, *offsety,
+              *scalex, *scaley);
+}
 
-    if (ww > fbw)
-        mx = (ww - fbw) / 2;
-    if (wh > fbh)
-        my = (wh - fbh) / 2;
+static gboolean draw_event(GtkWidget *widget, cairo_t *cr)
+{
+    VncDisplay *obj = VNC_DISPLAY(widget);
+    VncDisplayPrivate *priv = obj->priv;
+    int offsetx, offsety;
+    int width, height;
+    double scalex, scaley;
+    int fbwidth, fbheight;
+    int winwidth, winheight;
+
+    get_render_region_info(widget,
+                           &offsetx, &offsety,
+                           &width, &height,
+                           &scalex, &scaley,
+                           &fbwidth, &fbheight,
+                           &winwidth, &winheight);
 
-    /* If we don't have a pixmap, or we're not scaling, then
-       we need to fill with background color */
-    if (!priv->fb ||
-        !priv->allow_scaling) {
-        cairo_rectangle(cr, 0, 0, ww, wh);
-        /* Optionally cut out the inner area where the pixmap
-           will be drawn. This avoids 'flashing' since we're
-           not double-buffering. Note we're using the undocumented
-           behaviour of drawing the rectangle from right to left
-           to cut out the whole */
-        if (priv->fb)
-            cairo_rectangle(cr, mx + fbw, my,
-                            -1 * fbw, fbh);
-        cairo_fill(cr);
+    if (priv->fb) {
+        setup_surface_cache(obj, cr, fbwidth, fbheight);
     }
 
-    /* Draw the VNC display */
+    /* First fill the widget with the background colour, ... */
+    cairo_rectangle(cr, 0, 0, winwidth, winheight);
+
+    /* ...optionally cutting out the inner area where the pixmap
+       will be drawn. This avoids 'flashing' since we're
+       not double-buffering. Note we're using the undocumented
+       behaviour of drawing the rectangle from right to left
+       to cut out the hole */
+    if (priv->fb)
+        cairo_rectangle(cr, offsetx + width, offsety,
+                        -1 * width, height);
+    cairo_fill(cr);
+
+    /* Now render the remote desktop, scaling/offsetting
+     * as needed */
     if (priv->fb) {
-        if (priv->allow_scaling) {
-            double sx, sy;
-            /* Scale to fill window */
-            sx = (double)ww / (double)fbw;
-            sy = (double)wh / (double)fbh;
-            cairo_scale(cr, sx, sy);
-            cairo_set_source_surface(cr,
-                                     priv->fbCache,
-                                     0,
-                                     0);
-            if (!priv->smoothing) {
-                cairo_pattern_set_filter(cairo_get_source(cr),
-                                         CAIRO_FILTER_NEAREST);
-            }
-        } else {
-            cairo_set_source_surface(cr,
-                                     priv->fbCache,
-                                     mx,
-                                     my);
+        cairo_scale(cr, scalex, scaley);
+        cairo_set_source_surface(cr,
+                                 priv->fbCache,
+                                 offsetx / scalex,
+                                 offsety / scaley);
+        if (!priv->smoothing) {
+            cairo_pattern_set_filter(cairo_get_source(cr),
+                                     CAIRO_FILTER_NEAREST);
         }
         cairo_paint(cr);
     }
@@ -731,8 +779,11 @@ static gboolean scroll_event(GtkWidget *widget, GdkEventScroll *scroll)
 static gboolean motion_event(GtkWidget *widget, GdkEventMotion *motion)
 {
     VncDisplayPrivate *priv = VNC_DISPLAY(widget)->priv;
-    int ww, wh;
-    int fbw, fbh;
+    int offsetx, offsety;
+    int width, height;
+    double scalex, scaley;
+    int fbwidth, fbheight;
+    int winwidth, winheight;
 
     if (priv->conn == NULL || !vnc_connection_is_initialized(priv->conn))
         return FALSE;
@@ -740,9 +791,6 @@ static gboolean motion_event(GtkWidget *widget, GdkEventMotion *motion)
     if (!priv->fb)
         return FALSE;
 
-    fbw = vnc_framebuffer_get_width(VNC_FRAMEBUFFER(priv->fb));
-    fbh = vnc_framebuffer_get_height(VNC_FRAMEBUFFER(priv->fb));
-
     /* In relative mode, only move the server mouse pointer
      * if the client grab is active */
     if (!priv->absolute && !priv->in_pointer_grab)
@@ -751,33 +799,21 @@ static gboolean motion_event(GtkWidget *widget, GdkEventMotion *motion)
     if (priv->read_only)
         return FALSE;
 
-    ww = gdk_window_get_width(gtk_widget_get_window(widget));
-    wh = gdk_window_get_height(gtk_widget_get_window(widget));
+    get_render_region_info(widget,
+                           &offsetx, &offsety,
+                           &width, &height,
+                           &scalex, &scaley,
+                           &fbwidth, &fbheight,
+                           &winwidth, &winheight);
 
-    /* First apply adjustments to the coords in the motion event */
-    if (priv->allow_scaling) {
-        double sx, sy;
-        sx = (double)fbw / (double)ww;
-        sy = (double)fbh / (double)wh;
-
-        /* Scaling the desktop, so scale the mouse coords
-         * by same ratio */
-        motion->x *= sx;
-        motion->y *= sy;
-    } else {
-        int mw = 0, mh = 0;
+    /* First apply adjustments to the coords in the motion event
+     * based on the scaling and offsetting requirements */
 
-        if (ww > fbw)
-            mw = (ww - fbw) / 2;
-        if (wh > fbh)
-            mh = (wh - fbh) / 2;
+    motion->x -= offsetx;
+    motion->y -= offsety;
 
-        /* Not scaling, drawing the desktop centered
-         * in the larger window, so offset the mouse
-         * coords to match centering */
-        motion->x -= mw;
-        motion->y -= mh;
-    }
+    motion->x /= scalex;
+    motion->y /= scaley;
 
     /* Next adjust the real client pointer */
     if (!priv->absolute) {
@@ -822,10 +858,10 @@ static gboolean motion_event(GtkWidget *widget, GdkEventMotion *motion)
                 dx = 0;
             if (dy < 0)
                 dy = 0;
-            if (dx >= fbw)
-                dx = fbw - 1;
-            if (dy >= fbh)
-                dy = fbh - 1;
+            if (dx >= fbwidth)
+                dx = fbwidth - 1;
+            if (dy >= fbheight)
+                dy = fbheight - 1;
         } else {
             /* Just send the delta since last motion event */
             dx = (int)motion->x + 0x7FFF - priv->last_x;
@@ -1197,14 +1233,18 @@ static void on_framebuffer_update(VncConnection *conn G_GNUC_UNUSED,
     GtkWidget *widget = GTK_WIDGET(opaque);
     VncDisplay *obj = VNC_DISPLAY(widget);
     VncDisplayPrivate *priv = obj->priv;
-    int ww, wh;
-    int fbw, fbh;
-
-    fbw = vnc_framebuffer_get_width(VNC_FRAMEBUFFER(priv->fb));
-    fbh = vnc_framebuffer_get_height(VNC_FRAMEBUFFER(priv->fb));
-
-    ww = gdk_window_get_width(gtk_widget_get_window(widget));
-    wh = gdk_window_get_height(gtk_widget_get_window(widget));
+    int offsetx, offsety;
+    int width, height;
+    double scalex, scaley;
+    int fbwidth, fbheight;
+    int winwidth, winheight;
+
+    get_render_region_info(widget,
+                           &offsetx, &offsety,
+                           &width, &height,
+                           &scalex, &scaley,
+                           &fbwidth, &fbheight,
+                           &winwidth, &winheight);
 
     /* If we have a pixmap, update the region which changed.
      * If we don't have a pixmap, the entire thing will be
@@ -1222,19 +1262,14 @@ static void on_framebuffer_update(VncConnection *conn G_GNUC_UNUSED,
         cairo_destroy(cr);
     }
 
-    if (priv->allow_scaling) {
-        double sx, sy;
-
-        /* Scale the VNC region to produce expose region */
-
-        sx = (double)ww / (double)fbw;
-        sy = (double)wh / (double)fbh;
-
-        x *= sx;
-        y *= sy;
-        w *= sx;
-        h *= sy;
+    x *= scalex;
+    y *= scaley;
+    w *= scalex;
+    h *= scaley;
+    x += offsetx;
+    y += offsety;
 
+    if (priv->allow_scaling) {
         /* Without this, we get horizontal & vertical line artifacts
          * when drawing. This "fix" is somewhat dubious though. The
          * true mistake & fix almost certainly lies elsewhere.
@@ -1243,18 +1278,6 @@ static void on_framebuffer_update(VncConnection *conn G_GNUC_UNUSED,
         y -= 2;
         w += 4;
         h += 4;
-    } else {
-        int mw = 0, mh = 0;
-
-        /* Offset the VNC region to produce expose region */
-
-        if (ww > fbw)
-            mw = (ww - fbw) / 2;
-        if (wh > fbh)
-            mh = (wh - fbh) / 2;
-
-        x += mw;
-        y += mh;
     }
 
     gtk_widget_queue_draw_area(widget, x, y, w, h);


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]