Remmina - The GTK+ Remote Desktop Client  v1.4.2
Remmina is a remote desktop client written in GTK+, aiming to be useful for system administrators and travellers, who need to work with lots of remote computers in front of either large monitors or tiny netbooks. Remmina supports multiple network protocols in an integrated and consistent user interface. Currently RDP, VNC, NX, XDMCP and SSH are supported.
remmina_scrolled_viewport.c
Go to the documentation of this file.
1 /*
2  * Remmina - The GTK+ Remote Desktop Client
3  * Copyright (C) 2009 - Vic Lee
4  * Copyright (C) 2014-2015 Antenore Gatta, Fabio Castelli, Giovanni Panozzo
5  * Copyright (C) 2016-2020 Antenore Gatta, Giovanni Panozzo
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor,
20  * Boston, MA 02110-1301, USA.
21  *
22  * In addition, as a special exception, the copyright holders give
23  * permission to link the code of portions of this program with the
24  * OpenSSL library under certain conditions as described in each
25  * individual source file, and distribute linked combinations
26  * including the two.
27  * You must obey the GNU General Public License in all respects
28  * for all of the code used other than OpenSSL. * If you modify
29  * file(s) with this exception, you may extend this exception to your
30  * version of the file(s), but you are not obligated to do so. * If you
31  * do not wish to do so, delete this exception statement from your
32  * version. * If you delete this exception statement from all source
33  * files in the program, then also delete it here.
34  *
35  */
36 
37 #include <gtk/gtk.h>
38 #include "config.h"
40 #include "remmina_pref.h"
42 
43 G_DEFINE_TYPE( RemminaScrolledViewport, remmina_scrolled_viewport, GTK_TYPE_EVENT_BOX)
44 
45 static void remmina_scrolled_viewport_get_preferred_width(GtkWidget* widget, gint* minimum_width, gint* natural_width)
46 {
47  TRACE_CALL(__func__);
48  /* Just return a fake small size, so gtk_window_fullscreen() will not fail
49  * because our content is too big*/
50  if (minimum_width != NULL) *minimum_width = 100;
51  if (natural_width != NULL) *natural_width = 100;
52 }
53 
54 static void remmina_scrolled_viewport_get_preferred_height(GtkWidget* widget, gint* minimum_height, gint* natural_height)
55 {
56  TRACE_CALL(__func__);
57  /* Just return a fake small size, so gtk_window_fullscreen() will not fail
58  * because our content is too big*/
59  if (minimum_height != NULL) *minimum_height = 100;
60  if (natural_height != NULL) *natural_height = 100;
61 }
62 
63 /* Event handler when mouse move on borders */
64 static gboolean remmina_scrolled_viewport_motion_timeout(gpointer data)
65 {
66  TRACE_CALL(__func__);
68  GtkWidget *child;
69  GdkDisplay *display;
70 #if GTK_CHECK_VERSION(3, 20, 0)
71  GdkSeat *seat;
72 #else
73  GdkDeviceManager *device_manager;
74 #endif
75  GdkDevice *pointer;
76  GdkScreen *screen;
77  GdkWindow *gsvwin;
78  gint x, y, mx, my, w, h, rootx, rooty;
79  GtkAdjustment *adj;
80  gdouble value;
81 
82  if (!REMMINA_IS_SCROLLED_VIEWPORT(data))
83  return FALSE;
84  if (!GTK_IS_BIN(data))
85  return FALSE;
86  gsv = REMMINA_SCROLLED_VIEWPORT(data);
87  if (!gsv || !gsv->viewport_motion)
88  return FALSE;
89  child = gtk_bin_get_child(GTK_BIN(gsv));
90  if (!GTK_IS_VIEWPORT(child))
91  return FALSE;
92 
93  gsvwin = gtk_widget_get_window(GTK_WIDGET(gsv));
94  if (!gsv)
95  return FALSE;
96 
97  display = gdk_display_get_default();
98  if (!display)
99  return FALSE;
100 #if GTK_CHECK_VERSION(3, 20, 0)
101  seat = gdk_display_get_default_seat(display);
102  pointer = gdk_seat_get_pointer(seat);
103 #else
104  device_manager = gdk_display_get_device_manager(display);
105  pointer = gdk_device_manager_get_client_pointer(device_manager);
106 #endif
107  gdk_device_get_position(pointer, &screen, &x, &y);
108 
109  w = gdk_window_get_width(gsvwin) + SCROLL_BORDER_SIZE; // Add 2px of black scroll border
110  h = gdk_window_get_height(gsvwin) + SCROLL_BORDER_SIZE; // Add 2px of black scroll border
111 
112  gdk_window_get_root_origin(gsvwin, &rootx, &rooty );
113 
114  x -= rootx;
115  y -= rooty;
116 
117  mx = (x <= 0 ? -1 : (x >= w - 1 ? 1 : 0));
118  my = (y <= 0 ? -1 : (y >= h - 1 ? 1 : 0));
119  if (mx != 0) {
120  gint step = MAX(10, MIN(remmina_pref.auto_scroll_step, w / 5));
121  adj = gtk_scrollable_get_hadjustment(GTK_SCROLLABLE(child));
122  value = gtk_adjustment_get_value(GTK_ADJUSTMENT(adj)) + (gdouble)(mx * step);
123  value = MAX(0, MIN(value, gtk_adjustment_get_upper(GTK_ADJUSTMENT(adj)) - (gdouble)w + 2.0));
124  gtk_adjustment_set_value(GTK_ADJUSTMENT(adj), value);
125  }
126  if (my != 0) {
127  gint step = MAX(10, MIN(remmina_pref.auto_scroll_step, h / 5));
128  adj = gtk_scrollable_get_vadjustment(GTK_SCROLLABLE(child));
129  value = gtk_adjustment_get_value(GTK_ADJUSTMENT(adj)) + (gdouble)(my * step);
130  value = MAX(0, MIN(value, gtk_adjustment_get_upper(GTK_ADJUSTMENT(adj)) - (gdouble)h + 2.0));
131  gtk_adjustment_set_value(GTK_ADJUSTMENT(adj), value);
132  }
133  return TRUE;
134 }
135 
136 static gboolean remmina_scrolled_viewport_enter(GtkWidget *widget, GdkEventCrossing *event, gpointer data)
137 {
138  TRACE_CALL(__func__);
139  remmina_scrolled_viewport_remove_motion(REMMINA_SCROLLED_VIEWPORT(widget));
140  return FALSE;
141 }
142 
143 static gboolean remmina_scrolled_viewport_leave(GtkWidget *widget, GdkEventCrossing *event, gpointer data)
144 {
145  TRACE_CALL(__func__);
146  RemminaScrolledViewport *gsv = REMMINA_SCROLLED_VIEWPORT(widget);
147  gsv->viewport_motion = TRUE;
149  return FALSE;
150 }
151 
152 static void remmina_scrolled_viewport_destroy(GtkWidget *widget, gpointer data)
153 {
154  TRACE_CALL(__func__);
155  remmina_scrolled_viewport_remove_motion(REMMINA_SCROLLED_VIEWPORT(widget));
156 }
157 
159 {
160  TRACE_CALL(__func__);
161  GtkWidgetClass *widget_class;
162  widget_class = (GtkWidgetClass*)klass;
163 
164  widget_class->get_preferred_width = remmina_scrolled_viewport_get_preferred_width;
165  widget_class->get_preferred_height = remmina_scrolled_viewport_get_preferred_height;
166 
167 }
168 
170 {
171  TRACE_CALL(__func__);
172 }
173 
175 {
176  TRACE_CALL(__func__);
177  if (gsv->viewport_motion) {
178  gsv->viewport_motion = FALSE;
179  g_source_remove(gsv->viewport_motion_handler);
180  gsv->viewport_motion_handler = 0;
181  }
182 }
183 
184 GtkWidget*
186 {
187  TRACE_CALL(__func__);
189 
190  gsv = REMMINA_SCROLLED_VIEWPORT(g_object_new(REMMINA_TYPE_SCROLLED_VIEWPORT, NULL));
191 
192  gsv->viewport_motion = FALSE;
193  gsv->viewport_motion_handler = 0;
194 
195  gtk_widget_set_size_request(GTK_WIDGET(gsv), 1, 1);
196  gtk_widget_add_events(GTK_WIDGET(gsv), GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK);
197  g_signal_connect(G_OBJECT(gsv), "destroy", G_CALLBACK(remmina_scrolled_viewport_destroy), NULL);
198  g_signal_connect(G_OBJECT(gsv), "enter-notify-event", G_CALLBACK(remmina_scrolled_viewport_enter), NULL);
199  g_signal_connect(G_OBJECT(gsv), "leave-notify-event", G_CALLBACK(remmina_scrolled_viewport_leave), NULL);
200 
201  return GTK_WIDGET(gsv);
202 }
203 
static gboolean remmina_scrolled_viewport_motion_timeout(gpointer data)
static void remmina_scrolled_viewport_class_init(RemminaScrolledViewportClass *klass)
void remmina_scrolled_viewport_remove_motion(RemminaScrolledViewport *gsv)
gint auto_scroll_step
Definition: remmina_pref.h:125
static void remmina_scrolled_viewport_init(RemminaScrolledViewport *gsv)
static void remmina_scrolled_viewport_destroy(GtkWidget *widget, gpointer data)
static gboolean remmina_scrolled_viewport_leave(GtkWidget *widget, GdkEventCrossing *event, gpointer data)
GtkWidget * remmina_scrolled_viewport_new(void)
static gboolean remmina_scrolled_viewport_enter(GtkWidget *widget, GdkEventCrossing *event, gpointer data)
static void remmina_scrolled_viewport_get_preferred_height(GtkWidget *widget, gint *minimum_height, gint *natural_height)
RemminaPref remmina_pref
Definition: rcw.c:73
G_DEFINE_TYPE(RemminaScrolledViewport, remmina_scrolled_viewport, GTK_TYPE_EVENT_BOX)