rcw.c 136 KB
Newer Older
Antenore Gatta's avatar
Antenore Gatta committed
1 2 3 4
/*
 * Remmina - The GTK+ Remote Desktop Client
 * Copyright (C) 2009-2011 Vic Lee
 * Copyright (C) 2014-2015 Antenore Gatta, Fabio Castelli, Giovanni Panozzo
5
 * Copyright (C) 2016-2019 Antenore Gatta, Giovanni Panozzo
Antenore Gatta's avatar
Antenore Gatta committed
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA  02110-1301, USA.
 *
 *  In addition, as a special exception, the copyright holders give
 *  permission to link the code of portions of this program with the
 *  OpenSSL library under certain conditions as described in each
 *  individual source file, and distribute linked combinations
 *  including the two.
 *  You must obey the GNU General Public License in all respects
 *  for all of the code used other than OpenSSL. *  If you modify
 *  file(s) with this exception, you may extend this exception to your
 *  version of the file(s), but you are not obligated to do so. *  If you
 *  do not wish to do so, delete this exception statement from your
 *  version. *  If you delete this exception statement from all source
 *  files in the program, then also delete it here.
 *
 */
llyzs's avatar
llyzs committed
36

Antenore Gatta's avatar
Antenore Gatta committed
37

38 39
#include "config.h"

40
#include <cairo/cairo-xlib.h>
Antenore Gatta's avatar
Antenore Gatta committed
41 42
#include <gdk/gdk.h>
#include <gdk/gdkkeysyms.h>
43
#include <glib/gi18n.h>
Antenore Gatta's avatar
Antenore Gatta committed
44
#include <gtk/gtk.h>
45
#include <stdlib.h>
46

Antenore Gatta's avatar
Antenore Gatta committed
47
#include "remmina.h"
48
#include "remmina_main.h"
49
#include "rcw.h"
50 51
#include "remmina_file.h"
#include "remmina_file_manager.h"
52
#include "remmina_message_panel.h"
53
#include "remmina_ext_exec.h"
54
#include "remmina_plugin_manager.h"
55
#include "remmina_pref.h"
56 57
#include "remmina_protocol_widget.h"
#include "remmina_public.h"
58
#include "remmina_scrolled_viewport.h"
Antenore Gatta's avatar
Antenore Gatta committed
59
#include "remmina_utils.h"
60
#include "remmina_widget_pool.h"
Giovanni Panozzo's avatar
Giovanni Panozzo committed
61
#include "remmina_log.h"
62
#include "remmina/remmina_trace_calls.h"
llyzs's avatar
llyzs committed
63

64
#ifdef GDK_WINDOWING_WAYLAND
Antenore Gatta's avatar
Antenore Gatta committed
65
#include <gdk/gdkwayland.h>
66 67 68
#endif


69
#define DEBUG_KB_GRABBING 0
70
#include "remmina_exec.h"
71

72 73 74
gchar *remmina_pref_file;
RemminaPref remmina_pref;

Antenore Gatta's avatar
Antenore Gatta committed
75
G_DEFINE_TYPE(RemminaConnectionWindow, rcw, GTK_TYPE_WINDOW)
llyzs's avatar
llyzs committed
76 77 78

#define MOTION_TIME 100

79 80 81
/* default timeout used to hide the floating toolbar wen switching profile */
#define TB_HIDE_TIME_TIME 1000

82 83
#define FULL_SCREEN_TARGET_MONITOR_UNDEFINED -1

Antenore Gatta's avatar
Antenore Gatta committed
84
struct _RemminaConnectionWindowPriv {
Antenore Gatta's avatar
Antenore Gatta committed
85 86 87 88 89 90 91 92 93
	GtkNotebook *					notebook;
	guint						switch_page_handler;
	GtkWidget *					floating_toolbar_widget;
	GtkWidget *					overlay;
	GtkWidget *					revealer;
	GtkWidget *					overlay_ftb_overlay;

	GtkWidget *					floating_toolbar_label;
	gdouble						floating_toolbar_opacity;
94
	/* To avoid strange event-loop */
Antenore Gatta's avatar
Antenore Gatta committed
95
	guint						floating_toolbar_motion_handler;
Giovanni Panozzo's avatar
Giovanni Panozzo committed
96
	/* Other event sources to remove when deleting the object */
Antenore Gatta's avatar
Antenore Gatta committed
97
	guint						ftb_hide_eventsource;
98
	/* Timer to hide the toolbar */
Antenore Gatta's avatar
Antenore Gatta committed
99
	guint						hidetb_timer;
100

101
	/* Timer to save new window state and wxh */
Antenore Gatta's avatar
Antenore Gatta committed
102
	guint						savestate_eventsourceid;
103

Antenore Gatta's avatar
Antenore Gatta committed
104 105
	GtkWidget *					toolbar;
	GtkWidget *					grid;
llyzs's avatar
llyzs committed
106

107
	/* Toolitems that need to be handled */
Antenore Gatta's avatar
Antenore Gatta committed
108 109 110 111 112 113 114 115
	GtkToolItem *					toolitem_autofit;
	GtkToolItem *					toolitem_fullscreen;
	GtkToolItem *					toolitem_switch_page;
	GtkToolItem *					toolitem_dynres;
	GtkToolItem *					toolitem_scale;
	GtkToolItem *					toolitem_grab;
	GtkToolItem *					toolitem_preferences;
	GtkToolItem *					toolitem_tools;
116
	GtkToolItem *					toolitem_duplicate;
Antenore Gatta's avatar
Antenore Gatta committed
117 118 119 120 121 122 123 124 125 126
	GtkToolItem *					toolitem_screenshot;
	GtkWidget *					fullscreen_option_button;
	GtkWidget *					fullscreen_scaler_button;
	GtkWidget *					scaler_option_button;

	GtkWidget *					pin_button;
	gboolean					pin_down;

	gboolean					sticky;
	gint						grab_retry_eventsourceid;
Giovanni Panozzo's avatar
Giovanni Panozzo committed
127

128 129
	/* flag to disable toolbar signal handling when toolbar is
	 * reconfiguring, usually due to a tab switch */
Antenore Gatta's avatar
Antenore Gatta committed
130
	gboolean					toolbar_is_reconfiguring;
131

Giovanni Panozzo's avatar
Giovanni Panozzo committed
132 133
	/* This is the current view mode, i.e. VIEWPORT_FULLSCREEN_MODE
	 * as saved on the "viwemode" profile preference file */
Antenore Gatta's avatar
Antenore Gatta committed
134
	gint						view_mode;
Giovanni Panozzo's avatar
Giovanni Panozzo committed
135

Giovanni Panozzo's avatar
Giovanni Panozzo committed
136 137
	/* Status variables used when in fullscreen mode. Needed
	 * to restore a fullscreen mode after coming from scrolled */
Antenore Gatta's avatar
Antenore Gatta committed
138
	gint						fss_view_mode;
Giovanni Panozzo's avatar
Giovanni Panozzo committed
139 140
	/* Status variables used when in scrolled window mode. Needed
	 * to restore a scrolled window mode after coming from fullscreen */
Antenore Gatta's avatar
Antenore Gatta committed
141 142
	gint						ss_width, ss_height;
	gboolean					ss_maximized;
Giovanni Panozzo's avatar
Giovanni Panozzo committed
143

Antenore Gatta's avatar
Antenore Gatta committed
144 145 146 147
	gboolean					kbcaptured;
	gboolean					mouse_pointer_entered;
	gboolean					hostkey_activated;
	gboolean					hostkey_used;
148

Antenore Gatta's avatar
Antenore Gatta committed
149
	RemminaConnectionWindowOnDeleteConfirmMode	on_delete_confirm_mode;
llyzs's avatar
llyzs committed
150 151
};

Antenore Gatta's avatar
Antenore Gatta committed
152
typedef struct _RemminaConnectionObject {
Antenore Gatta's avatar
Antenore Gatta committed
153 154
	RemminaConnectionWindow *	cnnwin;
	RemminaFile *			remmina_file;
llyzs's avatar
llyzs committed
155

Antenore Gatta's avatar
Antenore Gatta committed
156 157 158
	GtkWidget *			proto;
	GtkWidget *			aspectframe;
	GtkWidget *			viewport;
llyzs's avatar
llyzs committed
159

Antenore Gatta's avatar
Antenore Gatta committed
160
	GtkWidget *			scrolled_container;
llyzs's avatar
llyzs committed
161

Antenore Gatta's avatar
Antenore Gatta committed
162
	gboolean			plugin_can_scale;
163

Antenore Gatta's avatar
Antenore Gatta committed
164 165
	gboolean			connected;
	gboolean			dynres_unlocked;
Giovanni Panozzo's avatar
Giovanni Panozzo committed
166

Antenore Gatta's avatar
Antenore Gatta committed
167
	gulong				deferred_open_size_allocate_handler;
llyzs's avatar
llyzs committed
168 169
} RemminaConnectionObject;

Antenore Gatta's avatar
Antenore Gatta committed
170
enum {
171 172 173 174
	TOOLBARPLACE_SIGNAL,
	LAST_SIGNAL
};

175
static guint rcw_signals[LAST_SIGNAL] =
176 177
{ 0 };

Antenore Gatta's avatar
Antenore Gatta committed
178
static RemminaConnectionWindow *rcw_create_scrolled(gint width, gint height, gboolean maximize);
179
static RemminaConnectionWindow *rcw_create_fullscreen(GtkWindow *old, gint view_mode);
Antenore Gatta's avatar
Antenore Gatta committed
180 181 182
static gboolean rcw_hostkey_func(RemminaProtocolWidget *gp, guint keyval, gboolean release);
static GtkWidget *rco_create_tab_page(RemminaConnectionObject *cnnobj);
static GtkWidget *rco_create_tab_label(RemminaConnectionObject *cnnobj);
183

Antenore Gatta's avatar
Antenore Gatta committed
184 185
void rcw_grab_focus(RemminaConnectionWindow *cnnwin);
static GtkWidget *rcw_create_toolbar(RemminaConnectionWindow *cnnwin, gint mode);
Giovanni Panozzo's avatar
Giovanni Panozzo committed
186
static void rcw_place_toolbar(GtkToolbar *toolbar, GtkGrid *grid, GtkWidget *sibling, int toolbar_placement);
Antenore Gatta's avatar
Antenore Gatta committed
187 188
static void rcw_keyboard_grab(RemminaConnectionWindow *cnnwin);
static GtkWidget *rcw_append_new_page(RemminaConnectionWindow *cnnwin, RemminaConnectionObject *cnnobj);
Giovanni Panozzo's avatar
Giovanni Panozzo committed
189

190

191
static void rcw_ftb_drag_begin(GtkWidget *widget, GdkDragContext *context, gpointer user_data);
Giovanni Panozzo's avatar
Giovanni Panozzo committed
192

193
static const GtkTargetEntry dnd_targets_ftb[] =
194
{
Giovanni Panozzo's avatar
Giovanni Panozzo committed
195
	{
Antenore Gatta's avatar
Antenore Gatta committed
196
		(char *)"text/x-remmina-ftb",
Antenore Gatta's avatar
Antenore Gatta committed
197
		GTK_TARGET_SAME_APP | GTK_TARGET_OTHER_WIDGET,
Giovanni Panozzo's avatar
Giovanni Panozzo committed
198 199 200 201
		0
	},
};

202 203 204
static const GtkTargetEntry dnd_targets_tb[] =
{
	{
Antenore Gatta's avatar
Antenore Gatta committed
205
		(char *)"text/x-remmina-tb",
206 207 208 209
		GTK_TARGET_SAME_APP,
		0
	},
};
210

Antenore Gatta's avatar
Antenore Gatta committed
211
static void rcw_class_init(RemminaConnectionWindowClass *klass)
212
{
213
	TRACE_CALL(__func__);
Antenore Gatta's avatar
Antenore Gatta committed
214
	GtkCssProvider *provider;
Jean-Louis's avatar
Jean-Louis committed
215
	provider = gtk_css_provider_new();
Giovanni Panozzo's avatar
Giovanni Panozzo committed
216

217
	/* It’s important to remove padding, border and shadow from GtkViewport or
Giovanni Panozzo's avatar
Giovanni Panozzo committed
218 219
	 * we will never know its internal area size, because GtkViweport::viewport_get_view_allocation,
	 * which returns the internal size of the GtkViewport, is private and we cannot access it */
220 221

#if GTK_CHECK_VERSION(3, 14, 0)
Antenore Gatta's avatar
Antenore Gatta committed
222
	gtk_css_provider_load_from_data(provider,
Antenore Gatta's avatar
Antenore Gatta committed
223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288
					"#remmina-cw-viewport, #remmina-cw-aspectframe {\n"
					"  padding:0;\n"
					"  border:0;\n"
					"  background-color: black;\n"
					"}\n"
					"GtkDrawingArea {\n"
					"}\n"
					"GtkToolbar {\n"
					"  -GtkWidget-window-dragging: 0;\n"
					"}\n"
					"#remmina-connection-window-fullscreen {\n"
					"  border-color: black;\n"
					"}\n"
					"#remmina-small-button {\n"
					"  outline-offset: 0;\n"
					"  outline-width: 0;\n"
					"  padding: 0;\n"
					"  border: 0;\n"
					"}\n"
					"#remmina-pin-button {\n"
					"  outline-offset: 0;\n"
					"  outline-width: 0;\n"
					"  padding: 2px;\n"
					"  border: 0;\n"
					"}\n"
					"#remmina-tab-page {\n"
					"  background-color: black;\n"
					"}\n"
					"#remmina-scrolled-container {\n"
					"}\n"
					"#remmina-scrolled-container.undershoot {\n"
					"  background: none;\n"
					"}\n"
					"#remmina-tab-page {\n"
					"}\n"
					"#ftbbox-upper {\n"
					"  background-color: white;\n"
					"  color: black;\n"
					"  border-style: none solid solid solid;\n"
					"  border-width: 1px;\n"
					"  border-radius: 4px;\n"
					"  padding: 0px;\n"
					"}\n"
					"#ftbbox-lower {\n"
					"  background-color: white;\n"
					"  color: black;\n"
					"  border-style: solid solid none solid;\n"
					"  border-width: 1px;\n"
					"  border-radius: 4px;\n"
					"  padding: 0px;\n"
					"}\n"
					"#ftb-handle {\n"
					"}\n"
					".message_panel {\n"
					"  border: 0px solid;\n"
					"  padding: 20px 20px 20px 20px;\n"
					"}\n"
					".message_panel entry {\n"
					"  background-image: none;\n"
					"  border-width: 4px;\n"
					"  border-radius: 8px;\n"
					"}\n"
					".message_panel .title_label {\n"
					"  font-size: 2em; \n"
					"}\n"
					, -1, NULL);
289 290

#else
Antenore Gatta's avatar
Antenore Gatta committed
291
	gtk_css_provider_load_from_data(provider,
Antenore Gatta's avatar
Antenore Gatta committed
292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341
					"#remmina-cw-viewport, #remmina-cw-aspectframe {\n"
					"  padding:0;\n"
					"  border:0;\n"
					"  background-color: black;\n"
					"}\n"
					"#remmina-cw-message-panel {\n"
					"}\n"
					"GtkDrawingArea {\n"
					"}\n"
					"GtkToolbar {\n"
					"  -GtkWidget-window-dragging: 0;\n"
					"}\n"
					"#remmina-connection-window-fullscreen {\n"
					"  border-color: black;\n"
					"}\n"
					"#remmina-small-button {\n"
					"  -GtkWidget-focus-padding: 0;\n"
					"  -GtkWidget-focus-line-width: 0;\n"
					"  padding: 0;\n"
					"  border: 0;\n"
					"}\n"
					"#remmina-pin-button {\n"
					"  -GtkWidget-focus-padding: 0;\n"
					"  -GtkWidget-focus-line-width: 0;\n"
					"  padding: 2px;\n"
					"  border: 0;\n"
					"}\n"
					"#remmina-scrolled-container {\n"
					"}\n"
					"#remmina-scrolled-container.undershoot {\n"
					"  background: none\n"
					"}\n"
					"#remmina-tab-page {\n"
					"}\n"
					"#ftbbox-upper {\n"
					"  border-style: none solid solid solid;\n"
					"  border-width: 1px;\n"
					"  border-radius: 4px;\n"
					"  padding: 0px;\n"
					"}\n"
					"#ftbbox-lower {\n"
					"  border-style: solid solid none solid;\n"
					"  border-width: 1px;\n"
					"  border-radius: 4px;\n"
					"  padding: 0px;\n"
					"}\n"
					"#ftb-handle {\n"
					"}\n"

					, -1, NULL);
342
#endif
Giovanni Panozzo's avatar
Giovanni Panozzo committed
343

Antenore Gatta's avatar
Antenore Gatta committed
344
	gtk_style_context_add_provider_for_screen(gdk_screen_get_default(),
Antenore Gatta's avatar
Antenore Gatta committed
345 346
						  GTK_STYLE_PROVIDER(provider),
						  GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
Giovanni Panozzo's avatar
Giovanni Panozzo committed
347

Jean-Louis's avatar
Jean-Louis committed
348
	g_object_unref(provider);
349

350 351
	/* Define a signal used to notify all rcws of toolbar move */
	rcw_signals[TOOLBARPLACE_SIGNAL] = g_signal_new("toolbar-place", G_TYPE_FROM_CLASS(klass),
Antenore Gatta's avatar
Antenore Gatta committed
352 353
							G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET(RemminaConnectionWindowClass, toolbar_place), NULL, NULL,
							g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
354 355
}

Giovanni Panozzo's avatar
Giovanni Panozzo committed
356 357 358
static RemminaConnectionObject *rcw_get_cnnobj_at_page(RemminaConnectionWindow *cnnwin, gint npage)
{
	GtkWidget *po;
Antenore Gatta's avatar
Antenore Gatta committed
359

Giovanni Panozzo's avatar
Giovanni Panozzo committed
360 361 362 363 364 365 366 367 368
	if (!cnnwin->priv->notebook)
		return NULL;
	po = gtk_notebook_get_nth_page(GTK_NOTEBOOK(cnnwin->priv->notebook), npage);
	return g_object_get_data(G_OBJECT(po), "cnnobj");
}

static RemminaConnectionObject *rcw_get_visible_cnnobj(RemminaConnectionWindow *cnnwin)
{
	gint np;
Antenore Gatta's avatar
Antenore Gatta committed
369

Giovanni Panozzo's avatar
Giovanni Panozzo committed
370 371 372 373 374 375
	np = gtk_notebook_get_current_page(GTK_NOTEBOOK(cnnwin->priv->notebook));
	if (np < 0)
		return NULL;
	return rcw_get_cnnobj_at_page(cnnwin, np);
}

Antenore Gatta's avatar
Antenore Gatta committed
376
static RemminaScaleMode get_current_allowed_scale_mode(RemminaConnectionObject *cnnobj, gboolean *dynres_avail, gboolean *scale_avail)
377
{
378
	TRACE_CALL(__func__);
379 380 381 382 383 384
	RemminaScaleMode scalemode;
	gboolean plugin_has_dynres, plugin_can_scale;

	scalemode = remmina_protocol_widget_get_current_scale_mode(REMMINA_PROTOCOL_WIDGET(cnnobj->proto));

	plugin_has_dynres = remmina_protocol_widget_query_feature_by_type(REMMINA_PROTOCOL_WIDGET(cnnobj->proto),
Antenore Gatta's avatar
Antenore Gatta committed
385
									  REMMINA_PROTOCOL_FEATURE_TYPE_SCALE);
386 387

	plugin_can_scale = remmina_protocol_widget_query_feature_by_type(REMMINA_PROTOCOL_WIDGET(cnnobj->proto),
Antenore Gatta's avatar
Antenore Gatta committed
388
									 REMMINA_PROTOCOL_FEATURE_TYPE_SCALE);
389 390

	/* forbid scalemode REMMINA_PROTOCOL_WIDGET_SCALE_MODE_DYNRES when not possible */
391
	if ((!plugin_has_dynres) && scalemode == REMMINA_PROTOCOL_WIDGET_SCALE_MODE_DYNRES)
392 393 394 395 396 397 398 399 400 401 402 403 404 405
		scalemode = REMMINA_PROTOCOL_WIDGET_SCALE_MODE_NONE;

	/* forbid scalemode REMMINA_PROTOCOL_WIDGET_SCALE_MODE_SCALED when not possible */
	if (!plugin_can_scale && scalemode == REMMINA_PROTOCOL_WIDGET_SCALE_MODE_SCALED)
		scalemode = REMMINA_PROTOCOL_WIDGET_SCALE_MODE_NONE;

	if (scale_avail)
		*scale_avail = plugin_can_scale;
	if (dynres_avail)
		*dynres_avail = (plugin_has_dynres && cnnobj->dynres_unlocked);

	return scalemode;
}

Antenore Gatta's avatar
Antenore Gatta committed
406
static void rco_disconnect_current_page(RemminaConnectionObject *cnnobj)
407
{
408
	TRACE_CALL(__func__);
409

Antenore Gatta's avatar
Antenore Gatta committed
410 411
	/* Disconnects the connection which is currently in view in the notebook */
	remmina_protocol_widget_close_connection(REMMINA_PROTOCOL_WIDGET(cnnobj->proto));
412 413
}

Antenore Gatta's avatar
Antenore Gatta committed
414
static void rcw_keyboard_ungrab(RemminaConnectionWindow *cnnwin)
415
{
416
	TRACE_CALL(__func__);
Antenore Gatta's avatar
Antenore Gatta committed
417
	GdkDisplay *display;
Giovanni Panozzo's avatar
Giovanni Panozzo committed
418 419 420
#if GTK_CHECK_VERSION(3, 20, 0)
	GdkSeat *seat;
#else
Jean-Louis's avatar
Jean-Louis committed
421
	GdkDeviceManager *manager;
Giovanni Panozzo's avatar
Giovanni Panozzo committed
422
#endif
Jean-Louis's avatar
Jean-Louis committed
423
	GdkDevice *keyboard = NULL;
424

Giovanni Panozzo's avatar
Giovanni Panozzo committed
425 426 427
	if (cnnwin->priv->grab_retry_eventsourceid) {
		g_source_remove(cnnwin->priv->grab_retry_eventsourceid);
		cnnwin->priv->grab_retry_eventsourceid = 0;
428 429
	}

Giovanni Panozzo's avatar
Giovanni Panozzo committed
430
	display = gtk_widget_get_display(GTK_WIDGET(cnnwin));
Giovanni Panozzo's avatar
Giovanni Panozzo committed
431 432 433 434
#if GTK_CHECK_VERSION(3, 20, 0)
	seat = gdk_display_get_default_seat(display);
	keyboard = gdk_seat_get_pointer(seat);
#else
Jean-Louis's avatar
Jean-Louis committed
435 436
	manager = gdk_display_get_device_manager(display);
	keyboard = gdk_device_manager_get_client_pointer(manager);
Giovanni Panozzo's avatar
Giovanni Panozzo committed
437 438
#endif

Antenore Gatta's avatar
Antenore Gatta committed
439
	if (!cnnwin->priv->kbcaptured)
440
		return;
441

Antenore Gatta's avatar
Antenore Gatta committed
442
	if (keyboard != NULL) {
Antenore Gatta's avatar
Antenore Gatta committed
443
		if (gdk_device_get_source(keyboard) != GDK_SOURCE_KEYBOARD)
444
			keyboard = gdk_device_get_associated_device(keyboard);
Antenore Gatta's avatar
Antenore Gatta committed
445

446 447 448
#if DEBUG_KB_GRABBING
		printf("DEBUG_KB_GRABBING: --- ungrabbing\n");
#endif
449

450 451
#if GTK_CHECK_VERSION(3, 24, 0)
		/* We can use gtk_seat_grab()/_ungrab() only after GTK 3.24 */
Giovanni Panozzo's avatar
Giovanni Panozzo committed
452
		gdk_seat_ungrab(seat);
453
#else
454
		G_GNUC_BEGIN_IGNORE_DEPRECATIONS
455
		gdk_device_ungrab(keyboard, GDK_CURRENT_TIME);
456
		G_GNUC_END_IGNORE_DEPRECATIONS
457
#endif
Giovanni Panozzo's avatar
Giovanni Panozzo committed
458
		cnnwin->priv->kbcaptured = FALSE;
459 460 461
	}
}

Giovanni Panozzo's avatar
Giovanni Panozzo committed
462 463


Giovanni Panozzo's avatar
Giovanni Panozzo committed
464
static gboolean rcw_keyboard_grab_retry(gpointer user_data)
465 466
{
	TRACE_CALL(__func__);
Antenore Gatta's avatar
Antenore Gatta committed
467
	RemminaConnectionWindow *cnnwin = (RemminaConnectionWindow *)user_data;
468

Giovanni Panozzo's avatar
Giovanni Panozzo committed
469 470
	rcw_keyboard_grab(cnnwin);
	cnnwin->priv->grab_retry_eventsourceid = 0;
471 472 473
	return G_SOURCE_REMOVE;
}

Antenore Gatta's avatar
Antenore Gatta committed
474
static void rcw_keyboard_grab(RemminaConnectionWindow *cnnwin)
475
{
476
	TRACE_CALL(__func__);
477
	GdkDisplay *display;
Giovanni Panozzo's avatar
Giovanni Panozzo committed
478 479 480
#if GTK_CHECK_VERSION(3, 20, 0)
	GdkSeat *seat;
#else
481
	GdkDeviceManager *manager;
Giovanni Panozzo's avatar
Giovanni Panozzo committed
482
#endif
483
	GdkGrabStatus ggs;
484 485
	GdkDevice *keyboard = NULL;

Antenore Gatta's avatar
Antenore Gatta committed
486
	if (cnnwin->priv->kbcaptured || !cnnwin->priv->mouse_pointer_entered)
487 488
		return;

Giovanni Panozzo's avatar
Giovanni Panozzo committed
489
	display = gtk_widget_get_display(GTK_WIDGET(cnnwin));
Giovanni Panozzo's avatar
Giovanni Panozzo committed
490 491 492 493
#if GTK_CHECK_VERSION(3, 20, 0)
	seat = gdk_display_get_default_seat(display);
	keyboard = gdk_seat_get_pointer(seat);
#else
494 495
	manager = gdk_display_get_device_manager(display);
	keyboard = gdk_device_manager_get_client_pointer(manager);
Giovanni Panozzo's avatar
Giovanni Panozzo committed
496
#endif
497

Antenore Gatta's avatar
Antenore Gatta committed
498
	if (keyboard != NULL) {
Antenore Gatta's avatar
Antenore Gatta committed
499 500
		if (gdk_device_get_source(keyboard) != GDK_SOURCE_KEYBOARD)
			keyboard = gdk_device_get_associated_device(keyboard);
501

Giovanni Panozzo's avatar
Giovanni Panozzo committed
502

503
#if DEBUG_KB_GRABBING
Giovanni Panozzo's avatar
Giovanni Panozzo committed
504
		printf("DEBUG_KB_GRABBING: profile asks for grabbing, let’s try.\n");
505
#endif
Antenore Gatta's avatar
Antenore Gatta committed
506 507 508 509 510 511 512 513
		/* Up to GTK version 3.20 we can grab the keyboard with gdk_device_grab().
		 * in GTK 3.20 gdk_seat_grab() should be used instead of gdk_device_grab().
		 * There is a bug in GTK up to 3.22: when gdk_device_grab() fails
		 * the widget is hidden:
		 * https://gitlab.gnome.org/GNOME/gtk/commit/726ad5a5ae7c4f167e8dd454cd7c250821c400ab
		 * The bug fix will be released with GTK 3.24.
		 * Also pease note that the newer gdk_seat_grab() is still calling gdk_device_grab().
		 */
514
#if GTK_CHECK_VERSION(3, 24, 0)
Giovanni Panozzo's avatar
Giovanni Panozzo committed
515
		ggs = gdk_seat_grab(seat, gtk_widget_get_window(GTK_WIDGET(cnnwin)),
Antenore Gatta's avatar
Antenore Gatta committed
516
				    GDK_SEAT_CAPABILITY_KEYBOARD, FALSE, NULL, NULL, NULL, NULL);
517
#else
Giovanni Panozzo's avatar
Giovanni Panozzo committed
518
		G_GNUC_BEGIN_IGNORE_DEPRECATIONS
Antenore Gatta's avatar
Antenore Gatta committed
519 520
			ggs = gdk_device_grab(keyboard, gtk_widget_get_window(GTK_WIDGET(cnnwin)), GDK_OWNERSHIP_WINDOW,
					      TRUE, GDK_KEY_PRESS | GDK_KEY_RELEASE, NULL, GDK_CURRENT_TIME);
Giovanni Panozzo's avatar
Giovanni Panozzo committed
521
		G_GNUC_END_IGNORE_DEPRECATIONS
522
#endif
Antenore Gatta's avatar
Antenore Gatta committed
523
		if (ggs != GDK_GRAB_SUCCESS) {
Giovanni Panozzo's avatar
Giovanni Panozzo committed
524
			/* Failure to GRAB keyboard */
Antenore Gatta's avatar
Antenore Gatta committed
525 526 527
#if DEBUG_KB_GRABBING
			printf("GRAB FAILED. GdkGrabStatus: %d\n", (int)ggs);
#endif
Giovanni Panozzo's avatar
Giovanni Panozzo committed
528
			/* Reschedule grabbing in half a second if not already done */
Antenore Gatta's avatar
Antenore Gatta committed
529
			if (cnnwin->priv->grab_retry_eventsourceid == 0)
Giovanni Panozzo's avatar
Giovanni Panozzo committed
530 531
				cnnwin->priv->grab_retry_eventsourceid = g_timeout_add(500, (GSourceFunc)rcw_keyboard_grab_retry, cnnwin);
		} else {
Antenore Gatta's avatar
Antenore Gatta committed
532
#if DEBUG_KB_GRABBING
Giovanni Panozzo's avatar
Giovanni Panozzo committed
533
			printf("GRAB SUCCESS\n");
Antenore Gatta's avatar
Antenore Gatta committed
534
#endif
Giovanni Panozzo's avatar
Giovanni Panozzo committed
535 536 537 538 539
			if (cnnwin->priv->grab_retry_eventsourceid != 0) {
				g_source_remove(cnnwin->priv->grab_retry_eventsourceid);
				cnnwin->priv->grab_retry_eventsourceid = 0;
			}
			cnnwin->priv->kbcaptured = TRUE;
540
		}
Antenore Gatta's avatar
Antenore Gatta committed
541
	} else {
Giovanni Panozzo's avatar
Giovanni Panozzo committed
542
		rcw_keyboard_ungrab(cnnwin);
543
	}
544 545
}

Antenore Gatta's avatar
Antenore Gatta committed
546
static void rcw_close_all_connections(RemminaConnectionWindow *cnnwin)
Giovanni Panozzo's avatar
Giovanni Panozzo committed
547
{
Antenore Gatta's avatar
Antenore Gatta committed
548 549 550 551
	RemminaConnectionWindowPriv *priv = cnnwin->priv;
	GtkNotebook *notebook = GTK_NOTEBOOK(priv->notebook);
	GtkWidget *w;
	RemminaConnectionObject *cnnobj;
Giovanni Panozzo's avatar
Giovanni Panozzo committed
552 553
	gint i, n;

Antenore Gatta's avatar
Antenore Gatta committed
554
	if (GTK_IS_WIDGET(notebook)) {
Giovanni Panozzo's avatar
Giovanni Panozzo committed
555
		n = gtk_notebook_get_n_pages(notebook);
Antenore Gatta's avatar
Antenore Gatta committed
556
		for (i = n - 1; i >= 0; i--) {
Giovanni Panozzo's avatar
Giovanni Panozzo committed
557
			w = gtk_notebook_get_nth_page(notebook, i);
Antenore Gatta's avatar
Antenore Gatta committed
558
			cnnobj = (RemminaConnectionObject *)g_object_get_data(G_OBJECT(w), "cnnobj");
Giovanni Panozzo's avatar
Giovanni Panozzo committed
559 560 561 562 563 564
			/* Do close the connection on this tab */
			remmina_protocol_widget_close_connection(REMMINA_PROTOCOL_WIDGET(cnnobj->proto));
		}
	}
}

Antenore Gatta's avatar
Antenore Gatta committed
565
gboolean rcw_delete(RemminaConnectionWindow *cnnwin)
566
{
567
	TRACE_CALL(__func__);
Antenore Gatta's avatar
Antenore Gatta committed
568 569 570
	RemminaConnectionWindowPriv *priv = cnnwin->priv;
	GtkNotebook *notebook = GTK_NOTEBOOK(priv->notebook);
	GtkWidget *dialog;
571 572
	gint i, n;

Giovanni Panozzo's avatar
Giovanni Panozzo committed
573
	if (!REMMINA_IS_CONNECTION_WINDOW(cnnwin))
574
		return TRUE;
Giovanni Panozzo's avatar
Giovanni Panozzo committed
575

576
	if (cnnwin->priv->on_delete_confirm_mode != RCW_ONDELETE_NOCONFIRM) {
577
		n = gtk_notebook_get_n_pages(notebook);
Antenore Gatta's avatar
Antenore Gatta committed
578
		if (n > 1) {
579
			dialog = gtk_message_dialog_new(GTK_WINDOW(cnnwin), GTK_DIALOG_MODAL, GTK_MESSAGE_QUESTION,
Antenore Gatta's avatar
Antenore Gatta committed
580 581
							GTK_BUTTONS_YES_NO,
							_("There are %i active connections in the current window. Are you sure to close?"), n);
582 583 584 585 586
			i = gtk_dialog_run(GTK_DIALOG(dialog));
			gtk_widget_destroy(dialog);
			if (i != GTK_RESPONSE_YES)
				return FALSE;
		}
587
	}
588
	rcw_close_all_connections(cnnwin);
Giovanni Panozzo's avatar
Giovanni Panozzo committed
589

590
	return TRUE;
Giovanni Panozzo's avatar
Giovanni Panozzo committed
591 592
}

Antenore Gatta's avatar
Antenore Gatta committed
593
static gboolean rcw_delete_event(GtkWidget *widget, GdkEvent *event, gpointer data)
Giovanni Panozzo's avatar
Giovanni Panozzo committed
594
{
595
	TRACE_CALL(__func__);
596
	rcw_delete(RCW(widget));
597 598 599
	return TRUE;
}

Antenore Gatta's avatar
Antenore Gatta committed
600
static void rcw_destroy(GtkWidget *widget, gpointer data)
601
{
602
	TRACE_CALL(__func__);
Antenore Gatta's avatar
Antenore Gatta committed
603 604
	RemminaConnectionWindowPriv *priv;
	RemminaConnectionWindow *cnnwin;
Giovanni Panozzo's avatar
Giovanni Panozzo committed
605 606 607 608

	if (!REMMINA_IS_CONNECTION_WINDOW(widget))
		return;

Antenore Gatta's avatar
Antenore Gatta committed
609
	cnnwin = (RemminaConnectionWindow *)widget;
Giovanni Panozzo's avatar
Giovanni Panozzo committed
610
	priv = cnnwin->priv;
611

612
	if (priv->kbcaptured)
Giovanni Panozzo's avatar
Giovanni Panozzo committed
613
		rcw_keyboard_ungrab(cnnwin);
614

Antenore Gatta's avatar
Antenore Gatta committed
615
	if (priv->floating_toolbar_motion_handler) {
616 617 618
		g_source_remove(priv->floating_toolbar_motion_handler);
		priv->floating_toolbar_motion_handler = 0;
	}
Antenore Gatta's avatar
Antenore Gatta committed
619
	if (priv->ftb_hide_eventsource) {
Giovanni Panozzo's avatar
Giovanni Panozzo committed
620 621
		g_source_remove(priv->ftb_hide_eventsource);
		priv->ftb_hide_eventsource = 0;
622
	}
Antenore Gatta's avatar
Antenore Gatta committed
623
	if (priv->savestate_eventsourceid) {
624 625 626
		g_source_remove(priv->savestate_eventsourceid);
		priv->savestate_eventsourceid = 0;
	}
Giovanni Panozzo's avatar
Giovanni Panozzo committed
627 628

	/* There is no need to destroy priv->floating_toolbar_widget,
629
	 * because it’s our child and will be destroyed automatically */
630

631
	/* Timer used to hide the toolbar */
Antenore Gatta's avatar
Antenore Gatta committed
632
	if (priv->hidetb_timer) {
633
		g_source_remove(priv->hidetb_timer);
Antenore Gatta's avatar
Antenore Gatta committed
634
		priv->hidetb_timer = 0;
635
	}
Antenore Gatta's avatar
Antenore Gatta committed
636
	if (priv->switch_page_handler) {
637 638 639
		g_source_remove(priv->switch_page_handler);
		priv->switch_page_handler = 0;
	}
640

Giovanni Panozzo's avatar
Giovanni Panozzo committed
641 642
	cnnwin->priv = NULL;
	g_free(priv);
643 644
}

645
gboolean rcw_notify_widget_toolbar_placement(GtkWidget *widget, gpointer data)
646
{
647
	TRACE_CALL(__func__);
648
	GType rcwtype;
649
	rcwtype = rcw_get_type();
650 651 652 653 654 655 656
	if (G_TYPE_CHECK_INSTANCE_TYPE(widget, rcwtype)) {
		g_signal_emit_by_name(G_OBJECT(widget), "toolbar-place");
		return TRUE;
	}
	return FALSE;
}

657
static gboolean rcw_tb_drag_failed(GtkWidget *widget, GdkDragContext *context,
Antenore Gatta's avatar
Antenore Gatta committed
658
				   GtkDragResult result, gpointer user_data)
659
{
660
	TRACE_CALL(__func__);
Antenore Gatta's avatar
Antenore Gatta committed
661
	RemminaConnectionWindowPriv *priv;
Giovanni Panozzo's avatar
Giovanni Panozzo committed
662
	RemminaConnectionWindow *cnnwin;
Giovanni Panozzo's avatar
Giovanni Panozzo committed
663

664

Antenore Gatta's avatar
Antenore Gatta committed
665
	cnnwin = (RemminaConnectionWindow *)user_data;
Giovanni Panozzo's avatar
Giovanni Panozzo committed
666
	priv = cnnwin->priv;
667 668 669 670 671 672 673

	if (priv->toolbar)
		gtk_widget_show(GTK_WIDGET(priv->toolbar));

	return TRUE;
}

674
static gboolean rcw_tb_drag_drop(GtkWidget *widget, GdkDragContext *context,
Antenore Gatta's avatar
Antenore Gatta committed
675
				 gint x, gint y, guint time, gpointer user_data)
676
{
677
	TRACE_CALL(__func__);
678 679
	GtkAllocation wa;
	gint new_toolbar_placement;
Antenore Gatta's avatar
Antenore Gatta committed
680 681
	RemminaConnectionWindowPriv *priv;
	RemminaConnectionWindow *cnnwin;
682

Antenore Gatta's avatar
Antenore Gatta committed
683
	cnnwin = (RemminaConnectionWindow *)user_data;
Giovanni Panozzo's avatar
Giovanni Panozzo committed
684
	priv = cnnwin->priv;
685 686 687 688

	gtk_widget_get_allocation(widget, &wa);

	if (wa.width * y >= wa.height * x) {
Antenore Gatta's avatar
Antenore Gatta committed
689
		if (wa.width * y > wa.height * (wa.width - x))
690 691 692
			new_toolbar_placement = TOOLBAR_PLACEMENT_BOTTOM;
		else
			new_toolbar_placement = TOOLBAR_PLACEMENT_LEFT;
Antenore Gatta's avatar
Antenore Gatta committed
693 694
	} else {
		if (wa.width * y > wa.height * (wa.width - x))
695 696 697 698 699 700 701
			new_toolbar_placement = TOOLBAR_PLACEMENT_RIGHT;
		else
			new_toolbar_placement = TOOLBAR_PLACEMENT_TOP;
	}

	gtk_drag_finish(context, TRUE, TRUE, time);

Antenore Gatta's avatar
Antenore Gatta committed
702
	if (new_toolbar_placement != remmina_pref.toolbar_placement) {
703 704 705 706 707
		/* Save new position */
		remmina_pref.toolbar_placement = new_toolbar_placement;
		remmina_pref_save();

		/* Signal all windows that the toolbar must be moved */
708
		remmina_widget_pool_foreach(rcw_notify_widget_toolbar_placement, NULL);
709 710 711 712 713 714 715
	}
	if (priv->toolbar)
		gtk_widget_show(GTK_WIDGET(priv->toolbar));

	return TRUE;
}

716
static void rcw_tb_drag_begin(GtkWidget *widget, GdkDragContext *context, gpointer user_data)
717
{
718
	TRACE_CALL(__func__);
719 720 721 722 723 724 725 726 727 728 729 730

	cairo_surface_t *surface;
	cairo_t *cr;
	GtkAllocation wa;
	double dashes[] = { 10 };

	gtk_widget_get_allocation(widget, &wa);

	surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 16, 16);
	cr = cairo_create(surface);
	cairo_set_source_rgb(cr, 0.6, 0.6, 0.6);
	cairo_set_line_width(cr, 4);
Antenore Gatta's avatar
Antenore Gatta committed
731
	cairo_set_dash(cr, dashes, 1, 0);
732 733 734 735 736 737 738 739 740
	cairo_rectangle(cr, 0, 0, 16, 16);
	cairo_stroke(cr);
	cairo_destroy(cr);

	gtk_widget_hide(widget);

	gtk_drag_set_icon_surface(context, surface);
}

Antenore Gatta's avatar
Antenore Gatta committed
741
void rcw_update_toolbar_opacity(RemminaConnectionWindow *cnnwin)
742
{
743
	TRACE_CALL(__func__);
Antenore Gatta's avatar
Antenore Gatta committed
744 745
	RemminaConnectionWindowPriv *priv = cnnwin->priv;
	RemminaConnectionObject *cnnobj;
Giovanni Panozzo's avatar
Giovanni Panozzo committed
746 747 748

	cnnobj = rcw_get_visible_cnnobj(cnnwin);
	if (!cnnobj) return;
749

Antenore Gatta's avatar
Antenore Gatta committed
750 751 752
	priv->floating_toolbar_opacity = (1.0 - TOOLBAR_OPACITY_MIN) / ((gdouble)TOOLBAR_OPACITY_LEVEL)
					 * ((gdouble)(TOOLBAR_OPACITY_LEVEL - remmina_file_get_int(cnnobj->remmina_file, "toolbar_opacity", 0)))
					 + TOOLBAR_OPACITY_MIN;
Antenore Gatta's avatar
Antenore Gatta committed
753
	if (priv->floating_toolbar_widget)
Giovanni Panozzo's avatar
Giovanni Panozzo committed
754
		gtk_widget_set_opacity(GTK_WIDGET(priv->overlay_ftb_overlay), priv->floating_toolbar_opacity);
755
}
Giovanni Panozzo's avatar
Giovanni Panozzo committed
756

Giovanni Panozzo's avatar
Giovanni Panozzo committed
757
static gboolean rcw_floating_toolbar_make_invisible(gpointer data)
Giovanni Panozzo's avatar
Giovanni Panozzo committed
758
{
759
	TRACE_CALL(__func__);
Antenore Gatta's avatar
Antenore Gatta committed
760
	RemminaConnectionWindowPriv *priv = (RemminaConnectionWindowPriv *)data;
Giovanni Panozzo's avatar
Giovanni Panozzo committed
761 762 763 764
	gtk_widget_set_opacity(GTK_WIDGET(priv->overlay_ftb_overlay), 0.0);
	priv->ftb_hide_eventsource = 0;
	return FALSE;
}
765

Antenore Gatta's avatar
Antenore Gatta committed
766
static void rcw_floating_toolbar_show(RemminaConnectionWindow *cnnwin, gboolean show)
767
{
768
	TRACE_CALL(__func__);
Antenore Gatta's avatar
Antenore Gatta committed
769
	RemminaConnectionWindowPriv *priv = cnnwin->priv;
770

Giovanni Panozzo's avatar
Giovanni Panozzo committed
771 772 773
	if (priv->floating_toolbar_widget == NULL)
		return;

Antenore Gatta's avatar
Antenore Gatta committed
774
	if (show || priv->pin_down) {
Giovanni Panozzo's avatar
Giovanni Panozzo committed
775
		/* Make the FTB no longer transparent, in case we have an hidden toolbar */
Giovanni Panozzo's avatar
Giovanni Panozzo committed
776
		rcw_update_toolbar_opacity(cnnwin);
Giovanni Panozzo's avatar
Giovanni Panozzo committed
777
		/* Remove outstanding hide events, if not yet active */
Antenore Gatta's avatar
Antenore Gatta committed
778
		if (priv->ftb_hide_eventsource) {
Giovanni Panozzo's avatar
Giovanni Panozzo committed
779 780 781
			g_source_remove(priv->ftb_hide_eventsource);
			priv->ftb_hide_eventsource = 0;
		}
Antenore Gatta's avatar
Antenore Gatta committed
782
	} else {
Giovanni Panozzo's avatar
Giovanni Panozzo committed
783 784
		/* If we are hiding and the toolbar must be made invisible, schedule
		 * a later toolbar hide */
Antenore Gatta's avatar
Antenore Gatta committed
785
		if (remmina_pref.fullscreen_toolbar_visibility == FLOATING_TOOLBAR_VISIBILITY_INVISIBLE)
Giovanni Panozzo's avatar
Giovanni Panozzo committed
786
			if (priv->ftb_hide_eventsource == 0)
Giovanni Panozzo's avatar
Giovanni Panozzo committed
787
				priv->ftb_hide_eventsource = g_timeout_add(1000, rcw_floating_toolbar_make_invisible, priv);
Giovanni Panozzo's avatar
Giovanni Panozzo committed
788 789 790
	}

	gtk_revealer_set_reveal_child(GTK_REVEALER(priv->revealer), show || priv->pin_down);
791 792
}

Antenore Gatta's avatar
Antenore Gatta committed
793
static void rco_get_desktop_size(RemminaConnectionObject *cnnobj, gint *width, gint *height)
794
{
795
	TRACE_CALL(__func__);
Antenore Gatta's avatar
Antenore Gatta committed
796
	RemminaProtocolWidget *gp = REMMINA_PROTOCOL_WIDGET(cnnobj->proto);
797

Giovanni Panozzo's avatar
Giovanni Panozzo committed
798

799 800
	*width = remmina_protocol_widget_get_width(gp);
	*height = remmina_protocol_widget_get_height(gp);
801 802 803 804 805 806
	if (*width == 0) {
		/* Before connecting we do not have real remote width/height,
		 * so we ask profile values */
		*width = remmina_protocol_widget_get_profile_remote_width(gp);
		*height = remmina_protocol_widget_get_profile_remote_height(gp);
	}
807 808
}

Antenore Gatta's avatar
Antenore Gatta committed
809
void rco_set_scrolled_policy(RemminaConnectionObject *cnnobj, GtkScrolledWindow *scrolled_window)
810
{
811
	TRACE_CALL(__func__);
812 813 814
	RemminaScaleMode scalemode;
	scalemode = get_current_allowed_scale_mode(cnnobj, NULL, NULL);
	gtk_scrolled_window_set_policy(scrolled_window,
Antenore Gatta's avatar
Antenore Gatta committed
815 816
				       scalemode == REMMINA_PROTOCOL_WIDGET_SCALE_MODE_SCALED ? GTK_POLICY_NEVER : GTK_POLICY_AUTOMATIC,
				       scalemode == REMMINA_PROTOCOL_WIDGET_SCALE_MODE_SCALED ? GTK_POLICY_NEVER : GTK_POLICY_AUTOMATIC);
817
}
llyzs's avatar
llyzs committed
818

Antenore Gatta's avatar
Antenore Gatta committed
819
static GtkWidget *rco_create_scrolled_container(RemminaConnectionObject *cnnobj, int view_mode)
Giovanni Panozzo's avatar
Giovanni Panozzo committed
820 821 822 823 824
{
	GtkWidget *scrolled_container;

	if (view_mode == VIEWPORT_FULLSCREEN_MODE) {
		scrolled_container = remmina_scrolled_viewport_new();
Antenore Gatta's avatar
Antenore Gatta committed
825
	} else {
Giovanni Panozzo's avatar
Giovanni Panozzo committed
826 827 828 829 830 831 832 833 834 835 836 837
		scrolled_container = gtk_scrolled_window_new(NULL, NULL);
		rco_set_scrolled_policy(cnnobj, GTK_SCROLLED_WINDOW(scrolled_container));
		gtk_container_set_border_width(GTK_CONTAINER(scrolled_container), 0);
		gtk_widget_set_can_focus(scrolled_container, FALSE);
	}

	gtk_widget_set_name(scrolled_container, "remmina-scrolled-container");
	gtk_widget_show(scrolled_container);

	return scrolled_container;
}

Antenore Gatta's avatar
Antenore Gatta committed
838
gboolean rcw_toolbar_autofit_restore(RemminaConnectionWindow *cnnwin)
llyzs's avatar
llyzs committed
839
{
840
	TRACE_CALL(__func__);
Giovanni Panozzo's avatar
Giovanni Panozzo committed
841

Antenore Gatta's avatar
Antenore Gatta committed
842 843
	RemminaConnectionWindowPriv *priv = cnnwin->priv;
	RemminaConnectionObject *cnnobj;
844 845
	gint dwidth, dheight;
	GtkAllocation nba, ca, ta;
846

847 848 849
	if (priv->toolbar_is_reconfiguring)
		return FALSE;

Giovanni Panozzo's avatar
Giovanni Panozzo committed
850 851
	if (!(cnnobj = rcw_get_visible_cnnobj(cnnwin))) return FALSE;

Antenore Gatta's avatar
Antenore Gatta committed
852
	if (cnnobj->connected && GTK_IS_SCROLLED_WINDOW(cnnobj->scrolled_container)) {
Giovanni Panozzo's avatar
Giovanni Panozzo committed
853
		rco_get_desktop_size(cnnobj, &dwidth, &dheight);
Giovanni Panozzo's avatar
Giovanni Panozzo committed
854
		gtk_widget_get_allocation(GTK_WIDGET(priv->notebook), &nba);
855
		gtk_widget_get_allocation(cnnobj->scrolled_container, &ca);
856
		gtk_widget_get_allocation(priv->toolbar, &ta);
857
		if (remmina_pref.toolbar_placement == TOOLBAR_PLACEMENT_LEFT ||
Antenore Gatta's avatar
Antenore Gatta committed
858
		    remmina_pref.toolbar_placement == TOOLBAR_PLACEMENT_RIGHT)
Giovanni Panozzo's avatar
Giovanni Panozzo committed
859
			gtk_window_resize(GTK_WINDOW(cnnobj->cnnwin), MAX(1, dwidth + ta.width + nba.width - ca.width),
Antenore Gatta's avatar
Antenore Gatta committed
860 861
					  MAX(1, dheight + nba.height - ca.height));
		else
Giovanni Panozzo's avatar
Giovanni Panozzo committed
862
			gtk_window_resize(GTK_WINDOW(cnnobj->cnnwin), MAX(1, dwidth + nba.width - ca.width),
Antenore Gatta's avatar
Antenore Gatta committed
863
					  MAX(1, dheight + ta.height + nba.height - ca.height));
Giovanni Panozzo's avatar
Giovanni Panozzo committed
864
		gtk_container_check_resize(GTK_CONTAINER(cnnobj->cnnwin));
865
	}
Antenore Gatta's avatar
Antenore Gatta committed
866
	if (GTK_IS_SCROLLED_WINDOW(cnnobj->scrolled_container))
867
		rco_set_scrolled_policy(cnnobj, GTK_SCROLLED_WINDOW(cnnobj->scrolled_container));
868
	return FALSE;
869 870
}

Antenore Gatta's avatar
Antenore Gatta committed
871
static void rcw_toolbar_autofit(GtkWidget *widget, RemminaConnectionWindow *cnnwin)
llyzs's avatar
llyzs committed
872
{
873
	TRACE_CALL(__func__);
Giovanni Panozzo's avatar
Giovanni Panozzo committed
874 875 876 877 878
	RemminaConnectionObject *cnnobj;

	if (cnnwin->priv->toolbar_is_reconfiguring)
		return;
	if (!(cnnobj = rcw_get_visible_cnnobj(cnnwin))) return;
llyzs's avatar
llyzs committed
879

Antenore Gatta's avatar