diff --git a/examples/gvncviewer.c b/examples/gvncviewer.c index e1794b5..606728c 100644 --- a/examples/gvncviewer.c +++ b/examples/gvncviewer.c @@ -423,6 +423,15 @@ 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, GtkWidget *vncdisplay) +{ + if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(menu))) + vnc_display_set_rotate(VNC_DISPLAY(vncdisplay), TRUE); + else + vnc_display_set_rotate(VNC_DISPLAY(vncdisplay), FALSE); +} + + static void do_power_control(VncDisplay *vncdisplay, VncConnectionPowerAction action) { VncConnection *conn = vnc_display_get_connection(vncdisplay); @@ -721,6 +730,7 @@ int main(int argc, char **argv) GtkWidget *smoothing; GtkWidget *keep_aspect_ratio; GtkWidget *resize; + GtkWidget *rotate; GtkWidget *showgrabkeydlg; GtkWidget *shutdown; GtkWidget *reboot; @@ -799,16 +809,19 @@ int main(int argc, char **argv) scaling = gtk_check_menu_item_new_with_mnemonic("Scaled display"); smoothing = gtk_check_menu_item_new_with_mnemonic("Smooth scaling"); keep_aspect_ratio = gtk_check_menu_item_new_with_mnemonic("Keep aspect ratio"); + rotate = gtk_check_menu_item_new_with_mnemonic("Rotate 90° clockwise"); resize = gtk_check_menu_item_new_with_mnemonic("Allow resizing"); gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(scaling), TRUE); gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(smoothing), TRUE); - gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(resize), TRUE); + gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(resize), FALSE); + gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(rotate), TRUE); gtk_menu_shell_append(GTK_MENU_SHELL(submenu), fullscreen); 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); @@ -891,7 +904,8 @@ int main(int argc, char **argv) vnc_display_set_pointer_local(VNC_DISPLAY(vnc), TRUE); vnc_display_set_scaling(VNC_DISPLAY(vnc), TRUE); - vnc_display_set_allow_resize(VNC_DISPLAY(vnc), TRUE); + vnc_display_set_allow_resize(VNC_DISPLAY(vnc), FALSE); + vnc_display_set_rotate(VNC_DISPLAY(vnc), TRUE); vnc_display_set_lossy_encoding(VNC_DISPLAY(vnc), TRUE); vnc_display_set_zoom_level(VNC_DISPLAY(vnc), opt_zoom); @@ -963,6 +977,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, "toggled", + 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..d5d3933 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_rotate; + vnc_display_set_rotate; + local: *; }; diff --git a/src/vncdisplay.c b/src/vncdisplay.c index f67448a..dcf8b20 100644 --- a/src/vncdisplay.c +++ b/src/vncdisplay.c @@ -87,6 +87,7 @@ struct _VncDisplayPrivate gboolean allow_resize; gboolean smoothing; gboolean keep_aspect_ratio; + gboolean rotate; 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_boolean (value, vnc->priv->rotate); + 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_rotate (vnc, g_value_get_boolean (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->rotate) { + rotwidth = *fbheight; + rotheight = *fbwidth; + } else { + rotwidth = *fbwidth; + rotheight = *fbheight; + } + 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,21 @@ 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_set_source_surface(cr, - priv->fbCache, - offsetx / scalex, - offsety / scaley); + if (priv->rotate) { + cairo_matrix_t mtx = {0., scaley, -scalex, 0., + (double)winwidth, 0.}; + cairo_transform(cr, &mtx); + cairo_set_source_surface(cr, + priv->fbCache, + offsety / scaley, + offsetx / scalex); + } else { + 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); @@ -840,8 +869,14 @@ static gboolean motion_event(GtkWidget *widget, GdkEventMotion *motion) motion->x -= offsetx; motion->y -= offsety; - motion->x /= scalex; - motion->y /= scaley; + if (priv->rotate) { + int x_tmp = motion->y / scaley; + motion->y = (width - motion->x) / scalex; + motion->x = x_tmp; + } else { + motion->x /= scalex; + motion->y /= scaley; + } /* Next adjust the real client pointer */ if (!priv->absolute) { @@ -881,7 +916,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 +1304,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->rotate ? + vnc_framebuffer_get_height(VNC_FRAMEBUFFER(priv->fb)) : + vnc_framebuffer_get_width(VNC_FRAMEBUFFER(priv->fb)); else *defwidth = 0; @@ -1290,7 +1327,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->rotate ? + vnc_framebuffer_get_width(VNC_FRAMEBUFFER(priv->fb)) : + vnc_framebuffer_get_height(VNC_FRAMEBUFFER(priv->fb)); else *defheight = 0; @@ -1336,12 +1375,24 @@ static void on_framebuffer_update(VncConnection *conn G_GNUC_UNUSED, cairo_destroy(cr); } - x *= scalex; - y *= scaley; - w *= scalex; - h *= scaley; - x += offsetx; - y += offsety; + if (priv->rotate) { + int tmp; + tmp = x; + x = (fbheight - y - h) * scalex; + y = tmp * scaley; + tmp = w; + w = h * scalex; + h = tmp * scaley; + x += offsetx; + y += offsety; + } else { + x *= scalex; + y *= scaley; + w *= scalex; + h *= scaley; + x += offsetx; + y += offsety; + } if (priv->allow_scaling) { /* Without this, we get horizontal & vertical line artifacts @@ -2464,7 +2515,19 @@ static void vnc_display_class_init(VncDisplayClass *klass) g_param_spec_boolean ( "keep-aspect-ratio", "Keep aspect ratio", "Keep the aspect ratio matching the framebuffer when scaling", - FALSE, + TRUE, + 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_ROTATE, + g_param_spec_boolean ( "rotate", + "Rotate 90° clockwise", + "Rotate the image of the remote desktop 90° clockwise", + TRUE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_NAME | @@ -2744,7 +2807,7 @@ static void vnc_display_init(VncDisplay *display) priv->absolute = TRUE; priv->read_only = FALSE; priv->allow_lossy = FALSE; - priv->allow_scaling = FALSE; + priv->allow_scaling = TRUE; priv->grab_pointer = FALSE; priv->grab_keyboard = FALSE; priv->local_pointer = FALSE; @@ -2752,7 +2815,8 @@ static void vnc_display_init(VncDisplay *display) priv->force_size = TRUE; priv->allow_resize = FALSE; priv->smoothing = TRUE; - priv->keep_aspect_ratio = FALSE; + priv->keep_aspect_ratio = TRUE; + priv->rotate = TRUE; 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 +3282,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 +3350,33 @@ void vnc_display_set_keep_aspect_ratio(VncDisplay *obj, gboolean enable) } +/** + * vnc_display_set_rotate: + * @obj: (transfer none): the VNC display widget + * @enable: TRUE to rotate the display of the remote desktop 90° clockwise, FALSE otherwise + * + * Set whether the display of the remote desktop should be rotated by 90° + * clockwise. + */ +void vnc_display_set_rotate(VncDisplay *obj, gboolean enable) +{ + int ww, wh; + + g_return_if_fail (VNC_IS_DISPLAY (obj)); + obj->priv->rotate = enable; + + if (obj->priv->fb != NULL) { + GdkWindow *window = gtk_widget_get_window(GTK_WIDGET(obj)); + + if (window != NULL) { + ww = gdk_window_get_width(gtk_widget_get_window(GTK_WIDGET(obj))); + wh = gdk_window_get_height(gtk_widget_get_window(GTK_WIDGET(obj))); + gtk_widget_queue_draw_area(GTK_WIDGET(obj), 0, 0, ww, wh); + } + } +} + + /** * vnc_display_set_depth: * @obj: (transfer none): the VNC display widget @@ -3395,6 +3486,23 @@ gboolean vnc_display_get_keep_aspect_ratio(VncDisplay *obj) } +/** + * vnc_display_get_rotate: + * @obj: (transfer none): the VNC display widget + * + * Determine whether the display of the remote desktop is rotated by + * 90° clockwise. + * + * Returns: TRUE if the display is rotated, FALSE otherwise + */ +gboolean vnc_display_get_rotate(VncDisplay *obj) +{ + g_return_val_if_fail (VNC_IS_DISPLAY (obj), FALSE); + + return obj->priv->rotate; +} + + /** * vnc_display_get_scaling: * @obj: (transfer none): the VNC display widget diff --git a/src/vncdisplay.h b/src/vncdisplay.h index 0f1120e..356a219 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_rotate(VncDisplay *obj, gboolean enable); +gboolean vnc_display_get_rotate(VncDisplay *obj); + void vnc_display_set_shared_flag(VncDisplay *obj, gboolean shared); gboolean vnc_display_get_shared_flag(VncDisplay *obj);