Remmina - The GTK+ Remote Desktop Client  v1.4.33
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-2023 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"
41 #include "remmina_log.h"
43 
44 G_DEFINE_TYPE( RemminaScrolledViewport, remmina_scrolled_viewport, GTK_TYPE_EVENT_BOX)
45 
46 static void remmina_scrolled_viewport_get_preferred_width(GtkWidget* widget, gint* minimum_width, gint* natural_width)
47 {
48  TRACE_CALL(__func__);
49  /* Just return a fake small size, so gtk_window_fullscreen() will not fail
50  * because our content is too big*/
51  if (minimum_width != NULL) *minimum_width = 100;
52  if (natural_width != NULL) *natural_width = 100;
53 }
54 
55 static void remmina_scrolled_viewport_get_preferred_height(GtkWidget* widget, gint* minimum_height, gint* natural_height)
56 {
57  TRACE_CALL(__func__);
58  /* Just return a fake small size, so gtk_window_fullscreen() will not fail
59  * because our content is too big*/
60  if (minimum_height != NULL) *minimum_height = 100;
61  if (natural_height != NULL) *natural_height = 100;
62 }
63 
64 /* Event handler when mouse move on borders
65  * Note that this handler may repeat itself. Zeroing out viewport_motion_handler before returning FALSE will
66  * relay the fact that this specific occurrence of timeout has been cancelled.
67  * A new one may be scheduled if the mouse pointer moves to the edge again.
68  */
69 static gboolean remmina_scrolled_viewport_motion_timeout(gpointer data)
70 {
71  TRACE_CALL(__func__);
73  GtkWidget *child;
74  GdkDisplay *display;
75 #if GTK_CHECK_VERSION(3, 20, 0)
76  GdkSeat *seat;
77 #else
78  GdkDeviceManager *device_manager;
79 #endif
80  GdkDevice *pointer;
81  GdkScreen *screen;
82  GdkWindow *gsvwin;
83  gint x, y, mx, my, w, h, rootx, rooty;
84  GtkAdjustment *adj;
85  gdouble value;
86 
87  gsv = REMMINA_SCROLLED_VIEWPORT(data);
88  if (!gsv || !gsv->viewport_motion_handler)
89  // Either the pointer is nullptr or the source id is already 0
90  return FALSE;
91  if (!REMMINA_IS_SCROLLED_VIEWPORT(data)) {
92  gsv->viewport_motion_handler = 0;
93  return FALSE;
94  }
95  if (!GTK_IS_BIN(data)) {
96  gsv->viewport_motion_handler = 0;
97  return FALSE;
98  }
99 
100  child = gtk_bin_get_child(GTK_BIN(gsv));
101  if (!GTK_IS_VIEWPORT(child)) {
102  gsv->viewport_motion_handler = 0;
103  return FALSE;
104  }
105 
106  gsvwin = gtk_widget_get_window(GTK_WIDGET(gsv));
107  display = gdk_display_get_default();
108  if (!display) {
109  gsv->viewport_motion_handler = 0;
110  return FALSE;
111  }
112 
113 #if GTK_CHECK_VERSION(3, 20, 0)
114  seat = gdk_display_get_default_seat(display);
115  pointer = gdk_seat_get_pointer(seat);
116 #else
117  device_manager = gdk_display_get_device_manager(display);
118  pointer = gdk_device_manager_get_client_pointer(device_manager);
119 #endif
120  gdk_device_get_position(pointer, &screen, &x, &y);
121 
122  w = gdk_window_get_width(gsvwin) + SCROLL_BORDER_SIZE; // Add 2px of black scroll border
123  h = gdk_window_get_height(gsvwin) + SCROLL_BORDER_SIZE; // Add 2px of black scroll border
124 
125  gdk_window_get_root_origin(gsvwin, &rootx, &rooty );
126 
127  x -= rootx;
128  y -= rooty;
129 
130  mx = (x <= 0 ? -1 : (x >= w - 1 ? 1 : 0));
131  my = (y <= 0 ? -1 : (y >= h - 1 ? 1 : 0));
132  if (mx != 0) {
133  gint step = MAX(10, MIN(remmina_pref.auto_scroll_step, w / 5));
134  adj = gtk_scrollable_get_hadjustment(GTK_SCROLLABLE(child));
135  value = gtk_adjustment_get_value(GTK_ADJUSTMENT(adj)) + (gdouble)(mx * step);
136  value = MAX(0, MIN(value, gtk_adjustment_get_upper(GTK_ADJUSTMENT(adj)) - (gdouble)w + 2.0));
137  gtk_adjustment_set_value(GTK_ADJUSTMENT(adj), value);
138  }
139  if (my != 0) {
140  gint step = MAX(10, MIN(remmina_pref.auto_scroll_step, h / 5));
141  adj = gtk_scrollable_get_vadjustment(GTK_SCROLLABLE(child));
142  value = gtk_adjustment_get_value(GTK_ADJUSTMENT(adj)) + (gdouble)(my * step);
143  value = MAX(0, MIN(value, gtk_adjustment_get_upper(GTK_ADJUSTMENT(adj)) - (gdouble)h + 2.0));
144  gtk_adjustment_set_value(GTK_ADJUSTMENT(adj), value);
145  }
146  return TRUE;
147 }
148 
149 static gboolean remmina_scrolled_viewport_enter(GtkWidget *widget, GdkEventCrossing *event, gpointer data)
150 {
151  TRACE_CALL(__func__);
152  remmina_scrolled_viewport_remove_motion(REMMINA_SCROLLED_VIEWPORT(widget));
153  return FALSE;
154 }
155 
156 static gboolean remmina_scrolled_viewport_leave(GtkWidget *widget, GdkEventCrossing *event, gpointer data)
157 {
158  TRACE_CALL(__func__);
159  RemminaScrolledViewport *gsv = REMMINA_SCROLLED_VIEWPORT(widget);
160  if (gsv->viewport_motion_handler) {
161  REMMINA_DEBUG("cleaning motion ...");
163  }
165  return FALSE;
166 }
167 
168 static void remmina_scrolled_viewport_destroy(GtkWidget *widget, gpointer data)
169 {
170  TRACE_CALL(__func__);
171  remmina_scrolled_viewport_remove_motion(REMMINA_SCROLLED_VIEWPORT(widget));
172 }
173 
175 {
176  TRACE_CALL(__func__);
177  GtkWidgetClass *widget_class;
178  widget_class = (GtkWidgetClass*)klass;
179 
180  widget_class->get_preferred_width = remmina_scrolled_viewport_get_preferred_width;
181  widget_class->get_preferred_height = remmina_scrolled_viewport_get_preferred_height;
182 
183 }
184 
186 {
187  TRACE_CALL(__func__);
188 }
189 
191 {
192  TRACE_CALL(__func__);
193  guint handler = gsv->viewport_motion_handler;
194  if (handler) {
195  gsv->viewport_motion_handler = 0;
196  g_source_remove(handler);
197  }
198 }
199 
200 GtkWidget*
202 {
203  TRACE_CALL(__func__);
205 
206  gsv = REMMINA_SCROLLED_VIEWPORT(g_object_new(REMMINA_TYPE_SCROLLED_VIEWPORT, NULL));
207 
208  gsv->viewport_motion_handler = 0;
209 
210  gtk_widget_set_size_request(GTK_WIDGET(gsv), 1, 1);
211  gtk_widget_add_events(GTK_WIDGET(gsv), GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK);
212  g_signal_connect(G_OBJECT(gsv), "destroy", G_CALLBACK(remmina_scrolled_viewport_destroy), NULL);
213  g_signal_connect(G_OBJECT(gsv), "enter-notify-event", G_CALLBACK(remmina_scrolled_viewport_enter), NULL);
214  g_signal_connect(G_OBJECT(gsv), "leave-notify-event", G_CALLBACK(remmina_scrolled_viewport_leave), NULL);
215 
216  return GTK_WIDGET(gsv);
217 }
218 
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:143
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:79
G_DEFINE_TYPE(RemminaScrolledViewport, remmina_scrolled_viewport, GTK_TYPE_EVENT_BOX)