[gtk-vnc] Add a button to rotate the screen
- From: Daniel P. Berrange <dberrange src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gtk-vnc] Add a button to rotate the screen
- Date: Thu, 27 May 2021 13:03:08 +0000 (UTC)
commit 251e0e8bbc8b2632253b9d55cacf53b7a15bfe79
Author: Thomas Loimer <thomas loimer tuwien ac at>
Date: Sat May 22 21:55:08 2021 +0200
Add a button to rotate the screen
On each click on the new menu item, the screen is rotated by additional
90 degrees clockwise.
examples/gvncviewer.c | 13 +++
src/libgtk-vnc_sym.version | 3 +
src/vncdisplay.c | 207 +++++++++++++++++++++++++++++++++++++++------
src/vncdisplay.h | 3 +
4 files changed, 202 insertions(+), 24 deletions(-)
---
diff --git a/examples/gvncviewer.c b/examples/gvncviewer.c
index e1794b5..4f0bfe2 100644
--- a/examples/gvncviewer.c
+++ b/examples/gvncviewer.c
@@ -423,6 +423,13 @@ static void do_keep_aspect_ratio(GtkWidget *menu, GtkWidget *vncdisplay)
vnc_display_set_keep_aspect_ratio(VNC_DISPLAY(vncdisplay), FALSE);
}
+static void do_rotate(GtkWidget *menu G_GNUC_UNUSED, GtkWidget *vncdisplay)
+{
+ guint rotation = vnc_display_get_rotation(VNC_DISPLAY(vncdisplay));
+ vnc_display_set_rotation(VNC_DISPLAY(vncdisplay), rotation + 90u);
+}
+
+
static void do_power_control(VncDisplay *vncdisplay, VncConnectionPowerAction action)
{
VncConnection *conn = vnc_display_get_connection(vncdisplay);
@@ -721,6 +728,7 @@ int main(int argc, char **argv)
GtkWidget *smoothing;
GtkWidget *keep_aspect_ratio;
GtkWidget *resize;
+ GtkWidget *rotate;
GtkWidget *showgrabkeydlg;
GtkWidget *shutdown;
GtkWidget *reboot;
@@ -800,6 +808,7 @@ int main(int argc, char **argv)
smoothing = gtk_check_menu_item_new_with_mnemonic("Smooth scaling");
keep_aspect_ratio = gtk_check_menu_item_new_with_mnemonic("Keep aspect ratio");
resize = gtk_check_menu_item_new_with_mnemonic("Allow resizing");
+ rotate = gtk_menu_item_new_with_mnemonic("Rotate ↷");
gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(scaling), TRUE);
gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(smoothing), TRUE);
@@ -809,6 +818,7 @@ int main(int argc, char **argv)
gtk_menu_shell_append(GTK_MENU_SHELL(submenu), scaling);
gtk_menu_shell_append(GTK_MENU_SHELL(submenu), smoothing);
gtk_menu_shell_append(GTK_MENU_SHELL(submenu), keep_aspect_ratio);
+ gtk_menu_shell_append(GTK_MENU_SHELL(submenu), rotate);
gtk_menu_shell_append(GTK_MENU_SHELL(submenu), resize);
gtk_menu_item_set_submenu(GTK_MENU_ITEM(view), submenu);
@@ -892,6 +902,7 @@ int main(int argc, char **argv)
vnc_display_set_scaling(VNC_DISPLAY(vnc), TRUE);
vnc_display_set_allow_resize(VNC_DISPLAY(vnc), TRUE);
+ vnc_display_set_rotation(VNC_DISPLAY(vnc), 0u);
vnc_display_set_lossy_encoding(VNC_DISPLAY(vnc), TRUE);
vnc_display_set_zoom_level(VNC_DISPLAY(vnc), opt_zoom);
@@ -963,6 +974,8 @@ int main(int argc, char **argv)
G_CALLBACK(do_smoothing), vnc);
g_signal_connect(keep_aspect_ratio, "toggled",
G_CALLBACK(do_keep_aspect_ratio), vnc);
+ g_signal_connect(rotate, "activate",
+ G_CALLBACK(do_rotate), vnc);
g_signal_connect(shutdown, "activate",
G_CALLBACK(do_shutdown), vnc);
g_signal_connect(reboot, "activate",
diff --git a/src/libgtk-vnc_sym.version b/src/libgtk-vnc_sym.version
index 4446b20..3adda9d 100644
--- a/src/libgtk-vnc_sym.version
+++ b/src/libgtk-vnc_sym.version
@@ -100,6 +100,9 @@
vnc_display_get_zoom_level;
vnc_display_set_zoom_level;
+ vnc_display_get_rotation;
+ vnc_display_set_rotation;
+
local:
*;
};
diff --git a/src/vncdisplay.c b/src/vncdisplay.c
index f67448a..ba02708 100644
--- a/src/vncdisplay.c
+++ b/src/vncdisplay.c
@@ -87,6 +87,7 @@ struct _VncDisplayPrivate
gboolean allow_resize;
gboolean smoothing;
gboolean keep_aspect_ratio;
+ guint rotation;
guint zoom_level;
GSList *preferable_auths;
@@ -123,6 +124,7 @@ enum
PROP_ALLOW_RESIZE,
PROP_SMOOTHING,
PROP_KEEP_ASPECT_RATIO,
+ PROP_ROTATE,
PROP_DEPTH,
PROP_ZOOM_LEVEL,
PROP_GRAB_KEYS,
@@ -232,6 +234,9 @@ vnc_display_get_property (GObject *object,
case PROP_KEEP_ASPECT_RATIO:
g_value_set_boolean (value, vnc->priv->keep_aspect_ratio);
break;
+ case PROP_ROTATE:
+ g_value_set_uint (value, vnc->priv->rotation);
+ break;
case PROP_DEPTH:
g_value_set_enum (value, vnc->priv->depth);
break;
@@ -293,6 +298,9 @@ vnc_display_set_property (GObject *object,
case PROP_KEEP_ASPECT_RATIO:
vnc_display_set_keep_aspect_ratio (vnc, g_value_get_boolean (value));
break;
+ case PROP_ROTATE:
+ vnc_display_set_rotation (vnc, g_value_get_uint (value));
+ break;
case PROP_DEPTH:
vnc_display_set_depth (vnc, g_value_get_enum (value));
break;
@@ -405,6 +413,9 @@ get_render_region_info(GtkWidget *widget,
{
VncDisplay *obj = VNC_DISPLAY(widget);
VncDisplayPrivate *priv = obj->priv;
+ /*
+ * Width and height of the unscaled, but possibly rotated remote desktop. */
+ int rotwidth, rotheight;
*winwidth = gdk_window_get_width(gtk_widget_get_window(widget));
*winheight = gdk_window_get_height(gtk_widget_get_window(widget));
@@ -424,36 +435,44 @@ get_render_region_info(GtkWidget *widget,
*fbwidth = vnc_framebuffer_get_width(VNC_FRAMEBUFFER(priv->fb));
*fbheight = vnc_framebuffer_get_height(VNC_FRAMEBUFFER(priv->fb));
+ if (priv->rotation == 0u || priv->rotation == 180u) {
+ rotwidth = *fbwidth;
+ rotheight = *fbheight;
+ } else {
+ rotwidth = *fbheight;
+ rotheight = *fbwidth;
+ }
+
if (priv->allow_scaling) {
*offsetx = 0;
*offsety = 0;
*width = *winwidth;
*height = *winheight;
- *scalex = (double)*winwidth / (double)*fbwidth;
- *scaley = (double)*winheight / (double)*fbheight;
+ *scalex = (double)*winwidth / (double)rotwidth;
+ *scaley = (double)*winheight / (double)rotheight;
if (priv->keep_aspect_ratio) {
if (*scalex > *scaley) {
*scalex = *scaley;
- *width = *fbwidth * *scalex;
+ *width = rotwidth * *scalex;
*offsetx = (*winwidth - *width) / 2;
} else if (*scalex < *scaley) {
*scaley = *scalex;
- *height = *fbheight * *scaley;
+ *height = rotheight * *scaley;
*offsety = (*winheight - *height) / 2;
}
}
} else {
- if (*winwidth > *fbwidth) {
- *offsetx = (*winwidth - *fbwidth) / 2;
- *width = *fbwidth;
+ if (*winwidth > rotwidth) {
+ *offsetx = (*winwidth - rotwidth) / 2;
+ *width = rotwidth;
} else {
*offsetx = 0;
*width = *winwidth;
}
- if (*winheight > *fbheight) {
- *offsety = (*winheight - *fbheight) / 2;
- *height = *fbheight;
+ if (*winheight > rotheight) {
+ *offsety = (*winheight - rotheight) / 2;
+ *height = rotheight;
} else {
*offsety = 0;
*height = *winheight;
@@ -506,11 +525,46 @@ static gboolean draw_event(GtkWidget *widget, cairo_t *cr)
/* Now render the remote desktop, scaling/offsetting
* as needed */
if (priv->fb) {
- cairo_scale(cr, scalex, scaley);
+ cairo_matrix_t mtx = {0., 0., 0., 0., 0., 0.};
+ double source_surface_offsetx, source_surface_offsety;
+
+ switch (priv->rotation) {
+ case 0u:
+ default:
+ mtx.xx = scalex;
+ mtx.yy = scaley;
+ source_surface_offsetx = offsetx / scalex;
+ source_surface_offsety = offsety / scaley;
+ break;
+ case 90u:
+ mtx.yx = scaley;
+ mtx.xy = -scalex;
+ mtx.x0 = (double)winwidth;
+ source_surface_offsetx = offsety / scaley;
+ source_surface_offsety = offsetx / scalex;
+ break;
+ case 180u:
+ mtx.xx = -scalex;
+ mtx.yy = -scaley;
+ mtx.x0 = (double)winwidth;
+ mtx.y0 = (double)winheight;
+ source_surface_offsetx = offsetx / scalex;
+ source_surface_offsety = offsety / scaley;
+ break;
+ case 270u:
+ mtx.yx = -scaley;
+ mtx.xy = scalex;
+ mtx.y0 = (double)winheight;
+ source_surface_offsetx = offsety / scaley;
+ source_surface_offsety = offsetx / scalex;
+ break;
+ }
+ cairo_transform(cr, &mtx);
cairo_set_source_surface(cr,
- priv->fbCache,
- offsetx / scalex,
- offsety / scaley);
+ priv->fbCache,
+ source_surface_offsetx,
+ source_surface_offsety);
+
if (!priv->smoothing) {
cairo_pattern_set_filter(cairo_get_source(cr),
CAIRO_FILTER_NEAREST);
@@ -840,8 +894,28 @@ static gboolean motion_event(GtkWidget *widget, GdkEventMotion *motion)
motion->x -= offsetx;
motion->y -= offsety;
- motion->x /= scalex;
- motion->y /= scaley;
+ /* The inverse transform of that applied in draw_event(). */
+ switch (priv->rotation) {
+ int tmp;
+ case 0u:
+ default:
+ motion->x /= scalex;
+ motion->y /= scaley;
+ break;
+ case 90u:
+ tmp = motion->x;
+ motion->x = motion->y / scaley;
+ motion->y = (width - tmp) / scalex;
+ break;
+ case 180u:
+ motion->x = (width - motion->x) / scalex;
+ motion->y = (height - motion->y) / scaley;
+ break;
+ case 270u:
+ tmp = motion->x;
+ motion->x = (height - motion->y) / scaley;
+ motion->y = tmp / scalex;
+ }
/* Next adjust the real client pointer */
if (!priv->absolute) {
@@ -881,7 +955,7 @@ static gboolean motion_event(GtkWidget *widget, GdkEventMotion *motion)
* them to the boundaries. We don't want to actually
* drop the events though, because even if the X coord
* is out of bounds we want the server to see Y coord
- * changes, and vica-verca. */
+ * changes, and vice-versa. */
if (dx < 0)
dx = 0;
if (dy < 0)
@@ -1269,7 +1343,9 @@ static void get_preferred_width(GtkWidget *widget,
vnc_connection_is_initialized(priv->conn) &&
priv->fb &&
priv->force_size)
- *defwidth = vnc_framebuffer_get_width(VNC_FRAMEBUFFER(priv->fb));
+ *defwidth = (priv->rotation == 0u || priv->rotation == 180u) ?
+ vnc_framebuffer_get_width(VNC_FRAMEBUFFER(priv->fb)) :
+ vnc_framebuffer_get_height(VNC_FRAMEBUFFER(priv->fb));
else
*defwidth = 0;
@@ -1290,7 +1366,9 @@ static void get_preferred_height(GtkWidget *widget,
vnc_connection_is_initialized(priv->conn) &&
priv->fb &&
priv->force_size)
- *defheight = vnc_framebuffer_get_height(VNC_FRAMEBUFFER(priv->fb));
+ *defheight = (priv->rotation == 0u || priv->rotation == 180u) ?
+ vnc_framebuffer_get_height(VNC_FRAMEBUFFER(priv->fb)) :
+ vnc_framebuffer_get_width(VNC_FRAMEBUFFER(priv->fb));
else
*defheight = 0;
@@ -1336,10 +1414,39 @@ static void on_framebuffer_update(VncConnection *conn G_GNUC_UNUSED,
cairo_destroy(cr);
}
- x *= scalex;
- y *= scaley;
- w *= scalex;
- h *= scaley;
+ switch (priv->rotation) {
+ /* This repeates the same transformation as in draw_event() above. */
+ /* To put cairo_matrix_t mtx into struct priv, and use here? */
+ int tmp;
+ case 0u:
+ default:
+ x *= scalex;
+ y *= scaley;
+ w *= scalex;
+ h *= scaley;
+ break;
+ case 90u:
+ tmp = x;
+ x = (fbheight - y - h) * scalex;
+ y = tmp * scaley;
+ tmp = w;
+ w = h * scalex;
+ h = tmp * scaley;
+ break;
+ case 180u:
+ x = (fbwidth - x - w) * scalex;
+ y = (fbheight - y - h) * scaley;
+ w *= scalex;
+ h *= scaley;
+ break;
+ case 270u:
+ tmp = x;
+ x = y * scalex;
+ y = (fbwidth - tmp - w) * scaley;
+ tmp = w;
+ w = h * scalex;
+ h = tmp * scaley;
+ }
x += offsetx;
y += offsety;
@@ -2471,6 +2578,20 @@ static void vnc_display_class_init(VncDisplayClass *klass)
G_PARAM_STATIC_NICK |
G_PARAM_STATIC_BLURB));
+ g_object_class_install_property (object_class,
+ PROP_ROTATE,
+ g_param_spec_uint ( "rotation",
+ "Rotate +90° clockwise",
+ "Rotate the image of the remote desktop 90°
clockwise",
+ 0,
+ 270,
+ 0,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT |
+ G_PARAM_STATIC_NAME |
+ G_PARAM_STATIC_NICK |
+ G_PARAM_STATIC_BLURB));
+
g_object_class_install_property (object_class,
PROP_DEPTH,
g_param_spec_enum ( "depth",
@@ -2753,6 +2874,7 @@ static void vnc_display_init(VncDisplay *display)
priv->allow_resize = FALSE;
priv->smoothing = TRUE;
priv->keep_aspect_ratio = FALSE;
+ priv->rotation = 0u;
priv->zoom_level = 100;
priv->vncgrabseq = vnc_grab_sequence_new_from_string("Control_L+Alt_L");
priv->vncactiveseq = g_new0(gboolean, priv->vncgrabseq->nkeysyms);
@@ -3218,7 +3340,7 @@ void vnc_display_set_force_size(VncDisplay *obj, gboolean enable)
* Set whether changes in the widget size will be translated
* into requests to resize the remote desktop. Resizing of
* the remote desktop is not guaranteed to be honoured by
- * servers, but when it is, it will avoid the need todo
+ * servers, but when it is, it will avoid the need to do
* scaling.
*/
void vnc_display_set_allow_resize(VncDisplay *obj, gboolean enable)
@@ -3286,6 +3408,27 @@ void vnc_display_set_keep_aspect_ratio(VncDisplay *obj, gboolean enable)
}
+/**
+ * vnc_display_set_rotation:
+ * @obj: (transfer none): the VNC display widget
+ * @rotation: The angle of rotation, degrees clockwise.
+ *
+ * Set the rotation angle to view the display of the remote desktop, in
+ * clockwise direction.
+ */
+void vnc_display_set_rotation(VncDisplay *obj, guint rotation)
+{
+ int ww, wh;
+
+ g_return_if_fail (VNC_IS_DISPLAY (obj));
+ obj->priv->rotation = rotation % 360u;
+
+ if (obj->priv->fb != NULL) {
+ gtk_widget_queue_resize(GTK_WIDGET(obj));
+ }
+}
+
+
/**
* vnc_display_set_depth:
* @obj: (transfer none): the VNC display widget
@@ -3395,6 +3538,22 @@ gboolean vnc_display_get_keep_aspect_ratio(VncDisplay *obj)
}
+/**
+ * vnc_display_get_rotation:
+ * @obj: (transfer none): the VNC display widget
+ *
+ * Determine the current rotation angle of the remote desktop.
+ *
+ * Returns: the rotation angle in clockwise direction
+ */
+guint vnc_display_get_rotation(VncDisplay *obj)
+{
+ g_return_val_if_fail (VNC_IS_DISPLAY (obj), 0u);
+
+ return obj->priv->rotation;
+}
+
+
/**
* vnc_display_get_scaling:
* @obj: (transfer none): the VNC display widget
diff --git a/src/vncdisplay.h b/src/vncdisplay.h
index 0f1120e..7d607e9 100644
--- a/src/vncdisplay.h
+++ b/src/vncdisplay.h
@@ -143,6 +143,9 @@ gboolean vnc_display_get_smoothing(VncDisplay *obj);
void vnc_display_set_keep_aspect_ratio(VncDisplay *obj, gboolean enable);
gboolean vnc_display_get_keep_aspect_ratio(VncDisplay *obj);
+void vnc_display_set_rotation(VncDisplay *obj, guint rotation);
+guint vnc_display_get_rotation(VncDisplay *obj);
+
void vnc_display_set_shared_flag(VncDisplay *obj, gboolean shared);
gboolean vnc_display_get_shared_flag(VncDisplay *obj);
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]