From 6b73522e2510d954a77577ae58ec8390dbd1f616 Mon Sep 17 00:00:00 2001 From: Antenore Gatta Date: Mon, 28 Aug 2023 15:18:31 +0000 Subject: Automatic doc build by remmina-ci --- public/rcw_8c.html | 296 ++++++++++---------- public/rcw_8c_source.html | 320 +++++++++++----------- public/rcw_8h.html | 20 +- public/rcw_8h_source.html | 24 +- public/remmina__chat__window_8c_source.html | 2 +- public/remmina__exec_8c_source.html | 8 +- public/remmina__file__editor_8c_source.html | 2 +- public/remmina__main_8c_source.html | 4 +- public/remmina__masterthread__exec_8h_source.html | 2 +- public/remmina__message__panel_8c_source.html | 2 +- public/remmina__plugin__manager_8c_source.html | 2 +- public/remmina__protocol__widget_8c_source.html | 16 +- public/remmina__protocol__widget_8h_source.html | 2 +- public/struct___remmina_connection_object.html | 22 +- 14 files changed, 361 insertions(+), 361 deletions(-) diff --git a/public/rcw_8c.html b/public/rcw_8c.html index 36c16a972..d9a62acd7 100644 --- a/public/rcw_8c.html +++ b/public/rcw_8c.html @@ -137,6 +137,8 @@ Functions   static void rcw_place_toolbar (GtkToolbar *toolbar, GtkGrid *grid, GtkWidget *sibling, int toolbar_placement)   +static void rco_update_toolbar (RemminaConnectionObject *cnnobj) +  static void rcw_keyboard_grab (RemminaConnectionWindow *cnnwin)   static GtkWidget * rcw_append_new_page (RemminaConnectionWindow *cnnwin, RemminaConnectionObject *cnnobj) @@ -279,8 +281,6 @@ Functions   static void rcw_toolbar_grab (GtkToolItem *toggle, RemminaConnectionWindow *cnnwin)   -static void rco_update_toolbar (RemminaConnectionObject *cnnobj) -  static void rcw_set_toolbar_visibility (RemminaConnectionWindow *cnnwin)   static void print_crossing_event (GdkEventCrossing *event) @@ -452,7 +452,7 @@ Variables LAST_SIGNAL  -

Definition at line 180 of file rcw.c.

+

Definition at line 181 of file rcw.c.

@@ -474,7 +474,7 @@ Variables GTKSOCKET_NOT_AVAIL_RESPONSE_NUM  -

Definition at line 4472 of file rcw.c.

+

Definition at line 4485 of file rcw.c.

@@ -513,7 +513,7 @@ Variables
-

Definition at line 4314 of file rcw.c.

+

Definition at line 4327 of file rcw.c.

@@ -541,7 +541,7 @@ Variables
-

Definition at line 2925 of file rcw.c.

+

Definition at line 2933 of file rcw.c.

@@ -621,7 +621,7 @@ Variables
-

Definition at line 391 of file rcw.c.

+

Definition at line 393 of file rcw.c.

@@ -659,7 +659,7 @@ Variables
-

Definition at line 3459 of file rcw.c.

+

Definition at line 3467 of file rcw.c.

@@ -697,7 +697,7 @@ Variables
-

Definition at line 1234 of file rcw.c.

+

Definition at line 1236 of file rcw.c.

@@ -735,7 +735,7 @@ Variables
-

Definition at line 1221 of file rcw.c.

+

Definition at line 1223 of file rcw.c.

@@ -763,7 +763,7 @@ Variables
-

Definition at line 4428 of file rcw.c.

+

Definition at line 4441 of file rcw.c.

@@ -791,7 +791,7 @@ Variables
-

Definition at line 2769 of file rcw.c.

+

Definition at line 2777 of file rcw.c.

@@ -829,7 +829,7 @@ Variables
-

Definition at line 1828 of file rcw.c.

+

Definition at line 1830 of file rcw.c.

@@ -867,7 +867,7 @@ Variables
-

Definition at line 1816 of file rcw.c.

+

Definition at line 1818 of file rcw.c.

@@ -905,7 +905,7 @@ Variables
-

Definition at line 1801 of file rcw.c.

+

Definition at line 1803 of file rcw.c.

@@ -949,7 +949,7 @@ Variables
-

Definition at line 1651 of file rcw.c.

+

Definition at line 1653 of file rcw.c.

@@ -977,7 +977,7 @@ Variables
-

Definition at line 1060 of file rcw.c.

+

Definition at line 1062 of file rcw.c.

@@ -997,7 +997,7 @@ Variables
-

Definition at line 3480 of file rcw.c.

+

Definition at line 3488 of file rcw.c.

@@ -1035,7 +1035,7 @@ Variables
-

Definition at line 931 of file rcw.c.

+

Definition at line 933 of file rcw.c.

@@ -1063,7 +1063,7 @@ Variables
-

Definition at line 3526 of file rcw.c.

+

Definition at line 3534 of file rcw.c.

@@ -1091,7 +1091,7 @@ Variables
-

Definition at line 3569 of file rcw.c.

+

Definition at line 3577 of file rcw.c.

@@ -1123,7 +1123,7 @@ Variables

Deletes a RemminaMessagePanel from the current cnnobj and if it was visible, make visible the last remaining one.

-

Definition at line 4722 of file rcw.c.

+

Definition at line 4735 of file rcw.c.

@@ -1151,7 +1151,7 @@ Variables
-

Definition at line 421 of file rcw.c.

+

Definition at line 423 of file rcw.c.

@@ -1187,7 +1187,7 @@ Variables
-

Definition at line 2891 of file rcw.c.

+

Definition at line 2899 of file rcw.c.

@@ -1231,7 +1231,7 @@ Variables
-

Definition at line 906 of file rcw.c.

+

Definition at line 908 of file rcw.c.

@@ -1261,7 +1261,7 @@ Variables
-

Definition at line 1009 of file rcw.c.

+

Definition at line 1011 of file rcw.c.

@@ -1305,7 +1305,7 @@ Variables
-

Definition at line 2866 of file rcw.c.

+

Definition at line 2874 of file rcw.c.

@@ -1335,7 +1335,7 @@ Variables
-

Definition at line 3515 of file rcw.c.

+

Definition at line 3523 of file rcw.c.

@@ -1366,7 +1366,7 @@ Variables

Remember recent list for quick connect, and save the current date in the last_used field.

-

Definition at line 4275 of file rcw.c.

+

Definition at line 4288 of file rcw.c.

@@ -1396,7 +1396,7 @@ Variables
-

Definition at line 4370 of file rcw.c.

+

Definition at line 4383 of file rcw.c.

@@ -1426,7 +1426,7 @@ Variables
-

Definition at line 4320 of file rcw.c.

+

Definition at line 4333 of file rcw.c.

@@ -1456,7 +1456,7 @@ Variables
-

Definition at line 4387 of file rcw.c.

+

Definition at line 4400 of file rcw.c.

@@ -1486,7 +1486,7 @@ Variables
-

Definition at line 4396 of file rcw.c.

+

Definition at line 4409 of file rcw.c.

@@ -1516,7 +1516,7 @@ Variables
-

Definition at line 4379 of file rcw.c.

+

Definition at line 4392 of file rcw.c.

@@ -1554,7 +1554,7 @@ Variables
-

Definition at line 1399 of file rcw.c.

+

Definition at line 1401 of file rcw.c.

@@ -1584,7 +1584,7 @@ Variables
-

Definition at line 922 of file rcw.c.

+

Definition at line 924 of file rcw.c.

@@ -1617,7 +1617,7 @@ Variables

Each cnnobj->page can have more than one RemminaMessagePanel, but 0 or 1 are visible.

This function adds a RemminaMessagePanel to cnnobj->page, move it to top, and makes it the only visible one.

-

Definition at line 4769 of file rcw.c.

+

Definition at line 4782 of file rcw.c.

@@ -1647,7 +1647,7 @@ Variables
-

Definition at line 1561 of file rcw.c.

+

Definition at line 1563 of file rcw.c.

@@ -1675,7 +1675,7 @@ Variables
-

Definition at line 2635 of file rcw.c.

+

Definition at line 2639 of file rcw.c.

@@ -1695,7 +1695,7 @@ Variables
-

Definition at line 1633 of file rcw.c.

+

Definition at line 1635 of file rcw.c.

@@ -1733,7 +1733,7 @@ Variables
-

Definition at line 1387 of file rcw.c.

+

Definition at line 1389 of file rcw.c.

@@ -1761,7 +1761,7 @@ Variables
-

Definition at line 3056 of file rcw.c.

+

Definition at line 3064 of file rcw.c.

@@ -1799,7 +1799,7 @@ Variables
-

Definition at line 3580 of file rcw.c.

+

Definition at line 3588 of file rcw.c.

@@ -1827,7 +1827,7 @@ Variables
-

Definition at line 221 of file rcw.c.

+

Definition at line 223 of file rcw.c.

@@ -1855,7 +1855,7 @@ Variables
-

Definition at line 629 of file rcw.c.

+

Definition at line 631 of file rcw.c.

@@ -1893,7 +1893,7 @@ Variables
-

Definition at line 3144 of file rcw.c.

+

Definition at line 3152 of file rcw.c.

@@ -1931,7 +1931,7 @@ Variables
-

Definition at line 3991 of file rcw.c.

+

Definition at line 4004 of file rcw.c.

@@ -1959,7 +1959,7 @@ Variables
-

Definition at line 3753 of file rcw.c.

+

Definition at line 3761 of file rcw.c.

@@ -1987,7 +1987,7 @@ Variables
-

Definition at line 3845 of file rcw.c.

+

Definition at line 3853 of file rcw.c.

@@ -2031,7 +2031,7 @@ Variables
-

Definition at line 3773 of file rcw.c.

+

Definition at line 3781 of file rcw.c.

@@ -2069,7 +2069,7 @@ Variables
-

Definition at line 2329 of file rcw.c.

+

Definition at line 2333 of file rcw.c.

@@ -2089,7 +2089,7 @@ Variables
-

Definition at line 4263 of file rcw.c.

+

Definition at line 4276 of file rcw.c.

@@ -2109,7 +2109,7 @@ Variables
-

Definition at line 648 of file rcw.c.

+

Definition at line 650 of file rcw.c.

@@ -2153,7 +2153,7 @@ Variables
-

Definition at line 694 of file rcw.c.

+

Definition at line 696 of file rcw.c.

@@ -2191,7 +2191,7 @@ Variables
-

Definition at line 701 of file rcw.c.

+

Definition at line 703 of file rcw.c.

@@ -2219,7 +2219,7 @@ Variables
-

Definition at line 4241 of file rcw.c.

+

Definition at line 4254 of file rcw.c.

@@ -2247,7 +2247,7 @@ Variables
-

Definition at line 2998 of file rcw.c.

+

Definition at line 3006 of file rcw.c.

@@ -2275,7 +2275,7 @@ Variables
-

Definition at line 869 of file rcw.c.

+

Definition at line 871 of file rcw.c.

@@ -2319,7 +2319,7 @@ Variables
-

Definition at line 2798 of file rcw.c.

+

Definition at line 2806 of file rcw.c.

@@ -2363,7 +2363,7 @@ Variables
-

Definition at line 2806 of file rcw.c.

+

Definition at line 2814 of file rcw.c.

@@ -2407,7 +2407,7 @@ Variables
-

Definition at line 3008 of file rcw.c.

+

Definition at line 3016 of file rcw.c.

@@ -2445,7 +2445,7 @@ Variables
-

Definition at line 879 of file rcw.c.

+

Definition at line 881 of file rcw.c.

@@ -2473,7 +2473,7 @@ Variables
-

Definition at line 2948 of file rcw.c.

+

Definition at line 2956 of file rcw.c.

@@ -2517,7 +2517,7 @@ Variables
-

Definition at line 3247 of file rcw.c.

+

Definition at line 3255 of file rcw.c.

@@ -2545,7 +2545,7 @@ Variables
-

Definition at line 2974 of file rcw.c.

+

Definition at line 2982 of file rcw.c.

@@ -2589,7 +2589,7 @@ Variables
-

Definition at line 3257 of file rcw.c.

+

Definition at line 3265 of file rcw.c.

@@ -2633,7 +2633,7 @@ Variables
-

Definition at line 3968 of file rcw.c.

+

Definition at line 3981 of file rcw.c.

@@ -2695,7 +2695,7 @@ Variables
-

Definition at line 3939 of file rcw.c.

+

Definition at line 3952 of file rcw.c.

@@ -2733,7 +2733,7 @@ Variables
-

Definition at line 1411 of file rcw.c.

+

Definition at line 1413 of file rcw.c.

@@ -2771,7 +2771,7 @@ Variables
-

Definition at line 367 of file rcw.c.

+

Definition at line 369 of file rcw.c.

@@ -2791,7 +2791,7 @@ Variables
-

Definition at line 4707 of file rcw.c.

+

Definition at line 4720 of file rcw.c.

@@ -2811,7 +2811,7 @@ Variables
-

Definition at line 4703 of file rcw.c.

+

Definition at line 4716 of file rcw.c.

@@ -2839,7 +2839,7 @@ Variables
-

Definition at line 377 of file rcw.c.

+

Definition at line 379 of file rcw.c.

@@ -2859,7 +2859,7 @@ Variables
-

Definition at line 3448 of file rcw.c.

+

Definition at line 3456 of file rcw.c.

@@ -2905,7 +2905,7 @@ Variables

Gets called if the user interacts with the gtksocket-is-not-available-warning-dialog.

-

Definition at line 4481 of file rcw.c.

+

Definition at line 4494 of file rcw.c.

@@ -2950,7 +2950,7 @@ Variables
Todo:
Add callback for hostname transparent overlay #832
-

Definition at line 4060 of file rcw.c.

+

Definition at line 4073 of file rcw.c.

@@ -2978,7 +2978,7 @@ Variables
-

Definition at line 3223 of file rcw.c.

+

Definition at line 3231 of file rcw.c.

@@ -3006,7 +3006,7 @@ Variables
-

Definition at line 545 of file rcw.c.

+

Definition at line 547 of file rcw.c.

@@ -3034,7 +3034,7 @@ Variables
-

Definition at line 484 of file rcw.c.

+

Definition at line 486 of file rcw.c.

@@ -3062,7 +3062,7 @@ Variables
-

Definition at line 429 of file rcw.c.

+

Definition at line 431 of file rcw.c.

@@ -3106,7 +3106,7 @@ Variables
-

Definition at line 3289 of file rcw.c.

+

Definition at line 3297 of file rcw.c.

@@ -3150,7 +3150,7 @@ Variables
-

Definition at line 3327 of file rcw.c.

+

Definition at line 3335 of file rcw.c.

@@ -3188,7 +3188,7 @@ Variables
-

Definition at line 1254 of file rcw.c.

+

Definition at line 1256 of file rcw.c.

@@ -3226,7 +3226,7 @@ Variables
-

Definition at line 3387 of file rcw.c.

+

Definition at line 3395 of file rcw.c.

@@ -3256,7 +3256,7 @@ Variables
-

Definition at line 756 of file rcw.c.

+

Definition at line 758 of file rcw.c.

@@ -3300,7 +3300,7 @@ Variables
-

Definition at line 3093 of file rcw.c.

+

Definition at line 3101 of file rcw.c.

@@ -3344,7 +3344,7 @@ Variables
-

Definition at line 2816 of file rcw.c.

+

Definition at line 2824 of file rcw.c.

@@ -3388,7 +3388,7 @@ Variables
-

Definition at line 2829 of file rcw.c.

+

Definition at line 2837 of file rcw.c.

@@ -3444,7 +3444,7 @@ Variables
-

Definition at line 3691 of file rcw.c.

+

Definition at line 3699 of file rcw.c.

@@ -3494,7 +3494,7 @@ Variables
-

Definition at line 3673 of file rcw.c.

+

Definition at line 3681 of file rcw.c.

@@ -3544,7 +3544,7 @@ Variables
-

Definition at line 3680 of file rcw.c.

+

Definition at line 3688 of file rcw.c.

@@ -3594,7 +3594,7 @@ Variables
-

Definition at line 3660 of file rcw.c.

+

Definition at line 3668 of file rcw.c.

@@ -3622,7 +3622,7 @@ Variables
-

Definition at line 3631 of file rcw.c.

+

Definition at line 3639 of file rcw.c.

@@ -3642,7 +3642,7 @@ Variables
-

Definition at line 4453 of file rcw.c.

+

Definition at line 4466 of file rcw.c.

@@ -3684,7 +3684,7 @@ Variables
-

Definition at line 4506 of file rcw.c.

+

Definition at line 4519 of file rcw.c.

@@ -3704,7 +3704,7 @@ Variables
-

Definition at line 4405 of file rcw.c.

+

Definition at line 4418 of file rcw.c.

@@ -3754,7 +3754,7 @@ Variables
-

Definition at line 2601 of file rcw.c.

+

Definition at line 2605 of file rcw.c.

@@ -3782,7 +3782,7 @@ Variables
-

Definition at line 512 of file rcw.c.

+

Definition at line 514 of file rcw.c.

@@ -3810,7 +3810,7 @@ Variables
-

Definition at line 497 of file rcw.c.

+

Definition at line 499 of file rcw.c.

@@ -3848,7 +3848,7 @@ Variables
-

Definition at line 1480 of file rcw.c.

+

Definition at line 1482 of file rcw.c.

@@ -3886,7 +3886,7 @@ Variables
-

Definition at line 1494 of file rcw.c.

+

Definition at line 1496 of file rcw.c.

@@ -3924,7 +3924,7 @@ Variables
-

Definition at line 1468 of file rcw.c.

+

Definition at line 1470 of file rcw.c.

@@ -3954,7 +3954,7 @@ Variables
-

Definition at line 4712 of file rcw.c.

+

Definition at line 4725 of file rcw.c.

@@ -3982,7 +3982,7 @@ Variables
-

Definition at line 2755 of file rcw.c.

+

Definition at line 2763 of file rcw.c.

@@ -4032,7 +4032,7 @@ Variables
-

Definition at line 1124 of file rcw.c.

+

Definition at line 1126 of file rcw.c.

@@ -4076,7 +4076,7 @@ Variables
-

Definition at line 3268 of file rcw.c.

+

Definition at line 3276 of file rcw.c.

@@ -4114,7 +4114,7 @@ Variables
-

Definition at line 1311 of file rcw.c.

+

Definition at line 1313 of file rcw.c.

@@ -4158,7 +4158,7 @@ Variables
-

Definition at line 828 of file rcw.c.

+

Definition at line 830 of file rcw.c.

@@ -4220,7 +4220,7 @@ Variables
-

Definition at line 786 of file rcw.c.

+

Definition at line 788 of file rcw.c.

@@ -4270,7 +4270,7 @@ Variables
-

Definition at line 769 of file rcw.c.

+

Definition at line 771 of file rcw.c.

@@ -4308,7 +4308,7 @@ Variables
-

Definition at line 988 of file rcw.c.

+

Definition at line 990 of file rcw.c.

@@ -4328,7 +4328,7 @@ Variables
-

Definition at line 950 of file rcw.c.

+

Definition at line 952 of file rcw.c.

@@ -4366,7 +4366,7 @@ Variables
-

Definition at line 2288 of file rcw.c.

+

Definition at line 2290 of file rcw.c.

@@ -4404,7 +4404,7 @@ Variables
-

Definition at line 2122 of file rcw.c.

+

Definition at line 2124 of file rcw.c.

@@ -4442,7 +4442,7 @@ Variables
-

Definition at line 1678 of file rcw.c.

+

Definition at line 1680 of file rcw.c.

@@ -4480,7 +4480,7 @@ Variables
-

Definition at line 1351 of file rcw.c.

+

Definition at line 1353 of file rcw.c.

@@ -4510,7 +4510,7 @@ Variables
-

Definition at line 1421 of file rcw.c.

+

Definition at line 1423 of file rcw.c.

@@ -4548,7 +4548,7 @@ Variables
-

Definition at line 2299 of file rcw.c.

+

Definition at line 2301 of file rcw.c.

@@ -4586,7 +4586,7 @@ Variables
-

Definition at line 1978 of file rcw.c.

+

Definition at line 1980 of file rcw.c.

@@ -4630,7 +4630,7 @@ Variables
-

Definition at line 1958 of file rcw.c.

+

Definition at line 1960 of file rcw.c.

@@ -4660,7 +4660,7 @@ Variables
-

Definition at line 1773 of file rcw.c.

+

Definition at line 1775 of file rcw.c.

@@ -4698,7 +4698,7 @@ Variables
-

Definition at line 2277 of file rcw.c.

+

Definition at line 2279 of file rcw.c.

@@ -4736,7 +4736,7 @@ Variables
-

Definition at line 1722 of file rcw.c.

+

Definition at line 1724 of file rcw.c.

@@ -4774,7 +4774,7 @@ Variables
-

Definition at line 1748 of file rcw.c.

+

Definition at line 1750 of file rcw.c.

@@ -4812,7 +4812,7 @@ Variables
-

Definition at line 3136 of file rcw.c.

+

Definition at line 3144 of file rcw.c.

@@ -4850,7 +4850,7 @@ Variables
-

Definition at line 3207 of file rcw.c.

+

Definition at line 3215 of file rcw.c.

@@ -4888,7 +4888,7 @@ Variables
-

Definition at line 1895 of file rcw.c.

+

Definition at line 1897 of file rcw.c.

@@ -4936,7 +4936,7 @@ Variables
-

Definition at line 1871 of file rcw.c.

+

Definition at line 1873 of file rcw.c.

@@ -4974,7 +4974,7 @@ Variables
-

Definition at line 1758 of file rcw.c.

+

Definition at line 1760 of file rcw.c.

@@ -5028,7 +5028,7 @@ Variables
-

Definition at line 1837 of file rcw.c.

+

Definition at line 1839 of file rcw.c.

@@ -5066,7 +5066,7 @@ Variables
-

Definition at line 1701 of file rcw.c.

+

Definition at line 1703 of file rcw.c.

@@ -5104,7 +5104,7 @@ Variables
-

Definition at line 1510 of file rcw.c.

+

Definition at line 1512 of file rcw.c.

@@ -5142,7 +5142,7 @@ Variables
-

Definition at line 2137 of file rcw.c.

+

Definition at line 2139 of file rcw.c.

@@ -5180,7 +5180,7 @@ Variables
-

Definition at line 1582 of file rcw.c.

+

Definition at line 1584 of file rcw.c.

@@ -5210,7 +5210,7 @@ Variables
-

Definition at line 1571 of file rcw.c.

+

Definition at line 1573 of file rcw.c.

@@ -5248,7 +5248,7 @@ Variables
-

Definition at line 2015 of file rcw.c.

+

Definition at line 2017 of file rcw.c.

@@ -5278,7 +5278,7 @@ Variables
-

Definition at line 1787 of file rcw.c.

+

Definition at line 1789 of file rcw.c.

@@ -5322,7 +5322,7 @@ Variables
-

Definition at line 3309 of file rcw.c.

+

Definition at line 3317 of file rcw.c.

@@ -5350,7 +5350,7 @@ Variables
-

Definition at line 3610 of file rcw.c.

+

Definition at line 3618 of file rcw.c.

@@ -5378,7 +5378,7 @@ Variables
-

Definition at line 3125 of file rcw.c.

+

Definition at line 3133 of file rcw.c.

@@ -5416,7 +5416,7 @@ Variables
-

Definition at line 3429 of file rcw.c.

+

Definition at line 3437 of file rcw.c.

@@ -5436,7 +5436,7 @@ Variables
-

Definition at line 853 of file rcw.c.

+

Definition at line 855 of file rcw.c.

@@ -5464,7 +5464,7 @@ Variables
-

Definition at line 1148 of file rcw.c.

+

Definition at line 1150 of file rcw.c.

@@ -5508,7 +5508,7 @@ Variables
-

Definition at line 4440 of file rcw.c.

+

Definition at line 4453 of file rcw.c.

@@ -5546,7 +5546,7 @@ Variables
-

Definition at line 4459 of file rcw.c.

+

Definition at line 4472 of file rcw.c.

@@ -5571,7 +5571,7 @@ Variables
Initial value:
=
{
{
(char *)"text/x-remmina-ftb",
GTK_TARGET_SAME_APP | GTK_TARGET_OTHER_WIDGET,
0
},
}
-

Definition at line 203 of file rcw.c.

+

Definition at line 205 of file rcw.c.

@@ -5595,7 +5595,7 @@ Variables
Initial value:
=
{
{
(char *)"text/x-remmina-tb",
GTK_TARGET_SAME_APP,
0
},
}
-

Definition at line 212 of file rcw.c.

+

Definition at line 214 of file rcw.c.

@@ -5619,7 +5619,7 @@ Variables
Initial value:
=
{ 0 }
-

Definition at line 185 of file rcw.c.

+

Definition at line 186 of file rcw.c.

diff --git a/public/rcw_8c_source.html b/public/rcw_8c_source.html index 1ebd88a8b..ce4fbaeca 100644 --- a/public/rcw_8c_source.html +++ b/public/rcw_8c_source.html @@ -86,160 +86,160 @@ $(document).ready(function(){initNavTree('rcw_8c_source.html','');});
rcw.c
-Go to the documentation of this file.
1 /*
2  * Remmina - The GTK+ Remote Desktop Client
3  * Copyright (C) 2009-2011 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 
38 #include "config.h"
39 
40 #ifdef GDK_WINDOWING_X11
41 #include <cairo/cairo-xlib.h>
42 #else
43 #include <cairo/cairo.h>
44 #endif
45 #include <gdk/gdk.h>
46 #include <gdk/gdkkeysyms.h>
47 #include <glib/gi18n.h>
48 #include <stdlib.h>
49 
50 #include "remmina.h"
51 #include "remmina_main.h"
52 #include "rcw.h"
54 #include "remmina_applet_menu.h"
55 #include "remmina_file.h"
56 #include "remmina_file_manager.h"
57 #include "remmina_log.h"
58 #include "remmina_message_panel.h"
59 #include "remmina_ext_exec.h"
60 #include "remmina_plugin_manager.h"
61 #include "remmina_pref.h"
63 #include "remmina_public.h"
65 #include "remmina_unlock.h"
66 #include "remmina_utils.h"
67 #include "remmina_widget_pool.h"
69 
70 #ifdef GDK_WINDOWING_WAYLAND
71 #include <gdk/gdkwayland.h>
72 #endif
73 
74 
75 #define DEBUG_KB_GRABBING 0
76 #include "remmina_exec.h"
77 
80 
81 G_DEFINE_TYPE(RemminaConnectionWindow, rcw, GTK_TYPE_WINDOW)
82 
83 #define MOTION_TIME 100
84 
85 /* default timeout used to hide the floating toolbar when switching profile */
86 #define TB_HIDE_TIME_TIME 1500
87 
88 #define FULL_SCREEN_TARGET_MONITOR_UNDEFINED -1
89 
90 struct _RemminaConnectionWindowPriv {
91  GtkNotebook * notebook;
92  GtkWidget * floating_toolbar_widget;
93  GtkWidget * overlay;
94  GtkWidget * revealer;
95  GtkWidget * overlay_ftb_overlay;
96 
97  GtkWidget * floating_toolbar_label;
98  gdouble floating_toolbar_opacity;
99 
100  /* Various delayed and timer event source ids */
101  guint acs_eventsourceid; // timeout
102  guint spf_eventsourceid; // idle
103  guint grab_retry_eventsourceid; // timeout
104  guint delayed_grab_eventsourceid;
105  guint ftb_hide_eventsource; // timeout
106  guint tar_eventsource; // timeout
107  guint hidetb_eventsource; // timeout
108  guint dwp_eventsourceid; // timeout
109 
110  GtkWidget * toolbar;
111  GtkWidget * grid;
112 
113  /* Toolitems that need to be handled */
114  GtkToolItem * toolitem_menu;
115  GtkToolItem * toolitem_autofit;
116  GtkToolItem * toolitem_fullscreen;
117  GtkToolItem * toolitem_switch_page;
118  GtkToolItem * toolitem_dynres;
119  GtkToolItem * toolitem_scale;
120  GtkToolItem * toolitem_grab;
121  GtkToolItem * toolitem_multimon;
122  GtkToolItem * toolitem_preferences;
123  GtkToolItem * toolitem_tools;
124  GtkToolItem * toolitem_new;
125  GtkToolItem * toolitem_duplicate;
126  GtkToolItem * toolitem_screenshot;
127  GtkWidget * fullscreen_option_button;
128  GtkWidget * fullscreen_scaler_button;
129  GtkWidget * scaler_option_button;
130 
131  GtkWidget * pin_button;
132  gboolean pin_down;
133 
134  gboolean sticky;
135 
136  /* Flag to turn off toolbar signal handling when toolbar is
137  * reconfiguring, usually due to a tab switch */
138  gboolean toolbar_is_reconfiguring;
139 
140  /* This is the current view mode, i.e. VIEWPORT_FULLSCREEN_MODE,
141  * as saved on the "viwemode" profile preference file */
142  gint view_mode;
143 
144  /* Status variables used when in fullscreen mode. Needed
145  * to restore a fullscreen mode after coming from scrolled */
146  gint fss_view_mode;
147  /* Status variables used when in scrolled window mode. Needed
148  * to restore a scrolled window mode after coming from fullscreen */
149  gint ss_width, ss_height;
150  gboolean ss_maximized;
151 
152  gboolean kbcaptured;
153  gboolean pointer_captured;
154  gboolean hostkey_activated;
155  gboolean hostkey_used;
156 
157  gboolean pointer_entered;
158 
159  RemminaConnectionWindowOnDeleteConfirmMode on_delete_confirm_mode;
160 };
161 
162 typedef struct _RemminaConnectionObject {
165 
166  GtkWidget * proto;
167  GtkWidget * aspectframe;
168  GtkWidget * viewport;
169 
170  GtkWidget * scrolled_container;
171 
173 
174  gboolean connected;
175  gboolean dynres_unlocked;
176 
179 
180 enum {
183 };
184 
185 static guint rcw_signals[LAST_SIGNAL] =
186 { 0 };
187 
188 static RemminaConnectionWindow *rcw_create_scrolled(gint width, gint height, gboolean maximize);
189 static RemminaConnectionWindow *rcw_create_fullscreen(GtkWindow *old, gint view_mode);
190 static gboolean rcw_hostkey_func(RemminaProtocolWidget *gp, guint keyval, gboolean release);
191 static GtkWidget *rco_create_tab_page(RemminaConnectionObject *cnnobj);
192 static GtkWidget *rco_create_tab_label(RemminaConnectionObject *cnnobj);
193 
195 static GtkWidget *rcw_create_toolbar(RemminaConnectionWindow *cnnwin, gint mode);
196 static void rcw_place_toolbar(GtkToolbar *toolbar, GtkGrid *grid, GtkWidget *sibling, int toolbar_placement);
197 static void rcw_keyboard_grab(RemminaConnectionWindow *cnnwin);
198 static GtkWidget *rcw_append_new_page(RemminaConnectionWindow *cnnwin, RemminaConnectionObject *cnnobj);
199 
200 
201 static void rcw_ftb_drag_begin(GtkWidget *widget, GdkDragContext *context, gpointer user_data);
202 
203 static const GtkTargetEntry dnd_targets_ftb[] =
204 {
205  {
206  (char *)"text/x-remmina-ftb",
207  GTK_TARGET_SAME_APP | GTK_TARGET_OTHER_WIDGET,
208  0
209  },
210 };
211 
212 static const GtkTargetEntry dnd_targets_tb[] =
213 {
214  {
215  (char *)"text/x-remmina-tb",
216  GTK_TARGET_SAME_APP,
217  0
218  },
219 };
220 
222 {
223  TRACE_CALL(__func__);
224  GtkCssProvider *provider;
225 
226  provider = gtk_css_provider_new();
227 
228  /* It’s important to remove padding, border and shadow from GtkViewport or
229  * we will never know its internal area size, because GtkViweport::viewport_get_view_allocation,
230  * which returns the internal size of the GtkViewport, is private and we cannot access it */
231 
232 #if GTK_CHECK_VERSION(3, 14, 0)
233  gtk_css_provider_load_from_data(provider,
234  "#remmina-cw-viewport, #remmina-cw-aspectframe {\n"
235  " padding:0;\n"
236  " border:0;\n"
237  " background-color: black;\n"
238  "}\n"
239  "GtkDrawingArea {\n"
240  "}\n"
241  "GtkToolbar {\n"
242  " -GtkWidget-window-dragging: 0;\n"
243  "}\n"
244  "#remmina-connection-window-fullscreen {\n"
245  " border-color: black;\n"
246  "}\n"
247  "#remmina-small-button {\n"
248  " outline-offset: 0;\n"
249  " outline-width: 0;\n"
250  " padding: 0;\n"
251  " border: 0;\n"
252  "}\n"
253  "#remmina-pin-button {\n"
254  " outline-offset: 0;\n"
255  " outline-width: 0;\n"
256  " padding: 2px;\n"
257  " border: 0;\n"
258  "}\n"
259  "#remmina-tab-page {\n"
260  " background-color: black;\n"
261  "}\n"
262  "#remmina-scrolled-container {\n"
263  "}\n"
264  "#remmina-scrolled-container.undershoot {\n"
265  " background: none;\n"
266  "}\n"
267  "#remmina-tab-page {\n"
268  "}\n"
269  "#ftbbox-upper {\n"
270  " background-color: white;\n"
271  " color: black;\n"
272  " border-style: none solid solid solid;\n"
273  " border-width: 1px;\n"
274  " border-radius: 4px;\n"
275  " padding: 0px;\n"
276  "}\n"
277  "#ftbbox-lower {\n"
278  " background-color: white;\n"
279  " color: black;\n"
280  " border-style: solid solid none solid;\n"
281  " border-width: 1px;\n"
282  " border-radius: 4px;\n"
283  " padding: 0px;\n"
284  "}\n"
285  "#ftb-handle {\n"
286  "}\n"
287  ".message_panel {\n"
288  " border: 0px solid;\n"
289  " padding: 20px 20px 20px 20px;\n"
290  "}\n"
291  ".message_panel entry {\n"
292  " background-image: none;\n"
293  " border-width: 4px;\n"
294  " border-radius: 8px;\n"
295  "}\n"
296  ".message_panel .title_label {\n"
297  " font-size: 2em; \n"
298  "}\n"
299  , -1, NULL);
300 
301 #else
302  gtk_css_provider_load_from_data(provider,
303  "#remmina-cw-viewport, #remmina-cw-aspectframe {\n"
304  " padding:0;\n"
305  " border:0;\n"
306  " background-color: black;\n"
307  "}\n"
308  "#remmina-cw-message-panel {\n"
309  "}\n"
310  "GtkDrawingArea {\n"
311  "}\n"
312  "GtkToolbar {\n"
313  " -GtkWidget-window-dragging: 0;\n"
314  "}\n"
315  "#remmina-connection-window-fullscreen {\n"
316  " border-color: black;\n"
317  "}\n"
318  "#remmina-small-button {\n"
319  " -GtkWidget-focus-padding: 0;\n"
320  " -GtkWidget-focus-line-width: 0;\n"
321  " padding: 0;\n"
322  " border: 0;\n"
323  "}\n"
324  "#remmina-pin-button {\n"
325  " -GtkWidget-focus-padding: 0;\n"
326  " -GtkWidget-focus-line-width: 0;\n"
327  " padding: 2px;\n"
328  " border: 0;\n"
329  "}\n"
330  "#remmina-scrolled-container {\n"
331  "}\n"
332  "#remmina-scrolled-container.undershoot {\n"
333  " background: none\n"
334  "}\n"
335  "#remmina-tab-page {\n"
336  "}\n"
337  "#ftbbox-upper {\n"
338  " border-style: none solid solid solid;\n"
339  " border-width: 1px;\n"
340  " border-radius: 4px;\n"
341  " padding: 0px;\n"
342  "}\n"
343  "#ftbbox-lower {\n"
344  " border-style: solid solid none solid;\n"
345  " border-width: 1px;\n"
346  " border-radius: 4px;\n"
347  " padding: 0px;\n"
348  "}\n"
349  "#ftb-handle {\n"
350  "}\n"
351 
352  , -1, NULL);
353 #endif
354 
355  gtk_style_context_add_provider_for_screen(gdk_screen_get_default(),
356  GTK_STYLE_PROVIDER(provider),
357  GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
358 
359  g_object_unref(provider);
360 
361  /* Define a signal used to notify all rcws of toolbar move */
362  rcw_signals[TOOLBARPLACE_SIGNAL] = g_signal_new("toolbar-place", G_TYPE_FROM_CLASS(klass),
363  G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET(RemminaConnectionWindowClass, toolbar_place), NULL, NULL,
364  g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
365 }
366 
368 {
369  GtkWidget *po;
370 
371  if (!cnnwin->priv->notebook)
372  return NULL;
373  po = gtk_notebook_get_nth_page(GTK_NOTEBOOK(cnnwin->priv->notebook), npage);
374  return g_object_get_data(G_OBJECT(po), "cnnobj");
375 }
376 
378 {
379  gint np;
380 
381  if (cnnwin != NULL && cnnwin->priv != NULL && cnnwin->priv->notebook != NULL) {
382  np = gtk_notebook_get_current_page(GTK_NOTEBOOK(cnnwin->priv->notebook));
383  if (np < 0)
384  return NULL;
385  return rcw_get_cnnobj_at_page(cnnwin, np);
386  } else {
387  return NULL;
388  }
389 }
390 
391 static RemminaScaleMode get_current_allowed_scale_mode(RemminaConnectionObject *cnnobj, gboolean *dynres_avail, gboolean *scale_avail)
392 {
393  TRACE_CALL(__func__);
394  RemminaScaleMode scalemode;
395  gboolean plugin_has_dynres, plugin_can_scale;
396 
397  scalemode = remmina_protocol_widget_get_current_scale_mode(REMMINA_PROTOCOL_WIDGET(cnnobj->proto));
398 
399  plugin_has_dynres = remmina_protocol_widget_query_feature_by_type(REMMINA_PROTOCOL_WIDGET(cnnobj->proto),
401 
402  plugin_can_scale = remmina_protocol_widget_query_feature_by_type(REMMINA_PROTOCOL_WIDGET(cnnobj->proto),
404 
405  /* Forbid scalemode REMMINA_PROTOCOL_WIDGET_SCALE_MODE_DYNRES when not possible */
406  if ((!plugin_has_dynres) && scalemode == REMMINA_PROTOCOL_WIDGET_SCALE_MODE_DYNRES)
408 
409  /* Forbid scalemode REMMINA_PROTOCOL_WIDGET_SCALE_MODE_SCALED when not possible */
410  if (!plugin_can_scale && scalemode == REMMINA_PROTOCOL_WIDGET_SCALE_MODE_SCALED)
412 
413  if (scale_avail)
414  *scale_avail = plugin_can_scale;
415  if (dynres_avail)
416  *dynres_avail = (plugin_has_dynres && cnnobj->dynres_unlocked);
417 
418  return scalemode;
419 }
420 
422 {
423  TRACE_CALL(__func__);
424 
425  /* Disconnects the connection which is currently in view in the notebook */
426  remmina_protocol_widget_close_connection(REMMINA_PROTOCOL_WIDGET(cnnobj->proto));
427 }
428 
430 {
431  TRACE_CALL(__func__);
432  GdkDisplay *display;
433 
434 #if GTK_CHECK_VERSION(3, 20, 0)
435  GdkSeat *seat;
436 #else
437  GdkDeviceManager *manager;
438  GdkDevice *keyboard = NULL;
439 #endif
440 
441  if (cnnwin->priv->grab_retry_eventsourceid) {
442  g_source_remove(cnnwin->priv->grab_retry_eventsourceid);
443  cnnwin->priv->grab_retry_eventsourceid = 0;
444  }
445  if (cnnwin->priv->delayed_grab_eventsourceid) {
446  g_source_remove(cnnwin->priv->delayed_grab_eventsourceid);
447  cnnwin->priv->delayed_grab_eventsourceid = 0;
448  }
449 
450  display = gtk_widget_get_display(GTK_WIDGET(cnnwin));
451 #if GTK_CHECK_VERSION(3, 20, 0)
452  seat = gdk_display_get_default_seat(display);
453  // keyboard = gdk_seat_get_pointer(seat);
454 #else
455  manager = gdk_display_get_device_manager(display);
456  keyboard = gdk_device_manager_get_client_pointer(manager);
457 #endif
458 
459  if (!cnnwin->priv->kbcaptured && !cnnwin->priv->pointer_captured)
460  return;
461 
462 #if DEBUG_KB_GRABBING
463  printf("DEBUG_KB_GRABBING: --- ungrabbing\n");
464 #endif
465 
466 
467 
468 #if GTK_CHECK_VERSION(3, 20, 0)
469  /* We can use gtk_seat_grab()/_ungrab() only after GTK 3.24 */
470  gdk_seat_ungrab(seat);
471 #else
472  if (keyboard != NULL) {
473  if (gdk_device_get_source(keyboard) != GDK_SOURCE_KEYBOARD)
474  keyboard = gdk_device_get_associated_device(keyboard);
475  G_GNUC_BEGIN_IGNORE_DEPRECATIONS
476  gdk_device_ungrab(keyboard, GDK_CURRENT_TIME);
477  G_GNUC_END_IGNORE_DEPRECATIONS
478  }
479 #endif
480  cnnwin->priv->kbcaptured = FALSE;
481  cnnwin->priv->pointer_captured = FALSE;
482 }
483 
484 static gboolean rcw_keyboard_grab_retry(gpointer user_data)
485 {
486  TRACE_CALL(__func__);
487  RemminaConnectionWindow *cnnwin = (RemminaConnectionWindow *)user_data;
488 
489 #if DEBUG_KB_GRABBING
490  printf("%s retry grab\n", __func__);
491 #endif
492  rcw_keyboard_grab(cnnwin);
493  cnnwin->priv->grab_retry_eventsourceid = 0;
494  return G_SOURCE_REMOVE;
495 }
496 
498 {
499  TRACE_CALL(__func__);
500 #if GTK_CHECK_VERSION(3, 20, 0)
501  GdkSeat *seat;
502  GdkDisplay *display;
503  if (!cnnwin->priv->pointer_captured)
504  return;
505 
506  display = gtk_widget_get_display(GTK_WIDGET(cnnwin));
507  seat = gdk_display_get_default_seat(display);
508  gdk_seat_ungrab(seat);
509 #endif
510 }
511 
513 {
514  TRACE_CALL(__func__);
515  /* This function in Wayland is useless and generates a spurious leave-notify event.
516  * Should we remove it ? https://gitlab.gnome.org/GNOME/mutter/-/issues/2450#note_1588081 */
517 #if GTK_CHECK_VERSION(3, 20, 0)
518  GdkSeat *seat;
519  GdkDisplay *display;
520  GdkGrabStatus ggs;
521 
522 
523  if (cnnwin->priv->pointer_captured) {
524 #if DEBUG_KB_GRABBING
525  printf("DEBUG_KB_GRABBING: pointer_captured is true, it should not\n");
526 #endif
527  return;
528  }
529 
530  display = gtk_widget_get_display(GTK_WIDGET(cnnwin));
531  seat = gdk_display_get_default_seat(display);
532  ggs = gdk_seat_grab(seat, gtk_widget_get_window(GTK_WIDGET(cnnwin)),
533  GDK_SEAT_CAPABILITY_ALL_POINTING, TRUE, NULL, NULL, NULL, NULL);
534  if (ggs != GDK_GRAB_SUCCESS) {
535 #if DEBUG_KB_GRABBING
536  printf("DEBUG_KB_GRABBING: GRAB of POINTER failed. GdkGrabStatus: %d\n", (int)ggs);
537 #endif
538  } else {
539  cnnwin->priv->pointer_captured = TRUE;
540  }
541 
542 #endif
543 }
544 
546 {
547  TRACE_CALL(__func__);
548  GdkDisplay *display;
549 
550 #if GTK_CHECK_VERSION(3, 20, 0)
551  GdkSeat *seat;
552 #else
553  GdkDeviceManager *manager;
554 #endif
555  GdkGrabStatus ggs;
556  GdkDevice *keyboard = NULL;
557 
558  if (cnnwin->priv->kbcaptured) {
559 #if DEBUG_KB_GRABBING
560  printf("DEBUG_KB_GRABBING: %s not grabbing because already grabbed.\n", __func__);
561 #endif
562  return;
563  }
564 
565  display = gtk_widget_get_display(GTK_WIDGET(cnnwin));
566 #if GTK_CHECK_VERSION(3, 20, 0)
567  seat = gdk_display_get_default_seat(display);
568  keyboard = gdk_seat_get_pointer(seat);
569 #else
570  manager = gdk_display_get_device_manager(display);
571  keyboard = gdk_device_manager_get_client_pointer(manager);
572 #endif
573 
574  if (keyboard != NULL) {
575  if (gdk_device_get_source(keyboard) != GDK_SOURCE_KEYBOARD)
576  keyboard = gdk_device_get_associated_device(keyboard);
577 
578 
579 #if DEBUG_KB_GRABBING
580  printf("DEBUG_KB_GRABBING: profile asks for grabbing, let’s try.\n");
581 #endif
582  /* Up to GTK version 3.20 we can grab the keyboard with gdk_device_grab().
583  * in GTK 3.20 gdk_seat_grab() should be used instead of gdk_device_grab().
584  * There is a bug in GTK up to 3.22: When gdk_device_grab() fails
585  * the widget is hidden:
586  * https://gitlab.gnome.org/GNOME/gtk/commit/726ad5a5ae7c4f167e8dd454cd7c250821c400ab
587  * The bugfix will be released with GTK 3.24.
588  * Also please note that the newer gdk_seat_grab() is still calling gdk_device_grab().
589  *
590  * Warning: gdk_seat_grab() will call XGrabKeyboard() or XIGrabDevice()
591  * which in turn will generate a core X input event FocusOut and FocusIn
592  * but not Xinput2 events.
593  * In some cases, GTK is unable to neutralize FocusIn and FocusOut core
594  * events (ie: i3wm+Plasma with GDK_CORE_DEVICE_EVENTS=1 because detail=NotifyNonlinear
595  * instead of detail=NotifyAncestor/detail=NotifyInferior)
596  * Receiving a FocusOut event for Remmina at this time will cause an infinite loop.
597  * Therefore is important for GTK to use Xinput2 instead of core X events
598  * by unsetting GDK_CORE_DEVICE_EVENTS
599  */
600 #if GTK_CHECK_VERSION(3, 20, 0)
601  ggs = gdk_seat_grab(seat, gtk_widget_get_window(GTK_WIDGET(cnnwin)),
602  GDK_SEAT_CAPABILITY_KEYBOARD, TRUE, NULL, NULL, NULL, NULL);
603 #else
604  ggs = gdk_device_grab(keyboard, gtk_widget_get_window(GTK_WIDGET(cnnwin)), GDK_OWNERSHIP_WINDOW,
605  TRUE, GDK_KEY_PRESS | GDK_KEY_RELEASE, NULL, GDK_CURRENT_TIME);
606 #endif
607  if (ggs != GDK_GRAB_SUCCESS) {
608 #if DEBUG_KB_GRABBING
609  printf("GRAB of keyboard failed.\n");
610 #endif
611  /* Reschedule grabbing in half a second if not already done */
612  if (cnnwin->priv->grab_retry_eventsourceid == 0)
613  cnnwin->priv->grab_retry_eventsourceid = g_timeout_add(500, (GSourceFunc)rcw_keyboard_grab_retry, cnnwin);
614  } else {
615 #if DEBUG_KB_GRABBING
616  printf("Keyboard grabbed\n");
617 #endif
618  if (cnnwin->priv->grab_retry_eventsourceid != 0) {
619  g_source_remove(cnnwin->priv->grab_retry_eventsourceid);
620  cnnwin->priv->grab_retry_eventsourceid = 0;
621  }
622  cnnwin->priv->kbcaptured = TRUE;
623  }
624  } else {
625  rcw_kp_ungrab(cnnwin);
626  }
627 }
628 
630 {
631  RemminaConnectionWindowPriv *priv = cnnwin->priv;
632  GtkNotebook *notebook = GTK_NOTEBOOK(priv->notebook);
633  GtkWidget *w;
634  RemminaConnectionObject *cnnobj;
635  gint i, n;
636 
637  if (GTK_IS_WIDGET(notebook)) {
638  n = gtk_notebook_get_n_pages(notebook);
639  for (i = n - 1; i >= 0; i--) {
640  w = gtk_notebook_get_nth_page(notebook, i);
641  cnnobj = (RemminaConnectionObject *)g_object_get_data(G_OBJECT(w), "cnnobj");
642  /* Do close the connection on this tab */
643  remmina_protocol_widget_close_connection(REMMINA_PROTOCOL_WIDGET(cnnobj->proto));
644  }
645  }
646 }
647 
649 {
650  TRACE_CALL(__func__);
651  RemminaConnectionWindowPriv *priv = cnnwin->priv;
652  GtkNotebook *notebook = GTK_NOTEBOOK(priv->notebook);
653  GtkWidget *dialog;
654  gint i, n, nopen;
655 
656  if (!REMMINA_IS_CONNECTION_WINDOW(cnnwin))
657  return TRUE;
658 
659  if (cnnwin->priv->on_delete_confirm_mode != RCW_ONDELETE_NOCONFIRM) {
660  n = gtk_notebook_get_n_pages(notebook);
661  nopen = 0;
662  /* count all non-closed connections */
663  for(i = 0; i < n; i ++) {
664  RemminaConnectionObject *cnnobj = rcw_get_cnnobj_at_page(cnnwin, i);
666  nopen ++;
667  }
668  if (nopen > 1) {
669  dialog = gtk_message_dialog_new(GTK_WINDOW(cnnwin), GTK_DIALOG_MODAL, GTK_MESSAGE_QUESTION,
670  GTK_BUTTONS_YES_NO,
671  _("Are you sure you want to close %i active connections in the current window?"), nopen);
672  i = gtk_dialog_run(GTK_DIALOG(dialog));
673  gtk_widget_destroy(dialog);
674  if (i != GTK_RESPONSE_YES)
675  return FALSE;
676  }
677  else if (nopen == 1) {
678  if (remmina_pref.confirm_close) {
679  dialog = gtk_message_dialog_new(GTK_WINDOW(cnnwin), GTK_DIALOG_MODAL, GTK_MESSAGE_QUESTION,
680  GTK_BUTTONS_YES_NO,
681  _("Are you sure you want to close this last active connection?"));
682  i = gtk_dialog_run(GTK_DIALOG(dialog));
683  gtk_widget_destroy(dialog);
684  if (i != GTK_RESPONSE_YES)
685  return FALSE;
686  }
687  }
688  }
690 
691  return TRUE;
692 }
693 
694 static gboolean rcw_delete_event(GtkWidget *widget, GdkEvent *event, gpointer data)
695 {
696  TRACE_CALL(__func__);
697  rcw_delete(RCW(widget));
698  return TRUE;
699 }
700 
701 static void rcw_destroy(GtkWidget *widget, gpointer data)
702 {
703  TRACE_CALL(__func__);
705  RemminaConnectionWindow *cnnwin;
706 
707  if (!REMMINA_IS_CONNECTION_WINDOW(widget))
708  return;
709 
710  cnnwin = (RemminaConnectionWindow *)widget;
711  priv = cnnwin->priv;
712 
713  if (priv->kbcaptured)
714  rcw_kp_ungrab(cnnwin);
715 
716  if (priv->acs_eventsourceid) {
717  g_source_remove(priv->acs_eventsourceid);
718  priv->acs_eventsourceid = 0;
719  }
720  if (priv->spf_eventsourceid) {
721  g_source_remove(priv->spf_eventsourceid);
722  priv->spf_eventsourceid = 0;
723  }
724  if (priv->grab_retry_eventsourceid) {
725  g_source_remove(priv->grab_retry_eventsourceid);
726  priv->grab_retry_eventsourceid = 0;
727  }
728  if (cnnwin->priv->delayed_grab_eventsourceid) {
729  g_source_remove(cnnwin->priv->delayed_grab_eventsourceid);
730  cnnwin->priv->delayed_grab_eventsourceid = 0;
731  }
732  if (priv->ftb_hide_eventsource) {
733  g_source_remove(priv->ftb_hide_eventsource);
734  priv->ftb_hide_eventsource = 0;
735  }
736  if (priv->tar_eventsource) {
737  g_source_remove(priv->tar_eventsource);
738  priv->tar_eventsource = 0;
739  }
740  if (priv->hidetb_eventsource) {
741  g_source_remove(priv->hidetb_eventsource);
742  priv->hidetb_eventsource = 0;
743  }
744  if (priv->dwp_eventsourceid) {
745  g_source_remove(priv->dwp_eventsourceid);
746  priv->dwp_eventsourceid = 0;
747  }
748 
749  /* There is no need to destroy priv->floating_toolbar_widget,
750  * because it’s our child and will be destroyed automatically */
751 
752  cnnwin->priv = NULL;
753  g_free(priv);
754 }
755 
756 gboolean rcw_notify_widget_toolbar_placement(GtkWidget *widget, gpointer data)
757 {
758  TRACE_CALL(__func__);
759  GType rcwtype;
760 
761  rcwtype = rcw_get_type();
762  if (G_TYPE_CHECK_INSTANCE_TYPE(widget, rcwtype)) {
763  g_signal_emit_by_name(G_OBJECT(widget), "toolbar-place");
764  return TRUE;
765  }
766  return FALSE;
767 }
768 
769 static gboolean rcw_tb_drag_failed(GtkWidget *widget, GdkDragContext *context,
770  GtkDragResult result, gpointer user_data)
771 {
772  TRACE_CALL(__func__);
774  RemminaConnectionWindow *cnnwin;
775 
776 
777  cnnwin = (RemminaConnectionWindow *)user_data;
778  priv = cnnwin->priv;
779 
780  if (priv->toolbar)
781  gtk_widget_show(GTK_WIDGET(priv->toolbar));
782 
783  return TRUE;
784 }
785 
786 static gboolean rcw_tb_drag_drop(GtkWidget *widget, GdkDragContext *context,
787  gint x, gint y, guint time, gpointer user_data)
788 {
789  TRACE_CALL(__func__);
790  GtkAllocation wa;
791  gint new_toolbar_placement;
793  RemminaConnectionWindow *cnnwin;
794 
795  cnnwin = (RemminaConnectionWindow *)user_data;
796  priv = cnnwin->priv;
797 
798  gtk_widget_get_allocation(widget, &wa);
799 
800  if (wa.width * y >= wa.height * x) {
801  if (wa.width * y > wa.height * (wa.width - x))
802  new_toolbar_placement = TOOLBAR_PLACEMENT_BOTTOM;
803  else
804  new_toolbar_placement = TOOLBAR_PLACEMENT_LEFT;
805  } else {
806  if (wa.width * y > wa.height * (wa.width - x))
807  new_toolbar_placement = TOOLBAR_PLACEMENT_RIGHT;
808  else
809  new_toolbar_placement = TOOLBAR_PLACEMENT_TOP;
810  }
811 
812  gtk_drag_finish(context, TRUE, TRUE, time);
813 
814  if (new_toolbar_placement != remmina_pref.toolbar_placement) {
815  /* Save new position */
816  remmina_pref.toolbar_placement = new_toolbar_placement;
818 
819  /* Signal all windows that the toolbar must be moved */
821  }
822  if (priv->toolbar)
823  gtk_widget_show(GTK_WIDGET(priv->toolbar));
824 
825  return TRUE;
826 }
827 
828 static void rcw_tb_drag_begin(GtkWidget *widget, GdkDragContext *context, gpointer user_data)
829 {
830  TRACE_CALL(__func__);
831 
832  cairo_surface_t *surface;
833  cairo_t *cr;
834  GtkAllocation wa;
835  double dashes[] = { 10 };
836 
837  gtk_widget_get_allocation(widget, &wa);
838 
839  surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 16, 16);
840  cr = cairo_create(surface);
841  cairo_set_source_rgb(cr, 0.6, 0.6, 0.6);
842  cairo_set_line_width(cr, 4);
843  cairo_set_dash(cr, dashes, 1, 0);
844  cairo_rectangle(cr, 0, 0, 16, 16);
845  cairo_stroke(cr);
846  cairo_destroy(cr);
847 
848  gtk_widget_hide(widget);
849 
850  gtk_drag_set_icon_surface(context, surface);
851 }
852 
854 {
855  TRACE_CALL(__func__);
856  RemminaConnectionWindowPriv *priv = cnnwin->priv;
857  RemminaConnectionObject *cnnobj;
858 
859  cnnobj = rcw_get_visible_cnnobj(cnnwin);
860  if (!cnnobj) return;
861 
862  priv->floating_toolbar_opacity = (1.0 - TOOLBAR_OPACITY_MIN) / ((gdouble)TOOLBAR_OPACITY_LEVEL)
863  * ((gdouble)(TOOLBAR_OPACITY_LEVEL - remmina_file_get_int(cnnobj->remmina_file, "toolbar_opacity", 0)))
864  + TOOLBAR_OPACITY_MIN;
865  if (priv->floating_toolbar_widget)
866  gtk_widget_set_opacity(GTK_WIDGET(priv->overlay_ftb_overlay), priv->floating_toolbar_opacity);
867 }
868 
869 static gboolean rcw_floating_toolbar_make_invisible(gpointer data)
870 {
871  TRACE_CALL(__func__);
873 
874  gtk_widget_set_opacity(GTK_WIDGET(priv->overlay_ftb_overlay), 0.0);
875  priv->ftb_hide_eventsource = 0;
876  return G_SOURCE_REMOVE;
877 }
878 
879 static void rcw_floating_toolbar_show(RemminaConnectionWindow *cnnwin, gboolean show)
880 {
881  TRACE_CALL(__func__);
882  RemminaConnectionWindowPriv *priv = cnnwin->priv;
883 
884  if (priv->floating_toolbar_widget == NULL)
885  return;
886 
887  if (show || priv->pin_down) {
888  /* Make the FTB no longer transparent, in case we have an hidden toolbar */
890  /* Remove outstanding hide events, if not yet active */
891  if (priv->ftb_hide_eventsource) {
892  g_source_remove(priv->ftb_hide_eventsource);
893  priv->ftb_hide_eventsource = 0;
894  }
895  } else {
896  /* If we are hiding and the toolbar must be made invisible, schedule
897  * a later toolbar hide */
899  if (priv->ftb_hide_eventsource == 0)
900  priv->ftb_hide_eventsource = g_timeout_add(1000, rcw_floating_toolbar_make_invisible, priv);
901  }
902 
903  gtk_revealer_set_reveal_child(GTK_REVEALER(priv->revealer), show || priv->pin_down);
904 }
905 
906 static void rco_get_desktop_size(RemminaConnectionObject *cnnobj, gint *width, gint *height)
907 {
908  TRACE_CALL(__func__);
909  RemminaProtocolWidget *gp = REMMINA_PROTOCOL_WIDGET(cnnobj->proto);
910 
911 
914  if (*width == 0) {
915  /* Before connecting we do not have real remote width/height,
916  * so we ask profile values */
919  }
920 }
921 
922 void rco_set_scrolled_policy(RemminaScaleMode scalemode, GtkScrolledWindow *scrolled_window)
923 {
924  TRACE_CALL(__func__);
925 
926  gtk_scrolled_window_set_policy(scrolled_window,
927  scalemode == REMMINA_PROTOCOL_WIDGET_SCALE_MODE_SCALED ? GTK_POLICY_NEVER : GTK_POLICY_AUTOMATIC,
928  scalemode == REMMINA_PROTOCOL_WIDGET_SCALE_MODE_SCALED ? GTK_POLICY_NEVER : GTK_POLICY_AUTOMATIC);
929 }
930 
931 static GtkWidget *rco_create_scrolled_container(RemminaScaleMode scalemode, int view_mode)
932 {
933  GtkWidget *scrolled_container;
934 
935  if (view_mode == VIEWPORT_FULLSCREEN_MODE) {
936  scrolled_container = remmina_scrolled_viewport_new();
937  } else {
938  scrolled_container = gtk_scrolled_window_new(NULL, NULL);
939  rco_set_scrolled_policy(scalemode, GTK_SCROLLED_WINDOW(scrolled_container));
940  gtk_container_set_border_width(GTK_CONTAINER(scrolled_container), 0);
941  gtk_widget_set_can_focus(scrolled_container, FALSE);
942  }
943 
944  gtk_widget_set_name(scrolled_container, "remmina-scrolled-container");
945  gtk_widget_show(scrolled_container);
946 
947  return scrolled_container;
948 }
949 
951 {
952  TRACE_CALL(__func__);
953 
954  RemminaConnectionWindowPriv *priv = cnnwin->priv;
955  RemminaConnectionObject *cnnobj;
956  gint dwidth, dheight;
957  GtkAllocation nba, ca, ta;
958 
959  cnnwin->priv->tar_eventsource = 0;
960 
961  if (priv->toolbar_is_reconfiguring)
962  return G_SOURCE_REMOVE;
963 
964  if (!(cnnobj = rcw_get_visible_cnnobj(cnnwin))) return FALSE;
965 
966  if (cnnobj->connected && GTK_IS_SCROLLED_WINDOW(cnnobj->scrolled_container)) {
967  rco_get_desktop_size(cnnobj, &dwidth, &dheight);
968  gtk_widget_get_allocation(GTK_WIDGET(priv->notebook), &nba);
969  gtk_widget_get_allocation(cnnobj->scrolled_container, &ca);
970  gtk_widget_get_allocation(priv->toolbar, &ta);
971  if (remmina_pref.toolbar_placement == TOOLBAR_PLACEMENT_LEFT ||
973  gtk_window_resize(GTK_WINDOW(cnnobj->cnnwin), MAX(1, dwidth + ta.width + nba.width - ca.width),
974  MAX(1, dheight + nba.height - ca.height));
975  else
976  gtk_window_resize(GTK_WINDOW(cnnobj->cnnwin), MAX(1, dwidth + nba.width - ca.width),
977  MAX(1, dheight + ta.height + nba.height - ca.height));
978  gtk_container_check_resize(GTK_CONTAINER(cnnobj->cnnwin));
979  }
980  if (GTK_IS_SCROLLED_WINDOW(cnnobj->scrolled_container)) {
981  RemminaScaleMode scalemode = get_current_allowed_scale_mode(cnnobj, NULL, NULL);
982  rco_set_scrolled_policy(scalemode, GTK_SCROLLED_WINDOW(cnnobj->scrolled_container));
983  }
984 
985  return G_SOURCE_REMOVE;
986 }
987 
988 static void rcw_toolbar_autofit(GtkToolItem *toggle, RemminaConnectionWindow *cnnwin)
989 {
990  TRACE_CALL(__func__);
991  RemminaConnectionObject *cnnobj;
992 
993  if (cnnwin->priv->toolbar_is_reconfiguring)
994  return;
995  if (!(cnnobj = rcw_get_visible_cnnobj(cnnwin))) return;
996 
997  if (GTK_IS_SCROLLED_WINDOW(cnnobj->scrolled_container)) {
998  if ((gdk_window_get_state(gtk_widget_get_window(GTK_WIDGET(cnnwin))) & GDK_WINDOW_STATE_MAXIMIZED) != 0)
999  gtk_window_unmaximize(GTK_WINDOW(cnnwin));
1000 
1001  /* It’s tricky to make the toolbars disappear automatically, while keeping scrollable.
1002  * Please tell me if you know a better way to do this */
1003  gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(cnnobj->scrolled_container), GTK_POLICY_NEVER, GTK_POLICY_NEVER);
1004 
1005  cnnwin->priv->tar_eventsource = g_timeout_add(200, (GSourceFunc)rcw_toolbar_autofit_restore, cnnwin);
1006  }
1007 }
1008 
1009 void rco_get_monitor_geometry(RemminaConnectionObject *cnnobj, GdkRectangle *sz)
1010 {
1011  TRACE_CALL(__func__);
1012 
1013  /* Fill sz with the monitor (or workarea) size and position
1014  * of the monitor (or workarea) where cnnobj->cnnwin is located */
1015 
1016  GdkRectangle monitor_geometry;
1017 
1018  sz->x = sz->y = sz->width = sz->height = 0;
1019 
1020  if (!cnnobj)
1021  return;
1022  if (!cnnobj->cnnwin)
1023  return;
1024  if (!gtk_widget_is_visible(GTK_WIDGET(cnnobj->cnnwin)))
1025  return;
1026 
1027 #if GTK_CHECK_VERSION(3, 22, 0)
1028  GdkDisplay *display;
1029  GdkMonitor *monitor;
1030  display = gtk_widget_get_display(GTK_WIDGET(cnnobj->cnnwin));
1031  monitor = gdk_display_get_monitor_at_window(display, gtk_widget_get_window(GTK_WIDGET(cnnobj->cnnwin)));
1032 #else
1033  GdkScreen *screen;
1034  gint monitor;
1035  screen = gtk_window_get_screen(GTK_WINDOW(cnnobj->cnnwin));
1036  monitor = gdk_screen_get_monitor_at_window(screen, gtk_widget_get_window(GTK_WIDGET(cnnobj->cnnwin)));
1037 #endif
1038 
1039 #if GTK_CHECK_VERSION(3, 22, 0)
1040  gdk_monitor_get_workarea(monitor, &monitor_geometry);
1041  /* Under Wayland, GTK 3.22, all values returned by gdk_monitor_get_geometry()
1042  * and gdk_monitor_get_workarea() seem to have been divided by the
1043  * gdk scale factor, so we need to adjust the returned rect
1044  * undoing the division */
1045 #ifdef GDK_WINDOWING_WAYLAND
1046  if (GDK_IS_WAYLAND_DISPLAY(display)) {
1047  int monitor_scale_factor = gdk_monitor_get_scale_factor(monitor);
1048  monitor_geometry.width *= monitor_scale_factor;
1049  monitor_geometry.height *= monitor_scale_factor;
1050  }
1051 #endif
1052 #elif gdk_screen_get_monitor_workarea
1053  gdk_screen_get_monitor_workarea(screen, monitor, &monitor_geometry);
1054 #else
1055  gdk_screen_get_monitor_geometry(screen, monitor, &monitor_geometry);
1056 #endif
1057  *sz = monitor_geometry;
1058 }
1059 
1061 {
1062  TRACE_CALL(__func__);
1063  gboolean scroll_required = FALSE;
1064 
1065  GdkRectangle monitor_geometry;
1066  gint rd_width, rd_height;
1067  gint bordersz;
1068  gint scalemode;
1069 
1070  scalemode = remmina_protocol_widget_get_current_scale_mode(REMMINA_PROTOCOL_WIDGET(cnnobj->proto));
1071 
1072  /* Get remote destkop size */
1073  rco_get_desktop_size(cnnobj, &rd_width, &rd_height);
1074 
1075  /* Get our monitor size */
1076  rco_get_monitor_geometry(cnnobj, &monitor_geometry);
1077 
1078  if (!remmina_protocol_widget_get_expand(REMMINA_PROTOCOL_WIDGET(cnnobj->proto)) &&
1079  (monitor_geometry.width < rd_width || monitor_geometry.height < rd_height) &&
1081  scroll_required = TRUE;
1082 
1083  switch (cnnobj->cnnwin->priv->view_mode) {
1085  gtk_window_resize(GTK_WINDOW(cnnobj->cnnwin), monitor_geometry.width, monitor_geometry.height);
1086  gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(cnnobj->scrolled_container),
1087  (scroll_required ? GTK_POLICY_AUTOMATIC : GTK_POLICY_NEVER),
1088  (scroll_required ? GTK_POLICY_AUTOMATIC : GTK_POLICY_NEVER));
1089  break;
1090 
1092  bordersz = scroll_required ? SCROLL_BORDER_SIZE : 0;
1093  gtk_window_resize(GTK_WINDOW(cnnobj->cnnwin), monitor_geometry.width, monitor_geometry.height);
1094  if (REMMINA_IS_SCROLLED_VIEWPORT(cnnobj->scrolled_container))
1095  /* Put a border around Notebook content (RemminaScrolledViewpord), so we can
1096  * move the mouse over the border to scroll */
1097  gtk_container_set_border_width(GTK_CONTAINER(cnnobj->scrolled_container), bordersz);
1098 
1099  break;
1100 
1101  case SCROLLED_WINDOW_MODE:
1102  if (remmina_file_get_int(cnnobj->remmina_file, "viewmode", UNDEFINED_MODE) == UNDEFINED_MODE) {
1103  /* ToDo: is this really needed ? When ? */
1104  gtk_window_set_default_size(GTK_WINDOW(cnnobj->cnnwin),
1105  MIN(rd_width, monitor_geometry.width), MIN(rd_height, monitor_geometry.height));
1106  if (rd_width >= monitor_geometry.width || rd_height >= monitor_geometry.height) {
1107  gtk_window_maximize(GTK_WINDOW(cnnobj->cnnwin));
1108  remmina_file_set_int(cnnobj->remmina_file, "window_maximize", TRUE);
1109  } else {
1110  rcw_toolbar_autofit(NULL, cnnobj->cnnwin);
1111  remmina_file_set_int(cnnobj->remmina_file, "window_maximize", FALSE);
1112  }
1113  } else {
1114  if (remmina_file_get_int(cnnobj->remmina_file, "window_maximize", FALSE))
1115  gtk_window_maximize(GTK_WINDOW(cnnobj->cnnwin));
1116  }
1117  break;
1118 
1119  default:
1120  break;
1121  }
1122 }
1123 
1124 static void rcw_set_tooltip(GtkWidget *item, const gchar *tip, guint key1, guint key2)
1125 {
1126  TRACE_CALL(__func__);
1127  gchar *s1;
1128  gchar *s2;
1129 
1130  if (remmina_pref.hostkey && key1) {
1131  if (key2)
1132  s1 = g_strdup_printf(" (%s + %s,%s)", gdk_keyval_name(remmina_pref.hostkey),
1133  gdk_keyval_name(gdk_keyval_to_upper(key1)), gdk_keyval_name(gdk_keyval_to_upper(key2)));
1134  else if (key1 == remmina_pref.hostkey)
1135  s1 = g_strdup_printf(" (%s)", gdk_keyval_name(remmina_pref.hostkey));
1136  else
1137  s1 = g_strdup_printf(" (%s + %s)", gdk_keyval_name(remmina_pref.hostkey),
1138  gdk_keyval_name(gdk_keyval_to_upper(key1)));
1139  } else {
1140  s1 = NULL;
1141  }
1142  s2 = g_strdup_printf("%s%s", tip, s1 ? s1 : "");
1143  gtk_widget_set_tooltip_text(item, s2);
1144  g_free(s2);
1145  g_free(s1);
1146 }
1147 
1149 {
1150  TRACE_CALL(__func__);
1151  RemminaScaleMode scalemode;
1152  gboolean scaledexpandedmode;
1153  int rdwidth, rdheight;
1154  gfloat aratio;
1155 
1156  if (!cnnobj->plugin_can_scale) {
1157  /* If we have a plugin that cannot scale,
1158  * (i.e. SFTP plugin), then we expand proto */
1159  gtk_widget_set_halign(GTK_WIDGET(cnnobj->proto), GTK_ALIGN_FILL);
1160  gtk_widget_set_valign(GTK_WIDGET(cnnobj->proto), GTK_ALIGN_FILL);
1161  } else {
1162  /* Plugin can scale */
1163 
1164  scalemode = get_current_allowed_scale_mode(cnnobj, NULL, NULL);
1165  scaledexpandedmode = remmina_protocol_widget_get_expand(REMMINA_PROTOCOL_WIDGET(cnnobj->proto));
1166 
1167  /* Check if we need aspectframe and create/destroy it accordingly */
1168  if (scalemode == REMMINA_PROTOCOL_WIDGET_SCALE_MODE_SCALED && !scaledexpandedmode) {
1169  /* We need an aspectframe as a parent of proto */
1170  rdwidth = remmina_protocol_widget_get_width(REMMINA_PROTOCOL_WIDGET(cnnobj->proto));
1171  rdheight = remmina_protocol_widget_get_height(REMMINA_PROTOCOL_WIDGET(cnnobj->proto));
1172  aratio = (gfloat)rdwidth / (gfloat)rdheight;
1173  if (!cnnobj->aspectframe) {
1174  /* We need a new aspectframe */
1175  cnnobj->aspectframe = gtk_aspect_frame_new(NULL, 0.5, 0.5, aratio, FALSE);
1176  gtk_widget_set_name(cnnobj->aspectframe, "remmina-cw-aspectframe");
1177  gtk_frame_set_shadow_type(GTK_FRAME(cnnobj->aspectframe), GTK_SHADOW_NONE);
1178  g_object_ref(cnnobj->proto);
1179  gtk_container_remove(GTK_CONTAINER(cnnobj->viewport), cnnobj->proto);
1180  gtk_container_add(GTK_CONTAINER(cnnobj->viewport), cnnobj->aspectframe);
1181  gtk_container_add(GTK_CONTAINER(cnnobj->aspectframe), cnnobj->proto);
1182  g_object_unref(cnnobj->proto);
1183  gtk_widget_show(cnnobj->aspectframe);
1184  if (cnnobj != NULL && cnnobj->cnnwin != NULL && cnnobj->cnnwin->priv->notebook != NULL)
1185  rcw_grab_focus(cnnobj->cnnwin);
1186  } else {
1187  gtk_aspect_frame_set(GTK_ASPECT_FRAME(cnnobj->aspectframe), 0.5, 0.5, aratio, FALSE);
1188  }
1189  } else {
1190  /* We do not need an aspectframe as a parent of proto */
1191  if (cnnobj->aspectframe) {
1192  /* We must remove the old aspectframe reparenting proto to viewport */
1193  g_object_ref(cnnobj->aspectframe);
1194  g_object_ref(cnnobj->proto);
1195  gtk_container_remove(GTK_CONTAINER(cnnobj->aspectframe), cnnobj->proto);
1196  gtk_container_remove(GTK_CONTAINER(cnnobj->viewport), cnnobj->aspectframe);
1197  g_object_unref(cnnobj->aspectframe);
1198  cnnobj->aspectframe = NULL;
1199  gtk_container_add(GTK_CONTAINER(cnnobj->viewport), cnnobj->proto);
1200  g_object_unref(cnnobj->proto);
1201  if (cnnobj != NULL && cnnobj->cnnwin != NULL && cnnobj->cnnwin->priv->notebook != NULL)
1202  rcw_grab_focus(cnnobj->cnnwin);
1203  }
1204  }
1205 
1207  /* We have a plugin that can be scaled, and the scale button
1208  * has been pressed. Give it the correct WxH maintaining aspect
1209  * ratio of remote destkop size */
1210  gtk_widget_set_halign(GTK_WIDGET(cnnobj->proto), GTK_ALIGN_FILL);
1211  gtk_widget_set_valign(GTK_WIDGET(cnnobj->proto), GTK_ALIGN_FILL);
1212  } else {
1213  /* Plugin can scale, but no scaling is active. Ensure that we have
1214  * aspectframe with a ratio of 1 */
1215  gtk_widget_set_halign(GTK_WIDGET(cnnobj->proto), GTK_ALIGN_CENTER);
1216  gtk_widget_set_valign(GTK_WIDGET(cnnobj->proto), GTK_ALIGN_CENTER);
1217  }
1218  }
1219 }
1220 
1221 static void nb_set_current_page(GtkNotebook *notebook, GtkWidget *page)
1222 {
1223  gint np, i;
1224 
1225  np = gtk_notebook_get_n_pages(notebook);
1226  for (i = 0; i < np; i++) {
1227  if (gtk_notebook_get_nth_page(notebook, i) == page) {
1228  gtk_notebook_set_current_page(notebook, i);
1229  break;
1230  }
1231  }
1232 }
1233 
1234 static void nb_migrate_message_panels(GtkWidget *frompage, GtkWidget *topage)
1235 {
1236  /* Migrate a single connection tab from a notebook to another one */
1237  GList *lst, *l;
1238 
1239  /* Reparent message panels */
1240  lst = gtk_container_get_children(GTK_CONTAINER(frompage));
1241  for (l = lst; l != NULL; l = l->next) {
1242  if (REMMINA_IS_MESSAGE_PANEL(l->data)) {
1243  g_object_ref(l->data);
1244  gtk_container_remove(GTK_CONTAINER(frompage), GTK_WIDGET(l->data));
1245  gtk_container_add(GTK_CONTAINER(topage), GTK_WIDGET(l->data));
1246  g_object_unref(l->data);
1247  gtk_box_reorder_child(GTK_BOX(topage), GTK_WIDGET(l->data), 0);
1248  }
1249  }
1250  g_list_free(lst);
1251 
1252 }
1253 
1255 {
1256  /* Migrate a complete notebook from a window to another */
1257 
1258  gchar *tag;
1259  gint cp, np, i;
1260  GtkNotebook *from_notebook;
1261  GtkWidget *frompage, *newpage, *old_scrolled_container;
1262  RemminaConnectionObject *cnnobj;
1263  RemminaScaleMode scalemode;
1264 
1265  /* Migrate TAG */
1266  tag = g_strdup((gchar *)g_object_get_data(G_OBJECT(from), "tag"));
1267  g_object_set_data_full(G_OBJECT(to), "tag", tag, (GDestroyNotify)g_free);
1268 
1269  /* Migrate notebook content */
1270  from_notebook = from->priv->notebook;
1271  if (from_notebook && GTK_IS_NOTEBOOK(from_notebook)) {
1272 
1273  cp = gtk_notebook_get_current_page(from_notebook);
1274  np = gtk_notebook_get_n_pages(from_notebook);
1275  /* Create pages on dest notebook and migrate
1276  * page content */
1277  for (i = 0; i < np; i++) {
1278  frompage = gtk_notebook_get_nth_page(from_notebook, i);
1279  cnnobj = g_object_get_data(G_OBJECT(frompage), "cnnobj");
1280 
1281  /* A scrolled container must be recreated, because it can be different on the new window/page
1282  depending on view_mode */
1283  scalemode = get_current_allowed_scale_mode(cnnobj, NULL, NULL);
1284  old_scrolled_container = cnnobj->scrolled_container;
1285  cnnobj->scrolled_container = rco_create_scrolled_container(scalemode, to->priv->view_mode);
1286 
1287  newpage = rcw_append_new_page(to, cnnobj);
1288 
1289  nb_migrate_message_panels(frompage, newpage);
1290 
1291  /* Reparent the viewport (which is inside scrolled_container) to the new page */
1292  g_object_ref(cnnobj->viewport);
1293  gtk_container_remove(GTK_CONTAINER(old_scrolled_container), cnnobj->viewport);
1294  gtk_container_add(GTK_CONTAINER(cnnobj->scrolled_container), cnnobj->viewport);
1295  g_object_unref(cnnobj->viewport);
1296 
1297  /* Destroy old scrolled_container. Not really needed, it will be destroyed
1298  * when removing the page from the notepad */
1299  gtk_widget_destroy(old_scrolled_container);
1300 
1301  }
1302 
1303  /* Remove all the pages from source notebook */
1304  for (i = np - 1; i >= 0; i--)
1305  gtk_notebook_remove_page(from_notebook, i);
1306  gtk_notebook_set_current_page(to->priv->notebook, cp);
1307 
1308  }
1309 }
1310 
1311 static void rcw_switch_viewmode(RemminaConnectionWindow *cnnwin, int newmode)
1312 {
1313  GdkWindowState s;
1314  RemminaConnectionWindow *newwin;
1315  gint old_width, old_height;
1316  int old_mode;
1317 
1318  old_mode = cnnwin->priv->view_mode;
1319  if (old_mode == newmode)
1320  return;
1321 
1322  if (newmode == VIEWPORT_FULLSCREEN_MODE || newmode == SCROLLED_FULLSCREEN_MODE) {
1323  if (old_mode == SCROLLED_WINDOW_MODE) {
1324  /* We are leaving SCROLLED_WINDOW_MODE, save W,H, and maximized
1325  * status before self destruction of cnnwin */
1326  gtk_window_get_size(GTK_WINDOW(cnnwin), &old_width, &old_height);
1327  s = gdk_window_get_state(gtk_widget_get_window(GTK_WIDGET(cnnwin)));
1328  }
1329  newwin = rcw_create_fullscreen(GTK_WINDOW(cnnwin), cnnwin->priv->fss_view_mode);
1330  rcw_migrate(cnnwin, newwin);
1331  if (old_mode == SCROLLED_WINDOW_MODE) {
1332  newwin->priv->ss_maximized = (s & GDK_WINDOW_STATE_MAXIMIZED) ? TRUE : FALSE;
1333  newwin->priv->ss_width = old_width;
1334  newwin->priv->ss_height = old_height;
1335  }
1336  } else {
1337  newwin = rcw_create_scrolled(cnnwin->priv->ss_width, cnnwin->priv->ss_height,
1338  cnnwin->priv->ss_maximized);
1339  rcw_migrate(cnnwin, newwin);
1340  if (old_mode == VIEWPORT_FULLSCREEN_MODE || old_mode == SCROLLED_FULLSCREEN_MODE)
1341  /* We are leaving a FULLSCREEN mode, save some parameters
1342  * status before self destruction of cnnwin */
1343  newwin->priv->fss_view_mode = old_mode;
1344  }
1345 
1346  /* Prevent unreleased hostkey from old window to be released here */
1347  newwin->priv->hostkey_used = TRUE;
1348 }
1349 
1350 
1351 static void rcw_toolbar_fullscreen(GtkToolItem *toggle, RemminaConnectionWindow *cnnwin)
1352 {
1353  TRACE_CALL(__func__);
1354 
1355  RemminaConnectionObject *cnnobj;
1356 
1357  if (cnnwin->priv->toolbar_is_reconfiguring)
1358  return;
1359 
1360  if (!(cnnobj = rcw_get_visible_cnnobj(cnnwin))) return;
1361 
1362  RemminaProtocolWidget *gp = REMMINA_PROTOCOL_WIDGET(cnnobj->proto);
1363 
1364  if (remmina_protocol_widget_get_multimon(gp) >= 1) {
1365  REMMINA_DEBUG("Fullscreen on all monitor");
1366  gdk_window_set_fullscreen_mode(gtk_widget_get_window(GTK_WIDGET(toggle)), GDK_FULLSCREEN_ON_ALL_MONITORS);
1367  } else {
1368  REMMINA_DEBUG("Fullscreen on one monitor");
1369  }
1370 
1371  if ((toggle != NULL && toggle == cnnwin->priv->toolitem_fullscreen)) {
1372  if (gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(toggle))) {
1374  gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(cnnwin->priv->toolitem_multimon), TRUE);
1375  rcw_switch_viewmode(cnnwin, cnnwin->priv->fss_view_mode);
1376  } else {
1378  }
1379  } else
1380  if (gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(cnnwin->priv->toolitem_multimon))) {
1381  rcw_switch_viewmode(cnnwin, cnnwin->priv->fss_view_mode);
1382  } else {
1384  }
1385 }
1386 
1387 static void rco_viewport_fullscreen_mode(GtkWidget *widget, RemminaConnectionObject *cnnobj)
1388 {
1389  TRACE_CALL(__func__);
1390  RemminaConnectionWindow *newwin;
1391 
1392  if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget)))
1393  return;
1394  cnnobj->cnnwin->priv->fss_view_mode = VIEWPORT_FULLSCREEN_MODE;
1395  newwin = rcw_create_fullscreen(GTK_WINDOW(cnnobj->cnnwin), VIEWPORT_FULLSCREEN_MODE);
1396  rcw_migrate(cnnobj->cnnwin, newwin);
1397 }
1398 
1399 static void rco_scrolled_fullscreen_mode(GtkWidget *widget, RemminaConnectionObject *cnnobj)
1400 {
1401  TRACE_CALL(__func__);
1402  RemminaConnectionWindow *newwin;
1403 
1404  if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget)))
1405  return;
1406  cnnobj->cnnwin->priv->fss_view_mode = SCROLLED_FULLSCREEN_MODE;
1407  newwin = rcw_create_fullscreen(GTK_WINDOW(cnnobj->cnnwin), SCROLLED_FULLSCREEN_MODE);
1408  rcw_migrate(cnnobj->cnnwin, newwin);
1409 }
1410 
1411 static void rcw_fullscreen_option_popdown(GtkWidget *widget, RemminaConnectionWindow *cnnwin)
1412 {
1413  TRACE_CALL(__func__);
1414  RemminaConnectionWindowPriv *priv = cnnwin->priv;
1415 
1416  priv->sticky = FALSE;
1417  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(priv->fullscreen_option_button), FALSE);
1418  rcw_floating_toolbar_show(cnnwin, FALSE);
1419 }
1420 
1421 void rcw_toolbar_fullscreen_option(GtkToolItem *toggle, RemminaConnectionWindow *cnnwin)
1422 {
1423  TRACE_CALL(__func__);
1424  RemminaConnectionObject *cnnobj;
1425  GtkWidget *menu;
1426  GtkWidget *menuitem;
1427  GSList *group;
1428 
1429  if (cnnwin->priv->toolbar_is_reconfiguring)
1430  return;
1431 
1432  if (!(cnnobj = rcw_get_visible_cnnobj(cnnwin))) return;
1433 
1434  if (!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(toggle)))
1435  return;
1436 
1437  cnnwin->priv->sticky = TRUE;
1438 
1439  menu = gtk_menu_new();
1440 
1441  menuitem = gtk_radio_menu_item_new_with_label(NULL, _("Viewport fullscreen mode"));
1442  gtk_widget_show(menuitem);
1443  gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
1444  group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(menuitem));
1445  if (cnnwin->priv->view_mode == VIEWPORT_FULLSCREEN_MODE)
1446  gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
1447  g_signal_connect(G_OBJECT(menuitem), "toggled", G_CALLBACK(rco_viewport_fullscreen_mode), cnnobj);
1448 
1449  menuitem = gtk_radio_menu_item_new_with_label(group, _("Scrolled fullscreen"));
1450  gtk_widget_show(menuitem);
1451  gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
1452  if (cnnwin->priv->view_mode == SCROLLED_FULLSCREEN_MODE)
1453  gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
1454  g_signal_connect(G_OBJECT(menuitem), "toggled", G_CALLBACK(rco_scrolled_fullscreen_mode), cnnobj);
1455 
1456  g_signal_connect(G_OBJECT(menu), "deactivate", G_CALLBACK(rcw_fullscreen_option_popdown), cnnwin);
1457 
1458 #if GTK_CHECK_VERSION(3, 22, 0)
1459  gtk_menu_popup_at_widget(GTK_MENU(menu), GTK_WIDGET(toggle),
1460  GDK_GRAVITY_SOUTH_WEST, GDK_GRAVITY_NORTH_WEST, NULL);
1461 #else
1462  gtk_menu_popup(GTK_MENU(menu), NULL, NULL, remmina_public_popup_position, cnnwin->priv->toolitem_fullscreen, 0,
1463  gtk_get_current_event_time());
1464 #endif
1465 }
1466 
1467 
1468 static void rcw_scaler_option_popdown(GtkWidget *widget, RemminaConnectionWindow *cnnwin)
1469 {
1470  TRACE_CALL(__func__);
1471  RemminaConnectionWindowPriv *priv = cnnwin->priv;
1472 
1473  if (priv->toolbar_is_reconfiguring)
1474  return;
1475  priv->sticky = FALSE;
1476  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(priv->scaler_option_button), FALSE);
1477  rcw_floating_toolbar_show(cnnwin, FALSE);
1478 }
1479 
1480 static void rcw_scaler_expand(GtkWidget *widget, RemminaConnectionWindow *cnnwin)
1481 {
1482  TRACE_CALL(__func__);
1483  RemminaConnectionObject *cnnobj;
1484 
1485  if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget)))
1486  return;
1487  cnnobj = rcw_get_visible_cnnobj(cnnwin);
1488  if (!cnnobj)
1489  return;
1490  remmina_protocol_widget_set_expand(REMMINA_PROTOCOL_WIDGET(cnnobj->proto), TRUE);
1491  remmina_file_set_int(cnnobj->remmina_file, "scaler_expand", TRUE);
1493 }
1494 static void rcw_scaler_keep_aspect(GtkWidget *widget, RemminaConnectionWindow *cnnwin)
1495 {
1496  TRACE_CALL(__func__);
1497  RemminaConnectionObject *cnnobj;
1498 
1499  if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget)))
1500  return;
1501  cnnobj = rcw_get_visible_cnnobj(cnnwin);
1502  if (!cnnobj)
1503  return;
1504 
1505  remmina_protocol_widget_set_expand(REMMINA_PROTOCOL_WIDGET(cnnobj->proto), FALSE);
1506  remmina_file_set_int(cnnobj->remmina_file, "scaler_expand", FALSE);
1508 }
1509 
1510 static void rcw_toolbar_scaler_option(GtkToolItem *toggle, RemminaConnectionWindow *cnnwin)
1511 {
1512  TRACE_CALL(__func__);
1514  RemminaConnectionObject *cnnobj;
1515  GtkWidget *menu;
1516  GtkWidget *menuitem;
1517  GSList *group;
1518  gboolean scaler_expand;
1519 
1520  if (cnnwin->priv->toolbar_is_reconfiguring)
1521  return;
1522 
1523  if (!(cnnobj = rcw_get_visible_cnnobj(cnnwin))) return;
1524  priv = cnnwin->priv;
1525 
1526  if (!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(toggle)))
1527  return;
1528 
1529  scaler_expand = remmina_protocol_widget_get_expand(REMMINA_PROTOCOL_WIDGET(cnnobj->proto));
1530 
1531  priv->sticky = TRUE;
1532 
1533  menu = gtk_menu_new();
1534 
1535  menuitem = gtk_radio_menu_item_new_with_label(NULL, _("Keep aspect ratio when scaled"));
1536  gtk_widget_show(menuitem);
1537  gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
1538  group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(menuitem));
1539  if (!scaler_expand)
1540  gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
1541  g_signal_connect(G_OBJECT(menuitem), "toggled", G_CALLBACK(rcw_scaler_keep_aspect), cnnwin);
1542 
1543  menuitem = gtk_radio_menu_item_new_with_label(group, _("Fill client window when scaled"));
1544  gtk_widget_show(menuitem);
1545  gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
1546  if (scaler_expand)
1547  gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
1548  g_signal_connect(G_OBJECT(menuitem), "toggled", G_CALLBACK(rcw_scaler_expand), cnnwin);
1549 
1550  g_signal_connect(G_OBJECT(menu), "deactivate", G_CALLBACK(rcw_scaler_option_popdown), cnnwin);
1551 
1552 #if GTK_CHECK_VERSION(3, 22, 0)
1553  gtk_menu_popup_at_widget(GTK_MENU(menu), GTK_WIDGET(toggle),
1554  GDK_GRAVITY_SOUTH_WEST, GDK_GRAVITY_NORTH_WEST, NULL);
1555 #else
1556  gtk_menu_popup(GTK_MENU(menu), NULL, NULL, remmina_public_popup_position, priv->toolitem_scale, 0,
1557  gtk_get_current_event_time());
1558 #endif
1559 }
1560 
1561 void rco_switch_page_activate(GtkMenuItem *menuitem, RemminaConnectionObject *cnnobj)
1562 {
1563  TRACE_CALL(__func__);
1564  RemminaConnectionWindowPriv *priv = cnnobj->cnnwin->priv;
1565  gint page_num;
1566 
1567  page_num = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(menuitem), "new-page-num"));
1568  gtk_notebook_set_current_page(GTK_NOTEBOOK(priv->notebook), page_num);
1569 }
1570 
1572 {
1573  TRACE_CALL(__func__);
1574  RemminaConnectionWindowPriv *priv = cnnwin->priv;
1575 
1576  priv->sticky = FALSE;
1577 
1578  gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(priv->toolitem_switch_page), FALSE);
1579  rcw_floating_toolbar_show(cnnwin, FALSE);
1580 }
1581 
1582 static void rcw_toolbar_switch_page(GtkToolItem *toggle, RemminaConnectionWindow *cnnwin)
1583 {
1584  TRACE_CALL(__func__);
1585 
1586  RemminaConnectionWindowPriv *priv = cnnwin->priv;
1587  RemminaConnectionObject *cnnobj;
1588 
1589  GtkWidget *menu;
1590  GtkWidget *menuitem;
1591  GtkWidget *image;
1592  gint i, n;
1593 
1594  if (priv->toolbar_is_reconfiguring)
1595  return;
1596  if (!(cnnobj = rcw_get_visible_cnnobj(cnnwin))) return;
1597 
1598  if (!gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(toggle)))
1599  return;
1600 
1601  priv->sticky = TRUE;
1602 
1603  menu = gtk_menu_new();
1604 
1605  n = gtk_notebook_get_n_pages(GTK_NOTEBOOK(priv->notebook));
1606  for (i = 0; i < n; i++) {
1607  cnnobj = rcw_get_cnnobj_at_page(cnnobj->cnnwin, i);
1608 
1609  menuitem = gtk_menu_item_new_with_label(remmina_file_get_string(cnnobj->remmina_file, "name"));
1610  gtk_widget_show(menuitem);
1611  gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
1612 
1613  image = gtk_image_new_from_icon_name(remmina_file_get_icon_name(cnnobj->remmina_file), GTK_ICON_SIZE_MENU);
1614  gtk_widget_show(image);
1615 
1616  g_object_set_data(G_OBJECT(menuitem), "new-page-num", GINT_TO_POINTER(i));
1617  g_signal_connect(G_OBJECT(menuitem), "activate", G_CALLBACK(rco_switch_page_activate), cnnobj);
1618  if (i == gtk_notebook_get_current_page(GTK_NOTEBOOK(priv->notebook)))
1619  gtk_widget_set_sensitive(menuitem, FALSE);
1620  }
1621 
1622  g_signal_connect(G_OBJECT(menu), "deactivate", G_CALLBACK(rcw_toolbar_switch_page_popdown),
1623  cnnwin);
1624 
1625 #if GTK_CHECK_VERSION(3, 22, 0)
1626  gtk_menu_popup_at_widget(GTK_MENU(menu), GTK_WIDGET(toggle),
1627  GDK_GRAVITY_SOUTH_WEST, GDK_GRAVITY_NORTH_WEST, NULL);
1628 #else
1629  gtk_menu_popup(GTK_MENU(menu), NULL, NULL, remmina_public_popup_position, widget, 0, gtk_get_current_event_time());
1630 #endif
1631 }
1632 
1634 {
1635  TRACE_CALL(__func__);
1636  RemminaConnectionWindowPriv *priv = cnnobj->cnnwin->priv;
1637  GtkToolItem *toolitem;
1638  RemminaScaleMode sc;
1639 
1640  toolitem = priv->toolitem_autofit;
1641  if (toolitem) {
1642  if (priv->view_mode != SCROLLED_WINDOW_MODE) {
1643  gtk_widget_set_sensitive(GTK_WIDGET(toolitem), FALSE);
1644  } else {
1645  sc = get_current_allowed_scale_mode(cnnobj, NULL, NULL);
1646  gtk_widget_set_sensitive(GTK_WIDGET(toolitem), sc == REMMINA_PROTOCOL_WIDGET_SCALE_MODE_NONE);
1647  }
1648  }
1649 }
1650 
1651 static void rco_change_scalemode(RemminaConnectionObject *cnnobj, gboolean bdyn, gboolean bscale)
1652 {
1653  RemminaScaleMode scalemode;
1654  RemminaConnectionWindowPriv *priv = cnnobj->cnnwin->priv;
1655 
1656  if (bdyn)
1658  else if (bscale)
1660  else
1662 
1663  remmina_protocol_widget_set_current_scale_mode(REMMINA_PROTOCOL_WIDGET(cnnobj->proto), scalemode);
1664  remmina_file_set_int(cnnobj->remmina_file, "scale", scalemode);
1665  gtk_widget_set_sensitive(GTK_WIDGET(priv->scaler_option_button), scalemode == REMMINA_PROTOCOL_WIDGET_SCALE_MODE_SCALED);
1667 
1668  remmina_protocol_widget_call_feature_by_type(REMMINA_PROTOCOL_WIDGET(cnnobj->proto),
1670 
1671  if (cnnobj->cnnwin->priv->view_mode != SCROLLED_WINDOW_MODE)
1672  rco_check_resize(cnnobj);
1673  if (GTK_IS_SCROLLED_WINDOW(cnnobj->scrolled_container)) {
1674  rco_set_scrolled_policy(scalemode, GTK_SCROLLED_WINDOW(cnnobj->scrolled_container));
1675  }
1676 }
1677 
1678 static void rcw_toolbar_dynres(GtkToolItem *toggle, RemminaConnectionWindow *cnnwin)
1679 {
1680  TRACE_CALL(__func__);
1681  gboolean bdyn, bscale;
1682  RemminaConnectionObject *cnnobj;
1683 
1684  if (cnnwin->priv->toolbar_is_reconfiguring)
1685  return;
1686  if (!(cnnobj = rcw_get_visible_cnnobj(cnnwin))) return;
1687 
1688  if (cnnobj->connected) {
1689  bdyn = gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(toggle));
1690  bscale = gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(cnnobj->cnnwin->priv->toolitem_scale));
1691 
1692  if (bdyn && bscale) {
1693  gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(cnnobj->cnnwin->priv->toolitem_scale), FALSE);
1694  bscale = FALSE;
1695  }
1696 
1697  rco_change_scalemode(cnnobj, bdyn, bscale);
1698  }
1699 }
1700 
1701 static void rcw_toolbar_scaled_mode(GtkToolItem *toggle, RemminaConnectionWindow *cnnwin)
1702 {
1703  TRACE_CALL(__func__);
1704  gboolean bdyn, bscale;
1705  RemminaConnectionObject *cnnobj;
1706 
1707  if (cnnwin->priv->toolbar_is_reconfiguring)
1708  return;
1709  if (!(cnnobj = rcw_get_visible_cnnobj(cnnwin))) return;
1710 
1711  bdyn = gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(cnnobj->cnnwin->priv->toolitem_dynres));
1712  bscale = gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(toggle));
1713 
1714  if (bdyn && bscale) {
1715  gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(cnnobj->cnnwin->priv->toolitem_dynres), FALSE);
1716  bdyn = FALSE;
1717  }
1718 
1719  rco_change_scalemode(cnnobj, bdyn, bscale);
1720 }
1721 
1722 static void rcw_toolbar_multi_monitor_mode(GtkToolItem *toggle, RemminaConnectionWindow *cnnwin)
1723 {
1724  TRACE_CALL(__func__);
1725  RemminaConnectionObject *cnnobj;
1726 
1727  if (cnnwin->priv->toolbar_is_reconfiguring)
1728  return;
1729 
1730  if (!(cnnobj = rcw_get_visible_cnnobj(cnnwin))) return;
1731 
1732  if (gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(toggle))) {
1733  REMMINA_DEBUG("Saving multimon as 1");
1734  remmina_file_set_int(cnnobj->remmina_file, "multimon", 1);
1736  remmina_protocol_widget_call_feature_by_type(REMMINA_PROTOCOL_WIDGET(cnnobj->proto),
1738  if (!gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(cnnwin->priv->toolitem_fullscreen)))
1739  gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(cnnwin->priv->toolitem_fullscreen), TRUE);
1740  } else {
1741  REMMINA_DEBUG("Saving multimon as 0");
1742  remmina_file_set_int(cnnobj->remmina_file, "multimon", 0);
1744  rcw_toolbar_fullscreen(NULL, cnnwin);
1745  }
1746 }
1747 
1748 static void rcw_toolbar_open_main(GtkToolItem *toggle, RemminaConnectionWindow *cnnwin)
1749 {
1750  TRACE_CALL(__func__);
1751 
1752  if (cnnwin->priv->toolbar_is_reconfiguring)
1753  return;
1754 
1756 }
1757 
1758 static void rcw_toolbar_preferences_popdown(GtkToolItem *toggle, RemminaConnectionWindow *cnnwin)
1759 {
1760  TRACE_CALL(__func__);
1761  RemminaConnectionObject *cnnobj;
1762 
1763  if (cnnwin->priv->toolbar_is_reconfiguring)
1764  return;
1765  if (!(cnnobj = rcw_get_visible_cnnobj(cnnwin))) return;
1766 
1767  cnnobj->cnnwin->priv->sticky = FALSE;
1768 
1769  gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(cnnobj->cnnwin->priv->toolitem_preferences), FALSE);
1770  rcw_floating_toolbar_show(cnnwin, FALSE);
1771 }
1772 
1773 void rcw_toolbar_menu_popdown(GtkToolItem *toggle, RemminaConnectionWindow *cnnwin)
1774 {
1775  TRACE_CALL(__func__);
1776  RemminaConnectionWindowPriv *priv = cnnwin->priv;
1777 
1778  if (priv->toolbar_is_reconfiguring)
1779  return;
1780 
1781  priv->sticky = FALSE;
1782 
1783  gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(priv->toolitem_menu), FALSE);
1784  rcw_floating_toolbar_show(cnnwin, FALSE);
1785 }
1786 
1787 void rcw_toolbar_tools_popdown(GtkToolItem *toggle, RemminaConnectionWindow *cnnwin)
1788 {
1789  TRACE_CALL(__func__);
1790  RemminaConnectionWindowPriv *priv = cnnwin->priv;
1791 
1792  if (priv->toolbar_is_reconfiguring)
1793  return;
1794 
1795  priv->sticky = FALSE;
1796 
1797  gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(priv->toolitem_tools), FALSE);
1798  rcw_floating_toolbar_show(cnnwin, FALSE);
1799 }
1800 
1801 static void rco_call_protocol_feature_radio(GtkMenuItem *menuitem, RemminaConnectionObject *cnnobj)
1802 {
1803  TRACE_CALL(__func__);
1804  RemminaProtocolFeature *feature;
1805  gpointer value;
1806 
1807  if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(menuitem))) {
1808  feature = (RemminaProtocolFeature *)g_object_get_data(G_OBJECT(menuitem), "feature-type");
1809  value = g_object_get_data(G_OBJECT(menuitem), "feature-value");
1810 
1811  remmina_file_set_string(cnnobj->remmina_file, (const gchar *)feature->opt2, (const gchar *)value);
1812  remmina_protocol_widget_call_feature_by_ref(REMMINA_PROTOCOL_WIDGET(cnnobj->proto), feature);
1813  }
1814 }
1815 
1816 static void rco_call_protocol_feature_check(GtkMenuItem *menuitem, RemminaConnectionObject *cnnobj)
1817 {
1818  TRACE_CALL(__func__);
1819  RemminaProtocolFeature *feature;
1820  gboolean value;
1821 
1822  feature = (RemminaProtocolFeature *)g_object_get_data(G_OBJECT(menuitem), "feature-type");
1823  value = gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(menuitem));
1824  remmina_file_set_int(cnnobj->remmina_file, (const gchar *)feature->opt2, value);
1825  remmina_protocol_widget_call_feature_by_ref(REMMINA_PROTOCOL_WIDGET(cnnobj->proto), feature);
1826 }
1827 
1828 static void rco_call_protocol_feature_activate(GtkMenuItem *menuitem, RemminaConnectionObject *cnnobj)
1829 {
1830  TRACE_CALL(__func__);
1831  RemminaProtocolFeature *feature;
1832 
1833  feature = (RemminaProtocolFeature *)g_object_get_data(G_OBJECT(menuitem), "feature-type");
1834  remmina_protocol_widget_call_feature_by_ref(REMMINA_PROTOCOL_WIDGET(cnnobj->proto), feature);
1835 }
1836 
1838  GtkWidget *menu, const RemminaProtocolFeature *feature, const gchar *domain, gboolean enabled)
1839 {
1840  TRACE_CALL(__func__);
1841  GtkWidget *menuitem;
1842  GSList *group;
1843  gint i;
1844  const gchar **list;
1845  const gchar *value;
1846 
1847  group = NULL;
1848  value = remmina_file_get_string(remminafile, (const gchar *)feature->opt2);
1849  list = (const gchar **)feature->opt3;
1850  for (i = 0; list[i]; i += 2) {
1851  menuitem = gtk_radio_menu_item_new_with_label(group, g_dgettext(domain, list[i + 1]));
1852  group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(menuitem));
1853  gtk_widget_show(menuitem);
1854  gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
1855 
1856  if (enabled) {
1857  g_object_set_data(G_OBJECT(menuitem), "feature-type", (gpointer)feature);
1858  g_object_set_data(G_OBJECT(menuitem), "feature-value", (gpointer)list[i]);
1859 
1860  if (value && g_strcmp0(list[i], value) == 0)
1861  gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
1862 
1863  g_signal_connect(G_OBJECT(menuitem), "toggled",
1864  G_CALLBACK(rco_call_protocol_feature_radio), cnnobj);
1865  } else {
1866  gtk_widget_set_sensitive(menuitem, FALSE);
1867  }
1868  }
1869 }
1870 
1872  GtkWidget *menu, const RemminaProtocolFeature *feature,
1873  const gchar *domain, gboolean enabled)
1874 {
1875  TRACE_CALL(__func__);
1876  GtkWidget *menuitem;
1877 
1878  menuitem = gtk_check_menu_item_new_with_label(g_dgettext(domain, (const gchar *)feature->opt3));
1879  gtk_widget_show(menuitem);
1880  gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
1881 
1882  if (enabled) {
1883  g_object_set_data(G_OBJECT(menuitem), "feature-type", (gpointer)feature);
1884 
1885  gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem),
1886  remmina_file_get_int(cnnobj->remmina_file, (const gchar *)feature->opt2, FALSE));
1887 
1888  g_signal_connect(G_OBJECT(menuitem), "toggled",
1889  G_CALLBACK(rco_call_protocol_feature_check), cnnobj);
1890  } else {
1891  gtk_widget_set_sensitive(menuitem, FALSE);
1892  }
1893 }
1894 
1895 static void rcw_toolbar_preferences(GtkToolItem *toggle, RemminaConnectionWindow *cnnwin)
1896 {
1897  TRACE_CALL(__func__);
1899  RemminaConnectionObject *cnnobj;
1900  const RemminaProtocolFeature *feature;
1901  GtkWidget *menu;
1902  GtkWidget *menuitem;
1903  gboolean separator;
1904  gchar *domain;
1905  gboolean enabled;
1906 
1907  if (cnnwin->priv->toolbar_is_reconfiguring)
1908  return;
1909  if (!(cnnobj = rcw_get_visible_cnnobj(cnnwin))) return;
1910  priv = cnnobj->cnnwin->priv;
1911 
1912  if (!gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(toggle)))
1913  return;
1914 
1915  priv->sticky = TRUE;
1916 
1917  separator = FALSE;
1918 
1919  domain = remmina_protocol_widget_get_domain(REMMINA_PROTOCOL_WIDGET(cnnobj->proto));
1920  menu = gtk_menu_new();
1921  for (feature = remmina_protocol_widget_get_features(REMMINA_PROTOCOL_WIDGET(cnnobj->proto)); feature && feature->type;
1922  feature++) {
1923  if (feature->type != REMMINA_PROTOCOL_FEATURE_TYPE_PREF)
1924  continue;
1925 
1926  if (separator) {
1927  menuitem = gtk_separator_menu_item_new();
1928  gtk_widget_show(menuitem);
1929  gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
1930  separator = FALSE;
1931  }
1932  enabled = remmina_protocol_widget_query_feature_by_ref(REMMINA_PROTOCOL_WIDGET(cnnobj->proto), feature);
1933  switch (GPOINTER_TO_INT(feature->opt1)) {
1934  case REMMINA_PROTOCOL_FEATURE_PREF_RADIO:
1935  rcw_toolbar_preferences_radio(cnnobj, cnnobj->remmina_file, menu, feature,
1936  domain, enabled);
1937  separator = TRUE;
1938  break;
1939  case REMMINA_PROTOCOL_FEATURE_PREF_CHECK:
1940  rcw_toolbar_preferences_check(cnnobj, menu, feature,
1941  domain, enabled);
1942  break;
1943  }
1944  }
1945 
1946  g_free(domain);
1947 
1948  g_signal_connect(G_OBJECT(menu), "deactivate", G_CALLBACK(rcw_toolbar_preferences_popdown), cnnwin);
1949 
1950 #if GTK_CHECK_VERSION(3, 22, 0)
1951  gtk_menu_popup_at_widget(GTK_MENU(menu), GTK_WIDGET(toggle),
1952  GDK_GRAVITY_SOUTH_WEST, GDK_GRAVITY_NORTH_WEST, NULL);
1953 #else
1954  gtk_menu_popup(GTK_MENU(menu), NULL, NULL, remmina_public_popup_position, widget, 0, gtk_get_current_event_time());
1955 #endif
1956 }
1957 
1959 {
1960  TRACE_CALL(__func__);
1961  gchar *s;
1962 
1963  switch (menuitem->item_type) {
1966  break;
1969  break;
1971  s = g_strdup_printf("%s,%s", menuitem->protocol, menuitem->name);
1973  g_free(s);
1974  break;
1975  }
1976 }
1977 
1978 static void rcw_toolbar_menu(GtkToolItem *toggle, RemminaConnectionWindow *cnnwin)
1979 {
1980  TRACE_CALL(__func__);
1982  RemminaConnectionObject *cnnobj;
1983  GtkWidget *menu;
1984  GtkWidget *menuitem = NULL;
1985 
1986  if (cnnwin->priv->toolbar_is_reconfiguring)
1987  return;
1988 
1989  if (!(cnnobj = rcw_get_visible_cnnobj(cnnwin))) return;
1990  priv = cnnobj->cnnwin->priv;
1991 
1992  if (!gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(toggle)))
1993  return;
1994 
1995  priv->sticky = TRUE;
1996 
1997  menu = remmina_applet_menu_new();
1998  remmina_applet_menu_set_hide_count(REMMINA_APPLET_MENU(menu), remmina_pref.applet_hide_count);
1999  remmina_applet_menu_populate(REMMINA_APPLET_MENU(menu));
2000 
2001  g_signal_connect(G_OBJECT(menu), "launch-item", G_CALLBACK(rcw_toolbar_menu_on_launch_item), NULL);
2002  //g_signal_connect(G_OBJECT(menu), "edit-item", G_CALLBACK(rcw_toolbar_menu_on_edit_item), NULL);
2003  menuitem = gtk_separator_menu_item_new();
2004  gtk_widget_show(menuitem);
2005  gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
2006 #if GTK_CHECK_VERSION(3, 22, 0)
2007  gtk_menu_popup_at_widget(GTK_MENU(menu), GTK_WIDGET(toggle),
2008  GDK_GRAVITY_SOUTH_WEST, GDK_GRAVITY_NORTH_WEST, NULL);
2009 #else
2010  gtk_menu_popup(GTK_MENU(menu), NULL, NULL, remmina_public_popup_position, widget, 0, gtk_get_current_event_time());
2011 #endif
2012  g_signal_connect(G_OBJECT(menu), "deactivate", G_CALLBACK(rcw_toolbar_menu_popdown), cnnwin);
2013 }
2014 
2015 static void rcw_toolbar_tools(GtkToolItem *toggle, RemminaConnectionWindow *cnnwin)
2016 {
2017  TRACE_CALL(__func__);
2019  RemminaConnectionObject *cnnobj;
2020  const RemminaProtocolFeature *feature;
2021  GtkWidget *menu;
2022  GtkWidget *menuitem = NULL;
2023  GtkMenu *submenu_keystrokes;
2024  const gchar *domain;
2025  gboolean enabled;
2026  gchar **keystrokes;
2027  gchar **keystroke_values;
2028  gint i;
2029 
2030  if (cnnwin->priv->toolbar_is_reconfiguring)
2031  return;
2032  if (!(cnnobj = rcw_get_visible_cnnobj(cnnwin))) return;
2033  priv = cnnobj->cnnwin->priv;
2034 
2035  if (!gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(toggle)))
2036  return;
2037 
2038  priv->sticky = TRUE;
2039 
2040  domain = remmina_protocol_widget_get_domain(REMMINA_PROTOCOL_WIDGET(cnnobj->proto));
2041  menu = gtk_menu_new();
2042  for (feature = remmina_protocol_widget_get_features(REMMINA_PROTOCOL_WIDGET(cnnobj->proto)); feature && feature->type;
2043  feature++) {
2044  if (feature->type != REMMINA_PROTOCOL_FEATURE_TYPE_TOOL)
2045  continue;
2046 
2047  if (feature->opt1)
2048  menuitem = gtk_menu_item_new_with_label(g_dgettext(domain, (const gchar *)feature->opt1));
2049  if (feature->opt3)
2050  rcw_set_tooltip(menuitem, "", GPOINTER_TO_UINT(feature->opt3), 0);
2051  gtk_widget_show(menuitem);
2052  gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
2053 
2054  enabled = remmina_protocol_widget_query_feature_by_ref(REMMINA_PROTOCOL_WIDGET(cnnobj->proto), feature);
2055  if (enabled) {
2056  g_object_set_data(G_OBJECT(menuitem), "feature-type", (gpointer)feature);
2057 
2058  g_signal_connect(G_OBJECT(menuitem), "activate",
2059  G_CALLBACK(rco_call_protocol_feature_activate), cnnobj);
2060  } else {
2061  gtk_widget_set_sensitive(menuitem, FALSE);
2062  }
2063  }
2064 
2065  /* If the plugin accepts keystrokes include the keystrokes menu */
2066  if (remmina_protocol_widget_plugin_receives_keystrokes(REMMINA_PROTOCOL_WIDGET(cnnobj->proto))) {
2067  /* Get the registered keystrokes list */
2068  keystrokes = g_strsplit(remmina_pref.keystrokes, STRING_DELIMITOR, -1);
2069  if (g_strv_length(keystrokes)) {
2070  /* Add a keystrokes submenu */
2071  menuitem = gtk_menu_item_new_with_label(_("Keystrokes"));
2072  submenu_keystrokes = GTK_MENU(gtk_menu_new());
2073  gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), GTK_WIDGET(submenu_keystrokes));
2074  gtk_widget_show(menuitem);
2075  gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
2076  /* Add each registered keystroke */
2077  for (i = 0; i < g_strv_length(keystrokes); i++) {
2078  keystroke_values = g_strsplit(keystrokes[i], STRING_DELIMITOR2, -1);
2079  if (g_strv_length(keystroke_values) > 1) {
2080  /* Add the keystroke if no description was available */
2081  menuitem = gtk_menu_item_new_with_label(
2082  g_strdup(keystroke_values[strlen(keystroke_values[0]) ? 0 : 1]));
2083  g_object_set_data(G_OBJECT(menuitem), "keystrokes", g_strdup(keystroke_values[1]));
2084  g_signal_connect_swapped(G_OBJECT(menuitem), "activate",
2086  REMMINA_PROTOCOL_WIDGET(cnnobj->proto));
2087  gtk_widget_show(menuitem);
2088  gtk_menu_shell_append(GTK_MENU_SHELL(submenu_keystrokes), menuitem);
2089  }
2090  g_strfreev(keystroke_values);
2091  }
2092  menuitem = gtk_menu_item_new_with_label(_("Send clipboard content as keystrokes"));
2093  static gchar k_tooltip[] =
2094  N_("CAUTION: Pasted text will be sent as a sequence of key-codes as if typed on your local keyboard.\n"
2095  "\n"
2096  " • For best results use same keyboard settings for both, client and server.\n"
2097  "\n"
2098  " • If client-keyboard is different from server-keyboard the received text can contain wrong or erroneous characters.\n"
2099  "\n"
2100  " • Unicode characters and other special characters that can't be translated to local key-codes won’t be sent to the server.\n"
2101  "\n");
2102  gtk_widget_set_tooltip_text(menuitem, k_tooltip);
2103  gtk_menu_shell_append(GTK_MENU_SHELL(submenu_keystrokes), menuitem);
2104  g_signal_connect_swapped(G_OBJECT(menuitem), "activate",
2106  REMMINA_PROTOCOL_WIDGET(cnnobj->proto));
2107  gtk_widget_show(menuitem);
2108  }
2109  g_strfreev(keystrokes);
2110  }
2111 
2112  g_signal_connect(G_OBJECT(menu), "deactivate", G_CALLBACK(rcw_toolbar_tools_popdown), cnnwin);
2113 
2114 #if GTK_CHECK_VERSION(3, 22, 0)
2115  gtk_menu_popup_at_widget(GTK_MENU(menu), GTK_WIDGET(toggle),
2116  GDK_GRAVITY_SOUTH_WEST, GDK_GRAVITY_NORTH_WEST, NULL);
2117 #else
2118  gtk_menu_popup(GTK_MENU(menu), NULL, NULL, remmina_public_popup_position, widget, 0, gtk_get_current_event_time());
2119 #endif
2120 }
2121 
2122 static void rcw_toolbar_duplicate(GtkToolItem *toggle, RemminaConnectionWindow *cnnwin)
2123 {
2124  TRACE_CALL(__func__);
2125 
2126  RemminaConnectionObject *cnnobj;
2127 
2128  if (cnnwin->priv->toolbar_is_reconfiguring)
2129  return;
2130  if (!(cnnobj = rcw_get_visible_cnnobj(cnnwin))) return;
2131 
2133 
2135 }
2136 
2137 static void rcw_toolbar_screenshot(GtkToolItem *toggle, RemminaConnectionWindow *cnnwin)
2138 {
2139  TRACE_CALL(__func__);
2140 
2141  GdkPixbuf *screenshot;
2142  GdkWindow *active_window;
2143  cairo_t *cr;
2144  gint width, height;
2145  GString *pngstr;
2146  gchar *pngname;
2147  GtkWidget *dialog;
2150  RemminaConnectionObject *cnnobj;
2151  cairo_surface_t *srcsurface;
2152  cairo_format_t cairo_format;
2153  cairo_surface_t *surface;
2154  int stride;
2155 
2156  if (cnnwin->priv->toolbar_is_reconfiguring)
2157  return;
2158  if (!(cnnobj = rcw_get_visible_cnnobj(cnnwin))) return;
2159 
2160  GDateTime *date = g_date_time_new_now_utc();
2161 
2162  // We will take a screenshot of the currently displayed RemminaProtocolWidget.
2163  gp = REMMINA_PROTOCOL_WIDGET(cnnobj->proto);
2164 
2165  gchar *denyclip = remmina_pref_get_value("deny_screenshot_clipboard");
2166 
2167  REMMINA_DEBUG("deny_screenshot_clipboard is set to %s", denyclip);
2168 
2169  GtkClipboard *c = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD);
2170 
2171  // Ask the plugin if it can give us a screenshot
2173  // Good, we have a screenshot from the plugin !
2174 
2175  REMMINA_DEBUG("Screenshot from plugin: w=%d h=%d bpp=%d bytespp=%d\n",
2176  rpsd.width, rpsd.height, rpsd.bitsPerPixel, rpsd.bytesPerPixel);
2177 
2178  width = rpsd.width;
2179  height = rpsd.height;
2180 
2181  if (rpsd.bitsPerPixel == 32)
2182  cairo_format = CAIRO_FORMAT_ARGB32;
2183  else if (rpsd.bitsPerPixel == 24)
2184  cairo_format = CAIRO_FORMAT_RGB24;
2185  else
2186  cairo_format = CAIRO_FORMAT_RGB16_565;
2187 
2188  stride = cairo_format_stride_for_width(cairo_format, width);
2189 
2190  srcsurface = cairo_image_surface_create_for_data(rpsd.buffer, cairo_format, width, height, stride);
2191  // Transfer the PixBuf in the main clipboard selection
2192  if (denyclip && (g_strcmp0(denyclip, "true")))
2193  gtk_clipboard_set_image(c, gdk_pixbuf_get_from_surface(
2194  srcsurface, 0, 0, width, height));
2195  surface = cairo_image_surface_create(CAIRO_FORMAT_RGB24, width, height);
2196  cr = cairo_create(surface);
2197  cairo_set_source_surface(cr, srcsurface, 0, 0);
2198  cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
2199  cairo_paint(cr);
2200  cairo_surface_destroy(srcsurface);
2201 
2202  free(rpsd.buffer);
2203  } else {
2204  // The plugin is not releasing us a screenshot, just try to catch one via GTK
2205 
2206  /* Warn the user if image is distorted */
2207  if (cnnobj->plugin_can_scale &&
2209  dialog = gtk_message_dialog_new(NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_WARNING, GTK_BUTTONS_OK,
2210  _("Turn off scaling to avoid screenshot distortion."));
2211  g_signal_connect(G_OBJECT(dialog), "response", G_CALLBACK(gtk_widget_destroy), NULL);
2212  gtk_widget_show(dialog);
2213  }
2214 
2215  // Get the screenshot.
2216  active_window = gtk_widget_get_window(GTK_WIDGET(gp));
2217  // width = gdk_window_get_width(gtk_widget_get_window(GTK_WIDGET(cnnobj->cnnwin)));
2218  width = gdk_window_get_width(active_window);
2219  // height = gdk_window_get_height(gtk_widget_get_window(GTK_WIDGET(cnnobj->cnnwin)));
2220  height = gdk_window_get_height(active_window);
2221 
2222  screenshot = gdk_pixbuf_get_from_window(active_window, 0, 0, width, height);
2223  if (screenshot == NULL)
2224  g_print("gdk_pixbuf_get_from_window failed\n");
2225 
2226  // Transfer the PixBuf in the main clipboard selection
2227  if (denyclip && (g_strcmp0(denyclip, "true")))
2228  gtk_clipboard_set_image(c, screenshot);
2229  // Prepare the destination Cairo surface.
2230  surface = cairo_image_surface_create(CAIRO_FORMAT_RGB24, width, height);
2231  cr = cairo_create(surface);
2232 
2233  // Copy the source pixbuf to the surface and paint it.
2234  gdk_cairo_set_source_pixbuf(cr, screenshot, 0, 0);
2235  cairo_paint(cr);
2236 
2237  // Deallocate screenshot pixbuf
2238  g_object_unref(screenshot);
2239  }
2240 
2241  //home/antenore/Pictures/remmina_%p_%h_%Y %m %d-%H%M%S.png pngname
2242  //home/antenore/Pictures/remmina_st_ _2018 9 24-151958.240374.png
2243 
2244  pngstr = g_string_new(g_strdup_printf("%s/%s.png",
2245  remmina_pref.screenshot_path,
2246  remmina_pref.screenshot_name));
2247  remmina_utils_string_replace_all(pngstr, "%p",
2248  remmina_file_get_string(cnnobj->remmina_file, "name"));
2249  remmina_utils_string_replace_all(pngstr, "%h",
2250  remmina_file_get_string(cnnobj->remmina_file, "server"));
2251  remmina_utils_string_replace_all(pngstr, "%Y",
2252  g_strdup_printf("%d", g_date_time_get_year(date)));
2253  remmina_utils_string_replace_all(pngstr, "%m", g_strdup_printf("%02d",
2254  g_date_time_get_month(date)));
2255  remmina_utils_string_replace_all(pngstr, "%d",
2256  g_strdup_printf("%02d", g_date_time_get_day_of_month(date)));
2257  remmina_utils_string_replace_all(pngstr, "%H",
2258  g_strdup_printf("%02d", g_date_time_get_hour(date)));
2259  remmina_utils_string_replace_all(pngstr, "%M",
2260  g_strdup_printf("%02d", g_date_time_get_minute(date)));
2261  remmina_utils_string_replace_all(pngstr, "%S",
2262  g_strdup_printf("%02d", g_date_time_get_second(date)));
2263  g_date_time_unref(date);
2264  pngname = g_string_free(pngstr, FALSE);
2265 
2266  cairo_surface_write_to_png(surface, pngname);
2267 
2268  /* send a desktop notification */
2269  if (g_file_test(pngname, G_FILE_TEST_EXISTS))
2270  remmina_public_send_notification("remmina-screenshot-is-ready-id", _("Screenshot taken"), pngname);
2271 
2272  //Clean up and return.
2273  cairo_destroy(cr);
2274  cairo_surface_destroy(surface);
2275 }
2276 
2277 static void rcw_toolbar_minimize(GtkToolItem *toggle, RemminaConnectionWindow *cnnwin)
2278 {
2279  TRACE_CALL(__func__);
2280 
2281  if (cnnwin->priv->toolbar_is_reconfiguring)
2282  return;
2283 
2284  rcw_floating_toolbar_show(cnnwin, FALSE);
2285  gtk_window_iconify(GTK_WINDOW(cnnwin));
2286 }
2287 
2288 static void rcw_toolbar_disconnect(GtkToolItem *toggle, RemminaConnectionWindow *cnnwin)
2289 {
2290  TRACE_CALL(__func__);
2291  RemminaConnectionObject *cnnobj;
2292 
2293  if (cnnwin->priv->toolbar_is_reconfiguring)
2294  return;
2295  if (!(cnnobj = rcw_get_visible_cnnobj(cnnwin))) return;
2297 }
2298 
2299 static void rcw_toolbar_grab(GtkToolItem *toggle, RemminaConnectionWindow *cnnwin)
2300 {
2301  TRACE_CALL(__func__);
2302  gboolean capture;
2303  RemminaConnectionObject *cnnobj;
2304 
2305  if (cnnwin->priv->toolbar_is_reconfiguring)
2306  return;
2307  if (!(cnnobj = rcw_get_visible_cnnobj(cnnwin))) return;
2308 
2309  capture = gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(toggle));
2310 
2311  if (cnnobj->connected){
2312  remmina_file_set_int(cnnobj->remmina_file, "keyboard_grab", capture);
2313  }
2314 
2315  if (capture && cnnobj->connected) {
2316 
2317 #if DEBUG_KB_GRABBING
2318  printf("DEBUG_KB_GRABBING: Grabbing for button\n");
2319 #endif
2320  rcw_keyboard_grab(cnnobj->cnnwin);
2321  if (cnnobj->cnnwin->priv->pointer_entered)
2322  rcw_pointer_grab(cnnobj->cnnwin);
2323  } else {
2324  rcw_kp_ungrab(cnnobj->cnnwin);
2325  }
2326 }
2327 
2328 static GtkWidget *
2330 {
2331  TRACE_CALL(__func__);
2332  RemminaConnectionWindowPriv *priv = cnnwin->priv;
2333  RemminaConnectionObject *cnnobj;
2334  GtkWidget *toolbar;
2335  GtkToolItem *toolitem;
2336  GtkWidget *widget;
2337  GtkWidget *arrow;
2338 
2339  GdkDisplay *display;
2340  gint n_monitors;
2341 
2342  display = gdk_display_get_default();
2343  n_monitors = gdk_display_get_n_monitors(display);
2344 
2345  cnnobj = rcw_get_visible_cnnobj(cnnwin);
2346 
2347  priv->toolbar_is_reconfiguring = TRUE;
2348 
2349  toolbar = gtk_toolbar_new();
2350  gtk_widget_show(toolbar);
2351  gtk_toolbar_set_style(GTK_TOOLBAR(toolbar), GTK_TOOLBAR_ICONS);
2352 
2353  /* Main actions */
2354 
2355  /* Menu */
2356  toolitem = gtk_toggle_tool_button_new();
2357  gtk_tool_button_set_icon_name(GTK_TOOL_BUTTON(toolitem), "view-more-symbolic");
2358  gtk_tool_button_set_label(GTK_TOOL_BUTTON(toolitem), _("_Menu"));
2359  gtk_tool_item_set_tooltip_text(toolitem, _("Menu"));
2360  gtk_toolbar_insert(GTK_TOOLBAR(toolbar), toolitem, -1);
2361  gtk_widget_show(GTK_WIDGET(toolitem));
2362  g_signal_connect(G_OBJECT(toolitem), "toggled", G_CALLBACK(rcw_toolbar_menu), cnnwin);
2363  priv->toolitem_menu = toolitem;
2364 
2365  /* Open Main window */
2366  toolitem = gtk_tool_button_new(NULL, "Open Remmina Main window");
2367  gtk_tool_button_set_icon_name(GTK_TOOL_BUTTON(toolitem), "go-home-symbolic");
2368  gtk_tool_item_set_tooltip_text(toolitem, _("Open the Remmina main window"));
2369  gtk_toolbar_insert(GTK_TOOLBAR(toolbar), toolitem, -1);
2370  gtk_widget_show(GTK_WIDGET(toolitem));
2371  g_signal_connect(G_OBJECT(toolitem), "clicked", G_CALLBACK(rcw_toolbar_open_main), cnnwin);
2372 
2373  priv->toolitem_new = toolitem;
2374 
2375  /* Duplicate session */
2376  toolitem = gtk_tool_button_new(NULL, "Duplicate connection");
2377  gtk_tool_button_set_icon_name(GTK_TOOL_BUTTON(toolitem), "org.remmina.Remmina-duplicate-symbolic");
2378  gtk_tool_item_set_tooltip_text(toolitem, _("Duplicate current connection"));
2379  gtk_toolbar_insert(GTK_TOOLBAR(toolbar), toolitem, -1);
2380  gtk_widget_show(GTK_WIDGET(toolitem));
2381  g_signal_connect(G_OBJECT(toolitem), "clicked", G_CALLBACK(rcw_toolbar_duplicate), cnnwin);
2382  if (!cnnobj)
2383  gtk_widget_set_sensitive(GTK_WIDGET(toolitem), FALSE);
2384 
2385  priv->toolitem_duplicate = toolitem;
2386 
2387  /* Separator */
2388  toolitem = gtk_separator_tool_item_new();
2389  gtk_toolbar_insert(GTK_TOOLBAR(toolbar), toolitem, -1);
2390  gtk_widget_show(GTK_WIDGET(toolitem));
2391 
2392  /* Auto-Fit */
2393  toolitem = gtk_tool_button_new(NULL, NULL);
2394  gtk_tool_button_set_icon_name(GTK_TOOL_BUTTON(toolitem), "org.remmina.Remmina-fit-window-symbolic");
2395  rcw_set_tooltip(GTK_WIDGET(toolitem), _("Resize the window to fit in remote resolution"),
2396  remmina_pref.shortcutkey_autofit, 0);
2397  g_signal_connect(G_OBJECT(toolitem), "clicked", G_CALLBACK(rcw_toolbar_autofit), cnnwin);
2398  priv->toolitem_autofit = toolitem;
2399  gtk_toolbar_insert(GTK_TOOLBAR(toolbar), toolitem, -1);
2400  gtk_widget_show(GTK_WIDGET(toolitem));
2401 
2402 
2403  /* Fullscreen toggle */
2404  toolitem = gtk_toggle_tool_button_new();
2405  gtk_tool_button_set_icon_name(GTK_TOOL_BUTTON(toolitem), "org.remmina.Remmina-fullscreen-symbolic");
2406  rcw_set_tooltip(GTK_WIDGET(toolitem), _("Toggle fullscreen mode"),
2407  remmina_pref.shortcutkey_fullscreen, 0);
2408  gtk_toolbar_insert(GTK_TOOLBAR(toolbar), toolitem, -1);
2409  gtk_widget_show(GTK_WIDGET(toolitem));
2410  priv->toolitem_fullscreen = toolitem;
2411  if (kioskmode) {
2412  gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(toolitem), FALSE);
2413  } else {
2414  gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(toolitem), mode != SCROLLED_WINDOW_MODE);
2415  g_signal_connect(G_OBJECT(toolitem), "clicked", G_CALLBACK(rcw_toolbar_fullscreen), cnnwin);
2416  }
2417 
2418  /* Fullscreen drop-down options */
2419  toolitem = gtk_tool_item_new();
2420  gtk_widget_show(GTK_WIDGET(toolitem));
2421  gtk_toolbar_insert(GTK_TOOLBAR(toolbar), toolitem, -1);
2422  widget = gtk_toggle_button_new();
2423  gtk_widget_show(widget);
2424  gtk_container_set_border_width(GTK_CONTAINER(widget), 0);
2425  gtk_button_set_relief(GTK_BUTTON(widget), GTK_RELIEF_NONE);
2426 #if GTK_CHECK_VERSION(3, 20, 0)
2427  gtk_widget_set_focus_on_click(GTK_WIDGET(widget), FALSE);
2428  if (remmina_pref.small_toolbutton)
2429  gtk_widget_set_name(widget, "remmina-small-button");
2430 
2431 #else
2432  gtk_button_set_focus_on_click(GTK_BUTTON(widget), FALSE);
2433 #endif
2434  gtk_container_add(GTK_CONTAINER(toolitem), widget);
2435 
2436 #if GTK_CHECK_VERSION(3, 14, 0)
2437  arrow = gtk_image_new_from_icon_name("org.remmina.Remmina-pan-down-symbolic", GTK_ICON_SIZE_SMALL_TOOLBAR);
2438 #else
2439  arrow = gtk_arrow_new(GTK_ARROW_DOWN, GTK_SHADOW_NONE);
2440 #endif
2441  gtk_widget_show(arrow);
2442  gtk_container_add(GTK_CONTAINER(widget), arrow);
2443  g_signal_connect(G_OBJECT(widget), "toggled", G_CALLBACK(rcw_toolbar_fullscreen_option), cnnwin);
2444  priv->fullscreen_option_button = widget;
2445  if (mode == SCROLLED_WINDOW_MODE)
2446  gtk_widget_set_sensitive(GTK_WIDGET(widget), FALSE);
2447 
2448  /* Multi monitor */
2449  if (n_monitors > 1) {
2450  toolitem = gtk_toggle_tool_button_new();
2451  gtk_tool_button_set_icon_name(GTK_TOOL_BUTTON(toolitem), "org.remmina.Remmina-multi-monitor-symbolic");
2452  rcw_set_tooltip(GTK_WIDGET(toolitem), _("Multi monitor"),
2453  remmina_pref.shortcutkey_multimon, 0);
2454  gtk_toolbar_insert(GTK_TOOLBAR(toolbar), toolitem, -1);
2455  gtk_widget_show(GTK_WIDGET(toolitem));
2456  g_signal_connect(G_OBJECT(toolitem), "toggled", G_CALLBACK(rcw_toolbar_multi_monitor_mode), cnnwin);
2457  priv->toolitem_multimon = toolitem;
2458  if (!cnnobj)
2459  gtk_widget_set_sensitive(GTK_WIDGET(toolitem), FALSE);
2460  else
2461  gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(toolitem),
2462  remmina_file_get_int(cnnobj->remmina_file, "multimon", FALSE));
2463  }
2464 
2465  /* Dynamic Resolution Update */
2466  toolitem = gtk_toggle_tool_button_new();
2467  gtk_tool_button_set_icon_name(GTK_TOOL_BUTTON(toolitem), "org.remmina.Remmina-dynres-symbolic");
2468  rcw_set_tooltip(GTK_WIDGET(toolitem), _("Toggle dynamic resolution update"),
2469  remmina_pref.shortcutkey_dynres, 0);
2470  gtk_toolbar_insert(GTK_TOOLBAR(toolbar), toolitem, -1);
2471  gtk_widget_show(GTK_WIDGET(toolitem));
2472  g_signal_connect(G_OBJECT(toolitem), "toggled", G_CALLBACK(rcw_toolbar_dynres), cnnwin);
2473  priv->toolitem_dynres = toolitem;
2474 
2475  /* Scaler button */
2476  toolitem = gtk_toggle_tool_button_new();
2477  gtk_tool_button_set_icon_name(GTK_TOOL_BUTTON(toolitem), "org.remmina.Remmina-scale-symbolic");
2478  rcw_set_tooltip(GTK_WIDGET(toolitem), _("Toggle scaled mode"), remmina_pref.shortcutkey_scale, 0);
2479  gtk_toolbar_insert(GTK_TOOLBAR(toolbar), toolitem, -1);
2480  gtk_widget_show(GTK_WIDGET(toolitem));
2481  g_signal_connect(G_OBJECT(toolitem), "toggled", G_CALLBACK(rcw_toolbar_scaled_mode), cnnwin);
2482  priv->toolitem_scale = toolitem;
2483 
2484  /* Scaler aspect ratio dropdown menu */
2485  toolitem = gtk_tool_item_new();
2486  gtk_widget_show(GTK_WIDGET(toolitem));
2487  gtk_toolbar_insert(GTK_TOOLBAR(toolbar), toolitem, -1);
2488  widget = gtk_toggle_button_new();
2489  gtk_widget_show(widget);
2490  gtk_container_set_border_width(GTK_CONTAINER(widget), 0);
2491  gtk_button_set_relief(GTK_BUTTON(widget), GTK_RELIEF_NONE);
2492 #if GTK_CHECK_VERSION(3, 20, 0)
2493  gtk_widget_set_focus_on_click(GTK_WIDGET(widget), FALSE);
2494 #else
2495  gtk_button_set_focus_on_click(GTK_BUTTON(widget), FALSE);
2496 #endif
2497  if (remmina_pref.small_toolbutton)
2498  gtk_widget_set_name(widget, "remmina-small-button");
2499  gtk_container_add(GTK_CONTAINER(toolitem), widget);
2500 #if GTK_CHECK_VERSION(3, 14, 0)
2501  arrow = gtk_image_new_from_icon_name("org.remmina.Remmina-pan-down-symbolic", GTK_ICON_SIZE_SMALL_TOOLBAR);
2502 #else
2503  arrow = gtk_arrow_new(GTK_ARROW_DOWN, GTK_SHADOW_NONE);
2504 #endif
2505  gtk_widget_show(arrow);
2506  gtk_container_add(GTK_CONTAINER(widget), arrow);
2507  g_signal_connect(G_OBJECT(widget), "toggled", G_CALLBACK(rcw_toolbar_scaler_option), cnnwin);
2508  priv->scaler_option_button = widget;
2509 
2510  /* Separator */
2511  toolitem = gtk_separator_tool_item_new();
2512  gtk_toolbar_insert(GTK_TOOLBAR(toolbar), toolitem, -1);
2513  gtk_widget_show(GTK_WIDGET(toolitem));
2514 
2515  /* Switch tabs */
2516  toolitem = gtk_toggle_tool_button_new();
2517  gtk_tool_button_set_icon_name(GTK_TOOL_BUTTON(toolitem), "org.remmina.Remmina-switch-page-symbolic");
2518  rcw_set_tooltip(GTK_WIDGET(toolitem), _("Switch tab pages"), remmina_pref.shortcutkey_prevtab,
2519  remmina_pref.shortcutkey_nexttab);
2520  gtk_toolbar_insert(GTK_TOOLBAR(toolbar), toolitem, -1);
2521  gtk_widget_show(GTK_WIDGET(toolitem));
2522  g_signal_connect(G_OBJECT(toolitem), "toggled", G_CALLBACK(rcw_toolbar_switch_page), cnnwin);
2523  priv->toolitem_switch_page = toolitem;
2524 
2525  /* Grab keyboard button */
2526  toolitem = gtk_toggle_tool_button_new();
2527  gtk_tool_button_set_icon_name(GTK_TOOL_BUTTON(toolitem), "org.remmina.Remmina-keyboard-symbolic");
2528  rcw_set_tooltip(GTK_WIDGET(toolitem), _("Grab all keyboard events"),
2529  remmina_pref.shortcutkey_grab, 0);
2530  gtk_toolbar_insert(GTK_TOOLBAR(toolbar), toolitem, -1);
2531  gtk_widget_show(GTK_WIDGET(toolitem));
2532  g_signal_connect(G_OBJECT(toolitem), "toggled", G_CALLBACK(rcw_toolbar_grab), cnnwin);
2533  priv->toolitem_grab = toolitem;
2534  if (!cnnobj)
2535  gtk_widget_set_sensitive(GTK_WIDGET(toolitem), FALSE);
2536  else {
2537  const gchar *protocol = remmina_file_get_string(cnnobj->remmina_file, "protocol");
2538  if (g_strcmp0(protocol, "SFTP") == 0 || g_strcmp0(protocol, "SSH") == 0)
2539  gtk_widget_set_sensitive(GTK_WIDGET(toolitem), FALSE);
2540  }
2541 
2542  /* Preferences */
2543  toolitem = gtk_toggle_tool_button_new();
2544  gtk_tool_button_set_icon_name(GTK_TOOL_BUTTON(toolitem), "org.remmina.Remmina-preferences-system-symbolic");
2545  gtk_tool_item_set_tooltip_text(toolitem, _("Preferences"));
2546  gtk_toolbar_insert(GTK_TOOLBAR(toolbar), toolitem, -1);
2547  gtk_widget_show(GTK_WIDGET(toolitem));
2548  g_signal_connect(G_OBJECT(toolitem), "toggled", G_CALLBACK(rcw_toolbar_preferences), cnnwin);
2549  priv->toolitem_preferences = toolitem;
2550 
2551  /* Tools */
2552  toolitem = gtk_toggle_tool_button_new();
2553  gtk_tool_button_set_icon_name(GTK_TOOL_BUTTON(toolitem), "org.remmina.Remmina-system-run-symbolic");
2554  gtk_tool_button_set_label(GTK_TOOL_BUTTON(toolitem), _("_Tools"));
2555  gtk_tool_item_set_tooltip_text(toolitem, _("Tools"));
2556  gtk_toolbar_insert(GTK_TOOLBAR(toolbar), toolitem, -1);
2557  gtk_widget_show(GTK_WIDGET(toolitem));
2558  g_signal_connect(G_OBJECT(toolitem), "toggled", G_CALLBACK(rcw_toolbar_tools), cnnwin);
2559  priv->toolitem_tools = toolitem;
2560 
2561  /* Separator */
2562  toolitem = gtk_separator_tool_item_new();
2563  gtk_toolbar_insert(GTK_TOOLBAR(toolbar), toolitem, -1);
2564  gtk_widget_show(GTK_WIDGET(toolitem));
2565 
2566  toolitem = gtk_tool_button_new(NULL, "_Screenshot");
2567  gtk_tool_button_set_icon_name(GTK_TOOL_BUTTON(toolitem), "org.remmina.Remmina-camera-photo-symbolic");
2568  rcw_set_tooltip(GTK_WIDGET(toolitem), _("Screenshot"), remmina_pref.shortcutkey_screenshot, 0);
2569  gtk_toolbar_insert(GTK_TOOLBAR(toolbar), toolitem, -1);
2570  gtk_widget_show(GTK_WIDGET(toolitem));
2571  g_signal_connect(G_OBJECT(toolitem), "clicked", G_CALLBACK(rcw_toolbar_screenshot), cnnwin);
2572  priv->toolitem_screenshot = toolitem;
2573 
2574  /* Separator */
2575  toolitem = gtk_separator_tool_item_new();
2576  gtk_toolbar_insert(GTK_TOOLBAR(toolbar), toolitem, -1);
2577  gtk_widget_show(GTK_WIDGET(toolitem));
2578 
2579  /* Minimize */
2580  toolitem = gtk_tool_button_new(NULL, "_Bottom");
2581  gtk_tool_button_set_icon_name(GTK_TOOL_BUTTON(toolitem), "org.remmina.Remmina-go-bottom-symbolic");
2582  rcw_set_tooltip(GTK_WIDGET(toolitem), _("Minimize window"), remmina_pref.shortcutkey_minimize, 0);
2583  gtk_toolbar_insert(GTK_TOOLBAR(toolbar), toolitem, -1);
2584  gtk_widget_show(GTK_WIDGET(toolitem));
2585  g_signal_connect(G_OBJECT(toolitem), "clicked", G_CALLBACK(rcw_toolbar_minimize), cnnwin);
2586  if (kioskmode)
2587  gtk_widget_set_sensitive(GTK_WIDGET(toolitem), FALSE);
2588 
2589  /* Disconnect */
2590  toolitem = gtk_tool_button_new(NULL, "_Disconnect");
2591  gtk_tool_button_set_icon_name(GTK_TOOL_BUTTON(toolitem), "org.remmina.Remmina-disconnect-symbolic");
2592  rcw_set_tooltip(GTK_WIDGET(toolitem), _("Disconnect"), remmina_pref.shortcutkey_disconnect, 0);
2593  gtk_toolbar_insert(GTK_TOOLBAR(toolbar), toolitem, -1);
2594  gtk_widget_show(GTK_WIDGET(toolitem));
2595  g_signal_connect(G_OBJECT(toolitem), "clicked", G_CALLBACK(rcw_toolbar_disconnect), cnnwin);
2596 
2597  priv->toolbar_is_reconfiguring = FALSE;
2598  return toolbar;
2599 }
2600 
2601 static void rcw_place_toolbar(GtkToolbar *toolbar, GtkGrid *grid, GtkWidget *sibling, int toolbar_placement)
2602 {
2603  /* Place the toolbar inside the grid and set its orientation */
2604 
2605  if (toolbar_placement == TOOLBAR_PLACEMENT_LEFT || toolbar_placement == TOOLBAR_PLACEMENT_RIGHT)
2606  gtk_orientable_set_orientation(GTK_ORIENTABLE(toolbar), GTK_ORIENTATION_VERTICAL);
2607  else
2608  gtk_orientable_set_orientation(GTK_ORIENTABLE(toolbar), GTK_ORIENTATION_HORIZONTAL);
2609 
2610 
2611  switch (toolbar_placement) {
2612  case TOOLBAR_PLACEMENT_TOP:
2613  gtk_widget_set_hexpand(GTK_WIDGET(toolbar), TRUE);
2614  gtk_widget_set_vexpand(GTK_WIDGET(toolbar), FALSE);
2615  gtk_grid_attach_next_to(GTK_GRID(grid), GTK_WIDGET(toolbar), sibling, GTK_POS_TOP, 1, 1);
2616  break;
2618  gtk_widget_set_vexpand(GTK_WIDGET(toolbar), TRUE);
2619  gtk_widget_set_hexpand(GTK_WIDGET(toolbar), FALSE);
2620  gtk_grid_attach_next_to(GTK_GRID(grid), GTK_WIDGET(toolbar), sibling, GTK_POS_RIGHT, 1, 1);
2621  break;
2623  gtk_widget_set_hexpand(GTK_WIDGET(toolbar), TRUE);
2624  gtk_widget_set_vexpand(GTK_WIDGET(toolbar), FALSE);
2625  gtk_grid_attach_next_to(GTK_GRID(grid), GTK_WIDGET(toolbar), sibling, GTK_POS_BOTTOM, 1, 1);
2626  break;
2628  gtk_widget_set_vexpand(GTK_WIDGET(toolbar), TRUE);
2629  gtk_widget_set_hexpand(GTK_WIDGET(toolbar), FALSE);
2630  gtk_grid_attach_next_to(GTK_GRID(grid), GTK_WIDGET(toolbar), sibling, GTK_POS_LEFT, 1, 1);
2631  break;
2632  }
2633 }
2634 
2636 {
2637  TRACE_CALL(__func__);
2638  RemminaConnectionWindowPriv *priv = cnnobj->cnnwin->priv;
2639  GtkToolItem *toolitem;
2640  gboolean bval, dynres_avail, scale_avail;
2641  gboolean test_floating_toolbar;
2642  RemminaScaleMode scalemode;
2643 
2644  priv->toolbar_is_reconfiguring = TRUE;
2645 
2647 
2648  toolitem = priv->toolitem_switch_page;
2649  if (kioskmode)
2650  bval = FALSE;
2651  else
2652  bval = (gtk_notebook_get_n_pages(GTK_NOTEBOOK(priv->notebook)) > 1);
2653  gtk_widget_set_sensitive(GTK_WIDGET(toolitem), bval);
2654 
2655  if (cnnobj->remmina_file->filename)
2656  gtk_widget_set_sensitive(GTK_WIDGET(priv->toolitem_duplicate), TRUE);
2657  else
2658  gtk_widget_set_sensitive(GTK_WIDGET(priv->toolitem_duplicate), FALSE);
2659 
2660  scalemode = get_current_allowed_scale_mode(cnnobj, &dynres_avail, &scale_avail);
2661  gtk_widget_set_sensitive(GTK_WIDGET(priv->toolitem_dynres), dynres_avail && cnnobj->connected);
2662  gtk_widget_set_sensitive(GTK_WIDGET(priv->toolitem_scale), scale_avail && cnnobj->connected);
2663 
2664  switch (scalemode) {
2666  gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(priv->toolitem_dynres), FALSE);
2667  gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(priv->toolitem_scale), FALSE);
2668  gtk_widget_set_sensitive(GTK_WIDGET(priv->scaler_option_button), FALSE);
2669  break;
2671  gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(priv->toolitem_dynres), FALSE);
2672  gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(priv->toolitem_scale), TRUE);
2673  gtk_widget_set_sensitive(GTK_WIDGET(priv->scaler_option_button), TRUE && cnnobj->connected);
2674  break;
2676  gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(priv->toolitem_dynres), TRUE);
2677  gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(priv->toolitem_scale), FALSE);
2678  gtk_widget_set_sensitive(GTK_WIDGET(priv->scaler_option_button), FALSE);
2679  break;
2680  }
2681 
2682  /* REMMINA_PROTOCOL_FEATURE_TYPE_MULTIMON */
2683  toolitem = priv->toolitem_multimon;
2684  if (toolitem) {
2685  gint hasmultimon = remmina_protocol_widget_query_feature_by_type(REMMINA_PROTOCOL_WIDGET(cnnobj->proto),
2687 
2688  gtk_widget_set_sensitive(GTK_WIDGET(toolitem), cnnobj->connected);
2689  gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(toolitem),
2690  remmina_file_get_int(cnnobj->remmina_file, "multimon", FALSE));
2691  gtk_widget_set_sensitive(GTK_WIDGET(toolitem), hasmultimon);
2692  }
2693 
2694  toolitem = priv->toolitem_grab;
2695  gtk_widget_set_sensitive(GTK_WIDGET(toolitem), cnnobj->connected);
2696  gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(toolitem),
2697  remmina_file_get_int(cnnobj->remmina_file, "keyboard_grab", FALSE));
2698  const gchar *protocol = remmina_file_get_string(cnnobj->remmina_file, "protocol");
2699  if (g_strcmp0(protocol, "SFTP") == 0 || g_strcmp0(protocol, "SSH") == 0) {
2700  gtk_widget_set_sensitive(GTK_WIDGET(toolitem), FALSE);
2701  gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(toolitem), FALSE);
2702  remmina_file_set_int(cnnobj->remmina_file, "keyboard_grab", FALSE);
2703  }
2704 
2705  toolitem = priv->toolitem_preferences;
2706  gtk_widget_set_sensitive(GTK_WIDGET(toolitem), cnnobj->connected);
2707  bval = remmina_protocol_widget_query_feature_by_type(REMMINA_PROTOCOL_WIDGET(cnnobj->proto),
2709  gtk_widget_set_sensitive(GTK_WIDGET(toolitem), bval && cnnobj->connected);
2710 
2711  toolitem = priv->toolitem_tools;
2712  bval = remmina_protocol_widget_query_feature_by_type(REMMINA_PROTOCOL_WIDGET(cnnobj->proto),
2714  gtk_widget_set_sensitive(GTK_WIDGET(toolitem), bval && cnnobj->connected);
2715 
2716  gtk_widget_set_sensitive(GTK_WIDGET(priv->toolitem_screenshot), cnnobj->connected);
2717 
2718  gtk_window_set_title(GTK_WINDOW(cnnobj->cnnwin), remmina_file_get_string(cnnobj->remmina_file, "name"));
2719 
2720  test_floating_toolbar = (priv->floating_toolbar_widget != NULL);
2721 
2722  if (test_floating_toolbar) {
2723  const gchar *str = remmina_file_get_string(cnnobj->remmina_file, "name");
2724  const gchar *format;
2725  GdkRGBA rgba;
2726  gchar *bg;
2727 
2728  bg = g_strdup(remmina_pref.grab_color);
2729  if (!gdk_rgba_parse(&rgba, bg)) {
2730  REMMINA_DEBUG("%s cannot be parsed as a color", bg);
2731  bg = g_strdup("#00FF00");
2732  } else {
2733  REMMINA_DEBUG("Using %s as background color", bg);
2734  }
2735 
2736  if (remmina_file_get_int(cnnobj->remmina_file, "keyboard_grab", FALSE)) {
2737  if (remmina_pref_get_boolean("grab_color_switch"))
2738  format = g_strconcat("<span bgcolor=\"", bg, "\" size=\"large\"><b>(G:ON) - \%s</b></span>", NULL);
2739  else
2740  format = "<big><b>(G:ON) - \%s</b></big>";
2741  } else {
2742  format = "<big><b>(G:OFF) - \%s</b></big>";
2743  }
2744  gchar *markup;
2745 
2746  markup = g_markup_printf_escaped(format, str);
2747  gtk_label_set_markup(GTK_LABEL(priv->floating_toolbar_label), markup);
2748  g_free(markup);
2749  g_free(bg);
2750  }
2751 
2752  priv->toolbar_is_reconfiguring = FALSE;
2753 }
2754 
2756 {
2757  TRACE_CALL(__func__);
2758  RemminaConnectionWindowPriv *priv = cnnwin->priv;
2759 
2760  if (priv->view_mode == SCROLLED_WINDOW_MODE) {
2761  if (remmina_pref.hide_connection_toolbar)
2762  gtk_widget_hide(priv->toolbar);
2763  else
2764  gtk_widget_show(priv->toolbar);
2765  }
2766 }
2767 
2768 #if DEBUG_KB_GRABBING
2769 static void print_crossing_event(GdkEventCrossing *event) {
2770  printf("DEBUG_KB_GRABBING: --- Crossing event detail: ");
2771  switch (event->detail) {
2772  case GDK_NOTIFY_ANCESTOR: printf("GDK_NOTIFY_ANCESTOR"); break;
2773  case GDK_NOTIFY_VIRTUAL: printf("GDK_NOTIFY_VIRTUAL"); break;
2774  case GDK_NOTIFY_NONLINEAR: printf("GDK_NOTIFY_NONLINEAR"); break;
2775  case GDK_NOTIFY_NONLINEAR_VIRTUAL: printf("GDK_NOTIFY_NONLINEAR_VIRTUAL"); break;
2776  case GDK_NOTIFY_UNKNOWN: printf("GDK_NOTIFY_UNKNOWN"); break;
2777  case GDK_NOTIFY_INFERIOR: printf("GDK_NOTIFY_INFERIOR"); break;
2778  default: printf("unknown");
2779  }
2780  printf("\n");
2781  printf("DEBUG_KB_GRABBING: --- Crossing event mode=");
2782  switch (event->mode) {
2783  case GDK_CROSSING_NORMAL: printf("GDK_CROSSING_NORMAL"); break;
2784  case GDK_CROSSING_GRAB: printf("GDK_CROSSING_GRAB"); break;
2785  case GDK_CROSSING_UNGRAB: printf("GDK_CROSSING_UNGRAB"); break;
2786  case GDK_CROSSING_GTK_GRAB: printf("GDK_CROSSING_GTK_GRAB"); break;
2787  case GDK_CROSSING_GTK_UNGRAB: printf("GDK_CROSSING_GTK_UNGRAB"); break;
2788  case GDK_CROSSING_STATE_CHANGED: printf("GDK_CROSSING_STATE_CHANGED"); break;
2789  case GDK_CROSSING_TOUCH_BEGIN: printf("GDK_CROSSING_TOUCH_BEGIN"); break;
2790  case GDK_CROSSING_TOUCH_END: printf("GDK_CROSSING_TOUCH_END"); break;
2791  case GDK_CROSSING_DEVICE_SWITCH: printf("GDK_CROSSING_DEVICE_SWITCH"); break;
2792  default: printf("unknown");
2793  }
2794  printf("\n");
2795 }
2796 #endif
2797 
2798 static gboolean rcw_floating_toolbar_on_enter(GtkWidget *widget, GdkEventCrossing *event,
2799  RemminaConnectionWindow *cnnwin)
2800 {
2801  TRACE_CALL(__func__);
2802  rcw_floating_toolbar_show(cnnwin, TRUE);
2803  return TRUE;
2804 }
2805 
2806 static gboolean rcw_floating_toolbar_on_leave(GtkWidget *widget, GdkEventCrossing *event,
2807  RemminaConnectionWindow *cnnwin)
2808 {
2809  TRACE_CALL(__func__);
2810  if (event->detail != GDK_NOTIFY_INFERIOR)
2811  rcw_floating_toolbar_show(cnnwin, FALSE);
2812  return TRUE;
2813 }
2814 
2815 
2816 static gboolean rcw_on_enter_notify_event(GtkWidget *widget, GdkEventCrossing *event,
2817  gpointer user_data)
2818 {
2819  TRACE_CALL(__func__);
2820 #if DEBUG_KB_GRABBING
2821  printf("DEBUG_KB_GRABBING: enter-notify-event on rcw received\n");
2822  print_crossing_event(event);
2823 #endif
2824  return FALSE;
2825 }
2826 
2827 
2828 
2829 static gboolean rcw_on_leave_notify_event(GtkWidget *widget, GdkEventCrossing *event,
2830  gpointer user_data)
2831 {
2832  TRACE_CALL(__func__);
2834 
2835 #if DEBUG_KB_GRABBING
2836  printf("DEBUG_KB_GRABBING: leave-notify-event on rcw received\n");
2837  print_crossing_event(event);
2838 #endif
2839 
2840  if (event->mode != GDK_CROSSING_NORMAL && event->mode != GDK_CROSSING_UNGRAB) {
2841 #if DEBUG_KB_GRABBING
2842  printf("DEBUG_KB_GRABBING: ignored because mode is not GDK_CROSSING_NORMAL GDK_CROSSING_UNGRAB\n");
2843 #endif
2844  return FALSE;
2845  }
2846 
2847  if (cnnwin->priv->delayed_grab_eventsourceid) {
2848  g_source_remove(cnnwin->priv->delayed_grab_eventsourceid);
2849  cnnwin->priv->delayed_grab_eventsourceid = 0;
2850  }
2851 
2852  /* Workaround for https://gitlab.gnome.org/GNOME/mutter/-/issues/2450#note_1586570 */
2853  if (event->mode != GDK_CROSSING_UNGRAB) {
2854  rcw_kp_ungrab(cnnwin);
2855  rcw_pointer_ungrab(cnnwin);
2856  } else {
2857 #if DEBUG_KB_GRABBING
2858  printf("DEBUG_KB_GRABBING: not ungrabbing, this event seems to be an unwanted event from GTK\n");
2859 #endif
2860  }
2861 
2862  return FALSE;
2863 }
2864 
2865 
2866 static gboolean rco_leave_protocol_widget(GtkWidget *widget, GdkEventCrossing *event,
2867  RemminaConnectionObject *cnnobj)
2868 {
2869  TRACE_CALL(__func__);
2870 
2871 #if DEBUG_KB_GRABBING
2872  printf("DEBUG_KB_GRABBING: received leave event on RCO.\n");
2873  print_crossing_event(event);
2874 #endif
2875 
2876  if (cnnobj->cnnwin->priv->delayed_grab_eventsourceid) {
2877  g_source_remove(cnnobj->cnnwin->priv->delayed_grab_eventsourceid);
2878  cnnobj->cnnwin->priv->delayed_grab_eventsourceid = 0;
2879  }
2880 
2881  cnnobj->cnnwin->priv->pointer_entered = FALSE;
2882 
2883  /* Ungrab only if the leave is due to normal mouse motion and not to an inferior */
2884  if (event->mode == GDK_CROSSING_NORMAL && event->detail != GDK_NOTIFY_INFERIOR)
2885  rcw_kp_ungrab(cnnobj->cnnwin);
2886 
2887  return FALSE;
2888 }
2889 
2890 
2891 gboolean rco_enter_protocol_widget(GtkWidget *widget, GdkEventCrossing *event,
2892  RemminaConnectionObject *cnnobj)
2893 {
2894  TRACE_CALL(__func__);
2895  gboolean active;
2896 
2897 #if DEBUG_KB_GRABBING
2898  printf("DEBUG_KB_GRABBING: %s: enter on protocol widget event received\n", __func__);
2899  print_crossing_event(event);
2900 #endif
2901 
2902  RemminaConnectionWindowPriv *priv = cnnobj->cnnwin->priv;
2903  if (!priv->sticky && event->mode == GDK_CROSSING_NORMAL)
2904  rcw_floating_toolbar_show(cnnobj->cnnwin, FALSE);
2905 
2906  priv->pointer_entered = TRUE;
2907 
2908  if (event->mode == GDK_CROSSING_UNGRAB) {
2909  // Someone steal our grab, take note and do not attempt to regrab
2910  cnnobj->cnnwin->priv->kbcaptured = FALSE;
2911  cnnobj->cnnwin->priv->pointer_captured = FALSE;
2912  return FALSE;
2913  }
2914 
2915  /* Check if we need grabbing */
2916  active = gtk_window_is_active(GTK_WINDOW(cnnobj->cnnwin));
2917  if (remmina_file_get_int(cnnobj->remmina_file, "keyboard_grab", FALSE) && active) {
2918  rcw_keyboard_grab(cnnobj->cnnwin);
2919  rcw_pointer_grab(cnnobj->cnnwin);
2920  }
2921 
2922  return FALSE;
2923 }
2924 
2926 {
2927  TRACE_CALL(__func__);
2928 
2929 #if DEBUG_KB_GRABBING
2930  printf("DEBUG_KB_GRABBING: %s\n", __func__);
2931 #endif
2932  if (cnnwin->priv->pointer_entered) {
2933 #if DEBUG_KB_GRABBING
2934  printf("DEBUG_KB_GRABBING: delayed requesting kb and pointer grab, because of pointer inside\n");
2935 #endif
2936  rcw_keyboard_grab(cnnwin);
2937  rcw_pointer_grab(cnnwin);
2938  }
2939 #if DEBUG_KB_GRABBING
2940  else {
2941  printf("DEBUG_KB_GRABBING: %s not grabbing because pointer_entered is false\n", __func__);
2942  }
2943 #endif
2944  cnnwin->priv->delayed_grab_eventsourceid = 0;
2945  return G_SOURCE_REMOVE;
2946 }
2947 
2949 {
2950  /* This function is the default signal handler for focus-in-event,
2951  * but can also be called after a window focus state change event
2952  * from rcw_state_event(). So expect to be called twice
2953  * when cnnwin gains the focus */
2954 
2955  TRACE_CALL(__func__);
2956  RemminaConnectionObject *cnnobj;
2957 
2958  if (!(cnnobj = rcw_get_visible_cnnobj(cnnwin))) return;
2959 
2960  if (cnnobj && cnnobj->connected && remmina_file_get_int(cnnobj->remmina_file, "keyboard_grab", FALSE)) {
2961 #if DEBUG_KB_GRABBING
2962  printf("DEBUG_KB_GRABBING: Received focus in on rcw, grabbing enabled: requesting kb grab, delayed\n");
2963 #endif
2964  if (cnnwin->priv->delayed_grab_eventsourceid == 0)
2965  cnnwin->priv->delayed_grab_eventsourceid = g_timeout_add(300, (GSourceFunc)focus_in_delayed_grab, cnnwin);
2966  }
2967 #if DEBUG_KB_GRABBING
2968  else {
2969  printf("DEBUG_KB_GRABBING: Received focus in on rcw, but a condition will prevent to grab\n");
2970  }
2971 #endif
2972 }
2973 
2975 {
2976  /* This function is the default signal handler for focus-out-event,
2977  * but can also be called after a window focus state change event
2978  * from rcw_state_event(). So expect to be called twice
2979  * when cnnwin loses the focus */
2980 
2981  TRACE_CALL(__func__);
2982  RemminaConnectionObject *cnnobj;
2983 
2984  rcw_kp_ungrab(cnnwin);
2985 
2986  cnnwin->priv->hostkey_activated = FALSE;
2987  if (!(cnnobj = rcw_get_visible_cnnobj(cnnwin))) return;
2988 
2989  if (REMMINA_IS_SCROLLED_VIEWPORT(cnnobj->scrolled_container))
2990  remmina_scrolled_viewport_remove_motion(REMMINA_SCROLLED_VIEWPORT(cnnobj->scrolled_container));
2991 
2992  if (cnnobj->proto && cnnobj->scrolled_container)
2993  remmina_protocol_widget_call_feature_by_type(REMMINA_PROTOCOL_WIDGET(cnnobj->proto),
2995 }
2996 
2997 static gboolean
2999 {
3000  TRACE_CALL(__func__);
3001  RemminaConnectionWindowPriv *priv = cnnwin->priv;
3002 
3003  priv->hidetb_eventsource = 0;
3004  rcw_floating_toolbar_show(cnnwin, FALSE);
3005  return G_SOURCE_REMOVE;
3006 }
3007 
3008 static gboolean rcw_floating_toolbar_on_scroll(GtkWidget *widget, GdkEventScroll *event,
3009  RemminaConnectionWindow *cnnwin)
3010 {
3011  TRACE_CALL(__func__);
3012  RemminaConnectionObject *cnnobj;
3013 
3014  int opacity;
3015 
3016  cnnobj = rcw_get_visible_cnnobj(cnnwin);
3017  if (!cnnobj)
3018  return TRUE;
3019 
3020  opacity = remmina_file_get_int(cnnobj->remmina_file, "toolbar_opacity", 0);
3021  switch (event->direction) {
3022  case GDK_SCROLL_UP:
3023  if (opacity > 0) {
3024  remmina_file_set_int(cnnobj->remmina_file, "toolbar_opacity", opacity - 1);
3026  return TRUE;
3027  }
3028  break;
3029  case GDK_SCROLL_DOWN:
3030  if (opacity < TOOLBAR_OPACITY_LEVEL) {
3031  remmina_file_set_int(cnnobj->remmina_file, "toolbar_opacity", opacity + 1);
3033  return TRUE;
3034  }
3035  break;
3036 #if GTK_CHECK_VERSION(3, 4, 0)
3037  case GDK_SCROLL_SMOOTH:
3038  if (event->delta_y < 0 && opacity > 0) {
3039  remmina_file_set_int(cnnobj->remmina_file, "toolbar_opacity", opacity - 1);
3041  return TRUE;
3042  }
3043  if (event->delta_y > 0 && opacity < TOOLBAR_OPACITY_LEVEL) {
3044  remmina_file_set_int(cnnobj->remmina_file, "toolbar_opacity", opacity + 1);
3046  return TRUE;
3047  }
3048  break;
3049 #endif
3050  default:
3051  break;
3052  }
3053  return TRUE;
3054 }
3055 
3056 static gboolean rcw_after_configure_scrolled(gpointer user_data)
3057 {
3058  TRACE_CALL(__func__);
3059  gint width, height;
3060  GdkWindowState s;
3061  gint ipg, npages;
3062  RemminaConnectionWindow *cnnwin;
3063 
3064  cnnwin = (RemminaConnectionWindow *)user_data;
3065 
3066  if (!cnnwin || !cnnwin->priv)
3067  return FALSE;
3068 
3069  s = gdk_window_get_state(gtk_widget_get_window(GTK_WIDGET(cnnwin)));
3070 
3071 
3072  /* Changed window_maximize, window_width and window_height for all
3073  * connections inside the notebook */
3074  npages = gtk_notebook_get_n_pages(GTK_NOTEBOOK(cnnwin->priv->notebook));
3075  for (ipg = 0; ipg < npages; ipg++) {
3076  RemminaConnectionObject *cnnobj;
3077  cnnobj = g_object_get_data(
3078  G_OBJECT(gtk_notebook_get_nth_page(GTK_NOTEBOOK(cnnwin->priv->notebook), ipg)),
3079  "cnnobj");
3080  if (s & GDK_WINDOW_STATE_MAXIMIZED) {
3081  remmina_file_set_int(cnnobj->remmina_file, "window_maximize", TRUE);
3082  } else {
3083  gtk_window_get_size(GTK_WINDOW(cnnobj->cnnwin), &width, &height);
3084  remmina_file_set_int(cnnobj->remmina_file, "window_width", width);
3085  remmina_file_set_int(cnnobj->remmina_file, "window_height", height);
3086  remmina_file_set_int(cnnobj->remmina_file, "window_maximize", FALSE);
3087  }
3088  }
3089  cnnwin->priv->acs_eventsourceid = 0;
3090  return FALSE;
3091 }
3092 
3093 static gboolean rcw_on_configure(GtkWidget *widget, GdkEventConfigure *event,
3094  gpointer data)
3095 {
3096  TRACE_CALL(__func__);
3097  RemminaConnectionWindow *cnnwin;
3098  RemminaConnectionObject *cnnobj;
3099 
3100  if (!REMMINA_IS_CONNECTION_WINDOW(widget))
3101  return FALSE;
3102 
3103  cnnwin = (RemminaConnectionWindow *)widget;
3104 
3105  if (!(cnnobj = rcw_get_visible_cnnobj(cnnwin))) return FALSE;
3106 
3107  if (cnnwin->priv->acs_eventsourceid) {
3108  g_source_remove(cnnwin->priv->acs_eventsourceid);
3109  cnnwin->priv->acs_eventsourceid = 0;
3110  }
3111 
3112  if (gtk_widget_get_window(GTK_WIDGET(cnnwin))
3113  && cnnwin->priv->view_mode == SCROLLED_WINDOW_MODE)
3114  /* Under GNOME Shell we receive this configure_event BEFORE a window
3115  * is really unmaximized, so we must read its new state and dimensions
3116  * later, not now */
3117  cnnwin->priv->acs_eventsourceid = g_timeout_add(500, rcw_after_configure_scrolled, cnnwin);
3118 
3119  if (cnnwin->priv->view_mode != SCROLLED_WINDOW_MODE)
3120  /* Notify window of change so that scroll border can be hidden or shown if needed */
3121  rco_check_resize(cnnobj);
3122  return FALSE;
3123 }
3124 
3126 {
3127  TRACE_CALL(__func__);
3128  if (cnnwin->priv->pin_down)
3129  gtk_button_set_image(GTK_BUTTON(cnnwin->priv->pin_button),
3130  gtk_image_new_from_icon_name("org.remmina.Remmina-pin-down-symbolic", GTK_ICON_SIZE_MENU));
3131  else
3132  gtk_button_set_image(GTK_BUTTON(cnnwin->priv->pin_button),
3133  gtk_image_new_from_icon_name("org.remmina.Remmina-pin-up-symbolic", GTK_ICON_SIZE_MENU));
3134 }
3135 
3136 static void rcw_toolbar_pin(GtkWidget *widget, RemminaConnectionWindow *cnnwin)
3137 {
3138  TRACE_CALL(__func__);
3139  remmina_pref.toolbar_pin_down = cnnwin->priv->pin_down = !cnnwin->priv->pin_down;
3141  rcw_update_pin(cnnwin);
3142 }
3143 
3145 {
3146  TRACE_CALL(__func__);
3147 
3148  RemminaConnectionWindowPriv *priv = cnnwin->priv;
3149  GtkWidget *ftb_widget;
3150  GtkWidget *vbox;
3151  GtkWidget *hbox;
3152  GtkWidget *label;
3153  GtkWidget *pinbutton;
3154  GtkWidget *tb;
3155 
3156 
3157  /* A widget to be used for GtkOverlay for GTK >= 3.10 */
3158  ftb_widget = gtk_event_box_new();
3159 
3160  vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
3161  gtk_widget_show(vbox);
3162 
3163  gtk_container_add(GTK_CONTAINER(ftb_widget), vbox);
3164 
3165  tb = rcw_create_toolbar(cnnwin, mode);
3166  hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
3167  gtk_widget_show(hbox);
3168 
3169 
3170  /* The pin button */
3171  pinbutton = gtk_button_new();
3172  gtk_widget_show(pinbutton);
3173  gtk_box_pack_start(GTK_BOX(hbox), pinbutton, FALSE, FALSE, 0);
3174  gtk_button_set_relief(GTK_BUTTON(pinbutton), GTK_RELIEF_NONE);
3175 #if GTK_CHECK_VERSION(3, 20, 0)
3176  gtk_widget_set_focus_on_click(GTK_WIDGET(pinbutton), FALSE);
3177 #else
3178  gtk_button_set_focus_on_click(GTK_BUTTON(pinbutton), FALSE);
3179 #endif
3180  gtk_widget_set_name(pinbutton, "remmina-pin-button");
3181  g_signal_connect(G_OBJECT(pinbutton), "clicked", G_CALLBACK(rcw_toolbar_pin), cnnwin);
3182  priv->pin_button = pinbutton;
3183  priv->pin_down = remmina_pref.toolbar_pin_down;
3184  rcw_update_pin(cnnwin);
3185 
3186 
3187  label = gtk_label_new("");
3188  gtk_label_set_max_width_chars(GTK_LABEL(label), 50);
3189  gtk_widget_show(label);
3190 
3191  gtk_box_pack_start(GTK_BOX(hbox), label, TRUE, TRUE, 0);
3192 
3193  priv->floating_toolbar_label = label;
3194 
3196  gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
3197  gtk_box_pack_start(GTK_BOX(vbox), tb, FALSE, FALSE, 0);
3198  } else {
3199  gtk_box_pack_start(GTK_BOX(vbox), tb, FALSE, FALSE, 0);
3200  gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
3201  }
3202 
3203  priv->floating_toolbar_widget = ftb_widget;
3204  gtk_widget_show(ftb_widget);
3205 }
3206 
3207 static void rcw_toolbar_place_signal(RemminaConnectionWindow *cnnwin, gpointer data)
3208 {
3209  TRACE_CALL(__func__);
3211 
3212  priv = cnnwin->priv;
3213  /* Detach old toolbar widget and reattach in new position in the grid */
3214  if (priv->toolbar && priv->grid) {
3215  g_object_ref(priv->toolbar);
3216  gtk_container_remove(GTK_CONTAINER(priv->grid), priv->toolbar);
3217  rcw_place_toolbar(GTK_TOOLBAR(priv->toolbar), GTK_GRID(priv->grid), GTK_WIDGET(priv->notebook), remmina_pref.toolbar_placement);
3218  g_object_unref(priv->toolbar);
3219  }
3220 }
3221 
3222 
3223 static void rcw_init(RemminaConnectionWindow *cnnwin)
3224 {
3225  TRACE_CALL(__func__);
3227 
3228  priv = g_new0(RemminaConnectionWindowPriv, 1);
3229  cnnwin->priv = priv;
3230 
3231  priv->view_mode = SCROLLED_WINDOW_MODE;
3232  if (kioskmode && kioskmode == TRUE)
3233  priv->view_mode = VIEWPORT_FULLSCREEN_MODE;
3234 
3235  priv->floating_toolbar_opacity = 1.0;
3236  priv->kbcaptured = FALSE;
3237  priv->pointer_captured = FALSE;
3238  priv->pointer_entered = FALSE;
3239  priv->fss_view_mode = VIEWPORT_FULLSCREEN_MODE;
3240  priv->ss_width = 640;
3241  priv->ss_height = 480;
3242  priv->ss_maximized = FALSE;
3243 
3244  remmina_widget_pool_register(GTK_WIDGET(cnnwin));
3245 }
3246 
3247 static gboolean rcw_focus_in_event(GtkWidget *widget, GdkEventWindowState *event, gpointer user_data)
3248 {
3249  TRACE_CALL(__func__);
3250 #if DEBUG_KB_GRABBING
3251  printf("DEBUG_KB_GRABBING: RCW focus-in-event received\n");
3252 #endif
3254  return FALSE;
3255 }
3256 
3257 static gboolean rcw_focus_out_event(GtkWidget *widget, GdkEventWindowState *event, gpointer user_data)
3258 {
3259  TRACE_CALL(__func__);
3260 #if DEBUG_KB_GRABBING
3261  printf("DEBUG_KB_GRABBING: RCW focus-out-event received\n");
3262 #endif
3264  return FALSE;
3265 }
3266 
3267 
3268 static gboolean rcw_state_event(GtkWidget *widget, GdkEventWindowState *event, gpointer user_data)
3269 {
3270  TRACE_CALL(__func__);
3271 
3272  if (!REMMINA_IS_CONNECTION_WINDOW(widget))
3273  return FALSE;
3274 
3275 #if DEBUG_KB_GRABBING
3276  printf("DEBUG_KB_GRABBING: window-state-event received\n");
3277 #endif
3278 
3279  if (event->changed_mask & GDK_WINDOW_STATE_FOCUSED) {
3280  if (event->new_window_state & GDK_WINDOW_STATE_FOCUSED)
3282  else
3284  }
3285 
3286  return FALSE;
3287 }
3288 
3289 static gboolean rcw_map_event(GtkWidget *widget, GdkEvent *event, gpointer data)
3290 {
3291  TRACE_CALL(__func__);
3292 
3293 
3294 
3296  RemminaConnectionObject *cnnobj;
3298 
3299  if (cnnwin->priv->toolbar_is_reconfiguring) return FALSE;
3300  if (!(cnnobj = rcw_get_visible_cnnobj(cnnwin))) return FALSE;
3301 
3302  gp = REMMINA_PROTOCOL_WIDGET(cnnobj->proto);
3303  REMMINA_DEBUG("Mapping: %s", gtk_widget_get_name(widget));
3305  REMMINA_DEBUG("Called plugin mapping function");
3306  return FALSE;
3307 }
3308 
3309 static gboolean rcw_unmap_event(GtkWidget *widget, GdkEvent *event, gpointer data)
3310 {
3311  TRACE_CALL(__func__);
3312 
3314  RemminaConnectionObject *cnnobj;
3316 
3317  if (cnnwin->priv->toolbar_is_reconfiguring) return FALSE;
3318  if (!(cnnobj = rcw_get_visible_cnnobj(cnnwin))) return FALSE;
3319 
3320  gp = REMMINA_PROTOCOL_WIDGET(cnnobj->proto);
3321  REMMINA_DEBUG("Unmapping: %s", gtk_widget_get_name(widget));
3323  REMMINA_DEBUG("Called plugin mapping function");
3324  return FALSE;
3325 }
3326 
3327 static gboolean rcw_map_event_fullscreen(GtkWidget *widget, GdkEvent *event, gpointer data)
3328 {
3329  TRACE_CALL(__func__);
3330  RemminaConnectionObject *cnnobj;
3331  gint target_monitor;
3332 
3333  REMMINA_DEBUG("Mapping: %s", gtk_widget_get_name(widget));
3334 
3335  if (!REMMINA_IS_CONNECTION_WINDOW(widget)) {
3336  REMMINA_DEBUG("Remmina Connection Window undefined, cannot go fullscreen");
3337  return FALSE;
3338  }
3339 
3340  //RemminaConnectionWindow *cnnwin = (RemminaConnectionWindow *)data;
3341  cnnobj = rcw_get_visible_cnnobj((RemminaConnectionWindow *)widget);
3342  //cnnobj = g_object_get_data(G_OBJECT(widget), "cnnobj");
3343  if (!cnnobj) {
3344  REMMINA_DEBUG("Remmina Connection Object undefined, cannot go fullscreen");
3345  return FALSE;
3346  }
3347 
3348  RemminaProtocolWidget *gp = REMMINA_PROTOCOL_WIDGET(cnnobj->proto);
3349 
3350  if (!gp)
3351  REMMINA_DEBUG("Remmina Protocol Widget undefined, cannot go fullscreen");
3352 
3353  if (remmina_protocol_widget_get_multimon(gp) >= 1) {
3354  REMMINA_DEBUG("Fullscreen on all monitor");
3355  gdk_window_set_fullscreen_mode(gtk_widget_get_window(widget), GDK_FULLSCREEN_ON_ALL_MONITORS);
3356  gdk_window_fullscreen(gtk_widget_get_window(widget));
3357  return TRUE;
3358  } else {
3359  REMMINA_DEBUG("Fullscreen on one monitor");
3360  }
3361 
3362  target_monitor = GPOINTER_TO_INT(data);
3363 
3364 #if GTK_CHECK_VERSION(3, 18, 0)
3365  if (remmina_pref.fullscreen_on_auto) {
3366  if (target_monitor == FULL_SCREEN_TARGET_MONITOR_UNDEFINED)
3367  gtk_window_fullscreen(GTK_WINDOW(widget));
3368  else
3369  gtk_window_fullscreen_on_monitor(GTK_WINDOW(widget), gtk_window_get_screen(GTK_WINDOW(widget)),
3370  target_monitor);
3371  } else {
3372  REMMINA_DEBUG("Fullscreen managed by WM or by the user, as per settings");
3373  gtk_window_fullscreen(GTK_WINDOW(widget));
3374  }
3375 #else
3376  REMMINA_DEBUG("Cannot fullscreen on a specific monitor, feature available from GTK 3.18");
3377  gtk_window_fullscreen(GTK_WINDOW(widget));
3378 #endif
3379 
3381  REMMINA_DEBUG("Called plugin mapping function");
3382 
3383  return FALSE;
3384 }
3385 
3386 static RemminaConnectionWindow *
3387 rcw_new(gboolean fullscreen, int full_screen_target_monitor)
3388 {
3389  TRACE_CALL(__func__);
3390  RemminaConnectionWindow *cnnwin;
3391 
3392  cnnwin = RCW(g_object_new(REMMINA_TYPE_CONNECTION_WINDOW, NULL));
3393  cnnwin->priv->on_delete_confirm_mode = RCW_ONDELETE_CONFIRM_IF_2_OR_MORE;
3394 
3395  if (fullscreen)
3396  /* Put the window in fullscreen after it is mapped to have it appear on the same monitor */
3397  g_signal_connect(G_OBJECT(cnnwin), "map-event", G_CALLBACK(rcw_map_event_fullscreen), GINT_TO_POINTER(full_screen_target_monitor));
3398  else
3399  g_signal_connect(G_OBJECT(cnnwin), "map-event", G_CALLBACK(rcw_map_event), NULL);
3400  g_signal_connect(G_OBJECT(cnnwin), "unmap-event", G_CALLBACK(rcw_unmap_event), NULL);
3401 
3402  gtk_container_set_border_width(GTK_CONTAINER(cnnwin), 0);
3403  g_signal_connect(G_OBJECT(cnnwin), "toolbar-place", G_CALLBACK(rcw_toolbar_place_signal), NULL);
3404 
3405  g_signal_connect(G_OBJECT(cnnwin), "delete-event", G_CALLBACK(rcw_delete_event), NULL);
3406  g_signal_connect(G_OBJECT(cnnwin), "destroy", G_CALLBACK(rcw_destroy), NULL);
3407 
3408  /* Under Xorg focus-in-event and focus-out-event don’t work when keyboard is grabbed
3409  * via gdk_device_grab. So we listen for window-state-event to detect focus in and focus out.
3410  * But we must also listen focus-in-event and focus-out-event because some
3411  * window managers missing _NET_WM_STATE_FOCUSED hint, does not update the window state
3412  * in case of focus change */
3413  g_signal_connect(G_OBJECT(cnnwin), "window-state-event", G_CALLBACK(rcw_state_event), NULL);
3414  g_signal_connect(G_OBJECT(cnnwin), "focus-in-event", G_CALLBACK(rcw_focus_in_event), NULL);
3415  g_signal_connect(G_OBJECT(cnnwin), "focus-out-event", G_CALLBACK(rcw_focus_out_event), NULL);
3416 
3417  g_signal_connect(G_OBJECT(cnnwin), "enter-notify-event", G_CALLBACK(rcw_on_enter_notify_event), NULL);
3418  g_signal_connect(G_OBJECT(cnnwin), "leave-notify-event", G_CALLBACK(rcw_on_leave_notify_event), NULL);
3419 
3420 
3421  g_signal_connect(G_OBJECT(cnnwin), "configure_event", G_CALLBACK(rcw_on_configure), NULL);
3422 
3423  return cnnwin;
3424 }
3425 
3426 /* This function will be called for the first connection. A tag is set to the window so that
3427  * other connections can determine if whether a new tab should be append to the same window
3428  */
3430 {
3431  TRACE_CALL(__func__);
3432  gchar *tag;
3433 
3434  switch (remmina_pref.tab_mode) {
3435  case REMMINA_TAB_BY_GROUP:
3436  tag = g_strdup(remmina_file_get_string(cnnobj->remmina_file, "group"));
3437  break;
3439  tag = g_strdup(remmina_file_get_string(cnnobj->remmina_file, "protocol"));
3440  break;
3441  default:
3442  tag = NULL;
3443  break;
3444  }
3445  g_object_set_data_full(G_OBJECT(cnnwin), "tag", tag, (GDestroyNotify)g_free);
3446 }
3447 
3449 {
3450  TRACE_CALL(__func__);
3451  RemminaConnectionObject *cnnobj;
3452 
3453  if (!(cnnobj = rcw_get_visible_cnnobj(cnnwin))) return;
3454 
3455  if (GTK_IS_WIDGET(cnnobj->proto))
3456  remmina_protocol_widget_grab_focus(REMMINA_PROTOCOL_WIDGET(cnnobj->proto));
3457 }
3458 
3459 static GtkWidget *nb_find_page_by_cnnobj(GtkNotebook *notebook, RemminaConnectionObject *cnnobj)
3460 {
3461  gint i, np;
3462  GtkWidget *found_page, *pg;
3463 
3464  if (cnnobj == NULL || cnnobj->cnnwin == NULL || cnnobj->cnnwin->priv == NULL)
3465  return NULL;
3466  found_page = NULL;
3467  np = gtk_notebook_get_n_pages(cnnobj->cnnwin->priv->notebook);
3468  for (i = 0; i < np; i++) {
3469  pg = gtk_notebook_get_nth_page(cnnobj->cnnwin->priv->notebook, i);
3470  if (g_object_get_data(G_OBJECT(pg), "cnnobj") == cnnobj) {
3471  found_page = pg;
3472  break;
3473  }
3474  }
3475 
3476  return found_page;
3477 }
3478 
3479 
3481 {
3482  TRACE_CALL(__func__);
3483  RemminaConnectionObject *cnnobj = gp->cnnobj;
3484  GtkWidget *page_to_remove;
3485 
3486 
3487  if (cnnobj && cnnobj->scrolled_container && REMMINA_IS_SCROLLED_VIEWPORT(cnnobj->scrolled_container)) {
3488  REMMINA_DEBUG("deleting motion");
3489  remmina_scrolled_viewport_remove_motion(REMMINA_SCROLLED_VIEWPORT(cnnobj->scrolled_container));
3490  }
3491 
3492  if (cnnobj && cnnobj->cnnwin) {
3493  page_to_remove = nb_find_page_by_cnnobj(cnnobj->cnnwin->priv->notebook, cnnobj);
3494  if (page_to_remove) {
3495  gtk_notebook_remove_page(
3496  cnnobj->cnnwin->priv->notebook,
3497  gtk_notebook_page_num(cnnobj->cnnwin->priv->notebook, page_to_remove));
3498  /* Invalidate pointers to objects destroyed by page removal */
3499  cnnobj->aspectframe = NULL;
3500  cnnobj->viewport = NULL;
3501  cnnobj->scrolled_container = NULL;
3502  /* we cannot invalidate cnnobj->proto, because it can be already been
3503  * detached from the widget hierarchy in rco_on_disconnect() */
3504  }
3505  }
3506  if (cnnobj) {
3507  cnnobj->remmina_file = NULL;
3508  g_free(cnnobj);
3509  gp->cnnobj = NULL;
3510  }
3511 
3513 }
3514 
3516 {
3517  TRACE_CALL(__func__);
3518  if (REMMINA_IS_PROTOCOL_WIDGET(cnnobj->proto)) {
3520  remmina_protocol_widget_close_connection(REMMINA_PROTOCOL_WIDGET(cnnobj->proto));
3521  else
3523  }
3524 }
3525 
3527 {
3528  TRACE_CALL(__func__);
3529  GtkWidget *hbox;
3530  GtkWidget *widget;
3531  GtkWidget *button;
3532 
3533  hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 4);
3534  gtk_widget_show(hbox);
3535 
3536  widget = gtk_image_new_from_icon_name(remmina_file_get_icon_name(cnnobj->remmina_file), GTK_ICON_SIZE_MENU);
3537  gtk_widget_show(widget);
3538  gtk_box_pack_start(GTK_BOX(hbox), widget, FALSE, FALSE, 0);
3539 
3540  widget = gtk_label_new(remmina_file_get_string(cnnobj->remmina_file, "name"));
3541  gtk_widget_set_valign(widget, GTK_ALIGN_CENTER);
3542  gtk_widget_set_halign(widget, GTK_ALIGN_CENTER);
3543 
3544  gtk_widget_show(widget);
3545  gtk_box_pack_start(GTK_BOX(hbox), widget, TRUE, TRUE, 0);
3546 
3547  button = gtk_button_new(); // The "x" to close the tab
3548  gtk_button_set_relief(GTK_BUTTON(button), GTK_RELIEF_NONE);
3549 #if GTK_CHECK_VERSION(3, 20, 0)
3550  gtk_widget_set_focus_on_click(GTK_WIDGET(widget), FALSE);
3551 #else
3552  gtk_button_set_focus_on_click(GTK_BUTTON(button), FALSE);
3553 #endif
3554  gtk_widget_set_name(button, "remmina-small-button");
3555  gtk_widget_show(button);
3556 
3557  widget = gtk_image_new_from_icon_name("window-close", GTK_ICON_SIZE_MENU);
3558  gtk_widget_show(widget);
3559  gtk_container_add(GTK_CONTAINER(button), widget);
3560 
3561  gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
3562 
3563  g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(rco_on_close_button_clicked), cnnobj);
3564 
3565 
3566  return hbox;
3567 }
3568 
3570 {
3571  GtkWidget *page;
3572 
3573  page = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
3574  gtk_widget_set_name(page, "remmina-tab-page");
3575 
3576 
3577  return page;
3578 }
3579 
3581 {
3582  TRACE_CALL(__func__);
3583  GtkWidget *page, *label;
3584  GtkNotebook *notebook;
3585 
3586  notebook = cnnwin->priv->notebook;
3587 
3588  page = rco_create_tab_page(cnnobj);
3589  g_object_set_data(G_OBJECT(page), "cnnobj", cnnobj);
3590  label = rco_create_tab_label(cnnobj);
3591 
3592  cnnobj->cnnwin = cnnwin;
3593 
3594  gtk_notebook_append_page(notebook, page, label);
3595  gtk_notebook_set_tab_reorderable(notebook, page, TRUE);
3596  gtk_notebook_set_tab_detachable(notebook, page, TRUE);
3597  /* This trick prevents the tab label from being focused */
3598  gtk_widget_set_can_focus(gtk_widget_get_parent(label), FALSE);
3599 
3600  if (gtk_widget_get_parent(cnnobj->scrolled_container) != NULL)
3601  printf("REMMINA WARNING in %s: scrolled_container already has a parent\n", __func__);
3602  gtk_box_pack_start(GTK_BOX(page), cnnobj->scrolled_container, TRUE, TRUE, 0);
3603 
3604  gtk_widget_show(page);
3605 
3606  return page;
3607 }
3608 
3609 
3611 {
3612  TRACE_CALL(__func__);
3613  GtkNotebook *notebook;
3614  gint n;
3615 
3616  notebook = GTK_NOTEBOOK(cnnwin->priv->notebook);
3617 
3618  switch (cnnwin->priv->view_mode) {
3619  case SCROLLED_WINDOW_MODE:
3620  n = gtk_notebook_get_n_pages(notebook);
3621  gtk_notebook_set_show_tabs(notebook, remmina_pref.always_show_tab ? TRUE : n > 1);
3622  gtk_notebook_set_show_border(notebook, remmina_pref.always_show_tab ? TRUE : n > 1);
3623  break;
3624  default:
3625  gtk_notebook_set_show_tabs(notebook, FALSE);
3626  gtk_notebook_set_show_border(notebook, FALSE);
3627  break;
3628  }
3629 }
3630 
3631 static gboolean rcw_on_switch_page_finalsel(gpointer user_data)
3632 {
3633  TRACE_CALL(__func__);
3635  RemminaConnectionObject *cnnobj;
3636 
3637  if (!user_data)
3638  return FALSE;
3639 
3640  cnnobj = (RemminaConnectionObject *)user_data;
3641  if (!cnnobj->cnnwin)
3642  return FALSE;
3643 
3644  priv = cnnobj->cnnwin->priv;
3645 
3646  if (GTK_IS_WIDGET(cnnobj->cnnwin)) {
3647  rcw_floating_toolbar_show(cnnobj->cnnwin, TRUE);
3648  if (!priv->hidetb_eventsource)
3649  priv->hidetb_eventsource = g_timeout_add(TB_HIDE_TIME_TIME, (GSourceFunc)
3651  rco_update_toolbar(cnnobj);
3652  rcw_grab_focus(cnnobj->cnnwin);
3653  if (priv->view_mode != SCROLLED_WINDOW_MODE)
3654  rco_check_resize(cnnobj);
3655  }
3656  priv->spf_eventsourceid = 0;
3657  return FALSE;
3658 }
3659 
3660 static void rcw_on_switch_page(GtkNotebook *notebook, GtkWidget *newpage, guint page_num,
3661  RemminaConnectionWindow *cnnwin)
3662 {
3663  TRACE_CALL(__func__);
3664  RemminaConnectionWindowPriv *priv = cnnwin->priv;
3665  RemminaConnectionObject *cnnobj_newpage;
3666 
3667  cnnobj_newpage = g_object_get_data(G_OBJECT(newpage), "cnnobj");
3668  if (priv->spf_eventsourceid)
3669  g_source_remove(priv->spf_eventsourceid);
3670  priv->spf_eventsourceid = g_idle_add(rcw_on_switch_page_finalsel, cnnobj_newpage);
3671 }
3672 
3673 static void rcw_on_page_added(GtkNotebook *notebook, GtkWidget *child, guint page_num,
3674  RemminaConnectionWindow *cnnwin)
3675 {
3676  if (gtk_notebook_get_n_pages(GTK_NOTEBOOK(cnnwin->priv->notebook)) > 0)
3677  rcw_update_notebook(cnnwin);
3678 }
3679 
3680 static void rcw_on_page_removed(GtkNotebook *notebook, GtkWidget *child, guint page_num,
3681  RemminaConnectionWindow *cnnwin)
3682 {
3683  TRACE_CALL(__func__);
3684 
3685  if (gtk_notebook_get_n_pages(GTK_NOTEBOOK(cnnwin->priv->notebook)) <= 0)
3686  gtk_widget_destroy(GTK_WIDGET(cnnwin));
3687 
3688 }
3689 
3690 static GtkNotebook *
3691 rcw_on_notebook_create_window(GtkNotebook *notebook, GtkWidget *page, gint x, gint y, gpointer data)
3692 {
3693  /* This signal callback is called by GTK when a detachable tab is dropped on the root window
3694  * or in an existing window */
3695 
3696  TRACE_CALL(__func__);
3697  RemminaConnectionWindow *srccnnwin;
3698  RemminaConnectionWindow *dstcnnwin;
3699  RemminaConnectionObject *cnnobj;
3700  GdkWindow *window;
3701  gchar *srctag;
3702  gint width, height;
3703 
3704 #if GTK_CHECK_VERSION(3, 20, 0)
3705  GdkSeat *seat;
3706 #else
3707  GdkDeviceManager *manager;
3708 #endif
3709  GdkDevice *device = NULL;
3710 
3711 #if GTK_CHECK_VERSION(3, 20, 0)
3712  seat = gdk_display_get_default_seat(gdk_display_get_default());
3713  device = gdk_seat_get_pointer(seat);
3714 #else
3715  manager = gdk_display_get_device_manager(gdk_display_get_default());
3716  device = gdk_device_manager_get_client_pointer(manager);
3717 #endif
3718 
3719  window = gdk_device_get_window_at_position(device, &x, &y);
3720  srccnnwin = RCW(gtk_widget_get_toplevel(GTK_WIDGET(notebook)));
3721  dstcnnwin = RCW(remmina_widget_pool_find_by_window(REMMINA_TYPE_CONNECTION_WINDOW, window));
3722 
3723  if (srccnnwin == dstcnnwin)
3724  return NULL;
3725 
3726  if (gtk_notebook_get_n_pages(GTK_NOTEBOOK(srccnnwin->priv->notebook)) == 1 && !dstcnnwin)
3727  return NULL;
3728 
3729  cnnobj = (RemminaConnectionObject *)g_object_get_data(G_OBJECT(page), "cnnobj");
3730 
3731  if (!dstcnnwin) {
3732  /* Drop is directed to a new rcw: create a new scrolled window to accommodate
3733  * the dropped connectionand move our cnnobj there. Width and
3734  * height of the new window are cloned from the current window */
3735  srctag = (gchar *)g_object_get_data(G_OBJECT(srccnnwin), "tag");
3736  gtk_window_get_size(GTK_WINDOW(srccnnwin), &width, &height);
3737  dstcnnwin = rcw_create_scrolled(width, height, FALSE); // New dropped window is never maximized
3738  g_object_set_data_full(G_OBJECT(dstcnnwin), "tag", g_strdup(srctag), (GDestroyNotify)g_free);
3739  /* when returning, GTK will move the whole tab to the new notebook.
3740  * Prepare cnnobj to be hosted in the new cnnwin */
3741  cnnobj->cnnwin = dstcnnwin;
3742  } else {
3743  cnnobj->cnnwin = dstcnnwin;
3744  }
3745 
3746  remmina_protocol_widget_set_hostkey_func(REMMINA_PROTOCOL_WIDGET(cnnobj->proto),
3747  (RemminaHostkeyFunc)rcw_hostkey_func);
3748 
3749  return GTK_NOTEBOOK(cnnobj->cnnwin->priv->notebook);
3750 }
3751 
3752 static GtkNotebook *
3754 {
3755  TRACE_CALL(__func__);
3756  GtkNotebook *notebook;
3757 
3758  notebook = GTK_NOTEBOOK(gtk_notebook_new());
3759 
3760  gtk_notebook_set_scrollable(GTK_NOTEBOOK(notebook), TRUE);
3761  gtk_widget_show(GTK_WIDGET(notebook));
3762 
3763  g_signal_connect(G_OBJECT(notebook), "create-window", G_CALLBACK(rcw_on_notebook_create_window), NULL);
3764  g_signal_connect(G_OBJECT(notebook), "switch-page", G_CALLBACK(rcw_on_switch_page), cnnwin);
3765  g_signal_connect(G_OBJECT(notebook), "page-added", G_CALLBACK(rcw_on_page_added), cnnwin);
3766  g_signal_connect(G_OBJECT(notebook), "page-removed", G_CALLBACK(rcw_on_page_removed), cnnwin);
3767  gtk_widget_set_can_focus(GTK_WIDGET(notebook), FALSE);
3768 
3769  return notebook;
3770 }
3771 
3772 /* Create a scrolled toplevel window */
3773 static RemminaConnectionWindow *rcw_create_scrolled(gint width, gint height, gboolean maximize)
3774 {
3775  TRACE_CALL(__func__);
3776  RemminaConnectionWindow *cnnwin;
3777  GtkWidget *grid;
3778  GtkWidget *toolbar;
3779  GtkNotebook *notebook;
3780  GtkSettings *settings = gtk_settings_get_default();
3781 
3782  cnnwin = rcw_new(FALSE, 0);
3783  gtk_widget_realize(GTK_WIDGET(cnnwin));
3784 
3785  gtk_window_set_default_size(GTK_WINDOW(cnnwin), width, height);
3786  g_object_set(settings, "gtk-application-prefer-dark-theme", remmina_pref.dark_theme, NULL);
3787 
3788  /* Create the toolbar */
3789  toolbar = rcw_create_toolbar(cnnwin, SCROLLED_WINDOW_MODE);
3790 
3791  /* Create the notebook */
3792  notebook = rcw_create_notebook(cnnwin);
3793 
3794  /* Create the grid container for toolbars+notebook and populate it */
3795  grid = gtk_grid_new();
3796 
3797 
3798  gtk_grid_attach(GTK_GRID(grid), GTK_WIDGET(notebook), 0, 0, 1, 1);
3799 
3800  gtk_widget_set_hexpand(GTK_WIDGET(notebook), TRUE);
3801  gtk_widget_set_vexpand(GTK_WIDGET(notebook), TRUE);
3802 
3803  rcw_place_toolbar(GTK_TOOLBAR(toolbar), GTK_GRID(grid), GTK_WIDGET(notebook), remmina_pref.toolbar_placement);
3804 
3805  gtk_container_add(GTK_CONTAINER(cnnwin), grid);
3806 
3807  /* Add drag capabilities to the toolbar */
3808  gtk_drag_source_set(GTK_WIDGET(toolbar), GDK_BUTTON1_MASK,
3809  dnd_targets_tb, sizeof dnd_targets_tb / sizeof *dnd_targets_tb, GDK_ACTION_MOVE);
3810  g_signal_connect_after(GTK_WIDGET(toolbar), "drag-begin", G_CALLBACK(rcw_tb_drag_begin), NULL);
3811  g_signal_connect(GTK_WIDGET(toolbar), "drag-failed", G_CALLBACK(rcw_tb_drag_failed), cnnwin);
3812 
3813  /* Add drop capabilities to the drop/dest target for the toolbar (the notebook) */
3814  gtk_drag_dest_set(GTK_WIDGET(notebook), GTK_DEST_DEFAULT_MOTION | GTK_DEST_DEFAULT_HIGHLIGHT,
3815  dnd_targets_tb, sizeof dnd_targets_tb / sizeof *dnd_targets_tb, GDK_ACTION_MOVE);
3816  gtk_drag_dest_set_track_motion(GTK_WIDGET(notebook), TRUE);
3817  g_signal_connect(GTK_WIDGET(notebook), "drag-drop", G_CALLBACK(rcw_tb_drag_drop), cnnwin);
3818 
3819  cnnwin->priv->view_mode = SCROLLED_WINDOW_MODE;
3820  cnnwin->priv->toolbar = toolbar;
3821  cnnwin->priv->grid = grid;
3822  cnnwin->priv->notebook = notebook;
3823  cnnwin->priv->ss_width = width;
3824  cnnwin->priv->ss_height = height;
3825  cnnwin->priv->ss_maximized = maximize;
3826 
3827  /* The notebook and all its child must be realized now, or a reparent will
3828  * call unrealize() and will destroy a GtkSocket */
3829  gtk_widget_show(grid);
3830  gtk_widget_show(GTK_WIDGET(cnnwin));
3831  GtkWindowGroup *wingrp = gtk_window_group_new();
3832 
3833  gtk_window_group_add_window(wingrp, GTK_WINDOW(cnnwin));
3834  gtk_window_set_transient_for(GTK_WINDOW(cnnwin), NULL);
3835 
3836  if (maximize)
3837  gtk_window_maximize(GTK_WINDOW(cnnwin));
3838 
3839 
3841 
3842  return cnnwin;
3843 }
3844 
3846 {
3847  TRACE_CALL(__func__);
3848 
3849  GtkWidget *revealer;
3851 
3852  priv = cnnwin->priv;
3853 
3854  if (priv->overlay_ftb_overlay != NULL) {
3855  gtk_widget_destroy(priv->overlay_ftb_overlay);
3856  priv->overlay_ftb_overlay = NULL;
3857  priv->revealer = NULL;
3858  }
3859 
3860  rcw_create_floating_toolbar(cnnwin, priv->fss_view_mode);
3861 
3862  priv->overlay_ftb_overlay = gtk_event_box_new();
3863 
3864  GtkWidget *vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
3865 
3866  gtk_container_set_border_width(GTK_CONTAINER(vbox), 0);
3867 
3868  GtkWidget *handle = gtk_drawing_area_new();
3869 
3870  gtk_widget_set_size_request(handle, 4, 4);
3871  gtk_widget_set_name(handle, "ftb-handle");
3872 
3873  revealer = gtk_revealer_new();
3874 
3875  gtk_widget_set_halign(GTK_WIDGET(priv->overlay_ftb_overlay), GTK_ALIGN_CENTER);
3876 
3878  gtk_box_pack_start(GTK_BOX(vbox), handle, FALSE, FALSE, 0);
3879  gtk_box_pack_start(GTK_BOX(vbox), revealer, FALSE, FALSE, 0);
3880  gtk_revealer_set_transition_type(GTK_REVEALER(revealer), GTK_REVEALER_TRANSITION_TYPE_SLIDE_UP);
3881  gtk_widget_set_valign(GTK_WIDGET(priv->overlay_ftb_overlay), GTK_ALIGN_END);
3882  } else {
3883  gtk_box_pack_start(GTK_BOX(vbox), revealer, FALSE, FALSE, 0);
3884  gtk_box_pack_start(GTK_BOX(vbox), handle, FALSE, FALSE, 0);
3885  gtk_revealer_set_transition_type(GTK_REVEALER(revealer), GTK_REVEALER_TRANSITION_TYPE_SLIDE_DOWN);
3886  gtk_widget_set_valign(GTK_WIDGET(priv->overlay_ftb_overlay), GTK_ALIGN_START);
3887  }
3888 
3889 
3890  gtk_container_add(GTK_CONTAINER(revealer), priv->floating_toolbar_widget);
3891  gtk_widget_set_halign(GTK_WIDGET(revealer), GTK_ALIGN_CENTER);
3892  gtk_widget_set_valign(GTK_WIDGET(revealer), GTK_ALIGN_START);
3893 
3894  priv->revealer = revealer;
3895 
3896  GtkWidget *fr;
3897 
3898  fr = gtk_frame_new(NULL);
3899  gtk_container_add(GTK_CONTAINER(priv->overlay_ftb_overlay), fr);
3900  gtk_container_add(GTK_CONTAINER(fr), vbox);
3901 
3902  gtk_widget_show(vbox);
3903  gtk_widget_show(revealer);
3904  gtk_widget_show(handle);
3905  gtk_widget_show(priv->overlay_ftb_overlay);
3906  gtk_widget_show(fr);
3907 
3909  gtk_widget_set_name(fr, "ftbbox-lower");
3910  else
3911  gtk_widget_set_name(fr, "ftbbox-upper");
3912 
3913  gtk_overlay_add_overlay(GTK_OVERLAY(priv->overlay), priv->overlay_ftb_overlay);
3914 
3915  rcw_floating_toolbar_show(cnnwin, TRUE);
3916 
3917  g_signal_connect(G_OBJECT(priv->overlay_ftb_overlay), "enter-notify-event", G_CALLBACK(rcw_floating_toolbar_on_enter), cnnwin);
3918  g_signal_connect(G_OBJECT(priv->overlay_ftb_overlay), "leave-notify-event", G_CALLBACK(rcw_floating_toolbar_on_leave), cnnwin);
3919  g_signal_connect(G_OBJECT(priv->overlay_ftb_overlay), "scroll-event", G_CALLBACK(rcw_floating_toolbar_on_scroll), cnnwin);
3920  gtk_widget_add_events(
3921  GTK_WIDGET(priv->overlay_ftb_overlay),
3922  GDK_SCROLL_MASK
3923 #if GTK_CHECK_VERSION(3, 4, 0)
3924  | GDK_SMOOTH_SCROLL_MASK
3925 #endif
3926  );
3927 
3928  /* Add drag and drop capabilities to the source */
3929  gtk_drag_source_set(GTK_WIDGET(priv->overlay_ftb_overlay), GDK_BUTTON1_MASK,
3930  dnd_targets_ftb, sizeof dnd_targets_ftb / sizeof *dnd_targets_ftb, GDK_ACTION_MOVE);
3931  g_signal_connect_after(GTK_WIDGET(priv->overlay_ftb_overlay), "drag-begin", G_CALLBACK(rcw_ftb_drag_begin), cnnwin);
3932 
3934  /* toolbar in fullscreenmode disabled, hide everything */
3935  gtk_widget_hide(fr);
3936 }
3937 
3938 
3939 static gboolean rcw_ftb_drag_drop(GtkWidget *widget, GdkDragContext *context,
3940  gint x, gint y, guint time, RemminaConnectionWindow *cnnwin)
3941 {
3942  TRACE_CALL(__func__);
3943  GtkAllocation wa;
3944  gint new_floating_toolbar_placement;
3945  RemminaConnectionObject *cnnobj;
3946 
3947  gtk_widget_get_allocation(widget, &wa);
3948 
3949  if (y >= wa.height / 2)
3950  new_floating_toolbar_placement = FLOATING_TOOLBAR_PLACEMENT_BOTTOM;
3951  else
3952  new_floating_toolbar_placement = FLOATING_TOOLBAR_PLACEMENT_TOP;
3953 
3954  gtk_drag_finish(context, TRUE, TRUE, time);
3955 
3956  if (new_floating_toolbar_placement != remmina_pref.floating_toolbar_placement) {
3957  /* Destroy and recreate the FTB */
3958  remmina_pref.floating_toolbar_placement = new_floating_toolbar_placement;
3961  cnnobj = rcw_get_visible_cnnobj(cnnwin);
3962  if (cnnobj) rco_update_toolbar(cnnobj);
3963  }
3964 
3965  return TRUE;
3966 }
3967 
3968 static void rcw_ftb_drag_begin(GtkWidget *widget, GdkDragContext *context, gpointer user_data)
3969 {
3970  TRACE_CALL(__func__);
3971 
3972  cairo_surface_t *surface;
3973  cairo_t *cr;
3974  GtkAllocation wa;
3975  double dashes[] = { 10 };
3976 
3977  gtk_widget_get_allocation(widget, &wa);
3978 
3979  surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, wa.width, wa.height);
3980  cr = cairo_create(surface);
3981  cairo_set_source_rgb(cr, 0.6, 0.6, 0.6);
3982  cairo_set_line_width(cr, 2);
3983  cairo_set_dash(cr, dashes, 1, 0);
3984  cairo_rectangle(cr, 0, 0, wa.width, wa.height);
3985  cairo_stroke(cr);
3986  cairo_destroy(cr);
3987 
3988  gtk_drag_set_icon_surface(context, surface);
3989 }
3990 
3991 RemminaConnectionWindow *rcw_create_fullscreen(GtkWindow *old, gint view_mode)
3992 {
3993  TRACE_CALL(__func__);
3994  RemminaConnectionWindow *cnnwin;
3995  GtkNotebook *notebook;
3996 
3997 #if GTK_CHECK_VERSION(3, 22, 0)
3998  gint n_monitors;
3999  gint i;
4000  GdkMonitor *old_monitor;
4001  GdkDisplay *old_display;
4002  GdkWindow *old_window;
4003 #endif
4004  gint full_screen_target_monitor;
4005 
4006  full_screen_target_monitor = FULL_SCREEN_TARGET_MONITOR_UNDEFINED;
4007  if (old) {
4008 #if GTK_CHECK_VERSION(3, 22, 0)
4009  old_window = gtk_widget_get_window(GTK_WIDGET(old));
4010  old_display = gdk_window_get_display(old_window);
4011  old_monitor = gdk_display_get_monitor_at_window(old_display, old_window);
4012  n_monitors = gdk_display_get_n_monitors(old_display);
4013  for (i = 0; i < n_monitors; ++i) {
4014  if (gdk_display_get_monitor(old_display, i) == old_monitor) {
4015  full_screen_target_monitor = i;
4016  break;
4017  }
4018  }
4019 #else
4020  full_screen_target_monitor = gdk_screen_get_monitor_at_window(
4021  gdk_screen_get_default(),
4022  gtk_widget_get_window(GTK_WIDGET(old)));
4023 #endif
4024  }
4025 
4026  cnnwin = rcw_new(TRUE, full_screen_target_monitor);
4027  gtk_widget_set_name(GTK_WIDGET(cnnwin), "remmina-connection-window-fullscreen");
4028  gtk_widget_realize(GTK_WIDGET(cnnwin));
4029 
4030  if (!view_mode)
4031  view_mode = VIEWPORT_FULLSCREEN_MODE;
4032 
4033  notebook = rcw_create_notebook(cnnwin);
4034 
4035  cnnwin->priv->overlay = gtk_overlay_new();
4036  gtk_container_add(GTK_CONTAINER(cnnwin), cnnwin->priv->overlay);
4037  gtk_container_add(GTK_CONTAINER(cnnwin->priv->overlay), GTK_WIDGET(notebook));
4038  gtk_widget_show(GTK_WIDGET(cnnwin->priv->overlay));
4039 
4040  cnnwin->priv->notebook = notebook;
4041  cnnwin->priv->view_mode = view_mode;
4042  cnnwin->priv->fss_view_mode = view_mode;
4043 
4044  /* Create the floating toolbar */
4046  /* Add drag and drop capabilities to the drop/dest target for floating toolbar */
4047  gtk_drag_dest_set(GTK_WIDGET(cnnwin->priv->overlay), GTK_DEST_DEFAULT_MOTION | GTK_DEST_DEFAULT_HIGHLIGHT,
4048  dnd_targets_ftb, sizeof dnd_targets_ftb / sizeof *dnd_targets_ftb, GDK_ACTION_MOVE);
4049  gtk_drag_dest_set_track_motion(GTK_WIDGET(cnnwin->priv->notebook), TRUE);
4050  g_signal_connect(GTK_WIDGET(cnnwin->priv->overlay), "drag-drop", G_CALLBACK(rcw_ftb_drag_drop), cnnwin);
4051 
4052  gtk_widget_show(GTK_WIDGET(cnnwin));
4053  GtkWindowGroup *wingrp = gtk_window_group_new();
4054  gtk_window_group_add_window(wingrp, GTK_WINDOW(cnnwin));
4055  gtk_window_set_transient_for(GTK_WINDOW(cnnwin), NULL);
4056 
4057  return cnnwin;
4058 }
4059 
4060 static gboolean rcw_hostkey_func(RemminaProtocolWidget *gp, guint keyval, gboolean release)
4061 {
4062  TRACE_CALL(__func__);
4063  RemminaConnectionObject *cnnobj = gp->cnnobj;
4064  RemminaConnectionWindowPriv *priv = cnnobj->cnnwin->priv;
4065  const RemminaProtocolFeature *feature;
4066  gint i;
4067 
4068  if (release) {
4069  if (remmina_pref.hostkey && keyval == remmina_pref.hostkey) {
4070  priv->hostkey_activated = FALSE;
4071  if (priv->hostkey_used)
4072  /* hostkey pressed + something else */
4073  return TRUE;
4074  }
4075  /* If hostkey is released without pressing other keys, we should execute the
4076  * shortcut key which is the same as hostkey. Be default, this is grab/ungrab
4077  * keyboard */
4078  else if (priv->hostkey_activated) {
4079  /* Trap all key releases when hostkey is pressed */
4080  /* hostkey pressed + something else */
4081  return TRUE;
4082  } else {
4083  /* Any key pressed, no hostkey */
4084  return FALSE;
4085  }
4086  } else if (remmina_pref.hostkey && keyval == remmina_pref.hostkey) {
4088  priv->hostkey_activated = TRUE;
4089  priv->hostkey_used = FALSE;
4090  return TRUE;
4091  } else if (!priv->hostkey_activated) {
4092  /* Any key pressed, no hostkey */
4093  return FALSE;
4094  }
4095 
4096  priv->hostkey_used = TRUE;
4097  keyval = gdk_keyval_to_lower(keyval);
4098  if (keyval == GDK_KEY_Up || keyval == GDK_KEY_Down
4099  || keyval == GDK_KEY_Left || keyval == GDK_KEY_Right) {
4100  GtkAdjustment *adjust;
4101  int pos;
4102 
4103  if (cnnobj->connected && GTK_IS_SCROLLED_WINDOW(cnnobj->scrolled_container)) {
4104  if (keyval == GDK_KEY_Up || keyval == GDK_KEY_Down)
4105  adjust = gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(cnnobj->scrolled_container));
4106  else
4107  adjust = gtk_scrolled_window_get_hadjustment(GTK_SCROLLED_WINDOW(cnnobj->scrolled_container));
4108 
4109  if (keyval == GDK_KEY_Up || keyval == GDK_KEY_Left)
4110  pos = 0;
4111  else
4112  pos = gtk_adjustment_get_upper(adjust);
4113 
4114  gtk_adjustment_set_value(adjust, pos);
4115  if (keyval == GDK_KEY_Up || keyval == GDK_KEY_Down)
4116  gtk_scrolled_window_set_vadjustment(GTK_SCROLLED_WINDOW(cnnobj->scrolled_container), adjust);
4117  else
4118  gtk_scrolled_window_set_hadjustment(GTK_SCROLLED_WINDOW(cnnobj->scrolled_container), adjust);
4119  } else if (REMMINA_IS_SCROLLED_VIEWPORT(cnnobj->scrolled_container)) {
4121  GtkWidget *child;
4122  GdkWindow *gsvwin;
4123  gint sz;
4124  GtkAdjustment *adj;
4125  gdouble value;
4126 
4127  if (!GTK_IS_BIN(cnnobj->scrolled_container))
4128  return FALSE;
4129 
4130  gsv = REMMINA_SCROLLED_VIEWPORT(cnnobj->scrolled_container);
4131  child = gtk_bin_get_child(GTK_BIN(gsv));
4132  if (!GTK_IS_VIEWPORT(child))
4133  return FALSE;
4134 
4135  gsvwin = gtk_widget_get_window(GTK_WIDGET(gsv));
4136  if (!gsv)
4137  return FALSE;
4138 
4139  if (keyval == GDK_KEY_Up || keyval == GDK_KEY_Down) {
4140  sz = gdk_window_get_height(gsvwin) + 2; // Add 2px of black scroll border
4141  adj = gtk_scrollable_get_vadjustment(GTK_SCROLLABLE(child));
4142  } else {
4143  sz = gdk_window_get_width(gsvwin) + 2; // Add 2px of black scroll border
4144  adj = gtk_scrollable_get_hadjustment(GTK_SCROLLABLE(child));
4145  }
4146 
4147  if (keyval == GDK_KEY_Up || keyval == GDK_KEY_Left)
4148  value = 0;
4149  else
4150  value = gtk_adjustment_get_upper(GTK_ADJUSTMENT(adj)) - (gdouble)sz + 2.0;
4151 
4152  gtk_adjustment_set_value(GTK_ADJUSTMENT(adj), value);
4153  }
4154  }
4155 
4156  if (keyval == remmina_pref.shortcutkey_fullscreen && !extrahardening) {
4157  switch (priv->view_mode) {
4158  case SCROLLED_WINDOW_MODE:
4159  rcw_switch_viewmode(cnnobj->cnnwin, priv->fss_view_mode);
4160  break;
4164  break;
4165  default:
4166  break;
4167  }
4168  } else if (keyval == remmina_pref.shortcutkey_autofit && !extrahardening) {
4169  if (priv->toolitem_autofit && gtk_widget_is_sensitive(GTK_WIDGET(priv->toolitem_autofit)))
4170  rcw_toolbar_autofit(GTK_TOOL_ITEM(gp), cnnobj->cnnwin);
4171  } else if (keyval == remmina_pref.shortcutkey_nexttab && !extrahardening) {
4172  i = gtk_notebook_get_current_page(GTK_NOTEBOOK(priv->notebook)) + 1;
4173  if (i >= gtk_notebook_get_n_pages(GTK_NOTEBOOK(priv->notebook)))
4174  i = 0;
4175  gtk_notebook_set_current_page(GTK_NOTEBOOK(priv->notebook), i);
4176  } else if (keyval == remmina_pref.shortcutkey_prevtab && !extrahardening) {
4177  i = gtk_notebook_get_current_page(GTK_NOTEBOOK(priv->notebook)) - 1;
4178  if (i < 0)
4179  i = gtk_notebook_get_n_pages(GTK_NOTEBOOK(priv->notebook)) - 1;
4180  gtk_notebook_set_current_page(GTK_NOTEBOOK(priv->notebook), i);
4181  } else if (keyval == remmina_pref.shortcutkey_scale && !extrahardening) {
4182  if (gtk_widget_is_sensitive(GTK_WIDGET(priv->toolitem_scale))) {
4183  gtk_toggle_tool_button_set_active(
4184  GTK_TOGGLE_TOOL_BUTTON(priv->toolitem_scale),
4185  !gtk_toggle_tool_button_get_active(
4186  GTK_TOGGLE_TOOL_BUTTON(
4187  priv->toolitem_scale)));
4188  }
4189  } else if (keyval == remmina_pref.shortcutkey_grab && !extrahardening) {
4190  gtk_toggle_tool_button_set_active(
4191  GTK_TOGGLE_TOOL_BUTTON(priv->toolitem_grab),
4192  !gtk_toggle_tool_button_get_active(
4193  GTK_TOGGLE_TOOL_BUTTON(
4194  priv->toolitem_grab)));
4195  } else if (keyval == remmina_pref.shortcutkey_minimize && !extrahardening) {
4196  rcw_toolbar_minimize(GTK_TOOL_ITEM(gp),
4197  cnnobj->cnnwin);
4198  } else if (keyval == remmina_pref.shortcutkey_viewonly && !extrahardening) {
4199  remmina_file_set_int(cnnobj->remmina_file, "viewonly",
4200  (remmina_file_get_int(cnnobj->remmina_file, "viewonly", 0)
4201  == 0) ? 1 : 0);
4202  } else if (keyval == remmina_pref.shortcutkey_screenshot && !extrahardening) {
4203  rcw_toolbar_screenshot(GTK_TOOL_ITEM(gp),
4204  cnnobj->cnnwin);
4205  } else if (keyval == remmina_pref.shortcutkey_disconnect && !extrahardening) {
4207  } else if (keyval == remmina_pref.shortcutkey_toolbar && !extrahardening) {
4208  if (priv->view_mode == SCROLLED_WINDOW_MODE) {
4209  remmina_pref.hide_connection_toolbar =
4210  !remmina_pref.hide_connection_toolbar;
4212  }
4213  } else {
4214  for (feature =
4216  REMMINA_PROTOCOL_WIDGET(
4217  cnnobj->proto));
4218  feature && feature->type;
4219  feature++) {
4220  if (feature->type
4222  && GPOINTER_TO_UINT(
4223  feature->opt3)
4224  == keyval) {
4226  REMMINA_PROTOCOL_WIDGET(
4227  cnnobj->proto),
4228  feature);
4229  break;
4230  }
4231  }
4232  }
4233  /* If a keypress makes the current cnnobj to move to another window,
4234  * priv is now invalid. So we can no longer use priv here */
4235  cnnobj->cnnwin->priv->hostkey_activated = FALSE;
4236 
4237  /* Trap all keypresses when hostkey is pressed */
4238  return TRUE;
4239 }
4240 
4242 {
4243  TRACE_CALL(__func__);
4244  const gchar *tag;
4245 
4246  switch (remmina_pref.tab_mode) {
4247  case REMMINA_TAB_BY_GROUP:
4248  tag = remmina_file_get_string(remminafile, "group");
4249  break;
4251  tag = remmina_file_get_string(remminafile, "protocol");
4252  break;
4253  case REMMINA_TAB_ALL:
4254  tag = NULL;
4255  break;
4256  case REMMINA_TAB_NONE:
4257  default:
4258  return NULL;
4259  }
4260  return RCW(remmina_widget_pool_find(REMMINA_TYPE_CONNECTION_WINDOW, tag));
4261 }
4262 
4263 gboolean rcw_delayed_window_present(gpointer user_data)
4264 {
4265  RemminaConnectionWindow *cnnwin = (RemminaConnectionWindow *)user_data;
4266 
4267  if (cnnwin) {
4268  gtk_window_present_with_time(GTK_WINDOW(cnnwin), (guint32)(g_get_monotonic_time() / 1000));
4269  rcw_grab_focus(cnnwin);
4270  }
4271  cnnwin->priv->dwp_eventsourceid = 0;
4272  return G_SOURCE_REMOVE;
4273 }
4274 
4276 {
4277  TRACE_CALL(__func__);
4278 
4279  REMMINA_DEBUG("Connect signal emitted");
4280 
4281  /* This signal handler is called by a plugin when it’s correctly connected
4282  * (and authenticated) */
4283 
4284  cnnobj->connected = TRUE;
4285 
4286  remmina_protocol_widget_set_hostkey_func(REMMINA_PROTOCOL_WIDGET(cnnobj->proto),
4287  (RemminaHostkeyFunc)rcw_hostkey_func);
4288 
4292  if (remmina_file_get_filename(cnnobj->remmina_file) == NULL)
4294  remmina_file_get_string(cnnobj->remmina_file, "server"));
4295  REMMINA_DEBUG("We save the last successful connection date");
4296  //remmina_file_set_string(cnnobj->remmina_file, "last_success", last_success);
4298  //REMMINA_DEBUG("Last connection made on %s.", last_success);
4299 
4300  REMMINA_DEBUG("Saving credentials");
4301  /* Save credentials */
4303 
4304  if (cnnobj->cnnwin->priv->floating_toolbar_widget)
4305  gtk_widget_show(cnnobj->cnnwin->priv->floating_toolbar_widget);
4306 
4307  rco_update_toolbar(cnnobj);
4308 
4309  REMMINA_DEBUG("Trying to present the window");
4310  /* Try to present window */
4311  cnnobj->cnnwin->priv->dwp_eventsourceid = g_timeout_add(200, rcw_delayed_window_present, (gpointer)cnnobj->cnnwin);
4312 }
4313 
4314 static void cb_lasterror_confirmed(void *cbdata, int btn)
4315 {
4316  TRACE_CALL(__func__);
4318 }
4319 
4321 {
4322  TRACE_CALL(__func__);
4323  RemminaConnectionObject *cnnobj = gp->cnnobj;
4324  RemminaConnectionWindowPriv *priv = cnnobj->cnnwin->priv;
4325  GtkWidget *pparent;
4326 
4327  REMMINA_DEBUG("Disconnect signal received on RemminaProtocolWidget");
4328  /* Detach the protocol widget from the notebook now, or we risk that a
4329  * window delete will destroy cnnobj->proto before we complete disconnection.
4330  */
4331  pparent = gtk_widget_get_parent(cnnobj->proto);
4332  if (pparent != NULL) {
4333  g_object_ref(cnnobj->proto);
4334  gtk_container_remove(GTK_CONTAINER(pparent), cnnobj->proto);
4335  }
4336 
4337  cnnobj->connected = FALSE;
4338 
4339  if (remmina_pref.save_view_mode) {
4340  if (cnnobj->cnnwin)
4341  remmina_file_set_int(cnnobj->remmina_file, "viewmode", cnnobj->cnnwin->priv->view_mode);
4343  }
4344 
4345  rcw_kp_ungrab(cnnobj->cnnwin);
4346  gtk_toggle_tool_button_set_active(
4347  GTK_TOGGLE_TOOL_BUTTON(priv->toolitem_grab),
4348  FALSE);
4349 
4351  /* We cannot close window immediately, but we must show a message panel */
4352  RemminaMessagePanel *mp;
4353  /* Destroy scrolled_container (and viewport) and all its children the plugin created
4354  * on it, so they will not receive GUI signals */
4355  if (cnnobj->scrolled_container) {
4356  gtk_widget_destroy(cnnobj->scrolled_container);
4357  cnnobj->scrolled_container = NULL;
4358  }
4359  cnnobj->viewport = NULL;
4362  rco_show_message_panel(gp->cnnobj, mp);
4363  REMMINA_DEBUG("Could not disconnect");
4364  } else {
4365  rco_closewin(gp);
4366  REMMINA_DEBUG("Disconnected");
4367  }
4368 }
4369 
4371 {
4372  TRACE_CALL(__func__);
4373  RemminaConnectionObject *cnnobj = gp->cnnobj;
4374 
4375  if (cnnobj && cnnobj->cnnwin && cnnobj->cnnwin->priv->view_mode != SCROLLED_WINDOW_MODE)
4376  rco_check_resize(cnnobj);
4377 }
4378 
4380 {
4381  TRACE_CALL(__func__);
4382  RemminaConnectionObject *cnnobj = gp->cnnobj;
4383 
4385 }
4386 
4388 {
4389  TRACE_CALL(__func__);
4390  RemminaConnectionObject *cnnobj = gp->cnnobj;
4391 
4392  cnnobj->dynres_unlocked = FALSE;
4393  rco_update_toolbar(cnnobj);
4394 }
4395 
4397 {
4398  TRACE_CALL(__func__);
4399  RemminaConnectionObject *cnnobj = gp->cnnobj;
4400 
4401  cnnobj->dynres_unlocked = TRUE;
4402  rco_update_toolbar(cnnobj);
4403 }
4404 
4405 gboolean rcw_open_from_filename(const gchar *filename)
4406 {
4407  TRACE_CALL(__func__);
4408  RemminaFile *remminafile;
4409  GtkWidget *dialog;
4410 
4411  remminafile = remmina_file_manager_load_file(filename);
4412  if (remminafile) {
4413  if (remmina_file_get_int (remminafile, "profile-lock", FALSE)
4415  return FALSE;
4416  rcw_open_from_file(remminafile);
4417  return TRUE;
4418  } else {
4419  dialog = gtk_message_dialog_new(NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE,
4420  _("The file “%s” is corrupted, unreadable, or could not be found."), filename);
4421  g_signal_connect(G_OBJECT(dialog), "response", G_CALLBACK(gtk_widget_destroy), NULL);
4422  gtk_widget_show(dialog);
4424  return FALSE;
4425  }
4426 }
4427 
4428 static gboolean open_connection_last_stage(gpointer user_data)
4429 {
4430  RemminaProtocolWidget *gp = (RemminaProtocolWidget *)user_data;
4431 
4432  /* Now we have an allocated size for our RemminaProtocolWidget. We can proceed with the connection */
4435  rco_check_resize(gp->cnnobj);
4436 
4437  return FALSE;
4438 }
4439 
4440 static void rpw_size_allocated_on_connection(GtkWidget *w, GdkRectangle *allocation, gpointer user_data)
4441 {
4443 
4444  /* Disconnect signal handler to avoid to be called again after a normal resize */
4445  g_signal_handler_disconnect(w, gp->cnnobj->deferred_open_size_allocate_handler);
4446 
4447  /* Allow extra 100 ms for size allocation (do we really need it?) */
4448  g_timeout_add(100, open_connection_last_stage, gp);
4449 
4450  return;
4451 }
4452 
4454 {
4455  TRACE_CALL(__func__);
4456  rcw_open_from_file_full(remminafile, NULL, NULL, NULL);
4457 }
4458 
4459 static void set_label_selectable(gpointer data, gpointer user_data)
4460 {
4461  TRACE_CALL(__func__);
4462  GtkWidget *widget = GTK_WIDGET(data);
4463 
4464  if (GTK_IS_LABEL(widget))
4465  gtk_label_set_selectable(GTK_LABEL(widget), TRUE);
4466 }
4467 
4475 };
4476 
4481 static void rcw_gtksocket_not_available_dialog_response(GtkDialog * self,
4482  gint response_id,
4483  RemminaConnectionObject * cnnobj)
4484 {
4485  TRACE_CALL(__func__);
4486 
4487  GError *error = NULL;
4488 
4489  if (response_id == GTKSOCKET_NOT_AVAIL_RESPONSE_OPEN_BROWSER) {
4490  gtk_show_uri_on_window(
4491  NULL,
4492  // TRANSLATORS: This should be a link to the Remmina wiki page:
4493  // TRANSLATORS: 'GtkSocket feature is not available'.
4494  _("https://gitlab.com/Remmina/Remmina/-/wikis/GtkSocket-feature-is-not-available-in-a-Wayland-session"),
4495  GDK_CURRENT_TIME, &error
4496  );
4497  }
4498 
4499  // Close the current page since it's useless without GtkSocket.
4500  // The user would need to manually click the close button.
4501  if (cnnobj) rco_disconnect_current_page(cnnobj);
4502 
4503  gtk_widget_destroy(GTK_WIDGET(self));
4504 }
4505 
4506 GtkWidget *rcw_open_from_file_full(RemminaFile *remminafile, GCallback disconnect_cb, gpointer data, guint *handler)
4507 {
4508  TRACE_CALL(__func__);
4509  RemminaConnectionObject *cnnobj;
4510 
4511  gint ret;
4512  GtkWidget *dialog;
4513  GtkWidget *newpage;
4514  gint width, height;
4515  gboolean maximize;
4516  gint view_mode;
4517  const gchar *msg;
4518  RemminaScaleMode scalemode;
4519 
4520  if (disconnect_cb) {
4521  g_print("disconnect_cb is deprecated inside rcw_open_from_file_full() and should be null\n");
4522  return NULL;
4523  }
4524 
4525 
4526  /* Create the RemminaConnectionObject */
4527  cnnobj = g_new0(RemminaConnectionObject, 1);
4528  cnnobj->remmina_file = remminafile;
4529 
4530  /* Create the RemminaProtocolWidget */
4531  cnnobj->proto = remmina_protocol_widget_new();
4532  remmina_protocol_widget_setup((RemminaProtocolWidget *)cnnobj->proto, remminafile, cnnobj);
4534  GtkWindow *wparent;
4535  wparent = remmina_main_get_window();
4537  dialog = gtk_message_dialog_new(wparent, GTK_DIALOG_DESTROY_WITH_PARENT,
4538  GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, "%s", msg);
4539  gtk_dialog_run(GTK_DIALOG(dialog));
4540  gtk_widget_destroy(dialog);
4541  /* We should destroy cnnobj->proto and cnnobj now… TODO: Fix this leak */
4542  return NULL;
4543  }
4544 
4545 
4546 
4547  /* Set a name for the widget, for CSS selector */
4548  gtk_widget_set_name(GTK_WIDGET(cnnobj->proto), "remmina-protocol-widget");
4549 
4550  gtk_widget_set_halign(GTK_WIDGET(cnnobj->proto), GTK_ALIGN_FILL);
4551  gtk_widget_set_valign(GTK_WIDGET(cnnobj->proto), GTK_ALIGN_FILL);
4552 
4553  if (data)
4554  g_object_set_data(G_OBJECT(cnnobj->proto), "user-data", data);
4555 
4556  view_mode = remmina_file_get_int(cnnobj->remmina_file, "viewmode", 0);
4557  if (kioskmode)
4558  view_mode = VIEWPORT_FULLSCREEN_MODE;
4559  gint ismultimon = remmina_file_get_int(cnnobj->remmina_file, "multimon", 0);
4560 
4561  if (ismultimon)
4562  view_mode = VIEWPORT_FULLSCREEN_MODE;
4563 
4564  if (fullscreen)
4565  view_mode = VIEWPORT_FULLSCREEN_MODE;
4566 
4567  /* Create the viewport to make the RemminaProtocolWidget scrollable */
4568  cnnobj->viewport = gtk_viewport_new(NULL, NULL);
4569  gtk_widget_set_name(cnnobj->viewport, "remmina-cw-viewport");
4570  gtk_widget_show(cnnobj->viewport);
4571  gtk_container_set_border_width(GTK_CONTAINER(cnnobj->viewport), 0);
4572  gtk_viewport_set_shadow_type(GTK_VIEWPORT(cnnobj->viewport), GTK_SHADOW_NONE);
4573 
4574  /* Create the scrolled container */
4575  scalemode = get_current_allowed_scale_mode(cnnobj, NULL, NULL);
4576  cnnobj->scrolled_container = rco_create_scrolled_container(scalemode, view_mode);
4577 
4578  gtk_container_add(GTK_CONTAINER(cnnobj->scrolled_container), cnnobj->viewport);
4579 
4580  /* Determine whether the plugin can scale or not. If the plugin can scale and we do
4581  * not want to expand, then we add a GtkAspectFrame to maintain aspect ratio during scaling */
4583  remmina_file_get_string(remminafile, "protocol"),
4585 
4586  cnnobj->aspectframe = NULL;
4587  gtk_container_add(GTK_CONTAINER(cnnobj->viewport), cnnobj->proto);
4588 
4589  /* Determine whether this connection will be put on a new window
4590  * or in an existing one */
4591  cnnobj->cnnwin = rcw_find(remminafile);
4592  if (!cnnobj->cnnwin) {
4593  /* Connection goes on a new toplevel window */
4594  switch (view_mode) {
4597  cnnobj->cnnwin = rcw_create_fullscreen(NULL, view_mode);
4598  break;
4599  case SCROLLED_WINDOW_MODE:
4600  default:
4601  width = remmina_file_get_int(cnnobj->remmina_file, "window_width", 640);
4602  height = remmina_file_get_int(cnnobj->remmina_file, "window_height", 480);
4603  maximize = remmina_file_get_int(cnnobj->remmina_file, "window_maximize", FALSE) ? TRUE : FALSE;
4604  cnnobj->cnnwin = rcw_create_scrolled(width, height, maximize);
4605  break;
4606  }
4607  rcw_update_tag(cnnobj->cnnwin, cnnobj);
4608  rcw_append_new_page(cnnobj->cnnwin, cnnobj);
4609  } else {
4610  newpage = rcw_append_new_page(cnnobj->cnnwin, cnnobj);
4611  gtk_window_present(GTK_WINDOW(cnnobj->cnnwin));
4612  nb_set_current_page(cnnobj->cnnwin->priv->notebook, newpage);
4613  }
4614 
4615  // Do not call remmina_protocol_widget_update_alignment(cnnobj); here or cnnobj->proto will not fill its parent size
4616  // and remmina_protocol_widget_update_remote_resolution() cannot autodetect available space
4617 
4618  gtk_widget_show(cnnobj->proto);
4619  g_signal_connect(G_OBJECT(cnnobj->proto), "connect", G_CALLBACK(rco_on_connect), cnnobj);
4620  g_signal_connect(G_OBJECT(cnnobj->proto), "disconnect", G_CALLBACK(rco_on_disconnect), NULL);
4621  g_signal_connect(G_OBJECT(cnnobj->proto), "desktop-resize", G_CALLBACK(rco_on_desktop_resize), NULL);
4622  g_signal_connect(G_OBJECT(cnnobj->proto), "update-align", G_CALLBACK(rco_on_update_align), NULL);
4623  g_signal_connect(G_OBJECT(cnnobj->proto), "lock-dynres", G_CALLBACK(rco_on_lock_dynres), NULL);
4624  g_signal_connect(G_OBJECT(cnnobj->proto), "unlock-dynres", G_CALLBACK(rco_on_unlock_dynres), NULL);
4625  g_signal_connect(G_OBJECT(cnnobj->proto), "enter-notify-event", G_CALLBACK(rco_enter_protocol_widget), cnnobj);
4626  g_signal_connect(G_OBJECT(cnnobj->proto), "leave-notify-event", G_CALLBACK(rco_leave_protocol_widget), cnnobj);
4627 
4628  if (!remmina_pref.save_view_mode)
4629  remmina_file_set_int(cnnobj->remmina_file, "viewmode", remmina_pref.default_mode);
4630 
4631 
4632  /* If it is a GtkSocket plugin and X11 is not available, we inform the
4633  * user and close the connection */
4635  remmina_file_get_string(remminafile, "protocol"),
4637  if (ret && !remmina_gtksocket_available()) {
4638  gchar *title = _("Warning: This plugin requires GtkSocket, but this "
4639  "feature is unavailable in a Wayland session.");
4640 
4641  gchar *err_msg =
4642  // TRANSLATORS: This should be a link to the Remmina wiki page:
4643  // 'GtkSocket feature is not available'.
4644  _("Plugins relying on GtkSocket can't run in a "
4645  "Wayland session.\nFor more info and a possible "
4646  "workaround, please visit the Remmina wiki at:\n\n"
4647  "https://gitlab.com/Remmina/Remmina/-/wikis/GtkSocket-feature-is-not-available-in-a-Wayland-session");
4648 
4649  dialog = gtk_message_dialog_new(
4650  GTK_WINDOW(cnnobj->cnnwin),
4651  GTK_DIALOG_MODAL,
4652  GTK_MESSAGE_WARNING,
4653  GTK_BUTTONS_OK,
4654  "%s", title);
4655 
4656  gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog), "%s",
4657  err_msg);
4658  gtk_dialog_add_button(GTK_DIALOG(dialog), _("Open in web browser"),
4660 
4661  REMMINA_CRITICAL(g_strdup_printf("%s\n%s", title, err_msg));
4662 
4663  g_signal_connect(G_OBJECT(dialog),
4664  "response",
4666  cnnobj);
4667 
4668  // Make Text selectable. Usefull because of the link in the text.
4669  GtkWidget *area = gtk_message_dialog_get_message_area(
4670  GTK_MESSAGE_DIALOG(dialog));
4671  GtkContainer *box = (GtkContainer *)area;
4672 
4673  GList *children = gtk_container_get_children(box);
4674  g_list_foreach(children, set_label_selectable, NULL);
4675  g_list_free(children);
4676 
4677  gtk_widget_show(dialog);
4678 
4679  return NULL; /* Should we destroy something before returning? */
4680  }
4681 
4682  if (cnnobj->cnnwin->priv->floating_toolbar_widget)
4683  gtk_widget_show(cnnobj->cnnwin->priv->floating_toolbar_widget);
4684 
4686  printf("OK, an error occurred in initializing the protocol plugin before connecting. The error is %s.\n"
4687  "TODO: Put this string as an error to show", remmina_protocol_widget_get_error_message((RemminaProtocolWidget *)cnnobj->proto));
4688  return cnnobj->proto;
4689  }
4690 
4691 
4692  /* GTK window setup is done here, and we are almost ready to call remmina_protocol_widget_open_connection().
4693  * But size has not yet been allocated by GTK
4694  * to the widgets. If we are in RES_USE_INITIAL_WINDOW_SIZE resolution mode or scale is REMMINA_PROTOCOL_WIDGET_SCALE_MODE_DYNRES,
4695  * we should wait for a size allocation from GTK for cnnobj->proto
4696  * before connecting */
4697 
4698  cnnobj->deferred_open_size_allocate_handler = g_signal_connect(G_OBJECT(cnnobj->proto), "size-allocate", G_CALLBACK(rpw_size_allocated_on_connection), NULL);
4699 
4700  return cnnobj->proto;
4701 }
4702 
4704 {
4705  return &cnnobj->cnnwin->window;
4706 }
4708 {
4709  return cnnobj->viewport;
4710 }
4711 
4713 {
4714  TRACE_CALL(__func__);
4715  cnnwin->priv->on_delete_confirm_mode = mode;
4716 }
4717 
4722 void rco_destroy_message_panel(RemminaConnectionObject *cnnobj, RemminaMessagePanel *mp)
4723 {
4724  TRACE_CALL(__func__);
4725  GList *childs, *cc;
4726  RemminaMessagePanel *lastPanel;
4727  gboolean was_visible;
4728  GtkWidget *page;
4729 
4730  page = nb_find_page_by_cnnobj(cnnobj->cnnwin->priv->notebook, cnnobj);
4731  childs = gtk_container_get_children(GTK_CONTAINER(page));
4732  cc = g_list_first(childs);
4733  while (cc != NULL) {
4734  if ((RemminaMessagePanel *)cc->data == mp)
4735  break;
4736  cc = g_list_next(cc);
4737  }
4738  g_list_free(childs);
4739 
4740  if (cc == NULL) {
4741  printf("Remmina: Warning. There was a request to destroy a RemminaMessagePanel that is not on the page\n");
4742  return;
4743  }
4744  was_visible = gtk_widget_is_visible(GTK_WIDGET(mp));
4745  gtk_widget_destroy(GTK_WIDGET(mp));
4746 
4747  /* And now, show the last remaining message panel, if needed */
4748  if (was_visible) {
4749  childs = gtk_container_get_children(GTK_CONTAINER(page));
4750  cc = g_list_first(childs);
4751  lastPanel = NULL;
4752  while (cc != NULL) {
4753  if (G_TYPE_CHECK_INSTANCE_TYPE(cc->data, REMMINA_TYPE_MESSAGE_PANEL))
4754  lastPanel = (RemminaMessagePanel *)cc->data;
4755  cc = g_list_next(cc);
4756  }
4757  g_list_free(childs);
4758  if (lastPanel)
4759  gtk_widget_show(GTK_WIDGET(lastPanel));
4760  }
4761 }
4762 
4769 void rco_show_message_panel(RemminaConnectionObject *cnnobj, RemminaMessagePanel *mp)
4770 {
4771  TRACE_CALL(__func__);
4772  GList *childs, *cc;
4773  GtkWidget *page;
4774 
4775  /* Hides all RemminaMessagePanels childs of cnnobj->page */
4776  page = nb_find_page_by_cnnobj(cnnobj->cnnwin->priv->notebook, cnnobj);
4777  childs = gtk_container_get_children(GTK_CONTAINER(page));
4778  cc = g_list_first(childs);
4779  while (cc != NULL) {
4780  if (G_TYPE_CHECK_INSTANCE_TYPE(cc->data, REMMINA_TYPE_MESSAGE_PANEL))
4781  gtk_widget_hide(GTK_WIDGET(cc->data));
4782  cc = g_list_next(cc);
4783  }
4784  g_list_free(childs);
4785 
4786  /* Add the new message panel at the top of cnnobj->page */
4787  gtk_box_pack_start(GTK_BOX(page), GTK_WIDGET(mp), FALSE, FALSE, 0);
4788  gtk_box_reorder_child(GTK_BOX(page), GTK_WIDGET(mp), 0);
4789 
4790  /* Show the message panel */
4791  gtk_widget_show_all(GTK_WIDGET(mp));
4792 
4793  /* Focus the correct field of the RemminaMessagePanel */
4795 }
static RemminaConnectionWindow * rcw_find(RemminaFile *remminafile)
Definition: rcw.c:4241
+Go to the documentation of this file.
1 /*
2  * Remmina - The GTK+ Remote Desktop Client
3  * Copyright (C) 2009-2011 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 
38 #include "config.h"
39 
40 #ifdef GDK_WINDOWING_X11
41 #include <cairo/cairo-xlib.h>
42 #else
43 #include <cairo/cairo.h>
44 #endif
45 #include <gdk/gdk.h>
46 #include <gdk/gdkkeysyms.h>
47 #include <glib/gi18n.h>
48 #include <stdlib.h>
49 
50 #include "remmina.h"
51 #include "remmina_main.h"
52 #include "rcw.h"
54 #include "remmina_applet_menu.h"
55 #include "remmina_file.h"
56 #include "remmina_file_manager.h"
57 #include "remmina_log.h"
58 #include "remmina_message_panel.h"
59 #include "remmina_ext_exec.h"
60 #include "remmina_plugin_manager.h"
61 #include "remmina_pref.h"
63 #include "remmina_public.h"
65 #include "remmina_unlock.h"
66 #include "remmina_utils.h"
67 #include "remmina_widget_pool.h"
69 
70 #ifdef GDK_WINDOWING_WAYLAND
71 #include <gdk/gdkwayland.h>
72 #endif
73 
74 
75 #define DEBUG_KB_GRABBING 0
76 #include "remmina_exec.h"
77 
80 
81 G_DEFINE_TYPE(RemminaConnectionWindow, rcw, GTK_TYPE_WINDOW)
82 
83 #define MOTION_TIME 100
84 
85 /* default timeout used to hide the floating toolbar when switching profile */
86 #define TB_HIDE_TIME_TIME 1500
87 
88 #define FULL_SCREEN_TARGET_MONITOR_UNDEFINED -1
89 
90 struct _RemminaConnectionWindowPriv {
91  GtkNotebook * notebook;
92  GtkWidget * floating_toolbar_widget;
93  GtkWidget * overlay;
94  GtkWidget * revealer;
95  GtkWidget * overlay_ftb_overlay;
96  GtkWidget * overlay_ftb_fr;
97 
98  GtkWidget * floating_toolbar_label;
99  gdouble floating_toolbar_opacity;
100 
101  /* Various delayed and timer event source ids */
102  guint acs_eventsourceid; // timeout
103  guint spf_eventsourceid; // idle
104  guint grab_retry_eventsourceid; // timeout
105  guint delayed_grab_eventsourceid;
106  guint ftb_hide_eventsource; // timeout
107  guint tar_eventsource; // timeout
108  guint hidetb_eventsource; // timeout
109  guint dwp_eventsourceid; // timeout
110 
111  GtkWidget * toolbar;
112  GtkWidget * grid;
113 
114  /* Toolitems that need to be handled */
115  GtkToolItem * toolitem_menu;
116  GtkToolItem * toolitem_autofit;
117  GtkToolItem * toolitem_fullscreen;
118  GtkToolItem * toolitem_switch_page;
119  GtkToolItem * toolitem_dynres;
120  GtkToolItem * toolitem_scale;
121  GtkToolItem * toolitem_grab;
122  GtkToolItem * toolitem_multimon;
123  GtkToolItem * toolitem_preferences;
124  GtkToolItem * toolitem_tools;
125  GtkToolItem * toolitem_new;
126  GtkToolItem * toolitem_duplicate;
127  GtkToolItem * toolitem_screenshot;
128  GtkWidget * fullscreen_option_button;
129  GtkWidget * fullscreen_scaler_button;
130  GtkWidget * scaler_option_button;
131 
132  GtkWidget * pin_button;
133  gboolean pin_down;
134 
135  gboolean sticky;
136 
137  /* Flag to turn off toolbar signal handling when toolbar is
138  * reconfiguring, usually due to a tab switch */
139  gboolean toolbar_is_reconfiguring;
140 
141  /* This is the current view mode, i.e. VIEWPORT_FULLSCREEN_MODE,
142  * as saved on the "viwemode" profile preference file */
143  gint view_mode;
144 
145  /* Status variables used when in fullscreen mode. Needed
146  * to restore a fullscreen mode after coming from scrolled */
147  gint fss_view_mode;
148  /* Status variables used when in scrolled window mode. Needed
149  * to restore a scrolled window mode after coming from fullscreen */
150  gint ss_width, ss_height;
151  gboolean ss_maximized;
152 
153  gboolean kbcaptured;
154  gboolean pointer_captured;
155  gboolean hostkey_activated;
156  gboolean hostkey_used;
157 
158  gboolean pointer_entered;
159 
160  RemminaConnectionWindowOnDeleteConfirmMode on_delete_confirm_mode;
161 };
162 
163 typedef struct _RemminaConnectionObject {
166 
167  GtkWidget * proto;
168  GtkWidget * aspectframe;
169  GtkWidget * viewport;
170 
171  GtkWidget * scrolled_container;
172 
174 
175  gboolean connected;
176  gboolean dynres_unlocked;
177 
180 
181 enum {
184 };
185 
186 static guint rcw_signals[LAST_SIGNAL] =
187 { 0 };
188 
189 static RemminaConnectionWindow *rcw_create_scrolled(gint width, gint height, gboolean maximize);
190 static RemminaConnectionWindow *rcw_create_fullscreen(GtkWindow *old, gint view_mode);
191 static gboolean rcw_hostkey_func(RemminaProtocolWidget *gp, guint keyval, gboolean release);
192 static GtkWidget *rco_create_tab_page(RemminaConnectionObject *cnnobj);
193 static GtkWidget *rco_create_tab_label(RemminaConnectionObject *cnnobj);
194 
196 static GtkWidget *rcw_create_toolbar(RemminaConnectionWindow *cnnwin, gint mode);
197 static void rcw_place_toolbar(GtkToolbar *toolbar, GtkGrid *grid, GtkWidget *sibling, int toolbar_placement);
198 static void rco_update_toolbar(RemminaConnectionObject *cnnobj);
199 static void rcw_keyboard_grab(RemminaConnectionWindow *cnnwin);
200 static GtkWidget *rcw_append_new_page(RemminaConnectionWindow *cnnwin, RemminaConnectionObject *cnnobj);
201 
202 
203 static void rcw_ftb_drag_begin(GtkWidget *widget, GdkDragContext *context, gpointer user_data);
204 
205 static const GtkTargetEntry dnd_targets_ftb[] =
206 {
207  {
208  (char *)"text/x-remmina-ftb",
209  GTK_TARGET_SAME_APP | GTK_TARGET_OTHER_WIDGET,
210  0
211  },
212 };
213 
214 static const GtkTargetEntry dnd_targets_tb[] =
215 {
216  {
217  (char *)"text/x-remmina-tb",
218  GTK_TARGET_SAME_APP,
219  0
220  },
221 };
222 
224 {
225  TRACE_CALL(__func__);
226  GtkCssProvider *provider;
227 
228  provider = gtk_css_provider_new();
229 
230  /* It’s important to remove padding, border and shadow from GtkViewport or
231  * we will never know its internal area size, because GtkViweport::viewport_get_view_allocation,
232  * which returns the internal size of the GtkViewport, is private and we cannot access it */
233 
234 #if GTK_CHECK_VERSION(3, 14, 0)
235  gtk_css_provider_load_from_data(provider,
236  "#remmina-cw-viewport, #remmina-cw-aspectframe {\n"
237  " padding:0;\n"
238  " border:0;\n"
239  " background-color: black;\n"
240  "}\n"
241  "GtkDrawingArea {\n"
242  "}\n"
243  "GtkToolbar {\n"
244  " -GtkWidget-window-dragging: 0;\n"
245  "}\n"
246  "#remmina-connection-window-fullscreen {\n"
247  " border-color: black;\n"
248  "}\n"
249  "#remmina-small-button {\n"
250  " outline-offset: 0;\n"
251  " outline-width: 0;\n"
252  " padding: 0;\n"
253  " border: 0;\n"
254  "}\n"
255  "#remmina-pin-button {\n"
256  " outline-offset: 0;\n"
257  " outline-width: 0;\n"
258  " padding: 2px;\n"
259  " border: 0;\n"
260  "}\n"
261  "#remmina-tab-page {\n"
262  " background-color: black;\n"
263  "}\n"
264  "#remmina-scrolled-container {\n"
265  "}\n"
266  "#remmina-scrolled-container.undershoot {\n"
267  " background: none;\n"
268  "}\n"
269  "#remmina-tab-page {\n"
270  "}\n"
271  "#ftbbox-upper {\n"
272  " background-color: white;\n"
273  " color: black;\n"
274  " border-style: none solid solid solid;\n"
275  " border-width: 1px;\n"
276  " border-radius: 4px;\n"
277  " padding: 0px;\n"
278  "}\n"
279  "#ftbbox-lower {\n"
280  " background-color: white;\n"
281  " color: black;\n"
282  " border-style: solid solid none solid;\n"
283  " border-width: 1px;\n"
284  " border-radius: 4px;\n"
285  " padding: 0px;\n"
286  "}\n"
287  "#ftb-handle {\n"
288  "}\n"
289  ".message_panel {\n"
290  " border: 0px solid;\n"
291  " padding: 20px 20px 20px 20px;\n"
292  "}\n"
293  ".message_panel entry {\n"
294  " background-image: none;\n"
295  " border-width: 4px;\n"
296  " border-radius: 8px;\n"
297  "}\n"
298  ".message_panel .title_label {\n"
299  " font-size: 2em; \n"
300  "}\n"
301  , -1, NULL);
302 
303 #else
304  gtk_css_provider_load_from_data(provider,
305  "#remmina-cw-viewport, #remmina-cw-aspectframe {\n"
306  " padding:0;\n"
307  " border:0;\n"
308  " background-color: black;\n"
309  "}\n"
310  "#remmina-cw-message-panel {\n"
311  "}\n"
312  "GtkDrawingArea {\n"
313  "}\n"
314  "GtkToolbar {\n"
315  " -GtkWidget-window-dragging: 0;\n"
316  "}\n"
317  "#remmina-connection-window-fullscreen {\n"
318  " border-color: black;\n"
319  "}\n"
320  "#remmina-small-button {\n"
321  " -GtkWidget-focus-padding: 0;\n"
322  " -GtkWidget-focus-line-width: 0;\n"
323  " padding: 0;\n"
324  " border: 0;\n"
325  "}\n"
326  "#remmina-pin-button {\n"
327  " -GtkWidget-focus-padding: 0;\n"
328  " -GtkWidget-focus-line-width: 0;\n"
329  " padding: 2px;\n"
330  " border: 0;\n"
331  "}\n"
332  "#remmina-scrolled-container {\n"
333  "}\n"
334  "#remmina-scrolled-container.undershoot {\n"
335  " background: none\n"
336  "}\n"
337  "#remmina-tab-page {\n"
338  "}\n"
339  "#ftbbox-upper {\n"
340  " border-style: none solid solid solid;\n"
341  " border-width: 1px;\n"
342  " border-radius: 4px;\n"
343  " padding: 0px;\n"
344  "}\n"
345  "#ftbbox-lower {\n"
346  " border-style: solid solid none solid;\n"
347  " border-width: 1px;\n"
348  " border-radius: 4px;\n"
349  " padding: 0px;\n"
350  "}\n"
351  "#ftb-handle {\n"
352  "}\n"
353 
354  , -1, NULL);
355 #endif
356 
357  gtk_style_context_add_provider_for_screen(gdk_screen_get_default(),
358  GTK_STYLE_PROVIDER(provider),
359  GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
360 
361  g_object_unref(provider);
362 
363  /* Define a signal used to notify all rcws of toolbar move */
364  rcw_signals[TOOLBARPLACE_SIGNAL] = g_signal_new("toolbar-place", G_TYPE_FROM_CLASS(klass),
365  G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET(RemminaConnectionWindowClass, toolbar_place), NULL, NULL,
366  g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
367 }
368 
370 {
371  GtkWidget *po;
372 
373  if (!cnnwin->priv->notebook)
374  return NULL;
375  po = gtk_notebook_get_nth_page(GTK_NOTEBOOK(cnnwin->priv->notebook), npage);
376  return g_object_get_data(G_OBJECT(po), "cnnobj");
377 }
378 
380 {
381  gint np;
382 
383  if (cnnwin != NULL && cnnwin->priv != NULL && cnnwin->priv->notebook != NULL) {
384  np = gtk_notebook_get_current_page(GTK_NOTEBOOK(cnnwin->priv->notebook));
385  if (np < 0)
386  return NULL;
387  return rcw_get_cnnobj_at_page(cnnwin, np);
388  } else {
389  return NULL;
390  }
391 }
392 
393 static RemminaScaleMode get_current_allowed_scale_mode(RemminaConnectionObject *cnnobj, gboolean *dynres_avail, gboolean *scale_avail)
394 {
395  TRACE_CALL(__func__);
396  RemminaScaleMode scalemode;
397  gboolean plugin_has_dynres, plugin_can_scale;
398 
399  scalemode = remmina_protocol_widget_get_current_scale_mode(REMMINA_PROTOCOL_WIDGET(cnnobj->proto));
400 
401  plugin_has_dynres = remmina_protocol_widget_query_feature_by_type(REMMINA_PROTOCOL_WIDGET(cnnobj->proto),
403 
404  plugin_can_scale = remmina_protocol_widget_query_feature_by_type(REMMINA_PROTOCOL_WIDGET(cnnobj->proto),
406 
407  /* Forbid scalemode REMMINA_PROTOCOL_WIDGET_SCALE_MODE_DYNRES when not possible */
408  if ((!plugin_has_dynres) && scalemode == REMMINA_PROTOCOL_WIDGET_SCALE_MODE_DYNRES)
410 
411  /* Forbid scalemode REMMINA_PROTOCOL_WIDGET_SCALE_MODE_SCALED when not possible */
412  if (!plugin_can_scale && scalemode == REMMINA_PROTOCOL_WIDGET_SCALE_MODE_SCALED)
414 
415  if (scale_avail)
416  *scale_avail = plugin_can_scale;
417  if (dynres_avail)
418  *dynres_avail = (plugin_has_dynres && cnnobj->dynres_unlocked);
419 
420  return scalemode;
421 }
422 
424 {
425  TRACE_CALL(__func__);
426 
427  /* Disconnects the connection which is currently in view in the notebook */
428  remmina_protocol_widget_close_connection(REMMINA_PROTOCOL_WIDGET(cnnobj->proto));
429 }
430 
432 {
433  TRACE_CALL(__func__);
434  GdkDisplay *display;
435 
436 #if GTK_CHECK_VERSION(3, 20, 0)
437  GdkSeat *seat;
438 #else
439  GdkDeviceManager *manager;
440  GdkDevice *keyboard = NULL;
441 #endif
442 
443  if (cnnwin->priv->grab_retry_eventsourceid) {
444  g_source_remove(cnnwin->priv->grab_retry_eventsourceid);
445  cnnwin->priv->grab_retry_eventsourceid = 0;
446  }
447  if (cnnwin->priv->delayed_grab_eventsourceid) {
448  g_source_remove(cnnwin->priv->delayed_grab_eventsourceid);
449  cnnwin->priv->delayed_grab_eventsourceid = 0;
450  }
451 
452  display = gtk_widget_get_display(GTK_WIDGET(cnnwin));
453 #if GTK_CHECK_VERSION(3, 20, 0)
454  seat = gdk_display_get_default_seat(display);
455  // keyboard = gdk_seat_get_pointer(seat);
456 #else
457  manager = gdk_display_get_device_manager(display);
458  keyboard = gdk_device_manager_get_client_pointer(manager);
459 #endif
460 
461  if (!cnnwin->priv->kbcaptured && !cnnwin->priv->pointer_captured)
462  return;
463 
464 #if DEBUG_KB_GRABBING
465  printf("DEBUG_KB_GRABBING: --- ungrabbing\n");
466 #endif
467 
468 
469 
470 #if GTK_CHECK_VERSION(3, 20, 0)
471  /* We can use gtk_seat_grab()/_ungrab() only after GTK 3.24 */
472  gdk_seat_ungrab(seat);
473 #else
474  if (keyboard != NULL) {
475  if (gdk_device_get_source(keyboard) != GDK_SOURCE_KEYBOARD)
476  keyboard = gdk_device_get_associated_device(keyboard);
477  G_GNUC_BEGIN_IGNORE_DEPRECATIONS
478  gdk_device_ungrab(keyboard, GDK_CURRENT_TIME);
479  G_GNUC_END_IGNORE_DEPRECATIONS
480  }
481 #endif
482  cnnwin->priv->kbcaptured = FALSE;
483  cnnwin->priv->pointer_captured = FALSE;
484 }
485 
486 static gboolean rcw_keyboard_grab_retry(gpointer user_data)
487 {
488  TRACE_CALL(__func__);
489  RemminaConnectionWindow *cnnwin = (RemminaConnectionWindow *)user_data;
490 
491 #if DEBUG_KB_GRABBING
492  printf("%s retry grab\n", __func__);
493 #endif
494  rcw_keyboard_grab(cnnwin);
495  cnnwin->priv->grab_retry_eventsourceid = 0;
496  return G_SOURCE_REMOVE;
497 }
498 
500 {
501  TRACE_CALL(__func__);
502 #if GTK_CHECK_VERSION(3, 20, 0)
503  GdkSeat *seat;
504  GdkDisplay *display;
505  if (!cnnwin->priv->pointer_captured)
506  return;
507 
508  display = gtk_widget_get_display(GTK_WIDGET(cnnwin));
509  seat = gdk_display_get_default_seat(display);
510  gdk_seat_ungrab(seat);
511 #endif
512 }
513 
515 {
516  TRACE_CALL(__func__);
517  /* This function in Wayland is useless and generates a spurious leave-notify event.
518  * Should we remove it ? https://gitlab.gnome.org/GNOME/mutter/-/issues/2450#note_1588081 */
519 #if GTK_CHECK_VERSION(3, 20, 0)
520  GdkSeat *seat;
521  GdkDisplay *display;
522  GdkGrabStatus ggs;
523 
524 
525  if (cnnwin->priv->pointer_captured) {
526 #if DEBUG_KB_GRABBING
527  printf("DEBUG_KB_GRABBING: pointer_captured is true, it should not\n");
528 #endif
529  return;
530  }
531 
532  display = gtk_widget_get_display(GTK_WIDGET(cnnwin));
533  seat = gdk_display_get_default_seat(display);
534  ggs = gdk_seat_grab(seat, gtk_widget_get_window(GTK_WIDGET(cnnwin)),
535  GDK_SEAT_CAPABILITY_ALL_POINTING, TRUE, NULL, NULL, NULL, NULL);
536  if (ggs != GDK_GRAB_SUCCESS) {
537 #if DEBUG_KB_GRABBING
538  printf("DEBUG_KB_GRABBING: GRAB of POINTER failed. GdkGrabStatus: %d\n", (int)ggs);
539 #endif
540  } else {
541  cnnwin->priv->pointer_captured = TRUE;
542  }
543 
544 #endif
545 }
546 
548 {
549  TRACE_CALL(__func__);
550  GdkDisplay *display;
551 
552 #if GTK_CHECK_VERSION(3, 20, 0)
553  GdkSeat *seat;
554 #else
555  GdkDeviceManager *manager;
556 #endif
557  GdkGrabStatus ggs;
558  GdkDevice *keyboard = NULL;
559 
560  if (cnnwin->priv->kbcaptured) {
561 #if DEBUG_KB_GRABBING
562  printf("DEBUG_KB_GRABBING: %s not grabbing because already grabbed.\n", __func__);
563 #endif
564  return;
565  }
566 
567  display = gtk_widget_get_display(GTK_WIDGET(cnnwin));
568 #if GTK_CHECK_VERSION(3, 20, 0)
569  seat = gdk_display_get_default_seat(display);
570  keyboard = gdk_seat_get_pointer(seat);
571 #else
572  manager = gdk_display_get_device_manager(display);
573  keyboard = gdk_device_manager_get_client_pointer(manager);
574 #endif
575 
576  if (keyboard != NULL) {
577  if (gdk_device_get_source(keyboard) != GDK_SOURCE_KEYBOARD)
578  keyboard = gdk_device_get_associated_device(keyboard);
579 
580 
581 #if DEBUG_KB_GRABBING
582  printf("DEBUG_KB_GRABBING: profile asks for grabbing, let’s try.\n");
583 #endif
584  /* Up to GTK version 3.20 we can grab the keyboard with gdk_device_grab().
585  * in GTK 3.20 gdk_seat_grab() should be used instead of gdk_device_grab().
586  * There is a bug in GTK up to 3.22: When gdk_device_grab() fails
587  * the widget is hidden:
588  * https://gitlab.gnome.org/GNOME/gtk/commit/726ad5a5ae7c4f167e8dd454cd7c250821c400ab
589  * The bugfix will be released with GTK 3.24.
590  * Also please note that the newer gdk_seat_grab() is still calling gdk_device_grab().
591  *
592  * Warning: gdk_seat_grab() will call XGrabKeyboard() or XIGrabDevice()
593  * which in turn will generate a core X input event FocusOut and FocusIn
594  * but not Xinput2 events.
595  * In some cases, GTK is unable to neutralize FocusIn and FocusOut core
596  * events (ie: i3wm+Plasma with GDK_CORE_DEVICE_EVENTS=1 because detail=NotifyNonlinear
597  * instead of detail=NotifyAncestor/detail=NotifyInferior)
598  * Receiving a FocusOut event for Remmina at this time will cause an infinite loop.
599  * Therefore is important for GTK to use Xinput2 instead of core X events
600  * by unsetting GDK_CORE_DEVICE_EVENTS
601  */
602 #if GTK_CHECK_VERSION(3, 20, 0)
603  ggs = gdk_seat_grab(seat, gtk_widget_get_window(GTK_WIDGET(cnnwin)),
604  GDK_SEAT_CAPABILITY_KEYBOARD, TRUE, NULL, NULL, NULL, NULL);
605 #else
606  ggs = gdk_device_grab(keyboard, gtk_widget_get_window(GTK_WIDGET(cnnwin)), GDK_OWNERSHIP_WINDOW,
607  TRUE, GDK_KEY_PRESS | GDK_KEY_RELEASE, NULL, GDK_CURRENT_TIME);
608 #endif
609  if (ggs != GDK_GRAB_SUCCESS) {
610 #if DEBUG_KB_GRABBING
611  printf("GRAB of keyboard failed.\n");
612 #endif
613  /* Reschedule grabbing in half a second if not already done */
614  if (cnnwin->priv->grab_retry_eventsourceid == 0)
615  cnnwin->priv->grab_retry_eventsourceid = g_timeout_add(500, (GSourceFunc)rcw_keyboard_grab_retry, cnnwin);
616  } else {
617 #if DEBUG_KB_GRABBING
618  printf("Keyboard grabbed\n");
619 #endif
620  if (cnnwin->priv->grab_retry_eventsourceid != 0) {
621  g_source_remove(cnnwin->priv->grab_retry_eventsourceid);
622  cnnwin->priv->grab_retry_eventsourceid = 0;
623  }
624  cnnwin->priv->kbcaptured = TRUE;
625  }
626  } else {
627  rcw_kp_ungrab(cnnwin);
628  }
629 }
630 
632 {
633  RemminaConnectionWindowPriv *priv = cnnwin->priv;
634  GtkNotebook *notebook = GTK_NOTEBOOK(priv->notebook);
635  GtkWidget *w;
636  RemminaConnectionObject *cnnobj;
637  gint i, n;
638 
639  if (GTK_IS_WIDGET(notebook)) {
640  n = gtk_notebook_get_n_pages(notebook);
641  for (i = n - 1; i >= 0; i--) {
642  w = gtk_notebook_get_nth_page(notebook, i);
643  cnnobj = (RemminaConnectionObject *)g_object_get_data(G_OBJECT(w), "cnnobj");
644  /* Do close the connection on this tab */
645  remmina_protocol_widget_close_connection(REMMINA_PROTOCOL_WIDGET(cnnobj->proto));
646  }
647  }
648 }
649 
651 {
652  TRACE_CALL(__func__);
653  RemminaConnectionWindowPriv *priv = cnnwin->priv;
654  GtkNotebook *notebook = GTK_NOTEBOOK(priv->notebook);
655  GtkWidget *dialog;
656  gint i, n, nopen;
657 
658  if (!REMMINA_IS_CONNECTION_WINDOW(cnnwin))
659  return TRUE;
660 
661  if (cnnwin->priv->on_delete_confirm_mode != RCW_ONDELETE_NOCONFIRM) {
662  n = gtk_notebook_get_n_pages(notebook);
663  nopen = 0;
664  /* count all non-closed connections */
665  for(i = 0; i < n; i ++) {
666  RemminaConnectionObject *cnnobj = rcw_get_cnnobj_at_page(cnnwin, i);
668  nopen ++;
669  }
670  if (nopen > 1) {
671  dialog = gtk_message_dialog_new(GTK_WINDOW(cnnwin), GTK_DIALOG_MODAL, GTK_MESSAGE_QUESTION,
672  GTK_BUTTONS_YES_NO,
673  _("Are you sure you want to close %i active connections in the current window?"), nopen);
674  i = gtk_dialog_run(GTK_DIALOG(dialog));
675  gtk_widget_destroy(dialog);
676  if (i != GTK_RESPONSE_YES)
677  return FALSE;
678  }
679  else if (nopen == 1) {
680  if (remmina_pref.confirm_close) {
681  dialog = gtk_message_dialog_new(GTK_WINDOW(cnnwin), GTK_DIALOG_MODAL, GTK_MESSAGE_QUESTION,
682  GTK_BUTTONS_YES_NO,
683  _("Are you sure you want to close this last active connection?"));
684  i = gtk_dialog_run(GTK_DIALOG(dialog));
685  gtk_widget_destroy(dialog);
686  if (i != GTK_RESPONSE_YES)
687  return FALSE;
688  }
689  }
690  }
692 
693  return TRUE;
694 }
695 
696 static gboolean rcw_delete_event(GtkWidget *widget, GdkEvent *event, gpointer data)
697 {
698  TRACE_CALL(__func__);
699  rcw_delete(RCW(widget));
700  return TRUE;
701 }
702 
703 static void rcw_destroy(GtkWidget *widget, gpointer data)
704 {
705  TRACE_CALL(__func__);
707  RemminaConnectionWindow *cnnwin;
708 
709  if (!REMMINA_IS_CONNECTION_WINDOW(widget))
710  return;
711 
712  cnnwin = (RemminaConnectionWindow *)widget;
713  priv = cnnwin->priv;
714 
715  if (priv->kbcaptured)
716  rcw_kp_ungrab(cnnwin);
717 
718  if (priv->acs_eventsourceid) {
719  g_source_remove(priv->acs_eventsourceid);
720  priv->acs_eventsourceid = 0;
721  }
722  if (priv->spf_eventsourceid) {
723  g_source_remove(priv->spf_eventsourceid);
724  priv->spf_eventsourceid = 0;
725  }
726  if (priv->grab_retry_eventsourceid) {
727  g_source_remove(priv->grab_retry_eventsourceid);
728  priv->grab_retry_eventsourceid = 0;
729  }
730  if (cnnwin->priv->delayed_grab_eventsourceid) {
731  g_source_remove(cnnwin->priv->delayed_grab_eventsourceid);
732  cnnwin->priv->delayed_grab_eventsourceid = 0;
733  }
734  if (priv->ftb_hide_eventsource) {
735  g_source_remove(priv->ftb_hide_eventsource);
736  priv->ftb_hide_eventsource = 0;
737  }
738  if (priv->tar_eventsource) {
739  g_source_remove(priv->tar_eventsource);
740  priv->tar_eventsource = 0;
741  }
742  if (priv->hidetb_eventsource) {
743  g_source_remove(priv->hidetb_eventsource);
744  priv->hidetb_eventsource = 0;
745  }
746  if (priv->dwp_eventsourceid) {
747  g_source_remove(priv->dwp_eventsourceid);
748  priv->dwp_eventsourceid = 0;
749  }
750 
751  /* There is no need to destroy priv->floating_toolbar_widget,
752  * because it’s our child and will be destroyed automatically */
753 
754  cnnwin->priv = NULL;
755  g_free(priv);
756 }
757 
758 gboolean rcw_notify_widget_toolbar_placement(GtkWidget *widget, gpointer data)
759 {
760  TRACE_CALL(__func__);
761  GType rcwtype;
762 
763  rcwtype = rcw_get_type();
764  if (G_TYPE_CHECK_INSTANCE_TYPE(widget, rcwtype)) {
765  g_signal_emit_by_name(G_OBJECT(widget), "toolbar-place");
766  return TRUE;
767  }
768  return FALSE;
769 }
770 
771 static gboolean rcw_tb_drag_failed(GtkWidget *widget, GdkDragContext *context,
772  GtkDragResult result, gpointer user_data)
773 {
774  TRACE_CALL(__func__);
776  RemminaConnectionWindow *cnnwin;
777 
778 
779  cnnwin = (RemminaConnectionWindow *)user_data;
780  priv = cnnwin->priv;
781 
782  if (priv->toolbar)
783  gtk_widget_show(GTK_WIDGET(priv->toolbar));
784 
785  return TRUE;
786 }
787 
788 static gboolean rcw_tb_drag_drop(GtkWidget *widget, GdkDragContext *context,
789  gint x, gint y, guint time, gpointer user_data)
790 {
791  TRACE_CALL(__func__);
792  GtkAllocation wa;
793  gint new_toolbar_placement;
795  RemminaConnectionWindow *cnnwin;
796 
797  cnnwin = (RemminaConnectionWindow *)user_data;
798  priv = cnnwin->priv;
799 
800  gtk_widget_get_allocation(widget, &wa);
801 
802  if (wa.width * y >= wa.height * x) {
803  if (wa.width * y > wa.height * (wa.width - x))
804  new_toolbar_placement = TOOLBAR_PLACEMENT_BOTTOM;
805  else
806  new_toolbar_placement = TOOLBAR_PLACEMENT_LEFT;
807  } else {
808  if (wa.width * y > wa.height * (wa.width - x))
809  new_toolbar_placement = TOOLBAR_PLACEMENT_RIGHT;
810  else
811  new_toolbar_placement = TOOLBAR_PLACEMENT_TOP;
812  }
813 
814  gtk_drag_finish(context, TRUE, TRUE, time);
815 
816  if (new_toolbar_placement != remmina_pref.toolbar_placement) {
817  /* Save new position */
818  remmina_pref.toolbar_placement = new_toolbar_placement;
820 
821  /* Signal all windows that the toolbar must be moved */
823  }
824  if (priv->toolbar)
825  gtk_widget_show(GTK_WIDGET(priv->toolbar));
826 
827  return TRUE;
828 }
829 
830 static void rcw_tb_drag_begin(GtkWidget *widget, GdkDragContext *context, gpointer user_data)
831 {
832  TRACE_CALL(__func__);
833 
834  cairo_surface_t *surface;
835  cairo_t *cr;
836  GtkAllocation wa;
837  double dashes[] = { 10 };
838 
839  gtk_widget_get_allocation(widget, &wa);
840 
841  surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 16, 16);
842  cr = cairo_create(surface);
843  cairo_set_source_rgb(cr, 0.6, 0.6, 0.6);
844  cairo_set_line_width(cr, 4);
845  cairo_set_dash(cr, dashes, 1, 0);
846  cairo_rectangle(cr, 0, 0, 16, 16);
847  cairo_stroke(cr);
848  cairo_destroy(cr);
849 
850  gtk_widget_hide(widget);
851 
852  gtk_drag_set_icon_surface(context, surface);
853 }
854 
856 {
857  TRACE_CALL(__func__);
858  RemminaConnectionWindowPriv *priv = cnnwin->priv;
859  RemminaConnectionObject *cnnobj;
860 
861  cnnobj = rcw_get_visible_cnnobj(cnnwin);
862  if (!cnnobj) return;
863 
864  priv->floating_toolbar_opacity = (1.0 - TOOLBAR_OPACITY_MIN) / ((gdouble)TOOLBAR_OPACITY_LEVEL)
865  * ((gdouble)(TOOLBAR_OPACITY_LEVEL - remmina_file_get_int(cnnobj->remmina_file, "toolbar_opacity", 0)))
866  + TOOLBAR_OPACITY_MIN;
867  if (priv->floating_toolbar_widget)
868  gtk_widget_set_opacity(GTK_WIDGET(priv->overlay_ftb_overlay), priv->floating_toolbar_opacity);
869 }
870 
871 static gboolean rcw_floating_toolbar_make_invisible(gpointer data)
872 {
873  TRACE_CALL(__func__);
875 
876  gtk_widget_set_opacity(GTK_WIDGET(priv->overlay_ftb_overlay), 0.0);
877  priv->ftb_hide_eventsource = 0;
878  return G_SOURCE_REMOVE;
879 }
880 
881 static void rcw_floating_toolbar_show(RemminaConnectionWindow *cnnwin, gboolean show)
882 {
883  TRACE_CALL(__func__);
884  RemminaConnectionWindowPriv *priv = cnnwin->priv;
885 
886  if (priv->floating_toolbar_widget == NULL)
887  return;
888 
889  if (show || priv->pin_down) {
890  /* Make the FTB no longer transparent, in case we have an hidden toolbar */
892  /* Remove outstanding hide events, if not yet active */
893  if (priv->ftb_hide_eventsource) {
894  g_source_remove(priv->ftb_hide_eventsource);
895  priv->ftb_hide_eventsource = 0;
896  }
897  } else {
898  /* If we are hiding and the toolbar must be made invisible, schedule
899  * a later toolbar hide */
901  if (priv->ftb_hide_eventsource == 0)
902  priv->ftb_hide_eventsource = g_timeout_add(1000, rcw_floating_toolbar_make_invisible, priv);
903  }
904 
905  gtk_revealer_set_reveal_child(GTK_REVEALER(priv->revealer), show || priv->pin_down);
906 }
907 
908 static void rco_get_desktop_size(RemminaConnectionObject *cnnobj, gint *width, gint *height)
909 {
910  TRACE_CALL(__func__);
911  RemminaProtocolWidget *gp = REMMINA_PROTOCOL_WIDGET(cnnobj->proto);
912 
913 
916  if (*width == 0) {
917  /* Before connecting we do not have real remote width/height,
918  * so we ask profile values */
921  }
922 }
923 
924 void rco_set_scrolled_policy(RemminaScaleMode scalemode, GtkScrolledWindow *scrolled_window)
925 {
926  TRACE_CALL(__func__);
927 
928  gtk_scrolled_window_set_policy(scrolled_window,
929  scalemode == REMMINA_PROTOCOL_WIDGET_SCALE_MODE_SCALED ? GTK_POLICY_NEVER : GTK_POLICY_AUTOMATIC,
930  scalemode == REMMINA_PROTOCOL_WIDGET_SCALE_MODE_SCALED ? GTK_POLICY_NEVER : GTK_POLICY_AUTOMATIC);
931 }
932 
933 static GtkWidget *rco_create_scrolled_container(RemminaScaleMode scalemode, int view_mode)
934 {
935  GtkWidget *scrolled_container;
936 
937  if (view_mode == VIEWPORT_FULLSCREEN_MODE) {
938  scrolled_container = remmina_scrolled_viewport_new();
939  } else {
940  scrolled_container = gtk_scrolled_window_new(NULL, NULL);
941  rco_set_scrolled_policy(scalemode, GTK_SCROLLED_WINDOW(scrolled_container));
942  gtk_container_set_border_width(GTK_CONTAINER(scrolled_container), 0);
943  gtk_widget_set_can_focus(scrolled_container, FALSE);
944  }
945 
946  gtk_widget_set_name(scrolled_container, "remmina-scrolled-container");
947  gtk_widget_show(scrolled_container);
948 
949  return scrolled_container;
950 }
951 
953 {
954  TRACE_CALL(__func__);
955 
956  RemminaConnectionWindowPriv *priv = cnnwin->priv;
957  RemminaConnectionObject *cnnobj;
958  gint dwidth, dheight;
959  GtkAllocation nba, ca, ta;
960 
961  cnnwin->priv->tar_eventsource = 0;
962 
963  if (priv->toolbar_is_reconfiguring)
964  return G_SOURCE_REMOVE;
965 
966  if (!(cnnobj = rcw_get_visible_cnnobj(cnnwin))) return FALSE;
967 
968  if (cnnobj->connected && GTK_IS_SCROLLED_WINDOW(cnnobj->scrolled_container)) {
969  rco_get_desktop_size(cnnobj, &dwidth, &dheight);
970  gtk_widget_get_allocation(GTK_WIDGET(priv->notebook), &nba);
971  gtk_widget_get_allocation(cnnobj->scrolled_container, &ca);
972  gtk_widget_get_allocation(priv->toolbar, &ta);
973  if (remmina_pref.toolbar_placement == TOOLBAR_PLACEMENT_LEFT ||
975  gtk_window_resize(GTK_WINDOW(cnnobj->cnnwin), MAX(1, dwidth + ta.width + nba.width - ca.width),
976  MAX(1, dheight + nba.height - ca.height));
977  else
978  gtk_window_resize(GTK_WINDOW(cnnobj->cnnwin), MAX(1, dwidth + nba.width - ca.width),
979  MAX(1, dheight + ta.height + nba.height - ca.height));
980  gtk_container_check_resize(GTK_CONTAINER(cnnobj->cnnwin));
981  }
982  if (GTK_IS_SCROLLED_WINDOW(cnnobj->scrolled_container)) {
983  RemminaScaleMode scalemode = get_current_allowed_scale_mode(cnnobj, NULL, NULL);
984  rco_set_scrolled_policy(scalemode, GTK_SCROLLED_WINDOW(cnnobj->scrolled_container));
985  }
986 
987  return G_SOURCE_REMOVE;
988 }
989 
990 static void rcw_toolbar_autofit(GtkToolItem *toggle, RemminaConnectionWindow *cnnwin)
991 {
992  TRACE_CALL(__func__);
993  RemminaConnectionObject *cnnobj;
994 
995  if (cnnwin->priv->toolbar_is_reconfiguring)
996  return;
997  if (!(cnnobj = rcw_get_visible_cnnobj(cnnwin))) return;
998 
999  if (GTK_IS_SCROLLED_WINDOW(cnnobj->scrolled_container)) {
1000  if ((gdk_window_get_state(gtk_widget_get_window(GTK_WIDGET(cnnwin))) & GDK_WINDOW_STATE_MAXIMIZED) != 0)
1001  gtk_window_unmaximize(GTK_WINDOW(cnnwin));
1002 
1003  /* It’s tricky to make the toolbars disappear automatically, while keeping scrollable.
1004  * Please tell me if you know a better way to do this */
1005  gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(cnnobj->scrolled_container), GTK_POLICY_NEVER, GTK_POLICY_NEVER);
1006 
1007  cnnwin->priv->tar_eventsource = g_timeout_add(200, (GSourceFunc)rcw_toolbar_autofit_restore, cnnwin);
1008  }
1009 }
1010 
1011 void rco_get_monitor_geometry(RemminaConnectionObject *cnnobj, GdkRectangle *sz)
1012 {
1013  TRACE_CALL(__func__);
1014 
1015  /* Fill sz with the monitor (or workarea) size and position
1016  * of the monitor (or workarea) where cnnobj->cnnwin is located */
1017 
1018  GdkRectangle monitor_geometry;
1019 
1020  sz->x = sz->y = sz->width = sz->height = 0;
1021 
1022  if (!cnnobj)
1023  return;
1024  if (!cnnobj->cnnwin)
1025  return;
1026  if (!gtk_widget_is_visible(GTK_WIDGET(cnnobj->cnnwin)))
1027  return;
1028 
1029 #if GTK_CHECK_VERSION(3, 22, 0)
1030  GdkDisplay *display;
1031  GdkMonitor *monitor;
1032  display = gtk_widget_get_display(GTK_WIDGET(cnnobj->cnnwin));
1033  monitor = gdk_display_get_monitor_at_window(display, gtk_widget_get_window(GTK_WIDGET(cnnobj->cnnwin)));
1034 #else
1035  GdkScreen *screen;
1036  gint monitor;
1037  screen = gtk_window_get_screen(GTK_WINDOW(cnnobj->cnnwin));
1038  monitor = gdk_screen_get_monitor_at_window(screen, gtk_widget_get_window(GTK_WIDGET(cnnobj->cnnwin)));
1039 #endif
1040 
1041 #if GTK_CHECK_VERSION(3, 22, 0)
1042  gdk_monitor_get_workarea(monitor, &monitor_geometry);
1043  /* Under Wayland, GTK 3.22, all values returned by gdk_monitor_get_geometry()
1044  * and gdk_monitor_get_workarea() seem to have been divided by the
1045  * gdk scale factor, so we need to adjust the returned rect
1046  * undoing the division */
1047 #ifdef GDK_WINDOWING_WAYLAND
1048  if (GDK_IS_WAYLAND_DISPLAY(display)) {
1049  int monitor_scale_factor = gdk_monitor_get_scale_factor(monitor);
1050  monitor_geometry.width *= monitor_scale_factor;
1051  monitor_geometry.height *= monitor_scale_factor;
1052  }
1053 #endif
1054 #elif gdk_screen_get_monitor_workarea
1055  gdk_screen_get_monitor_workarea(screen, monitor, &monitor_geometry);
1056 #else
1057  gdk_screen_get_monitor_geometry(screen, monitor, &monitor_geometry);
1058 #endif
1059  *sz = monitor_geometry;
1060 }
1061 
1063 {
1064  TRACE_CALL(__func__);
1065  gboolean scroll_required = FALSE;
1066 
1067  GdkRectangle monitor_geometry;
1068  gint rd_width, rd_height;
1069  gint bordersz;
1070  gint scalemode;
1071 
1072  scalemode = remmina_protocol_widget_get_current_scale_mode(REMMINA_PROTOCOL_WIDGET(cnnobj->proto));
1073 
1074  /* Get remote destkop size */
1075  rco_get_desktop_size(cnnobj, &rd_width, &rd_height);
1076 
1077  /* Get our monitor size */
1078  rco_get_monitor_geometry(cnnobj, &monitor_geometry);
1079 
1080  if (!remmina_protocol_widget_get_expand(REMMINA_PROTOCOL_WIDGET(cnnobj->proto)) &&
1081  (monitor_geometry.width < rd_width || monitor_geometry.height < rd_height) &&
1083  scroll_required = TRUE;
1084 
1085  switch (cnnobj->cnnwin->priv->view_mode) {
1087  gtk_window_resize(GTK_WINDOW(cnnobj->cnnwin), monitor_geometry.width, monitor_geometry.height);
1088  gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(cnnobj->scrolled_container),
1089  (scroll_required ? GTK_POLICY_AUTOMATIC : GTK_POLICY_NEVER),
1090  (scroll_required ? GTK_POLICY_AUTOMATIC : GTK_POLICY_NEVER));
1091  break;
1092 
1094  bordersz = scroll_required ? SCROLL_BORDER_SIZE : 0;
1095  gtk_window_resize(GTK_WINDOW(cnnobj->cnnwin), monitor_geometry.width, monitor_geometry.height);
1096  if (REMMINA_IS_SCROLLED_VIEWPORT(cnnobj->scrolled_container))
1097  /* Put a border around Notebook content (RemminaScrolledViewpord), so we can
1098  * move the mouse over the border to scroll */
1099  gtk_container_set_border_width(GTK_CONTAINER(cnnobj->scrolled_container), bordersz);
1100 
1101  break;
1102 
1103  case SCROLLED_WINDOW_MODE:
1104  if (remmina_file_get_int(cnnobj->remmina_file, "viewmode", UNDEFINED_MODE) == UNDEFINED_MODE) {
1105  /* ToDo: is this really needed ? When ? */
1106  gtk_window_set_default_size(GTK_WINDOW(cnnobj->cnnwin),
1107  MIN(rd_width, monitor_geometry.width), MIN(rd_height, monitor_geometry.height));
1108  if (rd_width >= monitor_geometry.width || rd_height >= monitor_geometry.height) {
1109  gtk_window_maximize(GTK_WINDOW(cnnobj->cnnwin));
1110  remmina_file_set_int(cnnobj->remmina_file, "window_maximize", TRUE);
1111  } else {
1112  rcw_toolbar_autofit(NULL, cnnobj->cnnwin);
1113  remmina_file_set_int(cnnobj->remmina_file, "window_maximize", FALSE);
1114  }
1115  } else {
1116  if (remmina_file_get_int(cnnobj->remmina_file, "window_maximize", FALSE))
1117  gtk_window_maximize(GTK_WINDOW(cnnobj->cnnwin));
1118  }
1119  break;
1120 
1121  default:
1122  break;
1123  }
1124 }
1125 
1126 static void rcw_set_tooltip(GtkWidget *item, const gchar *tip, guint key1, guint key2)
1127 {
1128  TRACE_CALL(__func__);
1129  gchar *s1;
1130  gchar *s2;
1131 
1132  if (remmina_pref.hostkey && key1) {
1133  if (key2)
1134  s1 = g_strdup_printf(" (%s + %s,%s)", gdk_keyval_name(remmina_pref.hostkey),
1135  gdk_keyval_name(gdk_keyval_to_upper(key1)), gdk_keyval_name(gdk_keyval_to_upper(key2)));
1136  else if (key1 == remmina_pref.hostkey)
1137  s1 = g_strdup_printf(" (%s)", gdk_keyval_name(remmina_pref.hostkey));
1138  else
1139  s1 = g_strdup_printf(" (%s + %s)", gdk_keyval_name(remmina_pref.hostkey),
1140  gdk_keyval_name(gdk_keyval_to_upper(key1)));
1141  } else {
1142  s1 = NULL;
1143  }
1144  s2 = g_strdup_printf("%s%s", tip, s1 ? s1 : "");
1145  gtk_widget_set_tooltip_text(item, s2);
1146  g_free(s2);
1147  g_free(s1);
1148 }
1149 
1151 {
1152  TRACE_CALL(__func__);
1153  RemminaScaleMode scalemode;
1154  gboolean scaledexpandedmode;
1155  int rdwidth, rdheight;
1156  gfloat aratio;
1157 
1158  if (!cnnobj->plugin_can_scale) {
1159  /* If we have a plugin that cannot scale,
1160  * (i.e. SFTP plugin), then we expand proto */
1161  gtk_widget_set_halign(GTK_WIDGET(cnnobj->proto), GTK_ALIGN_FILL);
1162  gtk_widget_set_valign(GTK_WIDGET(cnnobj->proto), GTK_ALIGN_FILL);
1163  } else {
1164  /* Plugin can scale */
1165 
1166  scalemode = get_current_allowed_scale_mode(cnnobj, NULL, NULL);
1167  scaledexpandedmode = remmina_protocol_widget_get_expand(REMMINA_PROTOCOL_WIDGET(cnnobj->proto));
1168 
1169  /* Check if we need aspectframe and create/destroy it accordingly */
1170  if (scalemode == REMMINA_PROTOCOL_WIDGET_SCALE_MODE_SCALED && !scaledexpandedmode) {
1171  /* We need an aspectframe as a parent of proto */
1172  rdwidth = remmina_protocol_widget_get_width(REMMINA_PROTOCOL_WIDGET(cnnobj->proto));
1173  rdheight = remmina_protocol_widget_get_height(REMMINA_PROTOCOL_WIDGET(cnnobj->proto));
1174  aratio = (gfloat)rdwidth / (gfloat)rdheight;
1175  if (!cnnobj->aspectframe) {
1176  /* We need a new aspectframe */
1177  cnnobj->aspectframe = gtk_aspect_frame_new(NULL, 0.5, 0.5, aratio, FALSE);
1178  gtk_widget_set_name(cnnobj->aspectframe, "remmina-cw-aspectframe");
1179  gtk_frame_set_shadow_type(GTK_FRAME(cnnobj->aspectframe), GTK_SHADOW_NONE);
1180  g_object_ref(cnnobj->proto);
1181  gtk_container_remove(GTK_CONTAINER(cnnobj->viewport), cnnobj->proto);
1182  gtk_container_add(GTK_CONTAINER(cnnobj->viewport), cnnobj->aspectframe);
1183  gtk_container_add(GTK_CONTAINER(cnnobj->aspectframe), cnnobj->proto);
1184  g_object_unref(cnnobj->proto);
1185  gtk_widget_show(cnnobj->aspectframe);
1186  if (cnnobj != NULL && cnnobj->cnnwin != NULL && cnnobj->cnnwin->priv->notebook != NULL)
1187  rcw_grab_focus(cnnobj->cnnwin);
1188  } else {
1189  gtk_aspect_frame_set(GTK_ASPECT_FRAME(cnnobj->aspectframe), 0.5, 0.5, aratio, FALSE);
1190  }
1191  } else {
1192  /* We do not need an aspectframe as a parent of proto */
1193  if (cnnobj->aspectframe) {
1194  /* We must remove the old aspectframe reparenting proto to viewport */
1195  g_object_ref(cnnobj->aspectframe);
1196  g_object_ref(cnnobj->proto);
1197  gtk_container_remove(GTK_CONTAINER(cnnobj->aspectframe), cnnobj->proto);
1198  gtk_container_remove(GTK_CONTAINER(cnnobj->viewport), cnnobj->aspectframe);
1199  g_object_unref(cnnobj->aspectframe);
1200  cnnobj->aspectframe = NULL;
1201  gtk_container_add(GTK_CONTAINER(cnnobj->viewport), cnnobj->proto);
1202  g_object_unref(cnnobj->proto);
1203  if (cnnobj != NULL && cnnobj->cnnwin != NULL && cnnobj->cnnwin->priv->notebook != NULL)
1204  rcw_grab_focus(cnnobj->cnnwin);
1205  }
1206  }
1207 
1209  /* We have a plugin that can be scaled, and the scale button
1210  * has been pressed. Give it the correct WxH maintaining aspect
1211  * ratio of remote destkop size */
1212  gtk_widget_set_halign(GTK_WIDGET(cnnobj->proto), GTK_ALIGN_FILL);
1213  gtk_widget_set_valign(GTK_WIDGET(cnnobj->proto), GTK_ALIGN_FILL);
1214  } else {
1215  /* Plugin can scale, but no scaling is active. Ensure that we have
1216  * aspectframe with a ratio of 1 */
1217  gtk_widget_set_halign(GTK_WIDGET(cnnobj->proto), GTK_ALIGN_CENTER);
1218  gtk_widget_set_valign(GTK_WIDGET(cnnobj->proto), GTK_ALIGN_CENTER);
1219  }
1220  }
1221 }
1222 
1223 static void nb_set_current_page(GtkNotebook *notebook, GtkWidget *page)
1224 {
1225  gint np, i;
1226 
1227  np = gtk_notebook_get_n_pages(notebook);
1228  for (i = 0; i < np; i++) {
1229  if (gtk_notebook_get_nth_page(notebook, i) == page) {
1230  gtk_notebook_set_current_page(notebook, i);
1231  break;
1232  }
1233  }
1234 }
1235 
1236 static void nb_migrate_message_panels(GtkWidget *frompage, GtkWidget *topage)
1237 {
1238  /* Migrate a single connection tab from a notebook to another one */
1239  GList *lst, *l;
1240 
1241  /* Reparent message panels */
1242  lst = gtk_container_get_children(GTK_CONTAINER(frompage));
1243  for (l = lst; l != NULL; l = l->next) {
1244  if (REMMINA_IS_MESSAGE_PANEL(l->data)) {
1245  g_object_ref(l->data);
1246  gtk_container_remove(GTK_CONTAINER(frompage), GTK_WIDGET(l->data));
1247  gtk_container_add(GTK_CONTAINER(topage), GTK_WIDGET(l->data));
1248  g_object_unref(l->data);
1249  gtk_box_reorder_child(GTK_BOX(topage), GTK_WIDGET(l->data), 0);
1250  }
1251  }
1252  g_list_free(lst);
1253 
1254 }
1255 
1257 {
1258  /* Migrate a complete notebook from a window to another */
1259 
1260  gchar *tag;
1261  gint cp, np, i;
1262  GtkNotebook *from_notebook;
1263  GtkWidget *frompage, *newpage, *old_scrolled_container;
1264  RemminaConnectionObject *cnnobj;
1265  RemminaScaleMode scalemode;
1266 
1267  /* Migrate TAG */
1268  tag = g_strdup((gchar *)g_object_get_data(G_OBJECT(from), "tag"));
1269  g_object_set_data_full(G_OBJECT(to), "tag", tag, (GDestroyNotify)g_free);
1270 
1271  /* Migrate notebook content */
1272  from_notebook = from->priv->notebook;
1273  if (from_notebook && GTK_IS_NOTEBOOK(from_notebook)) {
1274 
1275  cp = gtk_notebook_get_current_page(from_notebook);
1276  np = gtk_notebook_get_n_pages(from_notebook);
1277  /* Create pages on dest notebook and migrate
1278  * page content */
1279  for (i = 0; i < np; i++) {
1280  frompage = gtk_notebook_get_nth_page(from_notebook, i);
1281  cnnobj = g_object_get_data(G_OBJECT(frompage), "cnnobj");
1282 
1283  /* A scrolled container must be recreated, because it can be different on the new window/page
1284  depending on view_mode */
1285  scalemode = get_current_allowed_scale_mode(cnnobj, NULL, NULL);
1286  old_scrolled_container = cnnobj->scrolled_container;
1287  cnnobj->scrolled_container = rco_create_scrolled_container(scalemode, to->priv->view_mode);
1288 
1289  newpage = rcw_append_new_page(to, cnnobj);
1290 
1291  nb_migrate_message_panels(frompage, newpage);
1292 
1293  /* Reparent the viewport (which is inside scrolled_container) to the new page */
1294  g_object_ref(cnnobj->viewport);
1295  gtk_container_remove(GTK_CONTAINER(old_scrolled_container), cnnobj->viewport);
1296  gtk_container_add(GTK_CONTAINER(cnnobj->scrolled_container), cnnobj->viewport);
1297  g_object_unref(cnnobj->viewport);
1298 
1299  /* Destroy old scrolled_container. Not really needed, it will be destroyed
1300  * when removing the page from the notepad */
1301  gtk_widget_destroy(old_scrolled_container);
1302 
1303  }
1304 
1305  /* Remove all the pages from source notebook */
1306  for (i = np - 1; i >= 0; i--)
1307  gtk_notebook_remove_page(from_notebook, i);
1308  gtk_notebook_set_current_page(to->priv->notebook, cp);
1309 
1310  }
1311 }
1312 
1313 static void rcw_switch_viewmode(RemminaConnectionWindow *cnnwin, int newmode)
1314 {
1315  GdkWindowState s;
1316  RemminaConnectionWindow *newwin;
1317  gint old_width, old_height;
1318  int old_mode;
1319 
1320  old_mode = cnnwin->priv->view_mode;
1321  if (old_mode == newmode)
1322  return;
1323 
1324  if (newmode == VIEWPORT_FULLSCREEN_MODE || newmode == SCROLLED_FULLSCREEN_MODE) {
1325  if (old_mode == SCROLLED_WINDOW_MODE) {
1326  /* We are leaving SCROLLED_WINDOW_MODE, save W,H, and maximized
1327  * status before self destruction of cnnwin */
1328  gtk_window_get_size(GTK_WINDOW(cnnwin), &old_width, &old_height);
1329  s = gdk_window_get_state(gtk_widget_get_window(GTK_WIDGET(cnnwin)));
1330  }
1331  newwin = rcw_create_fullscreen(GTK_WINDOW(cnnwin), cnnwin->priv->fss_view_mode);
1332  rcw_migrate(cnnwin, newwin);
1333  if (old_mode == SCROLLED_WINDOW_MODE) {
1334  newwin->priv->ss_maximized = (s & GDK_WINDOW_STATE_MAXIMIZED) ? TRUE : FALSE;
1335  newwin->priv->ss_width = old_width;
1336  newwin->priv->ss_height = old_height;
1337  }
1338  } else {
1339  newwin = rcw_create_scrolled(cnnwin->priv->ss_width, cnnwin->priv->ss_height,
1340  cnnwin->priv->ss_maximized);
1341  rcw_migrate(cnnwin, newwin);
1342  if (old_mode == VIEWPORT_FULLSCREEN_MODE || old_mode == SCROLLED_FULLSCREEN_MODE)
1343  /* We are leaving a FULLSCREEN mode, save some parameters
1344  * status before self destruction of cnnwin */
1345  newwin->priv->fss_view_mode = old_mode;
1346  }
1347 
1348  /* Prevent unreleased hostkey from old window to be released here */
1349  newwin->priv->hostkey_used = TRUE;
1350 }
1351 
1352 
1353 static void rcw_toolbar_fullscreen(GtkToolItem *toggle, RemminaConnectionWindow *cnnwin)
1354 {
1355  TRACE_CALL(__func__);
1356 
1357  RemminaConnectionObject *cnnobj;
1358 
1359  if (cnnwin->priv->toolbar_is_reconfiguring)
1360  return;
1361 
1362  if (!(cnnobj = rcw_get_visible_cnnobj(cnnwin))) return;
1363 
1364  RemminaProtocolWidget *gp = REMMINA_PROTOCOL_WIDGET(cnnobj->proto);
1365 
1366  if (remmina_protocol_widget_get_multimon(gp) >= 1) {
1367  REMMINA_DEBUG("Fullscreen on all monitor");
1368  gdk_window_set_fullscreen_mode(gtk_widget_get_window(GTK_WIDGET(toggle)), GDK_FULLSCREEN_ON_ALL_MONITORS);
1369  } else {
1370  REMMINA_DEBUG("Fullscreen on one monitor");
1371  }
1372 
1373  if ((toggle != NULL && toggle == cnnwin->priv->toolitem_fullscreen)) {
1374  if (gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(toggle))) {
1376  gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(cnnwin->priv->toolitem_multimon), TRUE);
1377  rcw_switch_viewmode(cnnwin, cnnwin->priv->fss_view_mode);
1378  } else {
1380  }
1381  } else
1382  if (gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(cnnwin->priv->toolitem_multimon))) {
1383  rcw_switch_viewmode(cnnwin, cnnwin->priv->fss_view_mode);
1384  } else {
1386  }
1387 }
1388 
1389 static void rco_viewport_fullscreen_mode(GtkWidget *widget, RemminaConnectionObject *cnnobj)
1390 {
1391  TRACE_CALL(__func__);
1392  RemminaConnectionWindow *newwin;
1393 
1394  if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget)))
1395  return;
1396  cnnobj->cnnwin->priv->fss_view_mode = VIEWPORT_FULLSCREEN_MODE;
1397  newwin = rcw_create_fullscreen(GTK_WINDOW(cnnobj->cnnwin), VIEWPORT_FULLSCREEN_MODE);
1398  rcw_migrate(cnnobj->cnnwin, newwin);
1399 }
1400 
1401 static void rco_scrolled_fullscreen_mode(GtkWidget *widget, RemminaConnectionObject *cnnobj)
1402 {
1403  TRACE_CALL(__func__);
1404  RemminaConnectionWindow *newwin;
1405 
1406  if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget)))
1407  return;
1408  cnnobj->cnnwin->priv->fss_view_mode = SCROLLED_FULLSCREEN_MODE;
1409  newwin = rcw_create_fullscreen(GTK_WINDOW(cnnobj->cnnwin), SCROLLED_FULLSCREEN_MODE);
1410  rcw_migrate(cnnobj->cnnwin, newwin);
1411 }
1412 
1413 static void rcw_fullscreen_option_popdown(GtkWidget *widget, RemminaConnectionWindow *cnnwin)
1414 {
1415  TRACE_CALL(__func__);
1416  RemminaConnectionWindowPriv *priv = cnnwin->priv;
1417 
1418  priv->sticky = FALSE;
1419  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(priv->fullscreen_option_button), FALSE);
1420  rcw_floating_toolbar_show(cnnwin, FALSE);
1421 }
1422 
1423 void rcw_toolbar_fullscreen_option(GtkToolItem *toggle, RemminaConnectionWindow *cnnwin)
1424 {
1425  TRACE_CALL(__func__);
1426  RemminaConnectionObject *cnnobj;
1427  GtkWidget *menu;
1428  GtkWidget *menuitem;
1429  GSList *group;
1430 
1431  if (cnnwin->priv->toolbar_is_reconfiguring)
1432  return;
1433 
1434  if (!(cnnobj = rcw_get_visible_cnnobj(cnnwin))) return;
1435 
1436  if (!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(toggle)))
1437  return;
1438 
1439  cnnwin->priv->sticky = TRUE;
1440 
1441  menu = gtk_menu_new();
1442 
1443  menuitem = gtk_radio_menu_item_new_with_label(NULL, _("Viewport fullscreen mode"));
1444  gtk_widget_show(menuitem);
1445  gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
1446  group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(menuitem));
1447  if (cnnwin->priv->view_mode == VIEWPORT_FULLSCREEN_MODE)
1448  gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
1449  g_signal_connect(G_OBJECT(menuitem), "toggled", G_CALLBACK(rco_viewport_fullscreen_mode), cnnobj);
1450 
1451  menuitem = gtk_radio_menu_item_new_with_label(group, _("Scrolled fullscreen"));
1452  gtk_widget_show(menuitem);
1453  gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
1454  if (cnnwin->priv->view_mode == SCROLLED_FULLSCREEN_MODE)
1455  gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
1456  g_signal_connect(G_OBJECT(menuitem), "toggled", G_CALLBACK(rco_scrolled_fullscreen_mode), cnnobj);
1457 
1458  g_signal_connect(G_OBJECT(menu), "deactivate", G_CALLBACK(rcw_fullscreen_option_popdown), cnnwin);
1459 
1460 #if GTK_CHECK_VERSION(3, 22, 0)
1461  gtk_menu_popup_at_widget(GTK_MENU(menu), GTK_WIDGET(toggle),
1462  GDK_GRAVITY_SOUTH_WEST, GDK_GRAVITY_NORTH_WEST, NULL);
1463 #else
1464  gtk_menu_popup(GTK_MENU(menu), NULL, NULL, remmina_public_popup_position, cnnwin->priv->toolitem_fullscreen, 0,
1465  gtk_get_current_event_time());
1466 #endif
1467 }
1468 
1469 
1470 static void rcw_scaler_option_popdown(GtkWidget *widget, RemminaConnectionWindow *cnnwin)
1471 {
1472  TRACE_CALL(__func__);
1473  RemminaConnectionWindowPriv *priv = cnnwin->priv;
1474 
1475  if (priv->toolbar_is_reconfiguring)
1476  return;
1477  priv->sticky = FALSE;
1478  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(priv->scaler_option_button), FALSE);
1479  rcw_floating_toolbar_show(cnnwin, FALSE);
1480 }
1481 
1482 static void rcw_scaler_expand(GtkWidget *widget, RemminaConnectionWindow *cnnwin)
1483 {
1484  TRACE_CALL(__func__);
1485  RemminaConnectionObject *cnnobj;
1486 
1487  if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget)))
1488  return;
1489  cnnobj = rcw_get_visible_cnnobj(cnnwin);
1490  if (!cnnobj)
1491  return;
1492  remmina_protocol_widget_set_expand(REMMINA_PROTOCOL_WIDGET(cnnobj->proto), TRUE);
1493  remmina_file_set_int(cnnobj->remmina_file, "scaler_expand", TRUE);
1495 }
1496 static void rcw_scaler_keep_aspect(GtkWidget *widget, RemminaConnectionWindow *cnnwin)
1497 {
1498  TRACE_CALL(__func__);
1499  RemminaConnectionObject *cnnobj;
1500 
1501  if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget)))
1502  return;
1503  cnnobj = rcw_get_visible_cnnobj(cnnwin);
1504  if (!cnnobj)
1505  return;
1506 
1507  remmina_protocol_widget_set_expand(REMMINA_PROTOCOL_WIDGET(cnnobj->proto), FALSE);
1508  remmina_file_set_int(cnnobj->remmina_file, "scaler_expand", FALSE);
1510 }
1511 
1512 static void rcw_toolbar_scaler_option(GtkToolItem *toggle, RemminaConnectionWindow *cnnwin)
1513 {
1514  TRACE_CALL(__func__);
1516  RemminaConnectionObject *cnnobj;
1517  GtkWidget *menu;
1518  GtkWidget *menuitem;
1519  GSList *group;
1520  gboolean scaler_expand;
1521 
1522  if (cnnwin->priv->toolbar_is_reconfiguring)
1523  return;
1524 
1525  if (!(cnnobj = rcw_get_visible_cnnobj(cnnwin))) return;
1526  priv = cnnwin->priv;
1527 
1528  if (!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(toggle)))
1529  return;
1530 
1531  scaler_expand = remmina_protocol_widget_get_expand(REMMINA_PROTOCOL_WIDGET(cnnobj->proto));
1532 
1533  priv->sticky = TRUE;
1534 
1535  menu = gtk_menu_new();
1536 
1537  menuitem = gtk_radio_menu_item_new_with_label(NULL, _("Keep aspect ratio when scaled"));
1538  gtk_widget_show(menuitem);
1539  gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
1540  group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(menuitem));
1541  if (!scaler_expand)
1542  gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
1543  g_signal_connect(G_OBJECT(menuitem), "toggled", G_CALLBACK(rcw_scaler_keep_aspect), cnnwin);
1544 
1545  menuitem = gtk_radio_menu_item_new_with_label(group, _("Fill client window when scaled"));
1546  gtk_widget_show(menuitem);
1547  gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
1548  if (scaler_expand)
1549  gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
1550  g_signal_connect(G_OBJECT(menuitem), "toggled", G_CALLBACK(rcw_scaler_expand), cnnwin);
1551 
1552  g_signal_connect(G_OBJECT(menu), "deactivate", G_CALLBACK(rcw_scaler_option_popdown), cnnwin);
1553 
1554 #if GTK_CHECK_VERSION(3, 22, 0)
1555  gtk_menu_popup_at_widget(GTK_MENU(menu), GTK_WIDGET(toggle),
1556  GDK_GRAVITY_SOUTH_WEST, GDK_GRAVITY_NORTH_WEST, NULL);
1557 #else
1558  gtk_menu_popup(GTK_MENU(menu), NULL, NULL, remmina_public_popup_position, priv->toolitem_scale, 0,
1559  gtk_get_current_event_time());
1560 #endif
1561 }
1562 
1563 void rco_switch_page_activate(GtkMenuItem *menuitem, RemminaConnectionObject *cnnobj)
1564 {
1565  TRACE_CALL(__func__);
1566  RemminaConnectionWindowPriv *priv = cnnobj->cnnwin->priv;
1567  gint page_num;
1568 
1569  page_num = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(menuitem), "new-page-num"));
1570  gtk_notebook_set_current_page(GTK_NOTEBOOK(priv->notebook), page_num);
1571 }
1572 
1574 {
1575  TRACE_CALL(__func__);
1576  RemminaConnectionWindowPriv *priv = cnnwin->priv;
1577 
1578  priv->sticky = FALSE;
1579 
1580  gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(priv->toolitem_switch_page), FALSE);
1581  rcw_floating_toolbar_show(cnnwin, FALSE);
1582 }
1583 
1584 static void rcw_toolbar_switch_page(GtkToolItem *toggle, RemminaConnectionWindow *cnnwin)
1585 {
1586  TRACE_CALL(__func__);
1587 
1588  RemminaConnectionWindowPriv *priv = cnnwin->priv;
1589  RemminaConnectionObject *cnnobj;
1590 
1591  GtkWidget *menu;
1592  GtkWidget *menuitem;
1593  GtkWidget *image;
1594  gint i, n;
1595 
1596  if (priv->toolbar_is_reconfiguring)
1597  return;
1598  if (!(cnnobj = rcw_get_visible_cnnobj(cnnwin))) return;
1599 
1600  if (!gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(toggle)))
1601  return;
1602 
1603  priv->sticky = TRUE;
1604 
1605  menu = gtk_menu_new();
1606 
1607  n = gtk_notebook_get_n_pages(GTK_NOTEBOOK(priv->notebook));
1608  for (i = 0; i < n; i++) {
1609  cnnobj = rcw_get_cnnobj_at_page(cnnobj->cnnwin, i);
1610 
1611  menuitem = gtk_menu_item_new_with_label(remmina_file_get_string(cnnobj->remmina_file, "name"));
1612  gtk_widget_show(menuitem);
1613  gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
1614 
1615  image = gtk_image_new_from_icon_name(remmina_file_get_icon_name(cnnobj->remmina_file), GTK_ICON_SIZE_MENU);
1616  gtk_widget_show(image);
1617 
1618  g_object_set_data(G_OBJECT(menuitem), "new-page-num", GINT_TO_POINTER(i));
1619  g_signal_connect(G_OBJECT(menuitem), "activate", G_CALLBACK(rco_switch_page_activate), cnnobj);
1620  if (i == gtk_notebook_get_current_page(GTK_NOTEBOOK(priv->notebook)))
1621  gtk_widget_set_sensitive(menuitem, FALSE);
1622  }
1623 
1624  g_signal_connect(G_OBJECT(menu), "deactivate", G_CALLBACK(rcw_toolbar_switch_page_popdown),
1625  cnnwin);
1626 
1627 #if GTK_CHECK_VERSION(3, 22, 0)
1628  gtk_menu_popup_at_widget(GTK_MENU(menu), GTK_WIDGET(toggle),
1629  GDK_GRAVITY_SOUTH_WEST, GDK_GRAVITY_NORTH_WEST, NULL);
1630 #else
1631  gtk_menu_popup(GTK_MENU(menu), NULL, NULL, remmina_public_popup_position, widget, 0, gtk_get_current_event_time());
1632 #endif
1633 }
1634 
1636 {
1637  TRACE_CALL(__func__);
1638  RemminaConnectionWindowPriv *priv = cnnobj->cnnwin->priv;
1639  GtkToolItem *toolitem;
1640  RemminaScaleMode sc;
1641 
1642  toolitem = priv->toolitem_autofit;
1643  if (toolitem) {
1644  if (priv->view_mode != SCROLLED_WINDOW_MODE) {
1645  gtk_widget_set_sensitive(GTK_WIDGET(toolitem), FALSE);
1646  } else {
1647  sc = get_current_allowed_scale_mode(cnnobj, NULL, NULL);
1648  gtk_widget_set_sensitive(GTK_WIDGET(toolitem), sc == REMMINA_PROTOCOL_WIDGET_SCALE_MODE_NONE);
1649  }
1650  }
1651 }
1652 
1653 static void rco_change_scalemode(RemminaConnectionObject *cnnobj, gboolean bdyn, gboolean bscale)
1654 {
1655  RemminaScaleMode scalemode;
1656  RemminaConnectionWindowPriv *priv = cnnobj->cnnwin->priv;
1657 
1658  if (bdyn)
1660  else if (bscale)
1662  else
1664 
1665  remmina_protocol_widget_set_current_scale_mode(REMMINA_PROTOCOL_WIDGET(cnnobj->proto), scalemode);
1666  remmina_file_set_int(cnnobj->remmina_file, "scale", scalemode);
1667  gtk_widget_set_sensitive(GTK_WIDGET(priv->scaler_option_button), scalemode == REMMINA_PROTOCOL_WIDGET_SCALE_MODE_SCALED);
1669 
1670  remmina_protocol_widget_call_feature_by_type(REMMINA_PROTOCOL_WIDGET(cnnobj->proto),
1672 
1673  if (cnnobj->cnnwin->priv->view_mode != SCROLLED_WINDOW_MODE)
1674  rco_check_resize(cnnobj);
1675  if (GTK_IS_SCROLLED_WINDOW(cnnobj->scrolled_container)) {
1676  rco_set_scrolled_policy(scalemode, GTK_SCROLLED_WINDOW(cnnobj->scrolled_container));
1677  }
1678 }
1679 
1680 static void rcw_toolbar_dynres(GtkToolItem *toggle, RemminaConnectionWindow *cnnwin)
1681 {
1682  TRACE_CALL(__func__);
1683  gboolean bdyn, bscale;
1684  RemminaConnectionObject *cnnobj;
1685 
1686  if (cnnwin->priv->toolbar_is_reconfiguring)
1687  return;
1688  if (!(cnnobj = rcw_get_visible_cnnobj(cnnwin))) return;
1689 
1690  if (cnnobj->connected) {
1691  bdyn = gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(toggle));
1692  bscale = gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(cnnobj->cnnwin->priv->toolitem_scale));
1693 
1694  if (bdyn && bscale) {
1695  gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(cnnobj->cnnwin->priv->toolitem_scale), FALSE);
1696  bscale = FALSE;
1697  }
1698 
1699  rco_change_scalemode(cnnobj, bdyn, bscale);
1700  }
1701 }
1702 
1703 static void rcw_toolbar_scaled_mode(GtkToolItem *toggle, RemminaConnectionWindow *cnnwin)
1704 {
1705  TRACE_CALL(__func__);
1706  gboolean bdyn, bscale;
1707  RemminaConnectionObject *cnnobj;
1708 
1709  if (cnnwin->priv->toolbar_is_reconfiguring)
1710  return;
1711  if (!(cnnobj = rcw_get_visible_cnnobj(cnnwin))) return;
1712 
1713  bdyn = gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(cnnobj->cnnwin->priv->toolitem_dynres));
1714  bscale = gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(toggle));
1715 
1716  if (bdyn && bscale) {
1717  gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(cnnobj->cnnwin->priv->toolitem_dynres), FALSE);
1718  bdyn = FALSE;
1719  }
1720 
1721  rco_change_scalemode(cnnobj, bdyn, bscale);
1722 }
1723 
1724 static void rcw_toolbar_multi_monitor_mode(GtkToolItem *toggle, RemminaConnectionWindow *cnnwin)
1725 {
1726  TRACE_CALL(__func__);
1727  RemminaConnectionObject *cnnobj;
1728 
1729  if (cnnwin->priv->toolbar_is_reconfiguring)
1730  return;
1731 
1732  if (!(cnnobj = rcw_get_visible_cnnobj(cnnwin))) return;
1733 
1734  if (gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(toggle))) {
1735  REMMINA_DEBUG("Saving multimon as 1");
1736  remmina_file_set_int(cnnobj->remmina_file, "multimon", 1);
1738  remmina_protocol_widget_call_feature_by_type(REMMINA_PROTOCOL_WIDGET(cnnobj->proto),
1740  if (!gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(cnnwin->priv->toolitem_fullscreen)))
1741  gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(cnnwin->priv->toolitem_fullscreen), TRUE);
1742  } else {
1743  REMMINA_DEBUG("Saving multimon as 0");
1744  remmina_file_set_int(cnnobj->remmina_file, "multimon", 0);
1746  rcw_toolbar_fullscreen(NULL, cnnwin);
1747  }
1748 }
1749 
1750 static void rcw_toolbar_open_main(GtkToolItem *toggle, RemminaConnectionWindow *cnnwin)
1751 {
1752  TRACE_CALL(__func__);
1753 
1754  if (cnnwin->priv->toolbar_is_reconfiguring)
1755  return;
1756 
1758 }
1759 
1760 static void rcw_toolbar_preferences_popdown(GtkToolItem *toggle, RemminaConnectionWindow *cnnwin)
1761 {
1762  TRACE_CALL(__func__);
1763  RemminaConnectionObject *cnnobj;
1764 
1765  if (cnnwin->priv->toolbar_is_reconfiguring)
1766  return;
1767  if (!(cnnobj = rcw_get_visible_cnnobj(cnnwin))) return;
1768 
1769  cnnobj->cnnwin->priv->sticky = FALSE;
1770 
1771  gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(cnnobj->cnnwin->priv->toolitem_preferences), FALSE);
1772  rcw_floating_toolbar_show(cnnwin, FALSE);
1773 }
1774 
1775 void rcw_toolbar_menu_popdown(GtkToolItem *toggle, RemminaConnectionWindow *cnnwin)
1776 {
1777  TRACE_CALL(__func__);
1778  RemminaConnectionWindowPriv *priv = cnnwin->priv;
1779 
1780  if (priv->toolbar_is_reconfiguring)
1781  return;
1782 
1783  priv->sticky = FALSE;
1784 
1785  gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(priv->toolitem_menu), FALSE);
1786  rcw_floating_toolbar_show(cnnwin, FALSE);
1787 }
1788 
1789 void rcw_toolbar_tools_popdown(GtkToolItem *toggle, RemminaConnectionWindow *cnnwin)
1790 {
1791  TRACE_CALL(__func__);
1792  RemminaConnectionWindowPriv *priv = cnnwin->priv;
1793 
1794  if (priv->toolbar_is_reconfiguring)
1795  return;
1796 
1797  priv->sticky = FALSE;
1798 
1799  gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(priv->toolitem_tools), FALSE);
1800  rcw_floating_toolbar_show(cnnwin, FALSE);
1801 }
1802 
1803 static void rco_call_protocol_feature_radio(GtkMenuItem *menuitem, RemminaConnectionObject *cnnobj)
1804 {
1805  TRACE_CALL(__func__);
1806  RemminaProtocolFeature *feature;
1807  gpointer value;
1808 
1809  if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(menuitem))) {
1810  feature = (RemminaProtocolFeature *)g_object_get_data(G_OBJECT(menuitem), "feature-type");
1811  value = g_object_get_data(G_OBJECT(menuitem), "feature-value");
1812 
1813  remmina_file_set_string(cnnobj->remmina_file, (const gchar *)feature->opt2, (const gchar *)value);
1814  remmina_protocol_widget_call_feature_by_ref(REMMINA_PROTOCOL_WIDGET(cnnobj->proto), feature);
1815  }
1816 }
1817 
1818 static void rco_call_protocol_feature_check(GtkMenuItem *menuitem, RemminaConnectionObject *cnnobj)
1819 {
1820  TRACE_CALL(__func__);
1821  RemminaProtocolFeature *feature;
1822  gboolean value;
1823 
1824  feature = (RemminaProtocolFeature *)g_object_get_data(G_OBJECT(menuitem), "feature-type");
1825  value = gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(menuitem));
1826  remmina_file_set_int(cnnobj->remmina_file, (const gchar *)feature->opt2, value);
1827  remmina_protocol_widget_call_feature_by_ref(REMMINA_PROTOCOL_WIDGET(cnnobj->proto), feature);
1828 }
1829 
1830 static void rco_call_protocol_feature_activate(GtkMenuItem *menuitem, RemminaConnectionObject *cnnobj)
1831 {
1832  TRACE_CALL(__func__);
1833  RemminaProtocolFeature *feature;
1834 
1835  feature = (RemminaProtocolFeature *)g_object_get_data(G_OBJECT(menuitem), "feature-type");
1836  remmina_protocol_widget_call_feature_by_ref(REMMINA_PROTOCOL_WIDGET(cnnobj->proto), feature);
1837 }
1838 
1840  GtkWidget *menu, const RemminaProtocolFeature *feature, const gchar *domain, gboolean enabled)
1841 {
1842  TRACE_CALL(__func__);
1843  GtkWidget *menuitem;
1844  GSList *group;
1845  gint i;
1846  const gchar **list;
1847  const gchar *value;
1848 
1849  group = NULL;
1850  value = remmina_file_get_string(remminafile, (const gchar *)feature->opt2);
1851  list = (const gchar **)feature->opt3;
1852  for (i = 0; list[i]; i += 2) {
1853  menuitem = gtk_radio_menu_item_new_with_label(group, g_dgettext(domain, list[i + 1]));
1854  group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(menuitem));
1855  gtk_widget_show(menuitem);
1856  gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
1857 
1858  if (enabled) {
1859  g_object_set_data(G_OBJECT(menuitem), "feature-type", (gpointer)feature);
1860  g_object_set_data(G_OBJECT(menuitem), "feature-value", (gpointer)list[i]);
1861 
1862  if (value && g_strcmp0(list[i], value) == 0)
1863  gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
1864 
1865  g_signal_connect(G_OBJECT(menuitem), "toggled",
1866  G_CALLBACK(rco_call_protocol_feature_radio), cnnobj);
1867  } else {
1868  gtk_widget_set_sensitive(menuitem, FALSE);
1869  }
1870  }
1871 }
1872 
1874  GtkWidget *menu, const RemminaProtocolFeature *feature,
1875  const gchar *domain, gboolean enabled)
1876 {
1877  TRACE_CALL(__func__);
1878  GtkWidget *menuitem;
1879 
1880  menuitem = gtk_check_menu_item_new_with_label(g_dgettext(domain, (const gchar *)feature->opt3));
1881  gtk_widget_show(menuitem);
1882  gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
1883 
1884  if (enabled) {
1885  g_object_set_data(G_OBJECT(menuitem), "feature-type", (gpointer)feature);
1886 
1887  gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem),
1888  remmina_file_get_int(cnnobj->remmina_file, (const gchar *)feature->opt2, FALSE));
1889 
1890  g_signal_connect(G_OBJECT(menuitem), "toggled",
1891  G_CALLBACK(rco_call_protocol_feature_check), cnnobj);
1892  } else {
1893  gtk_widget_set_sensitive(menuitem, FALSE);
1894  }
1895 }
1896 
1897 static void rcw_toolbar_preferences(GtkToolItem *toggle, RemminaConnectionWindow *cnnwin)
1898 {
1899  TRACE_CALL(__func__);
1901  RemminaConnectionObject *cnnobj;
1902  const RemminaProtocolFeature *feature;
1903  GtkWidget *menu;
1904  GtkWidget *menuitem;
1905  gboolean separator;
1906  gchar *domain;
1907  gboolean enabled;
1908 
1909  if (cnnwin->priv->toolbar_is_reconfiguring)
1910  return;
1911  if (!(cnnobj = rcw_get_visible_cnnobj(cnnwin))) return;
1912  priv = cnnobj->cnnwin->priv;
1913 
1914  if (!gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(toggle)))
1915  return;
1916 
1917  priv->sticky = TRUE;
1918 
1919  separator = FALSE;
1920 
1921  domain = remmina_protocol_widget_get_domain(REMMINA_PROTOCOL_WIDGET(cnnobj->proto));
1922  menu = gtk_menu_new();
1923  for (feature = remmina_protocol_widget_get_features(REMMINA_PROTOCOL_WIDGET(cnnobj->proto)); feature && feature->type;
1924  feature++) {
1925  if (feature->type != REMMINA_PROTOCOL_FEATURE_TYPE_PREF)
1926  continue;
1927 
1928  if (separator) {
1929  menuitem = gtk_separator_menu_item_new();
1930  gtk_widget_show(menuitem);
1931  gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
1932  separator = FALSE;
1933  }
1934  enabled = remmina_protocol_widget_query_feature_by_ref(REMMINA_PROTOCOL_WIDGET(cnnobj->proto), feature);
1935  switch (GPOINTER_TO_INT(feature->opt1)) {
1936  case REMMINA_PROTOCOL_FEATURE_PREF_RADIO:
1937  rcw_toolbar_preferences_radio(cnnobj, cnnobj->remmina_file, menu, feature,
1938  domain, enabled);
1939  separator = TRUE;
1940  break;
1941  case REMMINA_PROTOCOL_FEATURE_PREF_CHECK:
1942  rcw_toolbar_preferences_check(cnnobj, menu, feature,
1943  domain, enabled);
1944  break;
1945  }
1946  }
1947 
1948  g_free(domain);
1949 
1950  g_signal_connect(G_OBJECT(menu), "deactivate", G_CALLBACK(rcw_toolbar_preferences_popdown), cnnwin);
1951 
1952 #if GTK_CHECK_VERSION(3, 22, 0)
1953  gtk_menu_popup_at_widget(GTK_MENU(menu), GTK_WIDGET(toggle),
1954  GDK_GRAVITY_SOUTH_WEST, GDK_GRAVITY_NORTH_WEST, NULL);
1955 #else
1956  gtk_menu_popup(GTK_MENU(menu), NULL, NULL, remmina_public_popup_position, widget, 0, gtk_get_current_event_time());
1957 #endif
1958 }
1959 
1961 {
1962  TRACE_CALL(__func__);
1963  gchar *s;
1964 
1965  switch (menuitem->item_type) {
1968  break;
1971  break;
1973  s = g_strdup_printf("%s,%s", menuitem->protocol, menuitem->name);
1975  g_free(s);
1976  break;
1977  }
1978 }
1979 
1980 static void rcw_toolbar_menu(GtkToolItem *toggle, RemminaConnectionWindow *cnnwin)
1981 {
1982  TRACE_CALL(__func__);
1984  RemminaConnectionObject *cnnobj;
1985  GtkWidget *menu;
1986  GtkWidget *menuitem = NULL;
1987 
1988  if (cnnwin->priv->toolbar_is_reconfiguring)
1989  return;
1990 
1991  if (!(cnnobj = rcw_get_visible_cnnobj(cnnwin))) return;
1992  priv = cnnobj->cnnwin->priv;
1993 
1994  if (!gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(toggle)))
1995  return;
1996 
1997  priv->sticky = TRUE;
1998 
1999  menu = remmina_applet_menu_new();
2000  remmina_applet_menu_set_hide_count(REMMINA_APPLET_MENU(menu), remmina_pref.applet_hide_count);
2001  remmina_applet_menu_populate(REMMINA_APPLET_MENU(menu));
2002 
2003  g_signal_connect(G_OBJECT(menu), "launch-item", G_CALLBACK(rcw_toolbar_menu_on_launch_item), NULL);
2004  //g_signal_connect(G_OBJECT(menu), "edit-item", G_CALLBACK(rcw_toolbar_menu_on_edit_item), NULL);
2005  menuitem = gtk_separator_menu_item_new();
2006  gtk_widget_show(menuitem);
2007  gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
2008 #if GTK_CHECK_VERSION(3, 22, 0)
2009  gtk_menu_popup_at_widget(GTK_MENU(menu), GTK_WIDGET(toggle),
2010  GDK_GRAVITY_SOUTH_WEST, GDK_GRAVITY_NORTH_WEST, NULL);
2011 #else
2012  gtk_menu_popup(GTK_MENU(menu), NULL, NULL, remmina_public_popup_position, widget, 0, gtk_get_current_event_time());
2013 #endif
2014  g_signal_connect(G_OBJECT(menu), "deactivate", G_CALLBACK(rcw_toolbar_menu_popdown), cnnwin);
2015 }
2016 
2017 static void rcw_toolbar_tools(GtkToolItem *toggle, RemminaConnectionWindow *cnnwin)
2018 {
2019  TRACE_CALL(__func__);
2021  RemminaConnectionObject *cnnobj;
2022  const RemminaProtocolFeature *feature;
2023  GtkWidget *menu;
2024  GtkWidget *menuitem = NULL;
2025  GtkMenu *submenu_keystrokes;
2026  const gchar *domain;
2027  gboolean enabled;
2028  gchar **keystrokes;
2029  gchar **keystroke_values;
2030  gint i;
2031 
2032  if (cnnwin->priv->toolbar_is_reconfiguring)
2033  return;
2034  if (!(cnnobj = rcw_get_visible_cnnobj(cnnwin))) return;
2035  priv = cnnobj->cnnwin->priv;
2036 
2037  if (!gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(toggle)))
2038  return;
2039 
2040  priv->sticky = TRUE;
2041 
2042  domain = remmina_protocol_widget_get_domain(REMMINA_PROTOCOL_WIDGET(cnnobj->proto));
2043  menu = gtk_menu_new();
2044  for (feature = remmina_protocol_widget_get_features(REMMINA_PROTOCOL_WIDGET(cnnobj->proto)); feature && feature->type;
2045  feature++) {
2046  if (feature->type != REMMINA_PROTOCOL_FEATURE_TYPE_TOOL)
2047  continue;
2048 
2049  if (feature->opt1)
2050  menuitem = gtk_menu_item_new_with_label(g_dgettext(domain, (const gchar *)feature->opt1));
2051  if (feature->opt3)
2052  rcw_set_tooltip(menuitem, "", GPOINTER_TO_UINT(feature->opt3), 0);
2053  gtk_widget_show(menuitem);
2054  gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
2055 
2056  enabled = remmina_protocol_widget_query_feature_by_ref(REMMINA_PROTOCOL_WIDGET(cnnobj->proto), feature);
2057  if (enabled) {
2058  g_object_set_data(G_OBJECT(menuitem), "feature-type", (gpointer)feature);
2059 
2060  g_signal_connect(G_OBJECT(menuitem), "activate",
2061  G_CALLBACK(rco_call_protocol_feature_activate), cnnobj);
2062  } else {
2063  gtk_widget_set_sensitive(menuitem, FALSE);
2064  }
2065  }
2066 
2067  /* If the plugin accepts keystrokes include the keystrokes menu */
2068  if (remmina_protocol_widget_plugin_receives_keystrokes(REMMINA_PROTOCOL_WIDGET(cnnobj->proto))) {
2069  /* Get the registered keystrokes list */
2070  keystrokes = g_strsplit(remmina_pref.keystrokes, STRING_DELIMITOR, -1);
2071  if (g_strv_length(keystrokes)) {
2072  /* Add a keystrokes submenu */
2073  menuitem = gtk_menu_item_new_with_label(_("Keystrokes"));
2074  submenu_keystrokes = GTK_MENU(gtk_menu_new());
2075  gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), GTK_WIDGET(submenu_keystrokes));
2076  gtk_widget_show(menuitem);
2077  gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
2078  /* Add each registered keystroke */
2079  for (i = 0; i < g_strv_length(keystrokes); i++) {
2080  keystroke_values = g_strsplit(keystrokes[i], STRING_DELIMITOR2, -1);
2081  if (g_strv_length(keystroke_values) > 1) {
2082  /* Add the keystroke if no description was available */
2083  menuitem = gtk_menu_item_new_with_label(
2084  g_strdup(keystroke_values[strlen(keystroke_values[0]) ? 0 : 1]));
2085  g_object_set_data(G_OBJECT(menuitem), "keystrokes", g_strdup(keystroke_values[1]));
2086  g_signal_connect_swapped(G_OBJECT(menuitem), "activate",
2088  REMMINA_PROTOCOL_WIDGET(cnnobj->proto));
2089  gtk_widget_show(menuitem);
2090  gtk_menu_shell_append(GTK_MENU_SHELL(submenu_keystrokes), menuitem);
2091  }
2092  g_strfreev(keystroke_values);
2093  }
2094  menuitem = gtk_menu_item_new_with_label(_("Send clipboard content as keystrokes"));
2095  static gchar k_tooltip[] =
2096  N_("CAUTION: Pasted text will be sent as a sequence of key-codes as if typed on your local keyboard.\n"
2097  "\n"
2098  " • For best results use same keyboard settings for both, client and server.\n"
2099  "\n"
2100  " • If client-keyboard is different from server-keyboard the received text can contain wrong or erroneous characters.\n"
2101  "\n"
2102  " • Unicode characters and other special characters that can't be translated to local key-codes won’t be sent to the server.\n"
2103  "\n");
2104  gtk_widget_set_tooltip_text(menuitem, k_tooltip);
2105  gtk_menu_shell_append(GTK_MENU_SHELL(submenu_keystrokes), menuitem);
2106  g_signal_connect_swapped(G_OBJECT(menuitem), "activate",
2108  REMMINA_PROTOCOL_WIDGET(cnnobj->proto));
2109  gtk_widget_show(menuitem);
2110  }
2111  g_strfreev(keystrokes);
2112  }
2113 
2114  g_signal_connect(G_OBJECT(menu), "deactivate", G_CALLBACK(rcw_toolbar_tools_popdown), cnnwin);
2115 
2116 #if GTK_CHECK_VERSION(3, 22, 0)
2117  gtk_menu_popup_at_widget(GTK_MENU(menu), GTK_WIDGET(toggle),
2118  GDK_GRAVITY_SOUTH_WEST, GDK_GRAVITY_NORTH_WEST, NULL);
2119 #else
2120  gtk_menu_popup(GTK_MENU(menu), NULL, NULL, remmina_public_popup_position, widget, 0, gtk_get_current_event_time());
2121 #endif
2122 }
2123 
2124 static void rcw_toolbar_duplicate(GtkToolItem *toggle, RemminaConnectionWindow *cnnwin)
2125 {
2126  TRACE_CALL(__func__);
2127 
2128  RemminaConnectionObject *cnnobj;
2129 
2130  if (cnnwin->priv->toolbar_is_reconfiguring)
2131  return;
2132  if (!(cnnobj = rcw_get_visible_cnnobj(cnnwin))) return;
2133 
2135 
2137 }
2138 
2139 static void rcw_toolbar_screenshot(GtkToolItem *toggle, RemminaConnectionWindow *cnnwin)
2140 {
2141  TRACE_CALL(__func__);
2142 
2143  GdkPixbuf *screenshot;
2144  GdkWindow *active_window;
2145  cairo_t *cr;
2146  gint width, height;
2147  GString *pngstr;
2148  gchar *pngname;
2149  GtkWidget *dialog;
2152  RemminaConnectionObject *cnnobj;
2153  cairo_surface_t *srcsurface;
2154  cairo_format_t cairo_format;
2155  cairo_surface_t *surface;
2156  int stride;
2157 
2158  if (cnnwin->priv->toolbar_is_reconfiguring)
2159  return;
2160  if (!(cnnobj = rcw_get_visible_cnnobj(cnnwin))) return;
2161 
2162  GDateTime *date = g_date_time_new_now_utc();
2163 
2164  // We will take a screenshot of the currently displayed RemminaProtocolWidget.
2165  gp = REMMINA_PROTOCOL_WIDGET(cnnobj->proto);
2166 
2167  gchar *denyclip = remmina_pref_get_value("deny_screenshot_clipboard");
2168 
2169  REMMINA_DEBUG("deny_screenshot_clipboard is set to %s", denyclip);
2170 
2171  GtkClipboard *c = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD);
2172 
2173  // Ask the plugin if it can give us a screenshot
2175  // Good, we have a screenshot from the plugin !
2176 
2177  REMMINA_DEBUG("Screenshot from plugin: w=%d h=%d bpp=%d bytespp=%d\n",
2178  rpsd.width, rpsd.height, rpsd.bitsPerPixel, rpsd.bytesPerPixel);
2179 
2180  width = rpsd.width;
2181  height = rpsd.height;
2182 
2183  if (rpsd.bitsPerPixel == 32)
2184  cairo_format = CAIRO_FORMAT_ARGB32;
2185  else if (rpsd.bitsPerPixel == 24)
2186  cairo_format = CAIRO_FORMAT_RGB24;
2187  else
2188  cairo_format = CAIRO_FORMAT_RGB16_565;
2189 
2190  stride = cairo_format_stride_for_width(cairo_format, width);
2191 
2192  srcsurface = cairo_image_surface_create_for_data(rpsd.buffer, cairo_format, width, height, stride);
2193  // Transfer the PixBuf in the main clipboard selection
2194  if (denyclip && (g_strcmp0(denyclip, "true")))
2195  gtk_clipboard_set_image(c, gdk_pixbuf_get_from_surface(
2196  srcsurface, 0, 0, width, height));
2197  surface = cairo_image_surface_create(CAIRO_FORMAT_RGB24, width, height);
2198  cr = cairo_create(surface);
2199  cairo_set_source_surface(cr, srcsurface, 0, 0);
2200  cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
2201  cairo_paint(cr);
2202  cairo_surface_destroy(srcsurface);
2203 
2204  free(rpsd.buffer);
2205  } else {
2206  // The plugin is not releasing us a screenshot, just try to catch one via GTK
2207 
2208  /* Warn the user if image is distorted */
2209  if (cnnobj->plugin_can_scale &&
2211  dialog = gtk_message_dialog_new(NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_WARNING, GTK_BUTTONS_OK,
2212  _("Turn off scaling to avoid screenshot distortion."));
2213  g_signal_connect(G_OBJECT(dialog), "response", G_CALLBACK(gtk_widget_destroy), NULL);
2214  gtk_widget_show(dialog);
2215  }
2216 
2217  // Get the screenshot.
2218  active_window = gtk_widget_get_window(GTK_WIDGET(gp));
2219  // width = gdk_window_get_width(gtk_widget_get_window(GTK_WIDGET(cnnobj->cnnwin)));
2220  width = gdk_window_get_width(active_window);
2221  // height = gdk_window_get_height(gtk_widget_get_window(GTK_WIDGET(cnnobj->cnnwin)));
2222  height = gdk_window_get_height(active_window);
2223 
2224  screenshot = gdk_pixbuf_get_from_window(active_window, 0, 0, width, height);
2225  if (screenshot == NULL)
2226  g_print("gdk_pixbuf_get_from_window failed\n");
2227 
2228  // Transfer the PixBuf in the main clipboard selection
2229  if (denyclip && (g_strcmp0(denyclip, "true")))
2230  gtk_clipboard_set_image(c, screenshot);
2231  // Prepare the destination Cairo surface.
2232  surface = cairo_image_surface_create(CAIRO_FORMAT_RGB24, width, height);
2233  cr = cairo_create(surface);
2234 
2235  // Copy the source pixbuf to the surface and paint it.
2236  gdk_cairo_set_source_pixbuf(cr, screenshot, 0, 0);
2237  cairo_paint(cr);
2238 
2239  // Deallocate screenshot pixbuf
2240  g_object_unref(screenshot);
2241  }
2242 
2243  //home/antenore/Pictures/remmina_%p_%h_%Y %m %d-%H%M%S.png pngname
2244  //home/antenore/Pictures/remmina_st_ _2018 9 24-151958.240374.png
2245 
2246  pngstr = g_string_new(g_strdup_printf("%s/%s.png",
2247  remmina_pref.screenshot_path,
2248  remmina_pref.screenshot_name));
2249  remmina_utils_string_replace_all(pngstr, "%p",
2250  remmina_file_get_string(cnnobj->remmina_file, "name"));
2251  remmina_utils_string_replace_all(pngstr, "%h",
2252  remmina_file_get_string(cnnobj->remmina_file, "server"));
2253  remmina_utils_string_replace_all(pngstr, "%Y",
2254  g_strdup_printf("%d", g_date_time_get_year(date)));
2255  remmina_utils_string_replace_all(pngstr, "%m", g_strdup_printf("%02d",
2256  g_date_time_get_month(date)));
2257  remmina_utils_string_replace_all(pngstr, "%d",
2258  g_strdup_printf("%02d", g_date_time_get_day_of_month(date)));
2259  remmina_utils_string_replace_all(pngstr, "%H",
2260  g_strdup_printf("%02d", g_date_time_get_hour(date)));
2261  remmina_utils_string_replace_all(pngstr, "%M",
2262  g_strdup_printf("%02d", g_date_time_get_minute(date)));
2263  remmina_utils_string_replace_all(pngstr, "%S",
2264  g_strdup_printf("%02d", g_date_time_get_second(date)));
2265  g_date_time_unref(date);
2266  pngname = g_string_free(pngstr, FALSE);
2267 
2268  cairo_surface_write_to_png(surface, pngname);
2269 
2270  /* send a desktop notification */
2271  if (g_file_test(pngname, G_FILE_TEST_EXISTS))
2272  remmina_public_send_notification("remmina-screenshot-is-ready-id", _("Screenshot taken"), pngname);
2273 
2274  //Clean up and return.
2275  cairo_destroy(cr);
2276  cairo_surface_destroy(surface);
2277 }
2278 
2279 static void rcw_toolbar_minimize(GtkToolItem *toggle, RemminaConnectionWindow *cnnwin)
2280 {
2281  TRACE_CALL(__func__);
2282 
2283  if (cnnwin->priv->toolbar_is_reconfiguring)
2284  return;
2285 
2286  rcw_floating_toolbar_show(cnnwin, FALSE);
2287  gtk_window_iconify(GTK_WINDOW(cnnwin));
2288 }
2289 
2290 static void rcw_toolbar_disconnect(GtkToolItem *toggle, RemminaConnectionWindow *cnnwin)
2291 {
2292  TRACE_CALL(__func__);
2293  RemminaConnectionObject *cnnobj;
2294 
2295  if (cnnwin->priv->toolbar_is_reconfiguring)
2296  return;
2297  if (!(cnnobj = rcw_get_visible_cnnobj(cnnwin))) return;
2299 }
2300 
2301 static void rcw_toolbar_grab(GtkToolItem *toggle, RemminaConnectionWindow *cnnwin)
2302 {
2303  TRACE_CALL(__func__);
2304  gboolean capture;
2305  RemminaConnectionObject *cnnobj;
2306 
2307  if (cnnwin->priv->toolbar_is_reconfiguring)
2308  return;
2309  if (!(cnnobj = rcw_get_visible_cnnobj(cnnwin))) return;
2310 
2311  capture = gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(toggle));
2312 
2313  if (cnnobj->connected){
2314  remmina_file_set_int(cnnobj->remmina_file, "keyboard_grab", capture);
2315  }
2316 
2317  if (capture && cnnobj->connected) {
2318 
2319 #if DEBUG_KB_GRABBING
2320  printf("DEBUG_KB_GRABBING: Grabbing for button\n");
2321 #endif
2322  rcw_keyboard_grab(cnnobj->cnnwin);
2323  if (cnnobj->cnnwin->priv->pointer_entered)
2324  rcw_pointer_grab(cnnobj->cnnwin);
2325  } else {
2326  rcw_kp_ungrab(cnnobj->cnnwin);
2327  }
2328 
2329  rco_update_toolbar(cnnobj);
2330 }
2331 
2332 static GtkWidget *
2334 {
2335  TRACE_CALL(__func__);
2336  RemminaConnectionWindowPriv *priv = cnnwin->priv;
2337  RemminaConnectionObject *cnnobj;
2338  GtkWidget *toolbar;
2339  GtkToolItem *toolitem;
2340  GtkWidget *widget;
2341  GtkWidget *arrow;
2342 
2343  GdkDisplay *display;
2344  gint n_monitors;
2345 
2346  display = gdk_display_get_default();
2347  n_monitors = gdk_display_get_n_monitors(display);
2348 
2349  cnnobj = rcw_get_visible_cnnobj(cnnwin);
2350 
2351  priv->toolbar_is_reconfiguring = TRUE;
2352 
2353  toolbar = gtk_toolbar_new();
2354  gtk_widget_show(toolbar);
2355  gtk_toolbar_set_style(GTK_TOOLBAR(toolbar), GTK_TOOLBAR_ICONS);
2356 
2357  /* Main actions */
2358 
2359  /* Menu */
2360  toolitem = gtk_toggle_tool_button_new();
2361  gtk_tool_button_set_icon_name(GTK_TOOL_BUTTON(toolitem), "view-more-symbolic");
2362  gtk_tool_button_set_label(GTK_TOOL_BUTTON(toolitem), _("_Menu"));
2363  gtk_tool_item_set_tooltip_text(toolitem, _("Menu"));
2364  gtk_toolbar_insert(GTK_TOOLBAR(toolbar), toolitem, -1);
2365  gtk_widget_show(GTK_WIDGET(toolitem));
2366  g_signal_connect(G_OBJECT(toolitem), "toggled", G_CALLBACK(rcw_toolbar_menu), cnnwin);
2367  priv->toolitem_menu = toolitem;
2368 
2369  /* Open Main window */
2370  toolitem = gtk_tool_button_new(NULL, "Open Remmina Main window");
2371  gtk_tool_button_set_icon_name(GTK_TOOL_BUTTON(toolitem), "go-home-symbolic");
2372  gtk_tool_item_set_tooltip_text(toolitem, _("Open the Remmina main window"));
2373  gtk_toolbar_insert(GTK_TOOLBAR(toolbar), toolitem, -1);
2374  gtk_widget_show(GTK_WIDGET(toolitem));
2375  g_signal_connect(G_OBJECT(toolitem), "clicked", G_CALLBACK(rcw_toolbar_open_main), cnnwin);
2376 
2377  priv->toolitem_new = toolitem;
2378 
2379  /* Duplicate session */
2380  toolitem = gtk_tool_button_new(NULL, "Duplicate connection");
2381  gtk_tool_button_set_icon_name(GTK_TOOL_BUTTON(toolitem), "org.remmina.Remmina-duplicate-symbolic");
2382  gtk_tool_item_set_tooltip_text(toolitem, _("Duplicate current connection"));
2383  gtk_toolbar_insert(GTK_TOOLBAR(toolbar), toolitem, -1);
2384  gtk_widget_show(GTK_WIDGET(toolitem));
2385  g_signal_connect(G_OBJECT(toolitem), "clicked", G_CALLBACK(rcw_toolbar_duplicate), cnnwin);
2386  if (!cnnobj)
2387  gtk_widget_set_sensitive(GTK_WIDGET(toolitem), FALSE);
2388 
2389  priv->toolitem_duplicate = toolitem;
2390 
2391  /* Separator */
2392  toolitem = gtk_separator_tool_item_new();
2393  gtk_toolbar_insert(GTK_TOOLBAR(toolbar), toolitem, -1);
2394  gtk_widget_show(GTK_WIDGET(toolitem));
2395 
2396  /* Auto-Fit */
2397  toolitem = gtk_tool_button_new(NULL, NULL);
2398  gtk_tool_button_set_icon_name(GTK_TOOL_BUTTON(toolitem), "org.remmina.Remmina-fit-window-symbolic");
2399  rcw_set_tooltip(GTK_WIDGET(toolitem), _("Resize the window to fit in remote resolution"),
2400  remmina_pref.shortcutkey_autofit, 0);
2401  g_signal_connect(G_OBJECT(toolitem), "clicked", G_CALLBACK(rcw_toolbar_autofit), cnnwin);
2402  priv->toolitem_autofit = toolitem;
2403  gtk_toolbar_insert(GTK_TOOLBAR(toolbar), toolitem, -1);
2404  gtk_widget_show(GTK_WIDGET(toolitem));
2405 
2406 
2407  /* Fullscreen toggle */
2408  toolitem = gtk_toggle_tool_button_new();
2409  gtk_tool_button_set_icon_name(GTK_TOOL_BUTTON(toolitem), "org.remmina.Remmina-fullscreen-symbolic");
2410  rcw_set_tooltip(GTK_WIDGET(toolitem), _("Toggle fullscreen mode"),
2411  remmina_pref.shortcutkey_fullscreen, 0);
2412  gtk_toolbar_insert(GTK_TOOLBAR(toolbar), toolitem, -1);
2413  gtk_widget_show(GTK_WIDGET(toolitem));
2414  priv->toolitem_fullscreen = toolitem;
2415  if (kioskmode) {
2416  gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(toolitem), FALSE);
2417  } else {
2418  gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(toolitem), mode != SCROLLED_WINDOW_MODE);
2419  g_signal_connect(G_OBJECT(toolitem), "clicked", G_CALLBACK(rcw_toolbar_fullscreen), cnnwin);
2420  }
2421 
2422  /* Fullscreen drop-down options */
2423  toolitem = gtk_tool_item_new();
2424  gtk_widget_show(GTK_WIDGET(toolitem));
2425  gtk_toolbar_insert(GTK_TOOLBAR(toolbar), toolitem, -1);
2426  widget = gtk_toggle_button_new();
2427  gtk_widget_show(widget);
2428  gtk_container_set_border_width(GTK_CONTAINER(widget), 0);
2429  gtk_button_set_relief(GTK_BUTTON(widget), GTK_RELIEF_NONE);
2430 #if GTK_CHECK_VERSION(3, 20, 0)
2431  gtk_widget_set_focus_on_click(GTK_WIDGET(widget), FALSE);
2432  if (remmina_pref.small_toolbutton)
2433  gtk_widget_set_name(widget, "remmina-small-button");
2434 
2435 #else
2436  gtk_button_set_focus_on_click(GTK_BUTTON(widget), FALSE);
2437 #endif
2438  gtk_container_add(GTK_CONTAINER(toolitem), widget);
2439 
2440 #if GTK_CHECK_VERSION(3, 14, 0)
2441  arrow = gtk_image_new_from_icon_name("org.remmina.Remmina-pan-down-symbolic", GTK_ICON_SIZE_SMALL_TOOLBAR);
2442 #else
2443  arrow = gtk_arrow_new(GTK_ARROW_DOWN, GTK_SHADOW_NONE);
2444 #endif
2445  gtk_widget_show(arrow);
2446  gtk_container_add(GTK_CONTAINER(widget), arrow);
2447  g_signal_connect(G_OBJECT(widget), "toggled", G_CALLBACK(rcw_toolbar_fullscreen_option), cnnwin);
2448  priv->fullscreen_option_button = widget;
2449  if (mode == SCROLLED_WINDOW_MODE)
2450  gtk_widget_set_sensitive(GTK_WIDGET(widget), FALSE);
2451 
2452  /* Multi monitor */
2453  if (n_monitors > 1) {
2454  toolitem = gtk_toggle_tool_button_new();
2455  gtk_tool_button_set_icon_name(GTK_TOOL_BUTTON(toolitem), "org.remmina.Remmina-multi-monitor-symbolic");
2456  rcw_set_tooltip(GTK_WIDGET(toolitem), _("Multi monitor"),
2457  remmina_pref.shortcutkey_multimon, 0);
2458  gtk_toolbar_insert(GTK_TOOLBAR(toolbar), toolitem, -1);
2459  gtk_widget_show(GTK_WIDGET(toolitem));
2460  g_signal_connect(G_OBJECT(toolitem), "toggled", G_CALLBACK(rcw_toolbar_multi_monitor_mode), cnnwin);
2461  priv->toolitem_multimon = toolitem;
2462  if (!cnnobj)
2463  gtk_widget_set_sensitive(GTK_WIDGET(toolitem), FALSE);
2464  else
2465  gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(toolitem),
2466  remmina_file_get_int(cnnobj->remmina_file, "multimon", FALSE));
2467  }
2468 
2469  /* Dynamic Resolution Update */
2470  toolitem = gtk_toggle_tool_button_new();
2471  gtk_tool_button_set_icon_name(GTK_TOOL_BUTTON(toolitem), "org.remmina.Remmina-dynres-symbolic");
2472  rcw_set_tooltip(GTK_WIDGET(toolitem), _("Toggle dynamic resolution update"),
2473  remmina_pref.shortcutkey_dynres, 0);
2474  gtk_toolbar_insert(GTK_TOOLBAR(toolbar), toolitem, -1);
2475  gtk_widget_show(GTK_WIDGET(toolitem));
2476  g_signal_connect(G_OBJECT(toolitem), "toggled", G_CALLBACK(rcw_toolbar_dynres), cnnwin);
2477  priv->toolitem_dynres = toolitem;
2478 
2479  /* Scaler button */
2480  toolitem = gtk_toggle_tool_button_new();
2481  gtk_tool_button_set_icon_name(GTK_TOOL_BUTTON(toolitem), "org.remmina.Remmina-scale-symbolic");
2482  rcw_set_tooltip(GTK_WIDGET(toolitem), _("Toggle scaled mode"), remmina_pref.shortcutkey_scale, 0);
2483  gtk_toolbar_insert(GTK_TOOLBAR(toolbar), toolitem, -1);
2484  gtk_widget_show(GTK_WIDGET(toolitem));
2485  g_signal_connect(G_OBJECT(toolitem), "toggled", G_CALLBACK(rcw_toolbar_scaled_mode), cnnwin);
2486  priv->toolitem_scale = toolitem;
2487 
2488  /* Scaler aspect ratio dropdown menu */
2489  toolitem = gtk_tool_item_new();
2490  gtk_widget_show(GTK_WIDGET(toolitem));
2491  gtk_toolbar_insert(GTK_TOOLBAR(toolbar), toolitem, -1);
2492  widget = gtk_toggle_button_new();
2493  gtk_widget_show(widget);
2494  gtk_container_set_border_width(GTK_CONTAINER(widget), 0);
2495  gtk_button_set_relief(GTK_BUTTON(widget), GTK_RELIEF_NONE);
2496 #if GTK_CHECK_VERSION(3, 20, 0)
2497  gtk_widget_set_focus_on_click(GTK_WIDGET(widget), FALSE);
2498 #else
2499  gtk_button_set_focus_on_click(GTK_BUTTON(widget), FALSE);
2500 #endif
2501  if (remmina_pref.small_toolbutton)
2502  gtk_widget_set_name(widget, "remmina-small-button");
2503  gtk_container_add(GTK_CONTAINER(toolitem), widget);
2504 #if GTK_CHECK_VERSION(3, 14, 0)
2505  arrow = gtk_image_new_from_icon_name("org.remmina.Remmina-pan-down-symbolic", GTK_ICON_SIZE_SMALL_TOOLBAR);
2506 #else
2507  arrow = gtk_arrow_new(GTK_ARROW_DOWN, GTK_SHADOW_NONE);
2508 #endif
2509  gtk_widget_show(arrow);
2510  gtk_container_add(GTK_CONTAINER(widget), arrow);
2511  g_signal_connect(G_OBJECT(widget), "toggled", G_CALLBACK(rcw_toolbar_scaler_option), cnnwin);
2512  priv->scaler_option_button = widget;
2513 
2514  /* Separator */
2515  toolitem = gtk_separator_tool_item_new();
2516  gtk_toolbar_insert(GTK_TOOLBAR(toolbar), toolitem, -1);
2517  gtk_widget_show(GTK_WIDGET(toolitem));
2518 
2519  /* Switch tabs */
2520  toolitem = gtk_toggle_tool_button_new();
2521  gtk_tool_button_set_icon_name(GTK_TOOL_BUTTON(toolitem), "org.remmina.Remmina-switch-page-symbolic");
2522  rcw_set_tooltip(GTK_WIDGET(toolitem), _("Switch tab pages"), remmina_pref.shortcutkey_prevtab,
2523  remmina_pref.shortcutkey_nexttab);
2524  gtk_toolbar_insert(GTK_TOOLBAR(toolbar), toolitem, -1);
2525  gtk_widget_show(GTK_WIDGET(toolitem));
2526  g_signal_connect(G_OBJECT(toolitem), "toggled", G_CALLBACK(rcw_toolbar_switch_page), cnnwin);
2527  priv->toolitem_switch_page = toolitem;
2528 
2529  /* Grab keyboard button */
2530  toolitem = gtk_toggle_tool_button_new();
2531  gtk_tool_button_set_icon_name(GTK_TOOL_BUTTON(toolitem), "org.remmina.Remmina-keyboard-symbolic");
2532  rcw_set_tooltip(GTK_WIDGET(toolitem), _("Grab all keyboard events"),
2533  remmina_pref.shortcutkey_grab, 0);
2534  gtk_toolbar_insert(GTK_TOOLBAR(toolbar), toolitem, -1);
2535  gtk_widget_show(GTK_WIDGET(toolitem));
2536  g_signal_connect(G_OBJECT(toolitem), "toggled", G_CALLBACK(rcw_toolbar_grab), cnnwin);
2537  priv->toolitem_grab = toolitem;
2538  if (!cnnobj)
2539  gtk_widget_set_sensitive(GTK_WIDGET(toolitem), FALSE);
2540  else {
2541  const gchar *protocol = remmina_file_get_string(cnnobj->remmina_file, "protocol");
2542  if (g_strcmp0(protocol, "SFTP") == 0 || g_strcmp0(protocol, "SSH") == 0)
2543  gtk_widget_set_sensitive(GTK_WIDGET(toolitem), FALSE);
2544  }
2545 
2546  /* Preferences */
2547  toolitem = gtk_toggle_tool_button_new();
2548  gtk_tool_button_set_icon_name(GTK_TOOL_BUTTON(toolitem), "org.remmina.Remmina-preferences-system-symbolic");
2549  gtk_tool_item_set_tooltip_text(toolitem, _("Preferences"));
2550  gtk_toolbar_insert(GTK_TOOLBAR(toolbar), toolitem, -1);
2551  gtk_widget_show(GTK_WIDGET(toolitem));
2552  g_signal_connect(G_OBJECT(toolitem), "toggled", G_CALLBACK(rcw_toolbar_preferences), cnnwin);
2553  priv->toolitem_preferences = toolitem;
2554 
2555  /* Tools */
2556  toolitem = gtk_toggle_tool_button_new();
2557  gtk_tool_button_set_icon_name(GTK_TOOL_BUTTON(toolitem), "org.remmina.Remmina-system-run-symbolic");
2558  gtk_tool_button_set_label(GTK_TOOL_BUTTON(toolitem), _("_Tools"));
2559  gtk_tool_item_set_tooltip_text(toolitem, _("Tools"));
2560  gtk_toolbar_insert(GTK_TOOLBAR(toolbar), toolitem, -1);
2561  gtk_widget_show(GTK_WIDGET(toolitem));
2562  g_signal_connect(G_OBJECT(toolitem), "toggled", G_CALLBACK(rcw_toolbar_tools), cnnwin);
2563  priv->toolitem_tools = toolitem;
2564 
2565  /* Separator */
2566  toolitem = gtk_separator_tool_item_new();
2567  gtk_toolbar_insert(GTK_TOOLBAR(toolbar), toolitem, -1);
2568  gtk_widget_show(GTK_WIDGET(toolitem));
2569 
2570  toolitem = gtk_tool_button_new(NULL, "_Screenshot");
2571  gtk_tool_button_set_icon_name(GTK_TOOL_BUTTON(toolitem), "org.remmina.Remmina-camera-photo-symbolic");
2572  rcw_set_tooltip(GTK_WIDGET(toolitem), _("Screenshot"), remmina_pref.shortcutkey_screenshot, 0);
2573  gtk_toolbar_insert(GTK_TOOLBAR(toolbar), toolitem, -1);
2574  gtk_widget_show(GTK_WIDGET(toolitem));
2575  g_signal_connect(G_OBJECT(toolitem), "clicked", G_CALLBACK(rcw_toolbar_screenshot), cnnwin);
2576  priv->toolitem_screenshot = toolitem;
2577 
2578  /* Separator */
2579  toolitem = gtk_separator_tool_item_new();
2580  gtk_toolbar_insert(GTK_TOOLBAR(toolbar), toolitem, -1);
2581  gtk_widget_show(GTK_WIDGET(toolitem));
2582 
2583  /* Minimize */
2584  toolitem = gtk_tool_button_new(NULL, "_Bottom");
2585  gtk_tool_button_set_icon_name(GTK_TOOL_BUTTON(toolitem), "org.remmina.Remmina-go-bottom-symbolic");
2586  rcw_set_tooltip(GTK_WIDGET(toolitem), _("Minimize window"), remmina_pref.shortcutkey_minimize, 0);
2587  gtk_toolbar_insert(GTK_TOOLBAR(toolbar), toolitem, -1);
2588  gtk_widget_show(GTK_WIDGET(toolitem));
2589  g_signal_connect(G_OBJECT(toolitem), "clicked", G_CALLBACK(rcw_toolbar_minimize), cnnwin);
2590  if (kioskmode)
2591  gtk_widget_set_sensitive(GTK_WIDGET(toolitem), FALSE);
2592 
2593  /* Disconnect */
2594  toolitem = gtk_tool_button_new(NULL, "_Disconnect");
2595  gtk_tool_button_set_icon_name(GTK_TOOL_BUTTON(toolitem), "org.remmina.Remmina-disconnect-symbolic");
2596  rcw_set_tooltip(GTK_WIDGET(toolitem), _("Disconnect"), remmina_pref.shortcutkey_disconnect, 0);
2597  gtk_toolbar_insert(GTK_TOOLBAR(toolbar), toolitem, -1);
2598  gtk_widget_show(GTK_WIDGET(toolitem));
2599  g_signal_connect(G_OBJECT(toolitem), "clicked", G_CALLBACK(rcw_toolbar_disconnect), cnnwin);
2600 
2601  priv->toolbar_is_reconfiguring = FALSE;
2602  return toolbar;
2603 }
2604 
2605 static void rcw_place_toolbar(GtkToolbar *toolbar, GtkGrid *grid, GtkWidget *sibling, int toolbar_placement)
2606 {
2607  /* Place the toolbar inside the grid and set its orientation */
2608 
2609  if (toolbar_placement == TOOLBAR_PLACEMENT_LEFT || toolbar_placement == TOOLBAR_PLACEMENT_RIGHT)
2610  gtk_orientable_set_orientation(GTK_ORIENTABLE(toolbar), GTK_ORIENTATION_VERTICAL);
2611  else
2612  gtk_orientable_set_orientation(GTK_ORIENTABLE(toolbar), GTK_ORIENTATION_HORIZONTAL);
2613 
2614 
2615  switch (toolbar_placement) {
2616  case TOOLBAR_PLACEMENT_TOP:
2617  gtk_widget_set_hexpand(GTK_WIDGET(toolbar), TRUE);
2618  gtk_widget_set_vexpand(GTK_WIDGET(toolbar), FALSE);
2619  gtk_grid_attach_next_to(GTK_GRID(grid), GTK_WIDGET(toolbar), sibling, GTK_POS_TOP, 1, 1);
2620  break;
2622  gtk_widget_set_vexpand(GTK_WIDGET(toolbar), TRUE);
2623  gtk_widget_set_hexpand(GTK_WIDGET(toolbar), FALSE);
2624  gtk_grid_attach_next_to(GTK_GRID(grid), GTK_WIDGET(toolbar), sibling, GTK_POS_RIGHT, 1, 1);
2625  break;
2627  gtk_widget_set_hexpand(GTK_WIDGET(toolbar), TRUE);
2628  gtk_widget_set_vexpand(GTK_WIDGET(toolbar), FALSE);
2629  gtk_grid_attach_next_to(GTK_GRID(grid), GTK_WIDGET(toolbar), sibling, GTK_POS_BOTTOM, 1, 1);
2630  break;
2632  gtk_widget_set_vexpand(GTK_WIDGET(toolbar), TRUE);
2633  gtk_widget_set_hexpand(GTK_WIDGET(toolbar), FALSE);
2634  gtk_grid_attach_next_to(GTK_GRID(grid), GTK_WIDGET(toolbar), sibling, GTK_POS_LEFT, 1, 1);
2635  break;
2636  }
2637 }
2638 
2640 {
2641  TRACE_CALL(__func__);
2642  RemminaConnectionWindowPriv *priv = cnnobj->cnnwin->priv;
2643  GtkToolItem *toolitem;
2644  gboolean bval, dynres_avail, scale_avail;
2645  gboolean test_floating_toolbar;
2646  RemminaScaleMode scalemode;
2647 
2648  priv->toolbar_is_reconfiguring = TRUE;
2649 
2651 
2652  toolitem = priv->toolitem_switch_page;
2653  if (kioskmode)
2654  bval = FALSE;
2655  else
2656  bval = (gtk_notebook_get_n_pages(GTK_NOTEBOOK(priv->notebook)) > 1);
2657  gtk_widget_set_sensitive(GTK_WIDGET(toolitem), bval);
2658 
2659  if (cnnobj->remmina_file->filename)
2660  gtk_widget_set_sensitive(GTK_WIDGET(priv->toolitem_duplicate), TRUE);
2661  else
2662  gtk_widget_set_sensitive(GTK_WIDGET(priv->toolitem_duplicate), FALSE);
2663 
2664  scalemode = get_current_allowed_scale_mode(cnnobj, &dynres_avail, &scale_avail);
2665  gtk_widget_set_sensitive(GTK_WIDGET(priv->toolitem_dynres), dynres_avail && cnnobj->connected);
2666  gtk_widget_set_sensitive(GTK_WIDGET(priv->toolitem_scale), scale_avail && cnnobj->connected);
2667 
2668  switch (scalemode) {
2670  gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(priv->toolitem_dynres), FALSE);
2671  gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(priv->toolitem_scale), FALSE);
2672  gtk_widget_set_sensitive(GTK_WIDGET(priv->scaler_option_button), FALSE);
2673  break;
2675  gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(priv->toolitem_dynres), FALSE);
2676  gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(priv->toolitem_scale), TRUE);
2677  gtk_widget_set_sensitive(GTK_WIDGET(priv->scaler_option_button), TRUE && cnnobj->connected);
2678  break;
2680  gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(priv->toolitem_dynres), TRUE);
2681  gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(priv->toolitem_scale), FALSE);
2682  gtk_widget_set_sensitive(GTK_WIDGET(priv->scaler_option_button), FALSE);
2683  break;
2684  }
2685 
2686  /* REMMINA_PROTOCOL_FEATURE_TYPE_MULTIMON */
2687  toolitem = priv->toolitem_multimon;
2688  if (toolitem) {
2689  gint hasmultimon = remmina_protocol_widget_query_feature_by_type(REMMINA_PROTOCOL_WIDGET(cnnobj->proto),
2691 
2692  gtk_widget_set_sensitive(GTK_WIDGET(toolitem), cnnobj->connected);
2693  gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(toolitem),
2694  remmina_file_get_int(cnnobj->remmina_file, "multimon", FALSE));
2695  gtk_widget_set_sensitive(GTK_WIDGET(toolitem), hasmultimon);
2696  }
2697 
2698  toolitem = priv->toolitem_grab;
2699  gtk_widget_set_sensitive(GTK_WIDGET(toolitem), cnnobj->connected);
2700  gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(toolitem),
2701  remmina_file_get_int(cnnobj->remmina_file, "keyboard_grab", FALSE));
2702  const gchar *protocol = remmina_file_get_string(cnnobj->remmina_file, "protocol");
2703  if (g_strcmp0(protocol, "SFTP") == 0 || g_strcmp0(protocol, "SSH") == 0) {
2704  gtk_widget_set_sensitive(GTK_WIDGET(toolitem), FALSE);
2705  gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(toolitem), FALSE);
2706  remmina_file_set_int(cnnobj->remmina_file, "keyboard_grab", FALSE);
2707  }
2708 
2709  toolitem = priv->toolitem_preferences;
2710  gtk_widget_set_sensitive(GTK_WIDGET(toolitem), cnnobj->connected);
2711  bval = remmina_protocol_widget_query_feature_by_type(REMMINA_PROTOCOL_WIDGET(cnnobj->proto),
2713  gtk_widget_set_sensitive(GTK_WIDGET(toolitem), bval && cnnobj->connected);
2714 
2715  toolitem = priv->toolitem_tools;
2716  bval = remmina_protocol_widget_query_feature_by_type(REMMINA_PROTOCOL_WIDGET(cnnobj->proto),
2718  gtk_widget_set_sensitive(GTK_WIDGET(toolitem), bval && cnnobj->connected);
2719 
2720  gtk_widget_set_sensitive(GTK_WIDGET(priv->toolitem_screenshot), cnnobj->connected);
2721 
2722  gtk_window_set_title(GTK_WINDOW(cnnobj->cnnwin), remmina_file_get_string(cnnobj->remmina_file, "name"));
2723 
2724  test_floating_toolbar = (priv->floating_toolbar_widget != NULL);
2725 
2726  if (test_floating_toolbar) {
2727  const gchar *str = remmina_file_get_string(cnnobj->remmina_file, "name");
2728  const gchar *format;
2729  GdkRGBA rgba;
2730  gchar *bg;
2731 
2732  bg = g_strdup(remmina_pref.grab_color);
2733  if (!gdk_rgba_parse(&rgba, bg)) {
2734  REMMINA_DEBUG("%s cannot be parsed as a color", bg);
2735  bg = g_strdup("#00FF00");
2736  } else {
2737  REMMINA_DEBUG("Using %s as background color", bg);
2738  }
2739 
2740  if (remmina_file_get_int(cnnobj->remmina_file, "keyboard_grab", FALSE)) {
2741  if (remmina_pref_get_boolean("grab_color_switch")) {
2742  gtk_widget_override_background_color(priv->overlay_ftb_fr, GTK_STATE_NORMAL, &rgba);
2743  format = g_strconcat("<span bgcolor=\"", bg, "\" size=\"large\"><b>(G: ON) - \%s</b></span>", NULL);
2744  } else {
2745  gtk_widget_override_background_color(priv->overlay_ftb_fr, GTK_STATE_NORMAL, NULL);
2746  format = "<big><b>(G: ON) - \%s</b></big>";
2747  }
2748  } else {
2749  gtk_widget_override_background_color(priv->overlay_ftb_fr, GTK_STATE_NORMAL, NULL);
2750  format = "<big><b>(G:OFF) - \%s</b></big>";
2751  }
2752  gchar *markup;
2753 
2754  markup = g_markup_printf_escaped(format, str);
2755  gtk_label_set_markup(GTK_LABEL(priv->floating_toolbar_label), markup);
2756  g_free(markup);
2757  g_free(bg);
2758  }
2759 
2760  priv->toolbar_is_reconfiguring = FALSE;
2761 }
2762 
2764 {
2765  TRACE_CALL(__func__);
2766  RemminaConnectionWindowPriv *priv = cnnwin->priv;
2767 
2768  if (priv->view_mode == SCROLLED_WINDOW_MODE) {
2769  if (remmina_pref.hide_connection_toolbar)
2770  gtk_widget_hide(priv->toolbar);
2771  else
2772  gtk_widget_show(priv->toolbar);
2773  }
2774 }
2775 
2776 #if DEBUG_KB_GRABBING
2777 static void print_crossing_event(GdkEventCrossing *event) {
2778  printf("DEBUG_KB_GRABBING: --- Crossing event detail: ");
2779  switch (event->detail) {
2780  case GDK_NOTIFY_ANCESTOR: printf("GDK_NOTIFY_ANCESTOR"); break;
2781  case GDK_NOTIFY_VIRTUAL: printf("GDK_NOTIFY_VIRTUAL"); break;
2782  case GDK_NOTIFY_NONLINEAR: printf("GDK_NOTIFY_NONLINEAR"); break;
2783  case GDK_NOTIFY_NONLINEAR_VIRTUAL: printf("GDK_NOTIFY_NONLINEAR_VIRTUAL"); break;
2784  case GDK_NOTIFY_UNKNOWN: printf("GDK_NOTIFY_UNKNOWN"); break;
2785  case GDK_NOTIFY_INFERIOR: printf("GDK_NOTIFY_INFERIOR"); break;
2786  default: printf("unknown");
2787  }
2788  printf("\n");
2789  printf("DEBUG_KB_GRABBING: --- Crossing event mode=");
2790  switch (event->mode) {
2791  case GDK_CROSSING_NORMAL: printf("GDK_CROSSING_NORMAL"); break;
2792  case GDK_CROSSING_GRAB: printf("GDK_CROSSING_GRAB"); break;
2793  case GDK_CROSSING_UNGRAB: printf("GDK_CROSSING_UNGRAB"); break;
2794  case GDK_CROSSING_GTK_GRAB: printf("GDK_CROSSING_GTK_GRAB"); break;
2795  case GDK_CROSSING_GTK_UNGRAB: printf("GDK_CROSSING_GTK_UNGRAB"); break;
2796  case GDK_CROSSING_STATE_CHANGED: printf("GDK_CROSSING_STATE_CHANGED"); break;
2797  case GDK_CROSSING_TOUCH_BEGIN: printf("GDK_CROSSING_TOUCH_BEGIN"); break;
2798  case GDK_CROSSING_TOUCH_END: printf("GDK_CROSSING_TOUCH_END"); break;
2799  case GDK_CROSSING_DEVICE_SWITCH: printf("GDK_CROSSING_DEVICE_SWITCH"); break;
2800  default: printf("unknown");
2801  }
2802  printf("\n");
2803 }
2804 #endif
2805 
2806 static gboolean rcw_floating_toolbar_on_enter(GtkWidget *widget, GdkEventCrossing *event,
2807  RemminaConnectionWindow *cnnwin)
2808 {
2809  TRACE_CALL(__func__);
2810  rcw_floating_toolbar_show(cnnwin, TRUE);
2811  return TRUE;
2812 }
2813 
2814 static gboolean rcw_floating_toolbar_on_leave(GtkWidget *widget, GdkEventCrossing *event,
2815  RemminaConnectionWindow *cnnwin)
2816 {
2817  TRACE_CALL(__func__);
2818  if (event->detail != GDK_NOTIFY_INFERIOR)
2819  rcw_floating_toolbar_show(cnnwin, FALSE);
2820  return TRUE;
2821 }
2822 
2823 
2824 static gboolean rcw_on_enter_notify_event(GtkWidget *widget, GdkEventCrossing *event,
2825  gpointer user_data)
2826 {
2827  TRACE_CALL(__func__);
2828 #if DEBUG_KB_GRABBING
2829  printf("DEBUG_KB_GRABBING: enter-notify-event on rcw received\n");
2830  print_crossing_event(event);
2831 #endif
2832  return FALSE;
2833 }
2834 
2835 
2836 
2837 static gboolean rcw_on_leave_notify_event(GtkWidget *widget, GdkEventCrossing *event,
2838  gpointer user_data)
2839 {
2840  TRACE_CALL(__func__);
2842 
2843 #if DEBUG_KB_GRABBING
2844  printf("DEBUG_KB_GRABBING: leave-notify-event on rcw received\n");
2845  print_crossing_event(event);
2846 #endif
2847 
2848  if (event->mode != GDK_CROSSING_NORMAL && event->mode != GDK_CROSSING_UNGRAB) {
2849 #if DEBUG_KB_GRABBING
2850  printf("DEBUG_KB_GRABBING: ignored because mode is not GDK_CROSSING_NORMAL GDK_CROSSING_UNGRAB\n");
2851 #endif
2852  return FALSE;
2853  }
2854 
2855  if (cnnwin->priv->delayed_grab_eventsourceid) {
2856  g_source_remove(cnnwin->priv->delayed_grab_eventsourceid);
2857  cnnwin->priv->delayed_grab_eventsourceid = 0;
2858  }
2859 
2860  /* Workaround for https://gitlab.gnome.org/GNOME/mutter/-/issues/2450#note_1586570 */
2861  if (event->mode != GDK_CROSSING_UNGRAB) {
2862  rcw_kp_ungrab(cnnwin);
2863  rcw_pointer_ungrab(cnnwin);
2864  } else {
2865 #if DEBUG_KB_GRABBING
2866  printf("DEBUG_KB_GRABBING: not ungrabbing, this event seems to be an unwanted event from GTK\n");
2867 #endif
2868  }
2869 
2870  return FALSE;
2871 }
2872 
2873 
2874 static gboolean rco_leave_protocol_widget(GtkWidget *widget, GdkEventCrossing *event,
2875  RemminaConnectionObject *cnnobj)
2876 {
2877  TRACE_CALL(__func__);
2878 
2879 #if DEBUG_KB_GRABBING
2880  printf("DEBUG_KB_GRABBING: received leave event on RCO.\n");
2881  print_crossing_event(event);
2882 #endif
2883 
2884  if (cnnobj->cnnwin->priv->delayed_grab_eventsourceid) {
2885  g_source_remove(cnnobj->cnnwin->priv->delayed_grab_eventsourceid);
2886  cnnobj->cnnwin->priv->delayed_grab_eventsourceid = 0;
2887  }
2888 
2889  cnnobj->cnnwin->priv->pointer_entered = FALSE;
2890 
2891  /* Ungrab only if the leave is due to normal mouse motion and not to an inferior */
2892  if (event->mode == GDK_CROSSING_NORMAL && event->detail != GDK_NOTIFY_INFERIOR)
2893  rcw_kp_ungrab(cnnobj->cnnwin);
2894 
2895  return FALSE;
2896 }
2897 
2898 
2899 gboolean rco_enter_protocol_widget(GtkWidget *widget, GdkEventCrossing *event,
2900  RemminaConnectionObject *cnnobj)
2901 {
2902  TRACE_CALL(__func__);
2903  gboolean active;
2904 
2905 #if DEBUG_KB_GRABBING
2906  printf("DEBUG_KB_GRABBING: %s: enter on protocol widget event received\n", __func__);
2907  print_crossing_event(event);
2908 #endif
2909 
2910  RemminaConnectionWindowPriv *priv = cnnobj->cnnwin->priv;
2911  if (!priv->sticky && event->mode == GDK_CROSSING_NORMAL)
2912  rcw_floating_toolbar_show(cnnobj->cnnwin, FALSE);
2913 
2914  priv->pointer_entered = TRUE;
2915 
2916  if (event->mode == GDK_CROSSING_UNGRAB) {
2917  // Someone steal our grab, take note and do not attempt to regrab
2918  cnnobj->cnnwin->priv->kbcaptured = FALSE;
2919  cnnobj->cnnwin->priv->pointer_captured = FALSE;
2920  return FALSE;
2921  }
2922 
2923  /* Check if we need grabbing */
2924  active = gtk_window_is_active(GTK_WINDOW(cnnobj->cnnwin));
2925  if (remmina_file_get_int(cnnobj->remmina_file, "keyboard_grab", FALSE) && active) {
2926  rcw_keyboard_grab(cnnobj->cnnwin);
2927  rcw_pointer_grab(cnnobj->cnnwin);
2928  }
2929 
2930  return FALSE;
2931 }
2932 
2934 {
2935  TRACE_CALL(__func__);
2936 
2937 #if DEBUG_KB_GRABBING
2938  printf("DEBUG_KB_GRABBING: %s\n", __func__);
2939 #endif
2940  if (cnnwin->priv->pointer_entered) {
2941 #if DEBUG_KB_GRABBING
2942  printf("DEBUG_KB_GRABBING: delayed requesting kb and pointer grab, because of pointer inside\n");
2943 #endif
2944  rcw_keyboard_grab(cnnwin);
2945  rcw_pointer_grab(cnnwin);
2946  }
2947 #if DEBUG_KB_GRABBING
2948  else {
2949  printf("DEBUG_KB_GRABBING: %s not grabbing because pointer_entered is false\n", __func__);
2950  }
2951 #endif
2952  cnnwin->priv->delayed_grab_eventsourceid = 0;
2953  return G_SOURCE_REMOVE;
2954 }
2955 
2957 {
2958  /* This function is the default signal handler for focus-in-event,
2959  * but can also be called after a window focus state change event
2960  * from rcw_state_event(). So expect to be called twice
2961  * when cnnwin gains the focus */
2962 
2963  TRACE_CALL(__func__);
2964  RemminaConnectionObject *cnnobj;
2965 
2966  if (!(cnnobj = rcw_get_visible_cnnobj(cnnwin))) return;
2967 
2968  if (cnnobj && cnnobj->connected && remmina_file_get_int(cnnobj->remmina_file, "keyboard_grab", FALSE)) {
2969 #if DEBUG_KB_GRABBING
2970  printf("DEBUG_KB_GRABBING: Received focus in on rcw, grabbing enabled: requesting kb grab, delayed\n");
2971 #endif
2972  if (cnnwin->priv->delayed_grab_eventsourceid == 0)
2973  cnnwin->priv->delayed_grab_eventsourceid = g_timeout_add(300, (GSourceFunc)focus_in_delayed_grab, cnnwin);
2974  }
2975 #if DEBUG_KB_GRABBING
2976  else {
2977  printf("DEBUG_KB_GRABBING: Received focus in on rcw, but a condition will prevent to grab\n");
2978  }
2979 #endif
2980 }
2981 
2983 {
2984  /* This function is the default signal handler for focus-out-event,
2985  * but can also be called after a window focus state change event
2986  * from rcw_state_event(). So expect to be called twice
2987  * when cnnwin loses the focus */
2988 
2989  TRACE_CALL(__func__);
2990  RemminaConnectionObject *cnnobj;
2991 
2992  rcw_kp_ungrab(cnnwin);
2993 
2994  cnnwin->priv->hostkey_activated = FALSE;
2995  if (!(cnnobj = rcw_get_visible_cnnobj(cnnwin))) return;
2996 
2997  if (REMMINA_IS_SCROLLED_VIEWPORT(cnnobj->scrolled_container))
2998  remmina_scrolled_viewport_remove_motion(REMMINA_SCROLLED_VIEWPORT(cnnobj->scrolled_container));
2999 
3000  if (cnnobj->proto && cnnobj->scrolled_container)
3001  remmina_protocol_widget_call_feature_by_type(REMMINA_PROTOCOL_WIDGET(cnnobj->proto),
3003 }
3004 
3005 static gboolean
3007 {
3008  TRACE_CALL(__func__);
3009  RemminaConnectionWindowPriv *priv = cnnwin->priv;
3010 
3011  priv->hidetb_eventsource = 0;
3012  rcw_floating_toolbar_show(cnnwin, FALSE);
3013  return G_SOURCE_REMOVE;
3014 }
3015 
3016 static gboolean rcw_floating_toolbar_on_scroll(GtkWidget *widget, GdkEventScroll *event,
3017  RemminaConnectionWindow *cnnwin)
3018 {
3019  TRACE_CALL(__func__);
3020  RemminaConnectionObject *cnnobj;
3021 
3022  int opacity;
3023 
3024  cnnobj = rcw_get_visible_cnnobj(cnnwin);
3025  if (!cnnobj)
3026  return TRUE;
3027 
3028  opacity = remmina_file_get_int(cnnobj->remmina_file, "toolbar_opacity", 0);
3029  switch (event->direction) {
3030  case GDK_SCROLL_UP:
3031  if (opacity > 0) {
3032  remmina_file_set_int(cnnobj->remmina_file, "toolbar_opacity", opacity - 1);
3034  return TRUE;
3035  }
3036  break;
3037  case GDK_SCROLL_DOWN:
3038  if (opacity < TOOLBAR_OPACITY_LEVEL) {
3039  remmina_file_set_int(cnnobj->remmina_file, "toolbar_opacity", opacity + 1);
3041  return TRUE;
3042  }
3043  break;
3044 #if GTK_CHECK_VERSION(3, 4, 0)
3045  case GDK_SCROLL_SMOOTH:
3046  if (event->delta_y < 0 && opacity > 0) {
3047  remmina_file_set_int(cnnobj->remmina_file, "toolbar_opacity", opacity - 1);
3049  return TRUE;
3050  }
3051  if (event->delta_y > 0 && opacity < TOOLBAR_OPACITY_LEVEL) {
3052  remmina_file_set_int(cnnobj->remmina_file, "toolbar_opacity", opacity + 1);
3054  return TRUE;
3055  }
3056  break;
3057 #endif
3058  default:
3059  break;
3060  }
3061  return TRUE;
3062 }
3063 
3064 static gboolean rcw_after_configure_scrolled(gpointer user_data)
3065 {
3066  TRACE_CALL(__func__);
3067  gint width, height;
3068  GdkWindowState s;
3069  gint ipg, npages;
3070  RemminaConnectionWindow *cnnwin;
3071 
3072  cnnwin = (RemminaConnectionWindow *)user_data;
3073 
3074  if (!cnnwin || !cnnwin->priv)
3075  return FALSE;
3076 
3077  s = gdk_window_get_state(gtk_widget_get_window(GTK_WIDGET(cnnwin)));
3078 
3079 
3080  /* Changed window_maximize, window_width and window_height for all
3081  * connections inside the notebook */
3082  npages = gtk_notebook_get_n_pages(GTK_NOTEBOOK(cnnwin->priv->notebook));
3083  for (ipg = 0; ipg < npages; ipg++) {
3084  RemminaConnectionObject *cnnobj;
3085  cnnobj = g_object_get_data(
3086  G_OBJECT(gtk_notebook_get_nth_page(GTK_NOTEBOOK(cnnwin->priv->notebook), ipg)),
3087  "cnnobj");
3088  if (s & GDK_WINDOW_STATE_MAXIMIZED) {
3089  remmina_file_set_int(cnnobj->remmina_file, "window_maximize", TRUE);
3090  } else {
3091  gtk_window_get_size(GTK_WINDOW(cnnobj->cnnwin), &width, &height);
3092  remmina_file_set_int(cnnobj->remmina_file, "window_width", width);
3093  remmina_file_set_int(cnnobj->remmina_file, "window_height", height);
3094  remmina_file_set_int(cnnobj->remmina_file, "window_maximize", FALSE);
3095  }
3096  }
3097  cnnwin->priv->acs_eventsourceid = 0;
3098  return FALSE;
3099 }
3100 
3101 static gboolean rcw_on_configure(GtkWidget *widget, GdkEventConfigure *event,
3102  gpointer data)
3103 {
3104  TRACE_CALL(__func__);
3105  RemminaConnectionWindow *cnnwin;
3106  RemminaConnectionObject *cnnobj;
3107 
3108  if (!REMMINA_IS_CONNECTION_WINDOW(widget))
3109  return FALSE;
3110 
3111  cnnwin = (RemminaConnectionWindow *)widget;
3112 
3113  if (!(cnnobj = rcw_get_visible_cnnobj(cnnwin))) return FALSE;
3114 
3115  if (cnnwin->priv->acs_eventsourceid) {
3116  g_source_remove(cnnwin->priv->acs_eventsourceid);
3117  cnnwin->priv->acs_eventsourceid = 0;
3118  }
3119 
3120  if (gtk_widget_get_window(GTK_WIDGET(cnnwin))
3121  && cnnwin->priv->view_mode == SCROLLED_WINDOW_MODE)
3122  /* Under GNOME Shell we receive this configure_event BEFORE a window
3123  * is really unmaximized, so we must read its new state and dimensions
3124  * later, not now */
3125  cnnwin->priv->acs_eventsourceid = g_timeout_add(500, rcw_after_configure_scrolled, cnnwin);
3126 
3127  if (cnnwin->priv->view_mode != SCROLLED_WINDOW_MODE)
3128  /* Notify window of change so that scroll border can be hidden or shown if needed */
3129  rco_check_resize(cnnobj);
3130  return FALSE;
3131 }
3132 
3134 {
3135  TRACE_CALL(__func__);
3136  if (cnnwin->priv->pin_down)
3137  gtk_button_set_image(GTK_BUTTON(cnnwin->priv->pin_button),
3138  gtk_image_new_from_icon_name("org.remmina.Remmina-pin-down-symbolic", GTK_ICON_SIZE_MENU));
3139  else
3140  gtk_button_set_image(GTK_BUTTON(cnnwin->priv->pin_button),
3141  gtk_image_new_from_icon_name("org.remmina.Remmina-pin-up-symbolic", GTK_ICON_SIZE_MENU));
3142 }
3143 
3144 static void rcw_toolbar_pin(GtkWidget *widget, RemminaConnectionWindow *cnnwin)
3145 {
3146  TRACE_CALL(__func__);
3147  remmina_pref.toolbar_pin_down = cnnwin->priv->pin_down = !cnnwin->priv->pin_down;
3149  rcw_update_pin(cnnwin);
3150 }
3151 
3153 {
3154  TRACE_CALL(__func__);
3155 
3156  RemminaConnectionWindowPriv *priv = cnnwin->priv;
3157  GtkWidget *ftb_widget;
3158  GtkWidget *vbox;
3159  GtkWidget *hbox;
3160  GtkWidget *label;
3161  GtkWidget *pinbutton;
3162  GtkWidget *tb;
3163 
3164 
3165  /* A widget to be used for GtkOverlay for GTK >= 3.10 */
3166  ftb_widget = gtk_event_box_new();
3167 
3168  vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
3169  gtk_widget_show(vbox);
3170 
3171  gtk_container_add(GTK_CONTAINER(ftb_widget), vbox);
3172 
3173  tb = rcw_create_toolbar(cnnwin, mode);
3174  hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
3175  gtk_widget_show(hbox);
3176 
3177 
3178  /* The pin button */
3179  pinbutton = gtk_button_new();
3180  gtk_widget_show(pinbutton);
3181  gtk_box_pack_start(GTK_BOX(hbox), pinbutton, FALSE, FALSE, 0);
3182  gtk_button_set_relief(GTK_BUTTON(pinbutton), GTK_RELIEF_NONE);
3183 #if GTK_CHECK_VERSION(3, 20, 0)
3184  gtk_widget_set_focus_on_click(GTK_WIDGET(pinbutton), FALSE);
3185 #else
3186  gtk_button_set_focus_on_click(GTK_BUTTON(pinbutton), FALSE);
3187 #endif
3188  gtk_widget_set_name(pinbutton, "remmina-pin-button");
3189  g_signal_connect(G_OBJECT(pinbutton), "clicked", G_CALLBACK(rcw_toolbar_pin), cnnwin);
3190  priv->pin_button = pinbutton;
3191  priv->pin_down = remmina_pref.toolbar_pin_down;
3192  rcw_update_pin(cnnwin);
3193 
3194 
3195  label = gtk_label_new("");
3196  gtk_label_set_max_width_chars(GTK_LABEL(label), 50);
3197  gtk_widget_show(label);
3198 
3199  gtk_box_pack_start(GTK_BOX(hbox), label, TRUE, TRUE, 0);
3200 
3201  priv->floating_toolbar_label = label;
3202 
3204  gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
3205  gtk_box_pack_start(GTK_BOX(vbox), tb, FALSE, FALSE, 0);
3206  } else {
3207  gtk_box_pack_start(GTK_BOX(vbox), tb, FALSE, FALSE, 0);
3208  gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
3209  }
3210 
3211  priv->floating_toolbar_widget = ftb_widget;
3212  gtk_widget_show(ftb_widget);
3213 }
3214 
3215 static void rcw_toolbar_place_signal(RemminaConnectionWindow *cnnwin, gpointer data)
3216 {
3217  TRACE_CALL(__func__);
3219 
3220  priv = cnnwin->priv;
3221  /* Detach old toolbar widget and reattach in new position in the grid */
3222  if (priv->toolbar && priv->grid) {
3223  g_object_ref(priv->toolbar);
3224  gtk_container_remove(GTK_CONTAINER(priv->grid), priv->toolbar);
3225  rcw_place_toolbar(GTK_TOOLBAR(priv->toolbar), GTK_GRID(priv->grid), GTK_WIDGET(priv->notebook), remmina_pref.toolbar_placement);
3226  g_object_unref(priv->toolbar);
3227  }
3228 }
3229 
3230 
3231 static void rcw_init(RemminaConnectionWindow *cnnwin)
3232 {
3233  TRACE_CALL(__func__);
3235 
3236  priv = g_new0(RemminaConnectionWindowPriv, 1);
3237  cnnwin->priv = priv;
3238 
3239  priv->view_mode = SCROLLED_WINDOW_MODE;
3240  if (kioskmode && kioskmode == TRUE)
3241  priv->view_mode = VIEWPORT_FULLSCREEN_MODE;
3242 
3243  priv->floating_toolbar_opacity = 1.0;
3244  priv->kbcaptured = FALSE;
3245  priv->pointer_captured = FALSE;
3246  priv->pointer_entered = FALSE;
3247  priv->fss_view_mode = VIEWPORT_FULLSCREEN_MODE;
3248  priv->ss_width = 640;
3249  priv->ss_height = 480;
3250  priv->ss_maximized = FALSE;
3251 
3252  remmina_widget_pool_register(GTK_WIDGET(cnnwin));
3253 }
3254 
3255 static gboolean rcw_focus_in_event(GtkWidget *widget, GdkEventWindowState *event, gpointer user_data)
3256 {
3257  TRACE_CALL(__func__);
3258 #if DEBUG_KB_GRABBING
3259  printf("DEBUG_KB_GRABBING: RCW focus-in-event received\n");
3260 #endif
3262  return FALSE;
3263 }
3264 
3265 static gboolean rcw_focus_out_event(GtkWidget *widget, GdkEventWindowState *event, gpointer user_data)
3266 {
3267  TRACE_CALL(__func__);
3268 #if DEBUG_KB_GRABBING
3269  printf("DEBUG_KB_GRABBING: RCW focus-out-event received\n");
3270 #endif
3272  return FALSE;
3273 }
3274 
3275 
3276 static gboolean rcw_state_event(GtkWidget *widget, GdkEventWindowState *event, gpointer user_data)
3277 {
3278  TRACE_CALL(__func__);
3279 
3280  if (!REMMINA_IS_CONNECTION_WINDOW(widget))
3281  return FALSE;
3282 
3283 #if DEBUG_KB_GRABBING
3284  printf("DEBUG_KB_GRABBING: window-state-event received\n");
3285 #endif
3286 
3287  if (event->changed_mask & GDK_WINDOW_STATE_FOCUSED) {
3288  if (event->new_window_state & GDK_WINDOW_STATE_FOCUSED)
3290  else
3292  }
3293 
3294  return FALSE;
3295 }
3296 
3297 static gboolean rcw_map_event(GtkWidget *widget, GdkEvent *event, gpointer data)
3298 {
3299  TRACE_CALL(__func__);
3300 
3301 
3302 
3304  RemminaConnectionObject *cnnobj;
3306 
3307  if (cnnwin->priv->toolbar_is_reconfiguring) return FALSE;
3308  if (!(cnnobj = rcw_get_visible_cnnobj(cnnwin))) return FALSE;
3309 
3310  gp = REMMINA_PROTOCOL_WIDGET(cnnobj->proto);
3311  REMMINA_DEBUG("Mapping: %s", gtk_widget_get_name(widget));
3313  REMMINA_DEBUG("Called plugin mapping function");
3314  return FALSE;
3315 }
3316 
3317 static gboolean rcw_unmap_event(GtkWidget *widget, GdkEvent *event, gpointer data)
3318 {
3319  TRACE_CALL(__func__);
3320 
3322  RemminaConnectionObject *cnnobj;
3324 
3325  if (cnnwin->priv->toolbar_is_reconfiguring) return FALSE;
3326  if (!(cnnobj = rcw_get_visible_cnnobj(cnnwin))) return FALSE;
3327 
3328  gp = REMMINA_PROTOCOL_WIDGET(cnnobj->proto);
3329  REMMINA_DEBUG("Unmapping: %s", gtk_widget_get_name(widget));
3331  REMMINA_DEBUG("Called plugin mapping function");
3332  return FALSE;
3333 }
3334 
3335 static gboolean rcw_map_event_fullscreen(GtkWidget *widget, GdkEvent *event, gpointer data)
3336 {
3337  TRACE_CALL(__func__);
3338  RemminaConnectionObject *cnnobj;
3339  gint target_monitor;
3340 
3341  REMMINA_DEBUG("Mapping: %s", gtk_widget_get_name(widget));
3342 
3343  if (!REMMINA_IS_CONNECTION_WINDOW(widget)) {
3344  REMMINA_DEBUG("Remmina Connection Window undefined, cannot go fullscreen");
3345  return FALSE;
3346  }
3347 
3348  //RemminaConnectionWindow *cnnwin = (RemminaConnectionWindow *)data;
3349  cnnobj = rcw_get_visible_cnnobj((RemminaConnectionWindow *)widget);
3350  //cnnobj = g_object_get_data(G_OBJECT(widget), "cnnobj");
3351  if (!cnnobj) {
3352  REMMINA_DEBUG("Remmina Connection Object undefined, cannot go fullscreen");
3353  return FALSE;
3354  }
3355 
3356  RemminaProtocolWidget *gp = REMMINA_PROTOCOL_WIDGET(cnnobj->proto);
3357 
3358  if (!gp)
3359  REMMINA_DEBUG("Remmina Protocol Widget undefined, cannot go fullscreen");
3360 
3361  if (remmina_protocol_widget_get_multimon(gp) >= 1) {
3362  REMMINA_DEBUG("Fullscreen on all monitor");
3363  gdk_window_set_fullscreen_mode(gtk_widget_get_window(widget), GDK_FULLSCREEN_ON_ALL_MONITORS);
3364  gdk_window_fullscreen(gtk_widget_get_window(widget));
3365  return TRUE;
3366  } else {
3367  REMMINA_DEBUG("Fullscreen on one monitor");
3368  }
3369 
3370  target_monitor = GPOINTER_TO_INT(data);
3371 
3372 #if GTK_CHECK_VERSION(3, 18, 0)
3373  if (remmina_pref.fullscreen_on_auto) {
3374  if (target_monitor == FULL_SCREEN_TARGET_MONITOR_UNDEFINED)
3375  gtk_window_fullscreen(GTK_WINDOW(widget));
3376  else
3377  gtk_window_fullscreen_on_monitor(GTK_WINDOW(widget), gtk_window_get_screen(GTK_WINDOW(widget)),
3378  target_monitor);
3379  } else {
3380  REMMINA_DEBUG("Fullscreen managed by WM or by the user, as per settings");
3381  gtk_window_fullscreen(GTK_WINDOW(widget));
3382  }
3383 #else
3384  REMMINA_DEBUG("Cannot fullscreen on a specific monitor, feature available from GTK 3.18");
3385  gtk_window_fullscreen(GTK_WINDOW(widget));
3386 #endif
3387 
3389  REMMINA_DEBUG("Called plugin mapping function");
3390 
3391  return FALSE;
3392 }
3393 
3394 static RemminaConnectionWindow *
3395 rcw_new(gboolean fullscreen, int full_screen_target_monitor)
3396 {
3397  TRACE_CALL(__func__);
3398  RemminaConnectionWindow *cnnwin;
3399 
3400  cnnwin = RCW(g_object_new(REMMINA_TYPE_CONNECTION_WINDOW, NULL));
3401  cnnwin->priv->on_delete_confirm_mode = RCW_ONDELETE_CONFIRM_IF_2_OR_MORE;
3402 
3403  if (fullscreen)
3404  /* Put the window in fullscreen after it is mapped to have it appear on the same monitor */
3405  g_signal_connect(G_OBJECT(cnnwin), "map-event", G_CALLBACK(rcw_map_event_fullscreen), GINT_TO_POINTER(full_screen_target_monitor));
3406  else
3407  g_signal_connect(G_OBJECT(cnnwin), "map-event", G_CALLBACK(rcw_map_event), NULL);
3408  g_signal_connect(G_OBJECT(cnnwin), "unmap-event", G_CALLBACK(rcw_unmap_event), NULL);
3409 
3410  gtk_container_set_border_width(GTK_CONTAINER(cnnwin), 0);
3411  g_signal_connect(G_OBJECT(cnnwin), "toolbar-place", G_CALLBACK(rcw_toolbar_place_signal), NULL);
3412 
3413  g_signal_connect(G_OBJECT(cnnwin), "delete-event", G_CALLBACK(rcw_delete_event), NULL);
3414  g_signal_connect(G_OBJECT(cnnwin), "destroy", G_CALLBACK(rcw_destroy), NULL);
3415 
3416  /* Under Xorg focus-in-event and focus-out-event don’t work when keyboard is grabbed
3417  * via gdk_device_grab. So we listen for window-state-event to detect focus in and focus out.
3418  * But we must also listen focus-in-event and focus-out-event because some
3419  * window managers missing _NET_WM_STATE_FOCUSED hint, does not update the window state
3420  * in case of focus change */
3421  g_signal_connect(G_OBJECT(cnnwin), "window-state-event", G_CALLBACK(rcw_state_event), NULL);
3422  g_signal_connect(G_OBJECT(cnnwin), "focus-in-event", G_CALLBACK(rcw_focus_in_event), NULL);
3423  g_signal_connect(G_OBJECT(cnnwin), "focus-out-event", G_CALLBACK(rcw_focus_out_event), NULL);
3424 
3425  g_signal_connect(G_OBJECT(cnnwin), "enter-notify-event", G_CALLBACK(rcw_on_enter_notify_event), NULL);
3426  g_signal_connect(G_OBJECT(cnnwin), "leave-notify-event", G_CALLBACK(rcw_on_leave_notify_event), NULL);
3427 
3428 
3429  g_signal_connect(G_OBJECT(cnnwin), "configure_event", G_CALLBACK(rcw_on_configure), NULL);
3430 
3431  return cnnwin;
3432 }
3433 
3434 /* This function will be called for the first connection. A tag is set to the window so that
3435  * other connections can determine if whether a new tab should be append to the same window
3436  */
3438 {
3439  TRACE_CALL(__func__);
3440  gchar *tag;
3441 
3442  switch (remmina_pref.tab_mode) {
3443  case REMMINA_TAB_BY_GROUP:
3444  tag = g_strdup(remmina_file_get_string(cnnobj->remmina_file, "group"));
3445  break;
3447  tag = g_strdup(remmina_file_get_string(cnnobj->remmina_file, "protocol"));
3448  break;
3449  default:
3450  tag = NULL;
3451  break;
3452  }
3453  g_object_set_data_full(G_OBJECT(cnnwin), "tag", tag, (GDestroyNotify)g_free);
3454 }
3455 
3457 {
3458  TRACE_CALL(__func__);
3459  RemminaConnectionObject *cnnobj;
3460 
3461  if (!(cnnobj = rcw_get_visible_cnnobj(cnnwin))) return;
3462 
3463  if (GTK_IS_WIDGET(cnnobj->proto))
3464  remmina_protocol_widget_grab_focus(REMMINA_PROTOCOL_WIDGET(cnnobj->proto));
3465 }
3466 
3467 static GtkWidget *nb_find_page_by_cnnobj(GtkNotebook *notebook, RemminaConnectionObject *cnnobj)
3468 {
3469  gint i, np;
3470  GtkWidget *found_page, *pg;
3471 
3472  if (cnnobj == NULL || cnnobj->cnnwin == NULL || cnnobj->cnnwin->priv == NULL)
3473  return NULL;
3474  found_page = NULL;
3475  np = gtk_notebook_get_n_pages(cnnobj->cnnwin->priv->notebook);
3476  for (i = 0; i < np; i++) {
3477  pg = gtk_notebook_get_nth_page(cnnobj->cnnwin->priv->notebook, i);
3478  if (g_object_get_data(G_OBJECT(pg), "cnnobj") == cnnobj) {
3479  found_page = pg;
3480  break;
3481  }
3482  }
3483 
3484  return found_page;
3485 }
3486 
3487 
3489 {
3490  TRACE_CALL(__func__);
3491  RemminaConnectionObject *cnnobj = gp->cnnobj;
3492  GtkWidget *page_to_remove;
3493 
3494 
3495  if (cnnobj && cnnobj->scrolled_container && REMMINA_IS_SCROLLED_VIEWPORT(cnnobj->scrolled_container)) {
3496  REMMINA_DEBUG("deleting motion");
3497  remmina_scrolled_viewport_remove_motion(REMMINA_SCROLLED_VIEWPORT(cnnobj->scrolled_container));
3498  }
3499 
3500  if (cnnobj && cnnobj->cnnwin) {
3501  page_to_remove = nb_find_page_by_cnnobj(cnnobj->cnnwin->priv->notebook, cnnobj);
3502  if (page_to_remove) {
3503  gtk_notebook_remove_page(
3504  cnnobj->cnnwin->priv->notebook,
3505  gtk_notebook_page_num(cnnobj->cnnwin->priv->notebook, page_to_remove));
3506  /* Invalidate pointers to objects destroyed by page removal */
3507  cnnobj->aspectframe = NULL;
3508  cnnobj->viewport = NULL;
3509  cnnobj->scrolled_container = NULL;
3510  /* we cannot invalidate cnnobj->proto, because it can be already been
3511  * detached from the widget hierarchy in rco_on_disconnect() */
3512  }
3513  }
3514  if (cnnobj) {
3515  cnnobj->remmina_file = NULL;
3516  g_free(cnnobj);
3517  gp->cnnobj = NULL;
3518  }
3519 
3521 }
3522 
3524 {
3525  TRACE_CALL(__func__);
3526  if (REMMINA_IS_PROTOCOL_WIDGET(cnnobj->proto)) {
3528  remmina_protocol_widget_close_connection(REMMINA_PROTOCOL_WIDGET(cnnobj->proto));
3529  else
3531  }
3532 }
3533 
3535 {
3536  TRACE_CALL(__func__);
3537  GtkWidget *hbox;
3538  GtkWidget *widget;
3539  GtkWidget *button;
3540 
3541  hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 4);
3542  gtk_widget_show(hbox);
3543 
3544  widget = gtk_image_new_from_icon_name(remmina_file_get_icon_name(cnnobj->remmina_file), GTK_ICON_SIZE_MENU);
3545  gtk_widget_show(widget);
3546  gtk_box_pack_start(GTK_BOX(hbox), widget, FALSE, FALSE, 0);
3547 
3548  widget = gtk_label_new(remmina_file_get_string(cnnobj->remmina_file, "name"));
3549  gtk_widget_set_valign(widget, GTK_ALIGN_CENTER);
3550  gtk_widget_set_halign(widget, GTK_ALIGN_CENTER);
3551 
3552  gtk_widget_show(widget);
3553  gtk_box_pack_start(GTK_BOX(hbox), widget, TRUE, TRUE, 0);
3554 
3555  button = gtk_button_new(); // The "x" to close the tab
3556  gtk_button_set_relief(GTK_BUTTON(button), GTK_RELIEF_NONE);
3557 #if GTK_CHECK_VERSION(3, 20, 0)
3558  gtk_widget_set_focus_on_click(GTK_WIDGET(widget), FALSE);
3559 #else
3560  gtk_button_set_focus_on_click(GTK_BUTTON(button), FALSE);
3561 #endif
3562  gtk_widget_set_name(button, "remmina-small-button");
3563  gtk_widget_show(button);
3564 
3565  widget = gtk_image_new_from_icon_name("window-close", GTK_ICON_SIZE_MENU);
3566  gtk_widget_show(widget);
3567  gtk_container_add(GTK_CONTAINER(button), widget);
3568 
3569  gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
3570 
3571  g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(rco_on_close_button_clicked), cnnobj);
3572 
3573 
3574  return hbox;
3575 }
3576 
3578 {
3579  GtkWidget *page;
3580 
3581  page = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
3582  gtk_widget_set_name(page, "remmina-tab-page");
3583 
3584 
3585  return page;
3586 }
3587 
3589 {
3590  TRACE_CALL(__func__);
3591  GtkWidget *page, *label;
3592  GtkNotebook *notebook;
3593 
3594  notebook = cnnwin->priv->notebook;
3595 
3596  page = rco_create_tab_page(cnnobj);
3597  g_object_set_data(G_OBJECT(page), "cnnobj", cnnobj);
3598  label = rco_create_tab_label(cnnobj);
3599 
3600  cnnobj->cnnwin = cnnwin;
3601 
3602  gtk_notebook_append_page(notebook, page, label);
3603  gtk_notebook_set_tab_reorderable(notebook, page, TRUE);
3604  gtk_notebook_set_tab_detachable(notebook, page, TRUE);
3605  /* This trick prevents the tab label from being focused */
3606  gtk_widget_set_can_focus(gtk_widget_get_parent(label), FALSE);
3607 
3608  if (gtk_widget_get_parent(cnnobj->scrolled_container) != NULL)
3609  printf("REMMINA WARNING in %s: scrolled_container already has a parent\n", __func__);
3610  gtk_box_pack_start(GTK_BOX(page), cnnobj->scrolled_container, TRUE, TRUE, 0);
3611 
3612  gtk_widget_show(page);
3613 
3614  return page;
3615 }
3616 
3617 
3619 {
3620  TRACE_CALL(__func__);
3621  GtkNotebook *notebook;
3622  gint n;
3623 
3624  notebook = GTK_NOTEBOOK(cnnwin->priv->notebook);
3625 
3626  switch (cnnwin->priv->view_mode) {
3627  case SCROLLED_WINDOW_MODE:
3628  n = gtk_notebook_get_n_pages(notebook);
3629  gtk_notebook_set_show_tabs(notebook, remmina_pref.always_show_tab ? TRUE : n > 1);
3630  gtk_notebook_set_show_border(notebook, remmina_pref.always_show_tab ? TRUE : n > 1);
3631  break;
3632  default:
3633  gtk_notebook_set_show_tabs(notebook, FALSE);
3634  gtk_notebook_set_show_border(notebook, FALSE);
3635  break;
3636  }
3637 }
3638 
3639 static gboolean rcw_on_switch_page_finalsel(gpointer user_data)
3640 {
3641  TRACE_CALL(__func__);
3643  RemminaConnectionObject *cnnobj;
3644 
3645  if (!user_data)
3646  return FALSE;
3647 
3648  cnnobj = (RemminaConnectionObject *)user_data;
3649  if (!cnnobj->cnnwin)
3650  return FALSE;
3651 
3652  priv = cnnobj->cnnwin->priv;
3653 
3654  if (GTK_IS_WIDGET(cnnobj->cnnwin)) {
3655  rcw_floating_toolbar_show(cnnobj->cnnwin, TRUE);
3656  if (!priv->hidetb_eventsource)
3657  priv->hidetb_eventsource = g_timeout_add(TB_HIDE_TIME_TIME, (GSourceFunc)
3659  rco_update_toolbar(cnnobj);
3660  rcw_grab_focus(cnnobj->cnnwin);
3661  if (priv->view_mode != SCROLLED_WINDOW_MODE)
3662  rco_check_resize(cnnobj);
3663  }
3664  priv->spf_eventsourceid = 0;
3665  return FALSE;
3666 }
3667 
3668 static void rcw_on_switch_page(GtkNotebook *notebook, GtkWidget *newpage, guint page_num,
3669  RemminaConnectionWindow *cnnwin)
3670 {
3671  TRACE_CALL(__func__);
3672  RemminaConnectionWindowPriv *priv = cnnwin->priv;
3673  RemminaConnectionObject *cnnobj_newpage;
3674 
3675  cnnobj_newpage = g_object_get_data(G_OBJECT(newpage), "cnnobj");
3676  if (priv->spf_eventsourceid)
3677  g_source_remove(priv->spf_eventsourceid);
3678  priv->spf_eventsourceid = g_idle_add(rcw_on_switch_page_finalsel, cnnobj_newpage);
3679 }
3680 
3681 static void rcw_on_page_added(GtkNotebook *notebook, GtkWidget *child, guint page_num,
3682  RemminaConnectionWindow *cnnwin)
3683 {
3684  if (gtk_notebook_get_n_pages(GTK_NOTEBOOK(cnnwin->priv->notebook)) > 0)
3685  rcw_update_notebook(cnnwin);
3686 }
3687 
3688 static void rcw_on_page_removed(GtkNotebook *notebook, GtkWidget *child, guint page_num,
3689  RemminaConnectionWindow *cnnwin)
3690 {
3691  TRACE_CALL(__func__);
3692 
3693  if (gtk_notebook_get_n_pages(GTK_NOTEBOOK(cnnwin->priv->notebook)) <= 0)
3694  gtk_widget_destroy(GTK_WIDGET(cnnwin));
3695 
3696 }
3697 
3698 static GtkNotebook *
3699 rcw_on_notebook_create_window(GtkNotebook *notebook, GtkWidget *page, gint x, gint y, gpointer data)
3700 {
3701  /* This signal callback is called by GTK when a detachable tab is dropped on the root window
3702  * or in an existing window */
3703 
3704  TRACE_CALL(__func__);
3705  RemminaConnectionWindow *srccnnwin;
3706  RemminaConnectionWindow *dstcnnwin;
3707  RemminaConnectionObject *cnnobj;
3708  GdkWindow *window;
3709  gchar *srctag;
3710  gint width, height;
3711 
3712 #if GTK_CHECK_VERSION(3, 20, 0)
3713  GdkSeat *seat;
3714 #else
3715  GdkDeviceManager *manager;
3716 #endif
3717  GdkDevice *device = NULL;
3718 
3719 #if GTK_CHECK_VERSION(3, 20, 0)
3720  seat = gdk_display_get_default_seat(gdk_display_get_default());
3721  device = gdk_seat_get_pointer(seat);
3722 #else
3723  manager = gdk_display_get_device_manager(gdk_display_get_default());
3724  device = gdk_device_manager_get_client_pointer(manager);
3725 #endif
3726 
3727  window = gdk_device_get_window_at_position(device, &x, &y);
3728  srccnnwin = RCW(gtk_widget_get_toplevel(GTK_WIDGET(notebook)));
3729  dstcnnwin = RCW(remmina_widget_pool_find_by_window(REMMINA_TYPE_CONNECTION_WINDOW, window));
3730 
3731  if (srccnnwin == dstcnnwin)
3732  return NULL;
3733 
3734  if (gtk_notebook_get_n_pages(GTK_NOTEBOOK(srccnnwin->priv->notebook)) == 1 && !dstcnnwin)
3735  return NULL;
3736 
3737  cnnobj = (RemminaConnectionObject *)g_object_get_data(G_OBJECT(page), "cnnobj");
3738 
3739  if (!dstcnnwin) {
3740  /* Drop is directed to a new rcw: create a new scrolled window to accommodate
3741  * the dropped connectionand move our cnnobj there. Width and
3742  * height of the new window are cloned from the current window */
3743  srctag = (gchar *)g_object_get_data(G_OBJECT(srccnnwin), "tag");
3744  gtk_window_get_size(GTK_WINDOW(srccnnwin), &width, &height);
3745  dstcnnwin = rcw_create_scrolled(width, height, FALSE); // New dropped window is never maximized
3746  g_object_set_data_full(G_OBJECT(dstcnnwin), "tag", g_strdup(srctag), (GDestroyNotify)g_free);
3747  /* when returning, GTK will move the whole tab to the new notebook.
3748  * Prepare cnnobj to be hosted in the new cnnwin */
3749  cnnobj->cnnwin = dstcnnwin;
3750  } else {
3751  cnnobj->cnnwin = dstcnnwin;
3752  }
3753 
3754  remmina_protocol_widget_set_hostkey_func(REMMINA_PROTOCOL_WIDGET(cnnobj->proto),
3755  (RemminaHostkeyFunc)rcw_hostkey_func);
3756 
3757  return GTK_NOTEBOOK(cnnobj->cnnwin->priv->notebook);
3758 }
3759 
3760 static GtkNotebook *
3762 {
3763  TRACE_CALL(__func__);
3764  GtkNotebook *notebook;
3765 
3766  notebook = GTK_NOTEBOOK(gtk_notebook_new());
3767 
3768  gtk_notebook_set_scrollable(GTK_NOTEBOOK(notebook), TRUE);
3769  gtk_widget_show(GTK_WIDGET(notebook));
3770 
3771  g_signal_connect(G_OBJECT(notebook), "create-window", G_CALLBACK(rcw_on_notebook_create_window), NULL);
3772  g_signal_connect(G_OBJECT(notebook), "switch-page", G_CALLBACK(rcw_on_switch_page), cnnwin);
3773  g_signal_connect(G_OBJECT(notebook), "page-added", G_CALLBACK(rcw_on_page_added), cnnwin);
3774  g_signal_connect(G_OBJECT(notebook), "page-removed", G_CALLBACK(rcw_on_page_removed), cnnwin);
3775  gtk_widget_set_can_focus(GTK_WIDGET(notebook), FALSE);
3776 
3777  return notebook;
3778 }
3779 
3780 /* Create a scrolled toplevel window */
3781 static RemminaConnectionWindow *rcw_create_scrolled(gint width, gint height, gboolean maximize)
3782 {
3783  TRACE_CALL(__func__);
3784  RemminaConnectionWindow *cnnwin;
3785  GtkWidget *grid;
3786  GtkWidget *toolbar;
3787  GtkNotebook *notebook;
3788  GtkSettings *settings = gtk_settings_get_default();
3789 
3790  cnnwin = rcw_new(FALSE, 0);
3791  gtk_widget_realize(GTK_WIDGET(cnnwin));
3792 
3793  gtk_window_set_default_size(GTK_WINDOW(cnnwin), width, height);
3794  g_object_set(settings, "gtk-application-prefer-dark-theme", remmina_pref.dark_theme, NULL);
3795 
3796  /* Create the toolbar */
3797  toolbar = rcw_create_toolbar(cnnwin, SCROLLED_WINDOW_MODE);
3798 
3799  /* Create the notebook */
3800  notebook = rcw_create_notebook(cnnwin);
3801 
3802  /* Create the grid container for toolbars+notebook and populate it */
3803  grid = gtk_grid_new();
3804 
3805 
3806  gtk_grid_attach(GTK_GRID(grid), GTK_WIDGET(notebook), 0, 0, 1, 1);
3807 
3808  gtk_widget_set_hexpand(GTK_WIDGET(notebook), TRUE);
3809  gtk_widget_set_vexpand(GTK_WIDGET(notebook), TRUE);
3810 
3811  rcw_place_toolbar(GTK_TOOLBAR(toolbar), GTK_GRID(grid), GTK_WIDGET(notebook), remmina_pref.toolbar_placement);
3812 
3813  gtk_container_add(GTK_CONTAINER(cnnwin), grid);
3814 
3815  /* Add drag capabilities to the toolbar */
3816  gtk_drag_source_set(GTK_WIDGET(toolbar), GDK_BUTTON1_MASK,
3817  dnd_targets_tb, sizeof dnd_targets_tb / sizeof *dnd_targets_tb, GDK_ACTION_MOVE);
3818  g_signal_connect_after(GTK_WIDGET(toolbar), "drag-begin", G_CALLBACK(rcw_tb_drag_begin), NULL);
3819  g_signal_connect(GTK_WIDGET(toolbar), "drag-failed", G_CALLBACK(rcw_tb_drag_failed), cnnwin);
3820 
3821  /* Add drop capabilities to the drop/dest target for the toolbar (the notebook) */
3822  gtk_drag_dest_set(GTK_WIDGET(notebook), GTK_DEST_DEFAULT_MOTION | GTK_DEST_DEFAULT_HIGHLIGHT,
3823  dnd_targets_tb, sizeof dnd_targets_tb / sizeof *dnd_targets_tb, GDK_ACTION_MOVE);
3824  gtk_drag_dest_set_track_motion(GTK_WIDGET(notebook), TRUE);
3825  g_signal_connect(GTK_WIDGET(notebook), "drag-drop", G_CALLBACK(rcw_tb_drag_drop), cnnwin);
3826 
3827  cnnwin->priv->view_mode = SCROLLED_WINDOW_MODE;
3828  cnnwin->priv->toolbar = toolbar;
3829  cnnwin->priv->grid = grid;
3830  cnnwin->priv->notebook = notebook;
3831  cnnwin->priv->ss_width = width;
3832  cnnwin->priv->ss_height = height;
3833  cnnwin->priv->ss_maximized = maximize;
3834 
3835  /* The notebook and all its child must be realized now, or a reparent will
3836  * call unrealize() and will destroy a GtkSocket */
3837  gtk_widget_show(grid);
3838  gtk_widget_show(GTK_WIDGET(cnnwin));
3839  GtkWindowGroup *wingrp = gtk_window_group_new();
3840 
3841  gtk_window_group_add_window(wingrp, GTK_WINDOW(cnnwin));
3842  gtk_window_set_transient_for(GTK_WINDOW(cnnwin), NULL);
3843 
3844  if (maximize)
3845  gtk_window_maximize(GTK_WINDOW(cnnwin));
3846 
3847 
3849 
3850  return cnnwin;
3851 }
3852 
3854 {
3855  TRACE_CALL(__func__);
3856 
3857  GtkWidget *revealer;
3859 
3860  priv = cnnwin->priv;
3861 
3862  if (priv->overlay_ftb_overlay != NULL) {
3863  gtk_widget_destroy(priv->overlay_ftb_overlay);
3864  priv->overlay_ftb_overlay = NULL;
3865  priv->revealer = NULL;
3866  }
3867  if (priv->overlay_ftb_fr != NULL) {
3868  gtk_widget_destroy(priv->overlay_ftb_fr);
3869  priv->overlay_ftb_fr = NULL;
3870  }
3871 
3872  rcw_create_floating_toolbar(cnnwin, priv->fss_view_mode);
3873 
3874  priv->overlay_ftb_overlay = gtk_event_box_new();
3875 
3876  GtkWidget *vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
3877 
3878  gtk_container_set_border_width(GTK_CONTAINER(vbox), 0);
3879 
3880  GtkWidget *handle = gtk_drawing_area_new();
3881 
3882  gtk_widget_set_size_request(handle, 4, 4);
3883  gtk_widget_set_name(handle, "ftb-handle");
3884 
3885  revealer = gtk_revealer_new();
3886 
3887  gtk_widget_set_halign(GTK_WIDGET(priv->overlay_ftb_overlay), GTK_ALIGN_CENTER);
3888 
3890  gtk_box_pack_start(GTK_BOX(vbox), handle, FALSE, FALSE, 0);
3891  gtk_box_pack_start(GTK_BOX(vbox), revealer, FALSE, FALSE, 0);
3892  gtk_revealer_set_transition_type(GTK_REVEALER(revealer), GTK_REVEALER_TRANSITION_TYPE_SLIDE_UP);
3893  gtk_widget_set_valign(GTK_WIDGET(priv->overlay_ftb_overlay), GTK_ALIGN_END);
3894  } else {
3895  gtk_box_pack_start(GTK_BOX(vbox), revealer, FALSE, FALSE, 0);
3896  gtk_box_pack_start(GTK_BOX(vbox), handle, FALSE, FALSE, 0);
3897  gtk_revealer_set_transition_type(GTK_REVEALER(revealer), GTK_REVEALER_TRANSITION_TYPE_SLIDE_DOWN);
3898  gtk_widget_set_valign(GTK_WIDGET(priv->overlay_ftb_overlay), GTK_ALIGN_START);
3899  }
3900 
3901 
3902  gtk_container_add(GTK_CONTAINER(revealer), priv->floating_toolbar_widget);
3903  gtk_widget_set_halign(GTK_WIDGET(revealer), GTK_ALIGN_CENTER);
3904  gtk_widget_set_valign(GTK_WIDGET(revealer), GTK_ALIGN_START);
3905 
3906  priv->revealer = revealer;
3907 
3908  GtkWidget *fr;
3909 
3910  fr = gtk_frame_new(NULL);
3911  priv->overlay_ftb_fr = fr;
3912  gtk_container_add(GTK_CONTAINER(priv->overlay_ftb_overlay), fr);
3913  gtk_container_add(GTK_CONTAINER(fr), vbox);
3914 
3915  gtk_widget_show(vbox);
3916  gtk_widget_show(revealer);
3917  gtk_widget_show(handle);
3918  gtk_widget_show(priv->overlay_ftb_overlay);
3919  gtk_widget_show(fr);
3920 
3922  gtk_widget_set_name(fr, "ftbbox-lower");
3923  else
3924  gtk_widget_set_name(fr, "ftbbox-upper");
3925 
3926  gtk_overlay_add_overlay(GTK_OVERLAY(priv->overlay), priv->overlay_ftb_overlay);
3927 
3928  rcw_floating_toolbar_show(cnnwin, TRUE);
3929 
3930  g_signal_connect(G_OBJECT(priv->overlay_ftb_overlay), "enter-notify-event", G_CALLBACK(rcw_floating_toolbar_on_enter), cnnwin);
3931  g_signal_connect(G_OBJECT(priv->overlay_ftb_overlay), "leave-notify-event", G_CALLBACK(rcw_floating_toolbar_on_leave), cnnwin);
3932  g_signal_connect(G_OBJECT(priv->overlay_ftb_overlay), "scroll-event", G_CALLBACK(rcw_floating_toolbar_on_scroll), cnnwin);
3933  gtk_widget_add_events(
3934  GTK_WIDGET(priv->overlay_ftb_overlay),
3935  GDK_SCROLL_MASK
3936 #if GTK_CHECK_VERSION(3, 4, 0)
3937  | GDK_SMOOTH_SCROLL_MASK
3938 #endif
3939  );
3940 
3941  /* Add drag and drop capabilities to the source */
3942  gtk_drag_source_set(GTK_WIDGET(priv->overlay_ftb_overlay), GDK_BUTTON1_MASK,
3943  dnd_targets_ftb, sizeof dnd_targets_ftb / sizeof *dnd_targets_ftb, GDK_ACTION_MOVE);
3944  g_signal_connect_after(GTK_WIDGET(priv->overlay_ftb_overlay), "drag-begin", G_CALLBACK(rcw_ftb_drag_begin), cnnwin);
3945 
3947  /* toolbar in fullscreenmode disabled, hide everything */
3948  gtk_widget_hide(fr);
3949 }
3950 
3951 
3952 static gboolean rcw_ftb_drag_drop(GtkWidget *widget, GdkDragContext *context,
3953  gint x, gint y, guint time, RemminaConnectionWindow *cnnwin)
3954 {
3955  TRACE_CALL(__func__);
3956  GtkAllocation wa;
3957  gint new_floating_toolbar_placement;
3958  RemminaConnectionObject *cnnobj;
3959 
3960  gtk_widget_get_allocation(widget, &wa);
3961 
3962  if (y >= wa.height / 2)
3963  new_floating_toolbar_placement = FLOATING_TOOLBAR_PLACEMENT_BOTTOM;
3964  else
3965  new_floating_toolbar_placement = FLOATING_TOOLBAR_PLACEMENT_TOP;
3966 
3967  gtk_drag_finish(context, TRUE, TRUE, time);
3968 
3969  if (new_floating_toolbar_placement != remmina_pref.floating_toolbar_placement) {
3970  /* Destroy and recreate the FTB */
3971  remmina_pref.floating_toolbar_placement = new_floating_toolbar_placement;
3974  cnnobj = rcw_get_visible_cnnobj(cnnwin);
3975  if (cnnobj) rco_update_toolbar(cnnobj);
3976  }
3977 
3978  return TRUE;
3979 }
3980 
3981 static void rcw_ftb_drag_begin(GtkWidget *widget, GdkDragContext *context, gpointer user_data)
3982 {
3983  TRACE_CALL(__func__);
3984 
3985  cairo_surface_t *surface;
3986  cairo_t *cr;
3987  GtkAllocation wa;
3988  double dashes[] = { 10 };
3989 
3990  gtk_widget_get_allocation(widget, &wa);
3991 
3992  surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, wa.width, wa.height);
3993  cr = cairo_create(surface);
3994  cairo_set_source_rgb(cr, 0.6, 0.6, 0.6);
3995  cairo_set_line_width(cr, 2);
3996  cairo_set_dash(cr, dashes, 1, 0);
3997  cairo_rectangle(cr, 0, 0, wa.width, wa.height);
3998  cairo_stroke(cr);
3999  cairo_destroy(cr);
4000 
4001  gtk_drag_set_icon_surface(context, surface);
4002 }
4003 
4004 RemminaConnectionWindow *rcw_create_fullscreen(GtkWindow *old, gint view_mode)
4005 {
4006  TRACE_CALL(__func__);
4007  RemminaConnectionWindow *cnnwin;
4008  GtkNotebook *notebook;
4009 
4010 #if GTK_CHECK_VERSION(3, 22, 0)
4011  gint n_monitors;
4012  gint i;
4013  GdkMonitor *old_monitor;
4014  GdkDisplay *old_display;
4015  GdkWindow *old_window;
4016 #endif
4017  gint full_screen_target_monitor;
4018 
4019  full_screen_target_monitor = FULL_SCREEN_TARGET_MONITOR_UNDEFINED;
4020  if (old) {
4021 #if GTK_CHECK_VERSION(3, 22, 0)
4022  old_window = gtk_widget_get_window(GTK_WIDGET(old));
4023  old_display = gdk_window_get_display(old_window);
4024  old_monitor = gdk_display_get_monitor_at_window(old_display, old_window);
4025  n_monitors = gdk_display_get_n_monitors(old_display);
4026  for (i = 0; i < n_monitors; ++i) {
4027  if (gdk_display_get_monitor(old_display, i) == old_monitor) {
4028  full_screen_target_monitor = i;
4029  break;
4030  }
4031  }
4032 #else
4033  full_screen_target_monitor = gdk_screen_get_monitor_at_window(
4034  gdk_screen_get_default(),
4035  gtk_widget_get_window(GTK_WIDGET(old)));
4036 #endif
4037  }
4038 
4039  cnnwin = rcw_new(TRUE, full_screen_target_monitor);
4040  gtk_widget_set_name(GTK_WIDGET(cnnwin), "remmina-connection-window-fullscreen");
4041  gtk_widget_realize(GTK_WIDGET(cnnwin));
4042 
4043  if (!view_mode)
4044  view_mode = VIEWPORT_FULLSCREEN_MODE;
4045 
4046  notebook = rcw_create_notebook(cnnwin);
4047 
4048  cnnwin->priv->overlay = gtk_overlay_new();
4049  gtk_container_add(GTK_CONTAINER(cnnwin), cnnwin->priv->overlay);
4050  gtk_container_add(GTK_CONTAINER(cnnwin->priv->overlay), GTK_WIDGET(notebook));
4051  gtk_widget_show(GTK_WIDGET(cnnwin->priv->overlay));
4052 
4053  cnnwin->priv->notebook = notebook;
4054  cnnwin->priv->view_mode = view_mode;
4055  cnnwin->priv->fss_view_mode = view_mode;
4056 
4057  /* Create the floating toolbar */
4059  /* Add drag and drop capabilities to the drop/dest target for floating toolbar */
4060  gtk_drag_dest_set(GTK_WIDGET(cnnwin->priv->overlay), GTK_DEST_DEFAULT_MOTION | GTK_DEST_DEFAULT_HIGHLIGHT,
4061  dnd_targets_ftb, sizeof dnd_targets_ftb / sizeof *dnd_targets_ftb, GDK_ACTION_MOVE);
4062  gtk_drag_dest_set_track_motion(GTK_WIDGET(cnnwin->priv->notebook), TRUE);
4063  g_signal_connect(GTK_WIDGET(cnnwin->priv->overlay), "drag-drop", G_CALLBACK(rcw_ftb_drag_drop), cnnwin);
4064 
4065  gtk_widget_show(GTK_WIDGET(cnnwin));
4066  GtkWindowGroup *wingrp = gtk_window_group_new();
4067  gtk_window_group_add_window(wingrp, GTK_WINDOW(cnnwin));
4068  gtk_window_set_transient_for(GTK_WINDOW(cnnwin), NULL);
4069 
4070  return cnnwin;
4071 }
4072 
4073 static gboolean rcw_hostkey_func(RemminaProtocolWidget *gp, guint keyval, gboolean release)
4074 {
4075  TRACE_CALL(__func__);
4076  RemminaConnectionObject *cnnobj = gp->cnnobj;
4077  RemminaConnectionWindowPriv *priv = cnnobj->cnnwin->priv;
4078  const RemminaProtocolFeature *feature;
4079  gint i;
4080 
4081  if (release) {
4082  if (remmina_pref.hostkey && keyval == remmina_pref.hostkey) {
4083  priv->hostkey_activated = FALSE;
4084  if (priv->hostkey_used)
4085  /* hostkey pressed + something else */
4086  return TRUE;
4087  }
4088  /* If hostkey is released without pressing other keys, we should execute the
4089  * shortcut key which is the same as hostkey. Be default, this is grab/ungrab
4090  * keyboard */
4091  else if (priv->hostkey_activated) {
4092  /* Trap all key releases when hostkey is pressed */
4093  /* hostkey pressed + something else */
4094  return TRUE;
4095  } else {
4096  /* Any key pressed, no hostkey */
4097  return FALSE;
4098  }
4099  } else if (remmina_pref.hostkey && keyval == remmina_pref.hostkey) {
4101  priv->hostkey_activated = TRUE;
4102  priv->hostkey_used = FALSE;
4103  return TRUE;
4104  } else if (!priv->hostkey_activated) {
4105  /* Any key pressed, no hostkey */
4106  return FALSE;
4107  }
4108 
4109  priv->hostkey_used = TRUE;
4110  keyval = gdk_keyval_to_lower(keyval);
4111  if (keyval == GDK_KEY_Up || keyval == GDK_KEY_Down
4112  || keyval == GDK_KEY_Left || keyval == GDK_KEY_Right) {
4113  GtkAdjustment *adjust;
4114  int pos;
4115 
4116  if (cnnobj->connected && GTK_IS_SCROLLED_WINDOW(cnnobj->scrolled_container)) {
4117  if (keyval == GDK_KEY_Up || keyval == GDK_KEY_Down)
4118  adjust = gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(cnnobj->scrolled_container));
4119  else
4120  adjust = gtk_scrolled_window_get_hadjustment(GTK_SCROLLED_WINDOW(cnnobj->scrolled_container));
4121 
4122  if (keyval == GDK_KEY_Up || keyval == GDK_KEY_Left)
4123  pos = 0;
4124  else
4125  pos = gtk_adjustment_get_upper(adjust);
4126 
4127  gtk_adjustment_set_value(adjust, pos);
4128  if (keyval == GDK_KEY_Up || keyval == GDK_KEY_Down)
4129  gtk_scrolled_window_set_vadjustment(GTK_SCROLLED_WINDOW(cnnobj->scrolled_container), adjust);
4130  else
4131  gtk_scrolled_window_set_hadjustment(GTK_SCROLLED_WINDOW(cnnobj->scrolled_container), adjust);
4132  } else if (REMMINA_IS_SCROLLED_VIEWPORT(cnnobj->scrolled_container)) {
4134  GtkWidget *child;
4135  GdkWindow *gsvwin;
4136  gint sz;
4137  GtkAdjustment *adj;
4138  gdouble value;
4139 
4140  if (!GTK_IS_BIN(cnnobj->scrolled_container))
4141  return FALSE;
4142 
4143  gsv = REMMINA_SCROLLED_VIEWPORT(cnnobj->scrolled_container);
4144  child = gtk_bin_get_child(GTK_BIN(gsv));
4145  if (!GTK_IS_VIEWPORT(child))
4146  return FALSE;
4147 
4148  gsvwin = gtk_widget_get_window(GTK_WIDGET(gsv));
4149  if (!gsv)
4150  return FALSE;
4151 
4152  if (keyval == GDK_KEY_Up || keyval == GDK_KEY_Down) {
4153  sz = gdk_window_get_height(gsvwin) + 2; // Add 2px of black scroll border
4154  adj = gtk_scrollable_get_vadjustment(GTK_SCROLLABLE(child));
4155  } else {
4156  sz = gdk_window_get_width(gsvwin) + 2; // Add 2px of black scroll border
4157  adj = gtk_scrollable_get_hadjustment(GTK_SCROLLABLE(child));
4158  }
4159 
4160  if (keyval == GDK_KEY_Up || keyval == GDK_KEY_Left)
4161  value = 0;
4162  else
4163  value = gtk_adjustment_get_upper(GTK_ADJUSTMENT(adj)) - (gdouble)sz + 2.0;
4164 
4165  gtk_adjustment_set_value(GTK_ADJUSTMENT(adj), value);
4166  }
4167  }
4168 
4169  if (keyval == remmina_pref.shortcutkey_fullscreen && !extrahardening) {
4170  switch (priv->view_mode) {
4171  case SCROLLED_WINDOW_MODE:
4172  rcw_switch_viewmode(cnnobj->cnnwin, priv->fss_view_mode);
4173  break;
4177  break;
4178  default:
4179  break;
4180  }
4181  } else if (keyval == remmina_pref.shortcutkey_autofit && !extrahardening) {
4182  if (priv->toolitem_autofit && gtk_widget_is_sensitive(GTK_WIDGET(priv->toolitem_autofit)))
4183  rcw_toolbar_autofit(GTK_TOOL_ITEM(gp), cnnobj->cnnwin);
4184  } else if (keyval == remmina_pref.shortcutkey_nexttab && !extrahardening) {
4185  i = gtk_notebook_get_current_page(GTK_NOTEBOOK(priv->notebook)) + 1;
4186  if (i >= gtk_notebook_get_n_pages(GTK_NOTEBOOK(priv->notebook)))
4187  i = 0;
4188  gtk_notebook_set_current_page(GTK_NOTEBOOK(priv->notebook), i);
4189  } else if (keyval == remmina_pref.shortcutkey_prevtab && !extrahardening) {
4190  i = gtk_notebook_get_current_page(GTK_NOTEBOOK(priv->notebook)) - 1;
4191  if (i < 0)
4192  i = gtk_notebook_get_n_pages(GTK_NOTEBOOK(priv->notebook)) - 1;
4193  gtk_notebook_set_current_page(GTK_NOTEBOOK(priv->notebook), i);
4194  } else if (keyval == remmina_pref.shortcutkey_scale && !extrahardening) {
4195  if (gtk_widget_is_sensitive(GTK_WIDGET(priv->toolitem_scale))) {
4196  gtk_toggle_tool_button_set_active(
4197  GTK_TOGGLE_TOOL_BUTTON(priv->toolitem_scale),
4198  !gtk_toggle_tool_button_get_active(
4199  GTK_TOGGLE_TOOL_BUTTON(
4200  priv->toolitem_scale)));
4201  }
4202  } else if (keyval == remmina_pref.shortcutkey_grab && !extrahardening) {
4203  gtk_toggle_tool_button_set_active(
4204  GTK_TOGGLE_TOOL_BUTTON(priv->toolitem_grab),
4205  !gtk_toggle_tool_button_get_active(
4206  GTK_TOGGLE_TOOL_BUTTON(
4207  priv->toolitem_grab)));
4208  } else if (keyval == remmina_pref.shortcutkey_minimize && !extrahardening) {
4209  rcw_toolbar_minimize(GTK_TOOL_ITEM(gp),
4210  cnnobj->cnnwin);
4211  } else if (keyval == remmina_pref.shortcutkey_viewonly && !extrahardening) {
4212  remmina_file_set_int(cnnobj->remmina_file, "viewonly",
4213  (remmina_file_get_int(cnnobj->remmina_file, "viewonly", 0)
4214  == 0) ? 1 : 0);
4215  } else if (keyval == remmina_pref.shortcutkey_screenshot && !extrahardening) {
4216  rcw_toolbar_screenshot(GTK_TOOL_ITEM(gp),
4217  cnnobj->cnnwin);
4218  } else if (keyval == remmina_pref.shortcutkey_disconnect && !extrahardening) {
4220  } else if (keyval == remmina_pref.shortcutkey_toolbar && !extrahardening) {
4221  if (priv->view_mode == SCROLLED_WINDOW_MODE) {
4222  remmina_pref.hide_connection_toolbar =
4223  !remmina_pref.hide_connection_toolbar;
4225  }
4226  } else {
4227  for (feature =
4229  REMMINA_PROTOCOL_WIDGET(
4230  cnnobj->proto));
4231  feature && feature->type;
4232  feature++) {
4233  if (feature->type
4235  && GPOINTER_TO_UINT(
4236  feature->opt3)
4237  == keyval) {
4239  REMMINA_PROTOCOL_WIDGET(
4240  cnnobj->proto),
4241  feature);
4242  break;
4243  }
4244  }
4245  }
4246  /* If a keypress makes the current cnnobj to move to another window,
4247  * priv is now invalid. So we can no longer use priv here */
4248  cnnobj->cnnwin->priv->hostkey_activated = FALSE;
4249 
4250  /* Trap all keypresses when hostkey is pressed */
4251  return TRUE;
4252 }
4253 
4255 {
4256  TRACE_CALL(__func__);
4257  const gchar *tag;
4258 
4259  switch (remmina_pref.tab_mode) {
4260  case REMMINA_TAB_BY_GROUP:
4261  tag = remmina_file_get_string(remminafile, "group");
4262  break;
4264  tag = remmina_file_get_string(remminafile, "protocol");
4265  break;
4266  case REMMINA_TAB_ALL:
4267  tag = NULL;
4268  break;
4269  case REMMINA_TAB_NONE:
4270  default:
4271  return NULL;
4272  }
4273  return RCW(remmina_widget_pool_find(REMMINA_TYPE_CONNECTION_WINDOW, tag));
4274 }
4275 
4276 gboolean rcw_delayed_window_present(gpointer user_data)
4277 {
4278  RemminaConnectionWindow *cnnwin = (RemminaConnectionWindow *)user_data;
4279 
4280  if (cnnwin) {
4281  gtk_window_present_with_time(GTK_WINDOW(cnnwin), (guint32)(g_get_monotonic_time() / 1000));
4282  rcw_grab_focus(cnnwin);
4283  }
4284  cnnwin->priv->dwp_eventsourceid = 0;
4285  return G_SOURCE_REMOVE;
4286 }
4287 
4289 {
4290  TRACE_CALL(__func__);
4291 
4292  REMMINA_DEBUG("Connect signal emitted");
4293 
4294  /* This signal handler is called by a plugin when it’s correctly connected
4295  * (and authenticated) */
4296 
4297  cnnobj->connected = TRUE;
4298 
4299  remmina_protocol_widget_set_hostkey_func(REMMINA_PROTOCOL_WIDGET(cnnobj->proto),
4300  (RemminaHostkeyFunc)rcw_hostkey_func);
4301 
4305  if (remmina_file_get_filename(cnnobj->remmina_file) == NULL)
4307  remmina_file_get_string(cnnobj->remmina_file, "server"));
4308  REMMINA_DEBUG("We save the last successful connection date");
4309  //remmina_file_set_string(cnnobj->remmina_file, "last_success", last_success);
4311  //REMMINA_DEBUG("Last connection made on %s.", last_success);
4312 
4313  REMMINA_DEBUG("Saving credentials");
4314  /* Save credentials */
4316 
4317  if (cnnobj->cnnwin->priv->floating_toolbar_widget)
4318  gtk_widget_show(cnnobj->cnnwin->priv->floating_toolbar_widget);
4319 
4320  rco_update_toolbar(cnnobj);
4321 
4322  REMMINA_DEBUG("Trying to present the window");
4323  /* Try to present window */
4324  cnnobj->cnnwin->priv->dwp_eventsourceid = g_timeout_add(200, rcw_delayed_window_present, (gpointer)cnnobj->cnnwin);
4325 }
4326 
4327 static void cb_lasterror_confirmed(void *cbdata, int btn)
4328 {
4329  TRACE_CALL(__func__);
4331 }
4332 
4334 {
4335  TRACE_CALL(__func__);
4336  RemminaConnectionObject *cnnobj = gp->cnnobj;
4337  RemminaConnectionWindowPriv *priv = cnnobj->cnnwin->priv;
4338  GtkWidget *pparent;
4339 
4340  REMMINA_DEBUG("Disconnect signal received on RemminaProtocolWidget");
4341  /* Detach the protocol widget from the notebook now, or we risk that a
4342  * window delete will destroy cnnobj->proto before we complete disconnection.
4343  */
4344  pparent = gtk_widget_get_parent(cnnobj->proto);
4345  if (pparent != NULL) {
4346  g_object_ref(cnnobj->proto);
4347  gtk_container_remove(GTK_CONTAINER(pparent), cnnobj->proto);
4348  }
4349 
4350  cnnobj->connected = FALSE;
4351 
4352  if (remmina_pref.save_view_mode) {
4353  if (cnnobj->cnnwin)
4354  remmina_file_set_int(cnnobj->remmina_file, "viewmode", cnnobj->cnnwin->priv->view_mode);
4356  }
4357 
4358  rcw_kp_ungrab(cnnobj->cnnwin);
4359  gtk_toggle_tool_button_set_active(
4360  GTK_TOGGLE_TOOL_BUTTON(priv->toolitem_grab),
4361  FALSE);
4362 
4364  /* We cannot close window immediately, but we must show a message panel */
4365  RemminaMessagePanel *mp;
4366  /* Destroy scrolled_container (and viewport) and all its children the plugin created
4367  * on it, so they will not receive GUI signals */
4368  if (cnnobj->scrolled_container) {
4369  gtk_widget_destroy(cnnobj->scrolled_container);
4370  cnnobj->scrolled_container = NULL;
4371  }
4372  cnnobj->viewport = NULL;
4375  rco_show_message_panel(gp->cnnobj, mp);
4376  REMMINA_DEBUG("Could not disconnect");
4377  } else {
4378  rco_closewin(gp);
4379  REMMINA_DEBUG("Disconnected");
4380  }
4381 }
4382 
4384 {
4385  TRACE_CALL(__func__);
4386  RemminaConnectionObject *cnnobj = gp->cnnobj;
4387 
4388  if (cnnobj && cnnobj->cnnwin && cnnobj->cnnwin->priv->view_mode != SCROLLED_WINDOW_MODE)
4389  rco_check_resize(cnnobj);
4390 }
4391 
4393 {
4394  TRACE_CALL(__func__);
4395  RemminaConnectionObject *cnnobj = gp->cnnobj;
4396 
4398 }
4399 
4401 {
4402  TRACE_CALL(__func__);
4403  RemminaConnectionObject *cnnobj = gp->cnnobj;
4404 
4405  cnnobj->dynres_unlocked = FALSE;
4406  rco_update_toolbar(cnnobj);
4407 }
4408 
4410 {
4411  TRACE_CALL(__func__);
4412  RemminaConnectionObject *cnnobj = gp->cnnobj;
4413 
4414  cnnobj->dynres_unlocked = TRUE;
4415  rco_update_toolbar(cnnobj);
4416 }
4417 
4418 gboolean rcw_open_from_filename(const gchar *filename)
4419 {
4420  TRACE_CALL(__func__);
4421  RemminaFile *remminafile;
4422  GtkWidget *dialog;
4423 
4424  remminafile = remmina_file_manager_load_file(filename);
4425  if (remminafile) {
4426  if (remmina_file_get_int (remminafile, "profile-lock", FALSE)
4428  return FALSE;
4429  rcw_open_from_file(remminafile);
4430  return TRUE;
4431  } else {
4432  dialog = gtk_message_dialog_new(NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE,
4433  _("The file “%s” is corrupted, unreadable, or could not be found."), filename);
4434  g_signal_connect(G_OBJECT(dialog), "response", G_CALLBACK(gtk_widget_destroy), NULL);
4435  gtk_widget_show(dialog);
4437  return FALSE;
4438  }
4439 }
4440 
4441 static gboolean open_connection_last_stage(gpointer user_data)
4442 {
4443  RemminaProtocolWidget *gp = (RemminaProtocolWidget *)user_data;
4444 
4445  /* Now we have an allocated size for our RemminaProtocolWidget. We can proceed with the connection */
4448  rco_check_resize(gp->cnnobj);
4449 
4450  return FALSE;
4451 }
4452 
4453 static void rpw_size_allocated_on_connection(GtkWidget *w, GdkRectangle *allocation, gpointer user_data)
4454 {
4456 
4457  /* Disconnect signal handler to avoid to be called again after a normal resize */
4458  g_signal_handler_disconnect(w, gp->cnnobj->deferred_open_size_allocate_handler);
4459 
4460  /* Allow extra 100 ms for size allocation (do we really need it?) */
4461  g_timeout_add(100, open_connection_last_stage, gp);
4462 
4463  return;
4464 }
4465 
4467 {
4468  TRACE_CALL(__func__);
4469  rcw_open_from_file_full(remminafile, NULL, NULL, NULL);
4470 }
4471 
4472 static void set_label_selectable(gpointer data, gpointer user_data)
4473 {
4474  TRACE_CALL(__func__);
4475  GtkWidget *widget = GTK_WIDGET(data);
4476 
4477  if (GTK_IS_LABEL(widget))
4478  gtk_label_set_selectable(GTK_LABEL(widget), TRUE);
4479 }
4480 
4488 };
4489 
4494 static void rcw_gtksocket_not_available_dialog_response(GtkDialog * self,
4495  gint response_id,
4496  RemminaConnectionObject * cnnobj)
4497 {
4498  TRACE_CALL(__func__);
4499 
4500  GError *error = NULL;
4501 
4502  if (response_id == GTKSOCKET_NOT_AVAIL_RESPONSE_OPEN_BROWSER) {
4503  gtk_show_uri_on_window(
4504  NULL,
4505  // TRANSLATORS: This should be a link to the Remmina wiki page:
4506  // TRANSLATORS: 'GtkSocket feature is not available'.
4507  _("https://gitlab.com/Remmina/Remmina/-/wikis/GtkSocket-feature-is-not-available-in-a-Wayland-session"),
4508  GDK_CURRENT_TIME, &error
4509  );
4510  }
4511 
4512  // Close the current page since it's useless without GtkSocket.
4513  // The user would need to manually click the close button.
4514  if (cnnobj) rco_disconnect_current_page(cnnobj);
4515 
4516  gtk_widget_destroy(GTK_WIDGET(self));
4517 }
4518 
4519 GtkWidget *rcw_open_from_file_full(RemminaFile *remminafile, GCallback disconnect_cb, gpointer data, guint *handler)
4520 {
4521  TRACE_CALL(__func__);
4522  RemminaConnectionObject *cnnobj;
4523 
4524  gint ret;
4525  GtkWidget *dialog;
4526  GtkWidget *newpage;
4527  gint width, height;
4528  gboolean maximize;
4529  gint view_mode;
4530  const gchar *msg;
4531  RemminaScaleMode scalemode;
4532 
4533  if (disconnect_cb) {
4534  g_print("disconnect_cb is deprecated inside rcw_open_from_file_full() and should be null\n");
4535  return NULL;
4536  }
4537 
4538 
4539  /* Create the RemminaConnectionObject */
4540  cnnobj = g_new0(RemminaConnectionObject, 1);
4541  cnnobj->remmina_file = remminafile;
4542 
4543  /* Create the RemminaProtocolWidget */
4544  cnnobj->proto = remmina_protocol_widget_new();
4545  remmina_protocol_widget_setup((RemminaProtocolWidget *)cnnobj->proto, remminafile, cnnobj);
4547  GtkWindow *wparent;
4548  wparent = remmina_main_get_window();
4550  dialog = gtk_message_dialog_new(wparent, GTK_DIALOG_DESTROY_WITH_PARENT,
4551  GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, "%s", msg);
4552  gtk_dialog_run(GTK_DIALOG(dialog));
4553  gtk_widget_destroy(dialog);
4554  /* We should destroy cnnobj->proto and cnnobj now… TODO: Fix this leak */
4555  return NULL;
4556  }
4557 
4558 
4559 
4560  /* Set a name for the widget, for CSS selector */
4561  gtk_widget_set_name(GTK_WIDGET(cnnobj->proto), "remmina-protocol-widget");
4562 
4563  gtk_widget_set_halign(GTK_WIDGET(cnnobj->proto), GTK_ALIGN_FILL);
4564  gtk_widget_set_valign(GTK_WIDGET(cnnobj->proto), GTK_ALIGN_FILL);
4565 
4566  if (data)
4567  g_object_set_data(G_OBJECT(cnnobj->proto), "user-data", data);
4568 
4569  view_mode = remmina_file_get_int(cnnobj->remmina_file, "viewmode", 0);
4570  if (kioskmode)
4571  view_mode = VIEWPORT_FULLSCREEN_MODE;
4572  gint ismultimon = remmina_file_get_int(cnnobj->remmina_file, "multimon", 0);
4573 
4574  if (ismultimon)
4575  view_mode = VIEWPORT_FULLSCREEN_MODE;
4576 
4577  if (fullscreen)
4578  view_mode = VIEWPORT_FULLSCREEN_MODE;
4579 
4580  /* Create the viewport to make the RemminaProtocolWidget scrollable */
4581  cnnobj->viewport = gtk_viewport_new(NULL, NULL);
4582  gtk_widget_set_name(cnnobj->viewport, "remmina-cw-viewport");
4583  gtk_widget_show(cnnobj->viewport);
4584  gtk_container_set_border_width(GTK_CONTAINER(cnnobj->viewport), 0);
4585  gtk_viewport_set_shadow_type(GTK_VIEWPORT(cnnobj->viewport), GTK_SHADOW_NONE);
4586 
4587  /* Create the scrolled container */
4588  scalemode = get_current_allowed_scale_mode(cnnobj, NULL, NULL);
4589  cnnobj->scrolled_container = rco_create_scrolled_container(scalemode, view_mode);
4590 
4591  gtk_container_add(GTK_CONTAINER(cnnobj->scrolled_container), cnnobj->viewport);
4592 
4593  /* Determine whether the plugin can scale or not. If the plugin can scale and we do
4594  * not want to expand, then we add a GtkAspectFrame to maintain aspect ratio during scaling */
4596  remmina_file_get_string(remminafile, "protocol"),
4598 
4599  cnnobj->aspectframe = NULL;
4600  gtk_container_add(GTK_CONTAINER(cnnobj->viewport), cnnobj->proto);
4601 
4602  /* Determine whether this connection will be put on a new window
4603  * or in an existing one */
4604  cnnobj->cnnwin = rcw_find(remminafile);
4605  if (!cnnobj->cnnwin) {
4606  /* Connection goes on a new toplevel window */
4607  switch (view_mode) {
4610  cnnobj->cnnwin = rcw_create_fullscreen(NULL, view_mode);
4611  break;
4612  case SCROLLED_WINDOW_MODE:
4613  default:
4614  width = remmina_file_get_int(cnnobj->remmina_file, "window_width", 640);
4615  height = remmina_file_get_int(cnnobj->remmina_file, "window_height", 480);
4616  maximize = remmina_file_get_int(cnnobj->remmina_file, "window_maximize", FALSE) ? TRUE : FALSE;
4617  cnnobj->cnnwin = rcw_create_scrolled(width, height, maximize);
4618  break;
4619  }
4620  rcw_update_tag(cnnobj->cnnwin, cnnobj);
4621  rcw_append_new_page(cnnobj->cnnwin, cnnobj);
4622  } else {
4623  newpage = rcw_append_new_page(cnnobj->cnnwin, cnnobj);
4624  gtk_window_present(GTK_WINDOW(cnnobj->cnnwin));
4625  nb_set_current_page(cnnobj->cnnwin->priv->notebook, newpage);
4626  }
4627 
4628  // Do not call remmina_protocol_widget_update_alignment(cnnobj); here or cnnobj->proto will not fill its parent size
4629  // and remmina_protocol_widget_update_remote_resolution() cannot autodetect available space
4630 
4631  gtk_widget_show(cnnobj->proto);
4632  g_signal_connect(G_OBJECT(cnnobj->proto), "connect", G_CALLBACK(rco_on_connect), cnnobj);
4633  g_signal_connect(G_OBJECT(cnnobj->proto), "disconnect", G_CALLBACK(rco_on_disconnect), NULL);
4634  g_signal_connect(G_OBJECT(cnnobj->proto), "desktop-resize", G_CALLBACK(rco_on_desktop_resize), NULL);
4635  g_signal_connect(G_OBJECT(cnnobj->proto), "update-align", G_CALLBACK(rco_on_update_align), NULL);
4636  g_signal_connect(G_OBJECT(cnnobj->proto), "lock-dynres", G_CALLBACK(rco_on_lock_dynres), NULL);
4637  g_signal_connect(G_OBJECT(cnnobj->proto), "unlock-dynres", G_CALLBACK(rco_on_unlock_dynres), NULL);
4638  g_signal_connect(G_OBJECT(cnnobj->proto), "enter-notify-event", G_CALLBACK(rco_enter_protocol_widget), cnnobj);
4639  g_signal_connect(G_OBJECT(cnnobj->proto), "leave-notify-event", G_CALLBACK(rco_leave_protocol_widget), cnnobj);
4640 
4641  if (!remmina_pref.save_view_mode)
4642  remmina_file_set_int(cnnobj->remmina_file, "viewmode", remmina_pref.default_mode);
4643 
4644 
4645  /* If it is a GtkSocket plugin and X11 is not available, we inform the
4646  * user and close the connection */
4648  remmina_file_get_string(remminafile, "protocol"),
4650  if (ret && !remmina_gtksocket_available()) {
4651  gchar *title = _("Warning: This plugin requires GtkSocket, but this "
4652  "feature is unavailable in a Wayland session.");
4653 
4654  gchar *err_msg =
4655  // TRANSLATORS: This should be a link to the Remmina wiki page:
4656  // 'GtkSocket feature is not available'.
4657  _("Plugins relying on GtkSocket can't run in a "
4658  "Wayland session.\nFor more info and a possible "
4659  "workaround, please visit the Remmina wiki at:\n\n"
4660  "https://gitlab.com/Remmina/Remmina/-/wikis/GtkSocket-feature-is-not-available-in-a-Wayland-session");
4661 
4662  dialog = gtk_message_dialog_new(
4663  GTK_WINDOW(cnnobj->cnnwin),
4664  GTK_DIALOG_MODAL,
4665  GTK_MESSAGE_WARNING,
4666  GTK_BUTTONS_OK,
4667  "%s", title);
4668 
4669  gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog), "%s",
4670  err_msg);
4671  gtk_dialog_add_button(GTK_DIALOG(dialog), _("Open in web browser"),
4673 
4674  REMMINA_CRITICAL(g_strdup_printf("%s\n%s", title, err_msg));
4675 
4676  g_signal_connect(G_OBJECT(dialog),
4677  "response",
4679  cnnobj);
4680 
4681  // Make Text selectable. Usefull because of the link in the text.
4682  GtkWidget *area = gtk_message_dialog_get_message_area(
4683  GTK_MESSAGE_DIALOG(dialog));
4684  GtkContainer *box = (GtkContainer *)area;
4685 
4686  GList *children = gtk_container_get_children(box);
4687  g_list_foreach(children, set_label_selectable, NULL);
4688  g_list_free(children);
4689 
4690  gtk_widget_show(dialog);
4691 
4692  return NULL; /* Should we destroy something before returning? */
4693  }
4694 
4695  if (cnnobj->cnnwin->priv->floating_toolbar_widget)
4696  gtk_widget_show(cnnobj->cnnwin->priv->floating_toolbar_widget);
4697 
4699  printf("OK, an error occurred in initializing the protocol plugin before connecting. The error is %s.\n"
4700  "TODO: Put this string as an error to show", remmina_protocol_widget_get_error_message((RemminaProtocolWidget *)cnnobj->proto));
4701  return cnnobj->proto;
4702  }
4703 
4704 
4705  /* GTK window setup is done here, and we are almost ready to call remmina_protocol_widget_open_connection().
4706  * But size has not yet been allocated by GTK
4707  * to the widgets. If we are in RES_USE_INITIAL_WINDOW_SIZE resolution mode or scale is REMMINA_PROTOCOL_WIDGET_SCALE_MODE_DYNRES,
4708  * we should wait for a size allocation from GTK for cnnobj->proto
4709  * before connecting */
4710 
4711  cnnobj->deferred_open_size_allocate_handler = g_signal_connect(G_OBJECT(cnnobj->proto), "size-allocate", G_CALLBACK(rpw_size_allocated_on_connection), NULL);
4712 
4713  return cnnobj->proto;
4714 }
4715 
4717 {
4718  return &cnnobj->cnnwin->window;
4719 }
4721 {
4722  return cnnobj->viewport;
4723 }
4724 
4726 {
4727  TRACE_CALL(__func__);
4728  cnnwin->priv->on_delete_confirm_mode = mode;
4729 }
4730 
4735 void rco_destroy_message_panel(RemminaConnectionObject *cnnobj, RemminaMessagePanel *mp)
4736 {
4737  TRACE_CALL(__func__);
4738  GList *childs, *cc;
4739  RemminaMessagePanel *lastPanel;
4740  gboolean was_visible;
4741  GtkWidget *page;
4742 
4743  page = nb_find_page_by_cnnobj(cnnobj->cnnwin->priv->notebook, cnnobj);
4744  childs = gtk_container_get_children(GTK_CONTAINER(page));
4745  cc = g_list_first(childs);
4746  while (cc != NULL) {
4747  if ((RemminaMessagePanel *)cc->data == mp)
4748  break;
4749  cc = g_list_next(cc);
4750  }
4751  g_list_free(childs);
4752 
4753  if (cc == NULL) {
4754  printf("Remmina: Warning. There was a request to destroy a RemminaMessagePanel that is not on the page\n");
4755  return;
4756  }
4757  was_visible = gtk_widget_is_visible(GTK_WIDGET(mp));
4758  gtk_widget_destroy(GTK_WIDGET(mp));
4759 
4760  /* And now, show the last remaining message panel, if needed */
4761  if (was_visible) {
4762  childs = gtk_container_get_children(GTK_CONTAINER(page));
4763  cc = g_list_first(childs);
4764  lastPanel = NULL;
4765  while (cc != NULL) {
4766  if (G_TYPE_CHECK_INSTANCE_TYPE(cc->data, REMMINA_TYPE_MESSAGE_PANEL))
4767  lastPanel = (RemminaMessagePanel *)cc->data;
4768  cc = g_list_next(cc);
4769  }
4770  g_list_free(childs);
4771  if (lastPanel)
4772  gtk_widget_show(GTK_WIDGET(lastPanel));
4773  }
4774 }
4775 
4782 void rco_show_message_panel(RemminaConnectionObject *cnnobj, RemminaMessagePanel *mp)
4783 {
4784  TRACE_CALL(__func__);
4785  GList *childs, *cc;
4786  GtkWidget *page;
4787 
4788  /* Hides all RemminaMessagePanels childs of cnnobj->page */
4789  page = nb_find_page_by_cnnobj(cnnobj->cnnwin->priv->notebook, cnnobj);
4790  childs = gtk_container_get_children(GTK_CONTAINER(page));
4791  cc = g_list_first(childs);
4792  while (cc != NULL) {
4793  if (G_TYPE_CHECK_INSTANCE_TYPE(cc->data, REMMINA_TYPE_MESSAGE_PANEL))
4794  gtk_widget_hide(GTK_WIDGET(cc->data));
4795  cc = g_list_next(cc);
4796  }
4797  g_list_free(childs);
4798 
4799  /* Add the new message panel at the top of cnnobj->page */
4800  gtk_box_pack_start(GTK_BOX(page), GTK_WIDGET(mp), FALSE, FALSE, 0);
4801  gtk_box_reorder_child(GTK_BOX(page), GTK_WIDGET(mp), 0);
4802 
4803  /* Show the message panel */
4804  gtk_widget_show_all(GTK_WIDGET(mp));
4805 
4806  /* Focus the correct field of the RemminaMessagePanel */
4808 }
static RemminaConnectionWindow * rcw_find(RemminaFile *remminafile)
Definition: rcw.c:4254
gboolean remmina_protocol_widget_get_expand(RemminaProtocolWidget *gp)
guint shortcutkey_fullscreen
Definition: remmina_pref.h:175
-
static void rco_update_toolbar(RemminaConnectionObject *cnnobj)
Definition: rcw.c:2635
+
static void rco_update_toolbar(RemminaConnectionObject *cnnobj)
Definition: rcw.c:2639
const gchar * remmina_protocol_widget_get_error_message(RemminaProtocolWidget *gp)
-
gulong deferred_open_size_allocate_handler
Definition: rcw.c:177
+
gulong deferred_open_size_allocate_handler
Definition: rcw.c:178
gint floating_toolbar_placement
Definition: remmina_pref.h:224
-
static gboolean rcw_after_configure_scrolled(gpointer user_data)
Definition: rcw.c:3056
-
static void rcw_focus_in(RemminaConnectionWindow *cnnwin)
Definition: rcw.c:2948
-
static void rcw_init(RemminaConnectionWindow *cnnwin)
Definition: rcw.c:3223
-
static RemminaScaleMode get_current_allowed_scale_mode(RemminaConnectionObject *cnnobj, gboolean *dynres_avail, gboolean *scale_avail)
Definition: rcw.c:391
+
static gboolean rcw_after_configure_scrolled(gpointer user_data)
Definition: rcw.c:3064
+
static void rcw_focus_in(RemminaConnectionWindow *cnnwin)
Definition: rcw.c:2956
+
static void rcw_init(RemminaConnectionWindow *cnnwin)
Definition: rcw.c:3231
+
static RemminaScaleMode get_current_allowed_scale_mode(RemminaConnectionObject *cnnobj, gboolean *dynres_avail, gboolean *scale_avail)
Definition: rcw.c:393
gboolean remmina_protocol_widget_query_feature_by_type(RemminaProtocolWidget *gp, RemminaProtocolFeatureType type)
const RemminaProtocolFeature * remmina_protocol_widget_get_features(RemminaProtocolWidget *gp)
guint shortcutkey_minimize
Definition: remmina_pref.h:185
const gchar * remmina_file_get_string(RemminaFile *remminafile, const gchar *setting)
Definition: remmina_file.c:516
-
static void rcw_update_tag(RemminaConnectionWindow *cnnwin, RemminaConnectionObject *cnnobj)
Definition: rcw.c:3429
+
static void rcw_update_tag(RemminaConnectionWindow *cnnwin, RemminaConnectionObject *cnnobj)
Definition: rcw.c:3437
GtkWidget * remmina_widget_pool_find_by_window(GType type, GdkWindow *window)
const gchar * grab_color
Definition: remmina_pref.h:159
-
static void rcw_tb_drag_begin(GtkWidget *widget, GdkDragContext *context, gpointer user_data)
Definition: rcw.c:828
+
static void rcw_tb_drag_begin(GtkWidget *widget, GdkDragContext *context, gpointer user_data)
Definition: rcw.c:830
gboolean remmina_protocol_widget_query_feature_by_ref(RemminaProtocolWidget *gp, const RemminaProtocolFeature *feature)
-
static void rcw_toolbar_multi_monitor_mode(GtkToolItem *toggle, RemminaConnectionWindow *cnnwin)
Definition: rcw.c:1722
-
static void rco_call_protocol_feature_check(GtkMenuItem *menuitem, RemminaConnectionObject *cnnobj)
Definition: rcw.c:1816
+
static void rcw_toolbar_multi_monitor_mode(GtkToolItem *toggle, RemminaConnectionWindow *cnnwin)
Definition: rcw.c:1724
+
static void rco_call_protocol_feature_check(GtkMenuItem *menuitem, RemminaConnectionObject *cnnobj)
Definition: rcw.c:1818
gchar * remmina_pref_file
Definition: rcw.c:78
-
static void rcw_toolbar_fullscreen(GtkToolItem *toggle, RemminaConnectionWindow *cnnwin)
Definition: rcw.c:1351
-
static void rcw_on_switch_page(GtkNotebook *notebook, GtkWidget *newpage, guint page_num, RemminaConnectionWindow *cnnwin)
Definition: rcw.c:3660
+
static void rcw_toolbar_fullscreen(GtkToolItem *toggle, RemminaConnectionWindow *cnnwin)
Definition: rcw.c:1353
+
static void rcw_on_switch_page(GtkNotebook *notebook, GtkWidget *newpage, guint page_num, RemminaConnectionWindow *cnnwin)
Definition: rcw.c:3668
GtkWindow * remmina_main_get_window()
gint remmina_protocol_widget_get_width(RemminaProtocolWidget *gp)
-
static gboolean rcw_hostkey_func(RemminaProtocolWidget *gp, guint keyval, gboolean release)
Definition: rcw.c:4060
+
static gboolean rcw_hostkey_func(RemminaProtocolWidget *gp, guint keyval, gboolean release)
Definition: rcw.c:4073
void remmina_scrolled_viewport_remove_motion(RemminaScrolledViewport *gsv)
gint remmina_protocol_widget_get_profile_remote_width(RemminaProtocolWidget *gp)
-
static void rcw_toolbar_preferences(GtkToolItem *toggle, RemminaConnectionWindow *cnnwin)
Definition: rcw.c:1895
+
static void rcw_toolbar_preferences(GtkToolItem *toggle, RemminaConnectionWindow *cnnwin)
Definition: rcw.c:1897
-
static void nb_set_current_page(GtkNotebook *notebook, GtkWidget *page)
Definition: rcw.c:1221
+
static void nb_set_current_page(GtkNotebook *notebook, GtkWidget *page)
Definition: rcw.c:1223
gchar * keystrokes
Definition: remmina_pref.h:146
-
static gboolean rco_leave_protocol_widget(GtkWidget *widget, GdkEventCrossing *event, RemminaConnectionObject *cnnobj)
Definition: rcw.c:2866
+
static gboolean rco_leave_protocol_widget(GtkWidget *widget, GdkEventCrossing *event, RemminaConnectionObject *cnnobj)
Definition: rcw.c:2874
const gchar * remmina_file_get_filename(RemminaFile *remminafile)
Definition: remmina_file.c:210
guint shortcutkey_dynres
Definition: remmina_pref.h:179
void remmina_public_send_notification(const gchar *notification_id, const gchar *notification_title, const gchar *notification_message)
-
void rco_on_disconnect(RemminaProtocolWidget *gp, gpointer data)
Definition: rcw.c:4320
+
void rco_on_disconnect(RemminaProtocolWidget *gp, gpointer data)
Definition: rcw.c:4333
guint shortcutkey_screenshot
Definition: remmina_pref.h:184
typedefG_BEGIN_DECLS struct _RemminaFile RemminaFile
Definition: types.h:44
guint shortcutkey_prevtab
Definition: remmina_pref.h:177
-
static RemminaConnectionWindow * rcw_create_scrolled(gint width, gint height, gboolean maximize)
Definition: rcw.c:3773
+
static RemminaConnectionWindow * rcw_create_scrolled(gint width, gint height, gboolean maximize)
Definition: rcw.c:3781
gboolean remmina_protocol_widget_has_error(RemminaProtocolWidget *gp)
-
static void rcw_toolbar_dynres(GtkToolItem *toggle, RemminaConnectionWindow *cnnwin)
Definition: rcw.c:1678
-
void rco_show_message_panel(RemminaConnectionObject *cnnobj, RemminaMessagePanel *mp)
Each cnnobj->page can have more than one RemminaMessagePanel, but 0 or 1 are visible.
Definition: rcw.c:4769
+
static void rcw_toolbar_dynres(GtkToolItem *toggle, RemminaConnectionWindow *cnnwin)
Definition: rcw.c:1680
+
void rco_show_message_panel(RemminaConnectionObject *cnnobj, RemminaMessagePanel *mp)
Each cnnobj->page can have more than one RemminaMessagePanel, but 0 or 1 are visible.
Definition: rcw.c:4782
-
static void rcw_toolbar_grab(GtkToolItem *toggle, RemminaConnectionWindow *cnnwin)
Definition: rcw.c:2299
+
static void rcw_toolbar_grab(GtkToolItem *toggle, RemminaConnectionWindow *cnnwin)
Definition: rcw.c:2301
gchar * remmina_protocol_widget_get_domain(RemminaProtocolWidget *gp)
-
gboolean plugin_can_scale
Definition: rcw.c:172
-
GtkWidget * rcw_open_from_file_full(RemminaFile *remminafile, GCallback disconnect_cb, gpointer data, guint *handler)
Definition: rcw.c:4506
+
gboolean plugin_can_scale
Definition: rcw.c:173
+
GtkWidget * rcw_open_from_file_full(RemminaFile *remminafile, GCallback disconnect_cb, gpointer data, guint *handler)
Definition: rcw.c:4519
void remmina_widget_pool_register(GtkWidget *widget)
-
static gboolean rcw_ftb_drag_drop(GtkWidget *widget, GdkDragContext *context, gint x, gint y, guint time, RemminaConnectionWindow *cnnwin)
Definition: rcw.c:3939
-
static void rcw_focus_out(RemminaConnectionWindow *cnnwin)
Definition: rcw.c:2974
-
static void rcw_toolbar_pin(GtkWidget *widget, RemminaConnectionWindow *cnnwin)
Definition: rcw.c:3136
-
static void rcw_destroy(GtkWidget *widget, gpointer data)
Definition: rcw.c:701
-
static void rcw_toolbar_menu_on_launch_item(RemminaAppletMenu *menu, RemminaAppletMenuItem *menuitem, gpointer data)
Definition: rcw.c:1958
+
static gboolean rcw_ftb_drag_drop(GtkWidget *widget, GdkDragContext *context, gint x, gint y, guint time, RemminaConnectionWindow *cnnwin)
Definition: rcw.c:3952
+
static void rcw_focus_out(RemminaConnectionWindow *cnnwin)
Definition: rcw.c:2982
+
static void rcw_toolbar_pin(GtkWidget *widget, RemminaConnectionWindow *cnnwin)
Definition: rcw.c:3144
+
static void rcw_destroy(GtkWidget *widget, gpointer data)
Definition: rcw.c:703
+
static void rcw_toolbar_menu_on_launch_item(RemminaAppletMenu *menu, RemminaAppletMenuItem *menuitem, gpointer data)
Definition: rcw.c:1960
-
static void rcw_create_floating_toolbar(RemminaConnectionWindow *cnnwin, gint mode)
Definition: rcw.c:3144
+
static void rcw_create_floating_toolbar(RemminaConnectionWindow *cnnwin, gint mode)
Definition: rcw.c:3152
-
void rcw_toolbar_preferences_radio(RemminaConnectionObject *cnnobj, RemminaFile *remminafile, GtkWidget *menu, const RemminaProtocolFeature *feature, const gchar *domain, gboolean enabled)
Definition: rcw.c:1837
+
void rcw_toolbar_preferences_radio(RemminaConnectionObject *cnnobj, RemminaFile *remminafile, GtkWidget *menu, const RemminaProtocolFeature *feature, const gchar *domain, gboolean enabled)
Definition: rcw.c:1839
void remmina_protocol_widget_setup(RemminaProtocolWidget *gp, RemminaFile *remminafile, RemminaConnectionObject *cnnobj)
-
static void rcw_create_overlay_ftb_overlay(RemminaConnectionWindow *cnnwin)
Definition: rcw.c:3845
-
gboolean rcw_delete(RemminaConnectionWindow *cnnwin)
Definition: rcw.c:648
+
static void rcw_create_overlay_ftb_overlay(RemminaConnectionWindow *cnnwin)
Definition: rcw.c:3853
+
gboolean rcw_delete(RemminaConnectionWindow *cnnwin)
Definition: rcw.c:650
void remmina_message_panel_focus_auth_entry(RemminaMessagePanel *mp)
-
static void rcw_scaler_keep_aspect(GtkWidget *widget, RemminaConnectionWindow *cnnwin)
Definition: rcw.c:1494
-
static void set_label_selectable(gpointer data, gpointer user_data)
Definition: rcw.c:4459
-
static gboolean rcw_tb_drag_drop(GtkWidget *widget, GdkDragContext *context, gint x, gint y, guint time, gpointer user_data)
Definition: rcw.c:786
+
static void rcw_scaler_keep_aspect(GtkWidget *widget, RemminaConnectionWindow *cnnwin)
Definition: rcw.c:1496
+
static void set_label_selectable(gpointer data, gpointer user_data)
Definition: rcw.c:4472
+
static gboolean rcw_tb_drag_drop(GtkWidget *widget, GdkDragContext *context, gint x, gint y, guint time, gpointer user_data)
Definition: rcw.c:788
-
static void rcw_scaler_expand(GtkWidget *widget, RemminaConnectionWindow *cnnwin)
Definition: rcw.c:1480
+
static void rcw_scaler_expand(GtkWidget *widget, RemminaConnectionWindow *cnnwin)
Definition: rcw.c:1482
-
static void rco_call_protocol_feature_radio(GtkMenuItem *menuitem, RemminaConnectionObject *cnnobj)
Definition: rcw.c:1801
-
GtkWidget * viewport
Definition: rcw.c:168
-
static void rcw_place_toolbar(GtkToolbar *toolbar, GtkGrid *grid, GtkWidget *sibling, int toolbar_placement)
Definition: rcw.c:2601
+
static void rco_call_protocol_feature_radio(GtkMenuItem *menuitem, RemminaConnectionObject *cnnobj)
Definition: rcw.c:1803
+
GtkWidget * viewport
Definition: rcw.c:169
+
static void rcw_place_toolbar(GtkToolbar *toolbar, GtkGrid *grid, GtkWidget *sibling, int toolbar_placement)
Definition: rcw.c:2605
guint shortcutkey_autofit
Definition: remmina_pref.h:176
-
static void rcw_toolbar_preferences_popdown(GtkToolItem *toggle, RemminaConnectionWindow *cnnwin)
Definition: rcw.c:1758
+
static void rcw_toolbar_preferences_popdown(GtkToolItem *toggle, RemminaConnectionWindow *cnnwin)
Definition: rcw.c:1760
void remmina_protocol_widget_call_feature_by_type(RemminaProtocolWidget *gp, RemminaProtocolFeatureType type, gint id)
-
static GtkWidget * rco_create_tab_label(RemminaConnectionObject *cnnobj)
Definition: rcw.c:3526
-
void rco_destroy_message_panel(RemminaConnectionObject *cnnobj, RemminaMessagePanel *mp)
Deletes a RemminaMessagePanel from the current cnnobj and if it was visible, make visible the last re...
Definition: rcw.c:4722
+
static GtkWidget * rco_create_tab_label(RemminaConnectionObject *cnnobj)
Definition: rcw.c:3534
+
void rco_destroy_message_panel(RemminaConnectionObject *cnnobj, RemminaMessagePanel *mp)
Deletes a RemminaMessagePanel from the current cnnobj and if it was visible, make visible the last re...
Definition: rcw.c:4735
void remmina_protocol_widget_set_hostkey_func(RemminaProtocolWidget *gp, RemminaHostkeyFunc func)
-
static void nb_migrate_message_panels(GtkWidget *frompage, GtkWidget *topage)
Definition: rcw.c:1234
- -
static void rco_viewport_fullscreen_mode(GtkWidget *widget, RemminaConnectionObject *cnnobj)
Definition: rcw.c:1387
+
static void nb_migrate_message_panels(GtkWidget *frompage, GtkWidget *topage)
Definition: rcw.c:1236
+ +
static void rco_viewport_fullscreen_mode(GtkWidget *widget, RemminaConnectionObject *cnnobj)
Definition: rcw.c:1389
gint remmina_protocol_widget_get_height(RemminaProtocolWidget *gp)
RemminaScaleMode remmina_protocol_widget_get_current_scale_mode(RemminaProtocolWidget *gp)
-
static void rcw_update_pin(RemminaConnectionWindow *cnnwin)
Definition: rcw.c:3125
-
void rcw_toolbar_fullscreen_option(GtkToolItem *toggle, RemminaConnectionWindow *cnnwin)
Definition: rcw.c:1421
+
static void rcw_update_pin(RemminaConnectionWindow *cnnwin)
Definition: rcw.c:3133
+
void rcw_toolbar_fullscreen_option(GtkToolItem *toggle, RemminaConnectionWindow *cnnwin)
Definition: rcw.c:1423
void remmina_protocol_widget_set_current_scale_mode(RemminaProtocolWidget *gp, RemminaScaleMode scalemode)
-
void rco_on_connect(RemminaProtocolWidget *gp, RemminaConnectionObject *cnnobj)
Definition: rcw.c:4275
-
GtkWidget * proto
Definition: rcw.c:166
-
static GtkWidget * nb_find_page_by_cnnobj(GtkNotebook *notebook, RemminaConnectionObject *cnnobj)
Definition: rcw.c:3459
+
void rco_on_connect(RemminaProtocolWidget *gp, RemminaConnectionObject *cnnobj)
Definition: rcw.c:4288
+
GtkWidget * proto
Definition: rcw.c:167
+
static GtkWidget * nb_find_page_by_cnnobj(GtkNotebook *notebook, RemminaConnectionObject *cnnobj)
Definition: rcw.c:3467
-
static gboolean rcw_keyboard_grab_retry(gpointer user_data)
Definition: rcw.c:484
+
static gboolean rcw_keyboard_grab_retry(gpointer user_data)
Definition: rcw.c:486
void remmina_pref_add_recent(const gchar *protocol, const gchar *server)
Definition: remmina_pref.c:936
-
static gboolean rcw_floating_toolbar_hide(RemminaConnectionWindow *cnnwin)
Definition: rcw.c:2998
-
static gboolean rcw_floating_toolbar_on_enter(GtkWidget *widget, GdkEventCrossing *event, RemminaConnectionWindow *cnnwin)
Definition: rcw.c:2798
-
static const GtkTargetEntry dnd_targets_tb[]
Definition: rcw.c:212
-
static void cb_lasterror_confirmed(void *cbdata, int btn)
Definition: rcw.c:4314
+
static gboolean rcw_floating_toolbar_hide(RemminaConnectionWindow *cnnwin)
Definition: rcw.c:3006
+
static gboolean rcw_floating_toolbar_on_enter(GtkWidget *widget, GdkEventCrossing *event, RemminaConnectionWindow *cnnwin)
Definition: rcw.c:2806
+
static const GtkTargetEntry dnd_targets_tb[]
Definition: rcw.c:214
+
static void cb_lasterror_confirmed(void *cbdata, int btn)
Definition: rcw.c:4327
-
static void rcw_set_toolbar_visibility(RemminaConnectionWindow *cnnwin)
Definition: rcw.c:2755
- +
static void rcw_set_toolbar_visibility(RemminaConnectionWindow *cnnwin)
Definition: rcw.c:2763
+
RemminaAppletMenuItemType item_type
-
void rco_switch_page_activate(GtkMenuItem *menuitem, RemminaConnectionObject *cnnobj)
Definition: rcw.c:1561
-
static void rcw_migrate(RemminaConnectionWindow *from, RemminaConnectionWindow *to)
Definition: rcw.c:1254
-
static void rco_get_desktop_size(RemminaConnectionObject *cnnobj, gint *width, gint *height)
Definition: rcw.c:906
+
void rco_switch_page_activate(GtkMenuItem *menuitem, RemminaConnectionObject *cnnobj)
Definition: rcw.c:1563
+
static void rcw_migrate(RemminaConnectionWindow *from, RemminaConnectionWindow *to)
Definition: rcw.c:1256
+
static void rco_get_desktop_size(RemminaConnectionObject *cnnobj, gint *width, gint *height)
Definition: rcw.c:908
gchar * remmina_pref_get_value(const gchar *key)
-
static RemminaConnectionObject * rcw_get_visible_cnnobj(RemminaConnectionWindow *cnnwin)
Definition: rcw.c:377
-
static gboolean rcw_focus_in_event(GtkWidget *widget, GdkEventWindowState *event, gpointer user_data)
Definition: rcw.c:3247
+
static RemminaConnectionObject * rcw_get_visible_cnnobj(RemminaConnectionWindow *cnnwin)
Definition: rcw.c:379
+
static gboolean rcw_focus_in_event(GtkWidget *widget, GdkEventWindowState *event, gpointer user_data)
Definition: rcw.c:3255
void remmina_protocol_widget_open_connection(RemminaProtocolWidget *gp)
-
void rcw_toolbar_menu_popdown(GtkToolItem *toggle, RemminaConnectionWindow *cnnwin)
Definition: rcw.c:1773
+
void rcw_toolbar_menu_popdown(GtkToolItem *toggle, RemminaConnectionWindow *cnnwin)
Definition: rcw.c:1775
GtkWidget * remmina_applet_menu_new(void)
RemminaConnectionWindowOnDeleteConfirmMode
Definition: rcw.h:66
-
static void rco_check_resize(RemminaConnectionObject *cnnobj)
Definition: rcw.c:1060
+
static void rco_check_resize(RemminaConnectionObject *cnnobj)
Definition: rcw.c:1062
struct _RemminaConnectionWindowPriv RemminaConnectionWindowPriv
Definition: rcw.h:52
gboolean remmina_protocol_widget_unmap_event(RemminaProtocolWidget *gp)
G_DEFINE_TYPE(RemminaConnectionWindow, rcw, GTK_TYPE_WINDOW)
Definition: rcw.c:81
-
static gboolean rcw_map_event_fullscreen(GtkWidget *widget, GdkEvent *event, gpointer data)
Definition: rcw.c:3327
+
static gboolean rcw_map_event_fullscreen(GtkWidget *widget, GdkEvent *event, gpointer data)
Definition: rcw.c:3335
void remmina_protocol_widget_send_keystrokes(RemminaProtocolWidget *gp, GtkMenuItem *widget)
Send to the plugin some keystrokes.
-
static void remmina_protocol_widget_update_alignment(RemminaConnectionObject *cnnobj)
Definition: rcw.c:1148
+
static void remmina_protocol_widget_update_alignment(RemminaConnectionObject *cnnobj)
Definition: rcw.c:1150
GtkWidget * remmina_scrolled_viewport_new(void)
-
static gboolean rcw_on_switch_page_finalsel(gpointer user_data)
Definition: rcw.c:3631
+
static gboolean rcw_on_switch_page_finalsel(gpointer user_data)
Definition: rcw.c:3639
void remmina_file_state_last_success(RemminaFile *remminafile)
Definition: remmina_file.c:948
-
static void print_crossing_event(GdkEventCrossing *event)
Definition: rcw.c:2769
-
static void rcw_toolbar_autofit(GtkToolItem *toggle, RemminaConnectionWindow *cnnwin)
Definition: rcw.c:988
+
static void print_crossing_event(GdkEventCrossing *event)
Definition: rcw.c:2777
+
static void rcw_toolbar_autofit(GtkToolItem *toggle, RemminaConnectionWindow *cnnwin)
Definition: rcw.c:990
General utility functions, non-GTK related.
-
void rcw_toolbar_tools_popdown(GtkToolItem *toggle, RemminaConnectionWindow *cnnwin)
Definition: rcw.c:1787
+
void rcw_toolbar_tools_popdown(GtkToolItem *toggle, RemminaConnectionWindow *cnnwin)
Definition: rcw.c:1789
guint shortcutkey_multimon
Definition: remmina_pref.h:181
-
static void rcw_ftb_drag_begin(GtkWidget *widget, GdkDragContext *context, gpointer user_data)
Definition: rcw.c:3968
+
static void rcw_ftb_drag_begin(GtkWidget *widget, GdkDragContext *context, gpointer user_data)
Definition: rcw.c:3981
const gchar * screenshot_path
Definition: remmina_pref.h:137
@@ -247,33 +247,33 @@ $(document).ready(function(){initNavTree('rcw_8c_source.html','');}); -
GtkWindow * rcw_get_gtkwindow(RemminaConnectionObject *cnnobj)
Definition: rcw.c:4703
- +
GtkWindow * rcw_get_gtkwindow(RemminaConnectionObject *cnnobj)
Definition: rcw.c:4716
+
GtkWidget * remmina_protocol_widget_new(void)
-
static const GtkTargetEntry dnd_targets_ftb[]
Definition: rcw.c:203
+
static const GtkTargetEntry dnd_targets_ftb[]
Definition: rcw.c:205
gboolean confirm_close
Definition: remmina_pref.h:147
-
static void rpw_size_allocated_on_connection(GtkWidget *w, GdkRectangle *allocation, gpointer user_data)
Definition: rcw.c:4440
+
static void rpw_size_allocated_on_connection(GtkWidget *w, GdkRectangle *allocation, gpointer user_data)
Definition: rcw.c:4453
gint toolbar_placement
Definition: remmina_pref.h:225
RemminaProtocolFeatureType type
Definition: types.h:73
-
gboolean rcw_toolbar_autofit_restore(RemminaConnectionWindow *cnnwin)
Definition: rcw.c:950
+
gboolean rcw_toolbar_autofit_restore(RemminaConnectionWindow *cnnwin)
Definition: rcw.c:952
gboolean remmina_protocol_widget_map_event(RemminaProtocolWidget *gp)
-
GtkWidget * aspectframe
Definition: rcw.c:167
-
static void rcw_toolbar_tools(GtkToolItem *toggle, RemminaConnectionWindow *cnnwin)
Definition: rcw.c:2015
-
static gboolean rcw_on_enter_notify_event(GtkWidget *widget, GdkEventCrossing *event, gpointer user_data)
Definition: rcw.c:2816
+
GtkWidget * aspectframe
Definition: rcw.c:168
+
static void rcw_toolbar_tools(GtkToolItem *toggle, RemminaConnectionWindow *cnnwin)
Definition: rcw.c:2017
+
static gboolean rcw_on_enter_notify_event(GtkWidget *widget, GdkEventCrossing *event, gpointer user_data)
Definition: rcw.c:2824
void remmina_applet_menu_set_hide_count(RemminaAppletMenu *menu, gboolean hide_count)
gboolean remmina_plugin_manager_query_feature_by_type(RemminaPluginType ptype, const gchar *name, RemminaProtocolFeatureType ftype)
-
static void rcw_on_page_removed(GtkNotebook *notebook, GtkWidget *child, guint page_num, RemminaConnectionWindow *cnnwin)
Definition: rcw.c:3680
-
static RemminaConnectionWindow * rcw_new(gboolean fullscreen, int full_screen_target_monitor)
Definition: rcw.c:3387
-
static gboolean rcw_on_leave_notify_event(GtkWidget *widget, GdkEventCrossing *event, gpointer user_data)
Definition: rcw.c:2829
-
void rcw_set_delete_confirm_mode(RemminaConnectionWindow *cnnwin, RemminaConnectionWindowOnDeleteConfirmMode mode)
Definition: rcw.c:4712
-
void rco_on_update_align(RemminaProtocolWidget *gp, gpointer data)
Definition: rcw.c:4379
-
gboolean rcw_notify_widget_toolbar_placement(GtkWidget *widget, gpointer data)
Definition: rcw.c:756
-
static void rco_scrolled_fullscreen_mode(GtkWidget *widget, RemminaConnectionObject *cnnobj)
Definition: rcw.c:1399
-
static void rco_disconnect_current_page(RemminaConnectionObject *cnnobj)
Definition: rcw.c:421
+
static void rcw_on_page_removed(GtkNotebook *notebook, GtkWidget *child, guint page_num, RemminaConnectionWindow *cnnwin)
Definition: rcw.c:3688
+
static RemminaConnectionWindow * rcw_new(gboolean fullscreen, int full_screen_target_monitor)
Definition: rcw.c:3395
+
static gboolean rcw_on_leave_notify_event(GtkWidget *widget, GdkEventCrossing *event, gpointer user_data)
Definition: rcw.c:2837
+
void rcw_set_delete_confirm_mode(RemminaConnectionWindow *cnnwin, RemminaConnectionWindowOnDeleteConfirmMode mode)
Definition: rcw.c:4725
+
void rco_on_update_align(RemminaProtocolWidget *gp, gpointer data)
Definition: rcw.c:4392
+
gboolean rcw_notify_widget_toolbar_placement(GtkWidget *widget, gpointer data)
Definition: rcw.c:758
+
static void rco_scrolled_fullscreen_mode(GtkWidget *widget, RemminaConnectionObject *cnnobj)
Definition: rcw.c:1401
+
static void rco_disconnect_current_page(RemminaConnectionObject *cnnobj)
Definition: rcw.c:423
void remmina_file_set_int(RemminaFile *remminafile, const gchar *setting, gint value)
Definition: remmina_file.c:585
guint shortcutkey_disconnect
Definition: remmina_pref.h:186
@@ -287,23 +287,23 @@ $(document).ready(function(){initNavTree('rcw_8c_source.html','');});
guint shortcutkey_grab
Definition: remmina_pref.h:182
gboolean(* RemminaHostkeyFunc)(RemminaProtocolWidget *gp, guint keyval, gboolean release)
gboolean always_show_tab
Definition: remmina_pref.h:152
-
gboolean dynres_unlocked
Definition: rcw.c:175
+
gboolean dynres_unlocked
Definition: rcw.c:176
gboolean toolbar_pin_down
Definition: remmina_pref.h:223
-
static void rcw_toolbar_switch_page(GtkToolItem *toggle, RemminaConnectionWindow *cnnwin)
Definition: rcw.c:1582
+
static void rcw_toolbar_switch_page(GtkToolItem *toggle, RemminaConnectionWindow *cnnwin)
Definition: rcw.c:1584
guint shortcutkey_scale
Definition: remmina_pref.h:180
-
static void rcw_toolbar_place_signal(RemminaConnectionWindow *cnnwin, gpointer data)
Definition: rcw.c:3207
+
static void rcw_toolbar_place_signal(RemminaConnectionWindow *cnnwin, gpointer data)
Definition: rcw.c:3215
void remmina_protocol_widget_send_clipboard(RemminaProtocolWidget *gp, GtkMenuItem *widget)
-
static GtkNotebook * rcw_on_notebook_create_window(GtkNotebook *notebook, GtkWidget *page, gint x, gint y, gpointer data)
Definition: rcw.c:3691
+
static GtkNotebook * rcw_on_notebook_create_window(GtkNotebook *notebook, GtkWidget *page, gint x, gint y, gpointer data)
Definition: rcw.c:3699
struct _RemminaConnectionObject RemminaConnectionObject
-
static void rcw_toolbar_minimize(GtkToolItem *toggle, RemminaConnectionWindow *cnnwin)
Definition: rcw.c:2277
+
static void rcw_toolbar_minimize(GtkToolItem *toggle, RemminaConnectionWindow *cnnwin)
Definition: rcw.c:2279
RemminaConnectionObject * cnnobj
-
static void rcw_pointer_grab(RemminaConnectionWindow *cnnwin)
Definition: rcw.c:512
-
static void rco_call_protocol_feature_activate(GtkMenuItem *menuitem, RemminaConnectionObject *cnnobj)
Definition: rcw.c:1828
-
static gboolean rcw_unmap_event(GtkWidget *widget, GdkEvent *event, gpointer data)
Definition: rcw.c:3309
-
static void rcw_toolbar_menu(GtkToolItem *toggle, RemminaConnectionWindow *cnnwin)
Definition: rcw.c:1978
-
static guint rcw_signals[LAST_SIGNAL]
Definition: rcw.c:185
+
static void rcw_pointer_grab(RemminaConnectionWindow *cnnwin)
Definition: rcw.c:514
+
static void rco_call_protocol_feature_activate(GtkMenuItem *menuitem, RemminaConnectionObject *cnnobj)
Definition: rcw.c:1830
+
static gboolean rcw_unmap_event(GtkWidget *widget, GdkEvent *event, gpointer data)
Definition: rcw.c:3317
+
static void rcw_toolbar_menu(GtkToolItem *toggle, RemminaConnectionWindow *cnnwin)
Definition: rcw.c:1980
+
static guint rcw_signals[LAST_SIGNAL]
Definition: rcw.c:186
void remmina_protocol_widget_update_remote_resolution(RemminaProtocolWidget *gp)
gboolean fullscreen
Definition: remmina.c:89
void remmina_protocol_widget_close_connection(RemminaProtocolWidget *gp)
@@ -312,133 +312,133 @@ $(document).ready(function(){initNavTree('rcw_8c_source.html','');});
gboolean remmina_protocol_widget_plugin_screenshot(RemminaProtocolWidget *gp, RemminaPluginScreenshotData *rpsd)
void remmina_applet_menu_populate(RemminaAppletMenu *menu)
gboolean remmina_pref_get_boolean(const gchar *key)
-
GTKSOCKET_NOT_AVAIL_RESPONSE_TYPE
These define the response id&#39;s of the gtksocket-is-not-available-warning-dialog buttons.
Definition: rcw.c:4472
-
static void rcw_kp_ungrab(RemminaConnectionWindow *cnnwin)
Definition: rcw.c:429
+
GTKSOCKET_NOT_AVAIL_RESPONSE_TYPE
These define the response id&#39;s of the gtksocket-is-not-available-warning-dialog buttons.
Definition: rcw.c:4485
+
static void rcw_kp_ungrab(RemminaConnectionWindow *cnnwin)
Definition: rcw.c:431
void remmina_protocol_widget_grab_focus(RemminaProtocolWidget *gp)
-
static void rcw_toolbar_scaled_mode(GtkToolItem *toggle, RemminaConnectionWindow *cnnwin)
Definition: rcw.c:1701
-
void rco_closewin(RemminaProtocolWidget *gp)
Definition: rcw.c:3480
-
static gboolean rcw_on_configure(GtkWidget *widget, GdkEventConfigure *event, gpointer data)
Definition: rcw.c:3093
-
static void rcw_fullscreen_option_popdown(GtkWidget *widget, RemminaConnectionWindow *cnnwin)
Definition: rcw.c:1411
-
void rcw_toolbar_preferences_check(RemminaConnectionObject *cnnobj, GtkWidget *menu, const RemminaProtocolFeature *feature, const gchar *domain, gboolean enabled)
Definition: rcw.c:1871
-
static RemminaConnectionWindow * rcw_create_fullscreen(GtkWindow *old, gint view_mode)
Definition: rcw.c:3991
-
static GtkWidget * rcw_create_toolbar(RemminaConnectionWindow *cnnwin, gint mode)
Definition: rcw.c:2329
-
static gboolean open_connection_last_stage(gpointer user_data)
Definition: rcw.c:4428
+
static void rcw_toolbar_scaled_mode(GtkToolItem *toggle, RemminaConnectionWindow *cnnwin)
Definition: rcw.c:1703
+
void rco_closewin(RemminaProtocolWidget *gp)
Definition: rcw.c:3488
+
static gboolean rcw_on_configure(GtkWidget *widget, GdkEventConfigure *event, gpointer data)
Definition: rcw.c:3101
+
static void rcw_fullscreen_option_popdown(GtkWidget *widget, RemminaConnectionWindow *cnnwin)
Definition: rcw.c:1413
+
void rcw_toolbar_preferences_check(RemminaConnectionObject *cnnobj, GtkWidget *menu, const RemminaProtocolFeature *feature, const gchar *domain, gboolean enabled)
Definition: rcw.c:1873
+
static RemminaConnectionWindow * rcw_create_fullscreen(GtkWindow *old, gint view_mode)
Definition: rcw.c:4004
+
static GtkWidget * rcw_create_toolbar(RemminaConnectionWindow *cnnwin, gint mode)
Definition: rcw.c:2333
+
static gboolean open_connection_last_stage(gpointer user_data)
Definition: rcw.c:4441
guint shortcutkey_nexttab
Definition: remmina_pref.h:178
-
static RemminaConnectionObject * rcw_get_cnnobj_at_page(RemminaConnectionWindow *cnnwin, gint npage)
Definition: rcw.c:367
-
static gboolean rcw_tb_drag_failed(GtkWidget *widget, GdkDragContext *context, GtkDragResult result, gpointer user_data)
Definition: rcw.c:769
+
static RemminaConnectionObject * rcw_get_cnnobj_at_page(RemminaConnectionWindow *cnnwin, gint npage)
Definition: rcw.c:369
+
static gboolean rcw_tb_drag_failed(GtkWidget *widget, GdkDragContext *context, GtkDragResult result, gpointer user_data)
Definition: rcw.c:771
gboolean applet_hide_count
Definition: remmina_pref.h:163
gint remmina_protocol_widget_get_multimon(RemminaProtocolWidget *gp)
-
GtkWidget * rcw_get_gtkviewport(RemminaConnectionObject *cnnobj)
Definition: rcw.c:4707
-
static gboolean rcw_floating_toolbar_on_leave(GtkWidget *widget, GdkEventCrossing *event, RemminaConnectionWindow *cnnwin)
Definition: rcw.c:2806
-
static gboolean rcw_state_event(GtkWidget *widget, GdkEventWindowState *event, gpointer user_data)
Definition: rcw.c:3268
-
static void rcw_class_init(RemminaConnectionWindowClass *klass)
Definition: rcw.c:221
-
void rco_on_unlock_dynres(RemminaProtocolWidget *gp, gpointer data)
Definition: rcw.c:4396
+
GtkWidget * rcw_get_gtkviewport(RemminaConnectionObject *cnnobj)
Definition: rcw.c:4720
+
static gboolean rcw_floating_toolbar_on_leave(GtkWidget *widget, GdkEventCrossing *event, RemminaConnectionWindow *cnnwin)
Definition: rcw.c:2814
+
static gboolean rcw_state_event(GtkWidget *widget, GdkEventWindowState *event, gpointer user_data)
Definition: rcw.c:3276
+
static void rcw_class_init(RemminaConnectionWindowClass *klass)
Definition: rcw.c:223
+
void rco_on_unlock_dynres(RemminaProtocolWidget *gp, gpointer data)
Definition: rcw.c:4409
-
static void rcw_toolbar_open_main(GtkToolItem *toggle, RemminaConnectionWindow *cnnwin)
Definition: rcw.c:1748
-
static GtkNotebook * rcw_create_notebook(RemminaConnectionWindow *cnnwin)
Definition: rcw.c:3753
-
static void rcw_toolbar_disconnect(GtkToolItem *toggle, RemminaConnectionWindow *cnnwin)
Definition: rcw.c:2288
+
static void rcw_toolbar_open_main(GtkToolItem *toggle, RemminaConnectionWindow *cnnwin)
Definition: rcw.c:1750
+
static GtkNotebook * rcw_create_notebook(RemminaConnectionWindow *cnnwin)
Definition: rcw.c:3761
+
static void rcw_toolbar_disconnect(GtkToolItem *toggle, RemminaConnectionWindow *cnnwin)
Definition: rcw.c:2290
RemminaConnectionWindowPriv * priv
Definition: rcw.h:56
gboolean remmina_protocol_widget_is_closed(RemminaProtocolWidget *gp)
-
void rcw_open_from_file(RemminaFile *remminafile)
Definition: rcw.c:4453
+
void rcw_open_from_file(RemminaFile *remminafile)
Definition: rcw.c:4466
- +
RemminaPref remmina_pref
Definition: rcw.c:79
RemminaScaleMode
Definition: types.h:142
-
static void rcw_floating_toolbar_show(RemminaConnectionWindow *cnnwin, gboolean show)
Definition: rcw.c:879
+
static void rcw_floating_toolbar_show(RemminaConnectionWindow *cnnwin, gboolean show)
Definition: rcw.c:881
GType rcw_get_type(void) G_GNUC_CONST
-
static GtkWidget * rco_create_scrolled_container(RemminaScaleMode scalemode, int view_mode)
Definition: rcw.c:931
-
static void rcw_toolbar_scaler_option(GtkToolItem *toggle, RemminaConnectionWindow *cnnwin)
Definition: rcw.c:1510
+
static GtkWidget * rco_create_scrolled_container(RemminaScaleMode scalemode, int view_mode)
Definition: rcw.c:933
+
static void rcw_toolbar_scaler_option(GtkToolItem *toggle, RemminaConnectionWindow *cnnwin)
Definition: rcw.c:1512
gint remmina_file_get_int(RemminaFile *remminafile, const gchar *setting, gint default_value)
Definition: remmina_file.c:603
-
void rco_on_desktop_resize(RemminaProtocolWidget *gp, gpointer data)
Definition: rcw.c:4370
+
void rco_on_desktop_resize(RemminaProtocolWidget *gp, gpointer data)
Definition: rcw.c:4383
guint remmina_utils_string_replace_all(GString *haystack, const gchar *needle, const gchar *replace)
Replaces all occurrences of needle in haystack with replace.
-
static gboolean rcw_focus_out_event(GtkWidget *widget, GdkEventWindowState *event, gpointer user_data)
Definition: rcw.c:3257
-
static void rcw_pointer_ungrab(RemminaConnectionWindow *cnnwin)
Definition: rcw.c:497
+
static gboolean rcw_focus_out_event(GtkWidget *widget, GdkEventWindowState *event, gpointer user_data)
Definition: rcw.c:3265
+
static void rcw_pointer_ungrab(RemminaConnectionWindow *cnnwin)
Definition: rcw.c:499
guint shortcutkey_toolbar
Definition: remmina_pref.h:187
-
GtkWidget * scrolled_container
Definition: rcw.c:170
+
GtkWidget * scrolled_container
Definition: rcw.c:171
void remmina_protocol_widget_call_feature_by_ref(RemminaProtocolWidget *gp, const RemminaProtocolFeature *feature)
-
RemminaFile * remmina_file
Definition: rcw.c:164
-
static void rcw_gtksocket_not_available_dialog_response(GtkDialog *self, gint response_id, RemminaConnectionObject *cnnobj)
Gets called if the user interacts with the gtksocket-is-not-available-warning-dialog.
Definition: rcw.c:4481
-
static void rco_change_scalemode(RemminaConnectionObject *cnnobj, gboolean bdyn, gboolean bscale)
Definition: rcw.c:1651
+
RemminaFile * remmina_file
Definition: rcw.c:165
+
static void rcw_gtksocket_not_available_dialog_response(GtkDialog *self, gint response_id, RemminaConnectionObject *cnnobj)
Gets called if the user interacts with the gtksocket-is-not-available-warning-dialog.
Definition: rcw.c:4494
+
static void rco_change_scalemode(RemminaConnectionObject *cnnobj, gboolean bdyn, gboolean bscale)
Definition: rcw.c:1653
gint remmina_protocol_widget_get_profile_remote_height(RemminaProtocolWidget *gp)
-
static void rcw_update_notebook(RemminaConnectionWindow *cnnwin)
Definition: rcw.c:3610
+
static void rcw_update_notebook(RemminaConnectionWindow *cnnwin)
Definition: rcw.c:3618
gboolean remmina_pref_save(void)
Definition: remmina_pref.c:785
-
void rcw_toolbar_switch_page_popdown(GtkWidget *widget, RemminaConnectionWindow *cnnwin)
Definition: rcw.c:1571
-
static gboolean rcw_floating_toolbar_make_invisible(gpointer data)
Definition: rcw.c:869
+
void rcw_toolbar_switch_page_popdown(GtkWidget *widget, RemminaConnectionWindow *cnnwin)
Definition: rcw.c:1573
+
static gboolean rcw_floating_toolbar_make_invisible(gpointer data)
Definition: rcw.c:871
const gchar * screenshot_name
Definition: remmina_pref.h:139
- -
static gboolean rcw_floating_toolbar_on_scroll(GtkWidget *widget, GdkEventScroll *event, RemminaConnectionWindow *cnnwin)
Definition: rcw.c:3008
+ +
static gboolean rcw_floating_toolbar_on_scroll(GtkWidget *widget, GdkEventScroll *event, RemminaConnectionWindow *cnnwin)
Definition: rcw.c:3016
gboolean hide_connection_toolbar
Definition: remmina_pref.h:154
-
void rcw_update_toolbar_opacity(RemminaConnectionWindow *cnnwin)
Definition: rcw.c:853
+
void rcw_update_toolbar_opacity(RemminaConnectionWindow *cnnwin)
Definition: rcw.c:855
-
gboolean rcw_open_from_filename(const gchar *filename)
Definition: rcw.c:4405
-
static void rcw_close_all_connections(RemminaConnectionWindow *cnnwin)
Definition: rcw.c:629
-
void rco_set_scrolled_policy(RemminaScaleMode scalemode, GtkScrolledWindow *scrolled_window)
Definition: rcw.c:922
+
gboolean rcw_open_from_filename(const gchar *filename)
Definition: rcw.c:4418
+
static void rcw_close_all_connections(RemminaConnectionWindow *cnnwin)
Definition: rcw.c:631
+
void rco_set_scrolled_policy(RemminaScaleMode scalemode, GtkScrolledWindow *scrolled_window)
Definition: rcw.c:924
-
static void rcw_toolbar_screenshot(GtkToolItem *toggle, RemminaConnectionWindow *cnnwin)
Definition: rcw.c:2137
-
RemminaConnectionWindow * cnnwin
Definition: rcw.c:163
+
static void rcw_toolbar_screenshot(GtkToolItem *toggle, RemminaConnectionWindow *cnnwin)
Definition: rcw.c:2139
+
RemminaConnectionWindow * cnnwin
Definition: rcw.c:164
void remmina_message_panel_setup_message(RemminaMessagePanel *mp, const gchar *message, RemminaMessagePanelCallback response_callback, gpointer response_callback_data)
gboolean kioskmode
Definition: remmina.c:87
void remmina_application_condexit(RemminaCondExitType why)
Definition: remmina_exec.c:123
void remmina_protocol_widget_set_expand(RemminaProtocolWidget *gp, gboolean expand)
-
static gboolean focus_in_delayed_grab(RemminaConnectionWindow *cnnwin)
Definition: rcw.c:2925
-
void rco_update_toolbar_autofit_button(RemminaConnectionObject *cnnobj)
Definition: rcw.c:1633
+
static gboolean focus_in_delayed_grab(RemminaConnectionWindow *cnnwin)
Definition: rcw.c:2933
+
void rco_update_toolbar_autofit_button(RemminaConnectionObject *cnnobj)
Definition: rcw.c:1635
gboolean extrahardening
Definition: remmina.c:90
void remmina_file_set_string(RemminaFile *remminafile, const gchar *setting, const gchar *value)
Definition: remmina_file.c:469
void remmina_file_save(RemminaFile *remminafile)
Definition: remmina_file.c:730
-
static GtkWidget * rcw_append_new_page(RemminaConnectionWindow *cnnwin, RemminaConnectionObject *cnnobj)
Definition: rcw.c:3580
-
void rco_get_monitor_geometry(RemminaConnectionObject *cnnobj, GdkRectangle *sz)
Definition: rcw.c:1009
+
static GtkWidget * rcw_append_new_page(RemminaConnectionWindow *cnnwin, RemminaConnectionObject *cnnobj)
Definition: rcw.c:3588
+
void rco_get_monitor_geometry(RemminaConnectionObject *cnnobj, GdkRectangle *sz)
Definition: rcw.c:1011
gboolean remmina_protocol_widget_plugin_receives_keystrokes(RemminaProtocolWidget *gp)
Check if the plugin accepts keystrokes.
RemminaFile * remmina_file_manager_load_file(const gchar *filename)
-
gboolean rco_enter_protocol_widget(GtkWidget *widget, GdkEventCrossing *event, RemminaConnectionObject *cnnobj)
Definition: rcw.c:2891
+
gboolean rco_enter_protocol_widget(GtkWidget *widget, GdkEventCrossing *event, RemminaConnectionObject *cnnobj)
Definition: rcw.c:2899
-
static GtkWidget * rco_create_tab_page(RemminaConnectionObject *cnnobj)
Definition: rcw.c:3569
-
static gboolean rcw_delete_event(GtkWidget *widget, GdkEvent *event, gpointer data)
Definition: rcw.c:694
-
static gboolean rcw_map_event(GtkWidget *widget, GdkEvent *event, gpointer data)
Definition: rcw.c:3289
+
static GtkWidget * rco_create_tab_page(RemminaConnectionObject *cnnobj)
Definition: rcw.c:3577
+
static gboolean rcw_delete_event(GtkWidget *widget, GdkEvent *event, gpointer data)
Definition: rcw.c:696
+
static gboolean rcw_map_event(GtkWidget *widget, GdkEvent *event, gpointer data)
Definition: rcw.c:3297
gint remmina_unlock_new(GtkWindow *parent)
const gchar * remmina_file_get_icon_name(RemminaFile *remminafile)
Definition: remmina_file.c:885
gint remmina_widget_pool_foreach(RemminaWidgetPoolForEachFunc callback, gpointer data)
-
gboolean connected
Definition: rcw.c:174
+
gboolean connected
Definition: rcw.c:175
guint shortcutkey_viewonly
Definition: remmina_pref.h:183
-
void rco_on_close_button_clicked(GtkButton *button, RemminaConnectionObject *cnnobj)
Definition: rcw.c:3515
-
static void rcw_switch_viewmode(RemminaConnectionWindow *cnnwin, int newmode)
Definition: rcw.c:1311
+
void rco_on_close_button_clicked(GtkButton *button, RemminaConnectionObject *cnnobj)
Definition: rcw.c:3523
+
static void rcw_switch_viewmode(RemminaConnectionWindow *cnnwin, int newmode)
Definition: rcw.c:1313
unsigned char * buffer
Definition: types.h:84
-
static void rcw_on_page_added(GtkNotebook *notebook, GtkWidget *child, guint page_num, RemminaConnectionWindow *cnnwin)
Definition: rcw.c:3673
+
static void rcw_on_page_added(GtkNotebook *notebook, GtkWidget *child, guint page_num, RemminaConnectionWindow *cnnwin)
Definition: rcw.c:3681
gboolean fullscreen_on_auto
Definition: remmina_pref.h:151
N_("Unable to connect to VNC server")
Definition: vnc_plugin.c:953
-
static void rcw_toolbar_duplicate(GtkToolItem *toggle, RemminaConnectionWindow *cnnwin)
Definition: rcw.c:2122
-
void rcw_grab_focus(RemminaConnectionWindow *cnnwin)
Definition: rcw.c:3448
+
static void rcw_toolbar_duplicate(GtkToolItem *toggle, RemminaConnectionWindow *cnnwin)
Definition: rcw.c:2124
+
void rcw_grab_focus(RemminaConnectionWindow *cnnwin)
Definition: rcw.c:3456
gboolean dark_theme
Definition: remmina_pref.h:149
RemminaMessagePanel * remmina_message_panel_new()
-
static void rcw_scaler_option_popdown(GtkWidget *widget, RemminaConnectionWindow *cnnwin)
Definition: rcw.c:1468
+
static void rcw_scaler_option_popdown(GtkWidget *widget, RemminaConnectionWindow *cnnwin)
Definition: rcw.c:1470
gboolean save_view_mode
Definition: remmina_pref.h:140
-
static void rcw_set_tooltip(GtkWidget *item, const gchar *tip, guint key1, guint key2)
Definition: rcw.c:1124
+
static void rcw_set_tooltip(GtkWidget *item, const gchar *tip, guint key1, guint key2)
Definition: rcw.c:1126
gint fullscreen_toolbar_visibility
Definition: remmina_pref.h:158
GtkWidget * remmina_widget_pool_find(GType type, const gchar *tag)
-
gboolean rcw_delayed_window_present(gpointer user_data)
Definition: rcw.c:4263
+
gboolean rcw_delayed_window_present(gpointer user_data)
Definition: rcw.c:4276
GtkWindow window
Definition: rcw.h:55
-
void rco_on_lock_dynres(RemminaProtocolWidget *gp, gpointer data)
Definition: rcw.c:4387
+
void rco_on_lock_dynres(RemminaProtocolWidget *gp, gpointer data)
Definition: rcw.c:4400
-
static void rcw_keyboard_grab(RemminaConnectionWindow *cnnwin)
Definition: rcw.c:545
+
static void rcw_keyboard_grab(RemminaConnectionWindow *cnnwin)
Definition: rcw.c:547
diff --git a/public/rcw_8h.html b/public/rcw_8h.html index e173e37ea..f76533c1a 100644 --- a/public/rcw_8h.html +++ b/public/rcw_8h.html @@ -257,7 +257,7 @@ Functions

Deletes a RemminaMessagePanel from the current cnnobj and if it was visible, make visible the last remaining one.

-

Definition at line 4722 of file rcw.c.

+

Definition at line 4735 of file rcw.c.

@@ -287,7 +287,7 @@ Functions
-

Definition at line 1009 of file rcw.c.

+

Definition at line 1011 of file rcw.c.

@@ -320,7 +320,7 @@ Functions

Each cnnobj->page can have more than one RemminaMessagePanel, but 0 or 1 are visible.

This function adds a RemminaMessagePanel to cnnobj->page, move it to top, and makes it the only visible one.

-

Definition at line 4769 of file rcw.c.

+

Definition at line 4782 of file rcw.c.

@@ -340,7 +340,7 @@ Functions
-

Definition at line 648 of file rcw.c.

+

Definition at line 650 of file rcw.c.

@@ -360,7 +360,7 @@ Functions
-

Definition at line 4707 of file rcw.c.

+

Definition at line 4720 of file rcw.c.

@@ -380,7 +380,7 @@ Functions
-

Definition at line 4703 of file rcw.c.

+

Definition at line 4716 of file rcw.c.

@@ -418,7 +418,7 @@ Functions
-

Definition at line 4453 of file rcw.c.

+

Definition at line 4466 of file rcw.c.

@@ -460,7 +460,7 @@ Functions
-

Definition at line 4506 of file rcw.c.

+

Definition at line 4519 of file rcw.c.

@@ -480,7 +480,7 @@ Functions
-

Definition at line 4405 of file rcw.c.

+

Definition at line 4418 of file rcw.c.

@@ -510,7 +510,7 @@ Functions
-

Definition at line 4712 of file rcw.c.

+

Definition at line 4725 of file rcw.c.

diff --git a/public/rcw_8h_source.html b/public/rcw_8h_source.html index 3bbb1cb50..89d12ae9f 100644 --- a/public/rcw_8h_source.html +++ b/public/rcw_8h_source.html @@ -86,33 +86,33 @@ $(document).ready(function(){initNavTree('rcw_8h_source.html','');});
rcw.h
-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 #pragma once
38 
39 #include <gtk/gtk.h>
40 #include "remmina_file.h"
41 #include "remmina_message_panel.h"
42 
43 G_BEGIN_DECLS
44 
45 #define REMMINA_TYPE_CONNECTION_WINDOW (rcw_get_type())
46 #define RCW(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), REMMINA_TYPE_CONNECTION_WINDOW, RemminaConnectionWindow))
47 #define RCW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), REMMINA_TYPE_CONNECTION_WINDOW, RemminaConnectionWindowClass))
48 #define REMMINA_IS_CONNECTION_WINDOW(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), REMMINA_TYPE_CONNECTION_WINDOW))
49 #define REMMINA_IS_CONNECTION_WINDOW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), REMMINA_TYPE_CONNECTION_WINDOW))
50 #define RCW_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), REMMINA_TYPE_CONNECTION_WINDOW, RemminaConnectionWindowClass))
51 
52 typedef struct _RemminaConnectionWindowPriv RemminaConnectionWindowPriv;
53 
54 typedef struct _RemminaConnectionWindow {
55  GtkWindow window;
58 
60  GtkWindowClass parent_class;
61  void (*toolbar_place)(RemminaConnectionWindow *gp);
63 
65 
66 typedef enum {
70 
71 GType rcw_get_type(void)
72 G_GNUC_CONST;
73 
74 /* Open a new connection window for a .remmina file */
75 gboolean rcw_open_from_filename(const gchar *filename);
76 /* Open a new connection window for a given RemminaFile struct. The struct will be freed after the call */
77 void rcw_open_from_file(RemminaFile *remminafile);
80 GtkWidget *rcw_open_from_file_full(RemminaFile *remminafile, GCallback disconnect_cb, gpointer data, guint *handler);
81 GtkWindow* rcw_get_gtkwindow(RemminaConnectionObject *cnnobj);
83 
84 void rco_destroy_message_panel(RemminaConnectionObject *cnnobj, RemminaMessagePanel *mp);
85 void rco_show_message_panel(RemminaConnectionObject *cnnobj, RemminaMessagePanel *mp);
86 void rco_get_monitor_geometry(RemminaConnectionObject *cnnobj, GdkRectangle *sz);
87 
88 
89 
90 #define MESSAGE_PANEL_SPINNER 0x00000001
91 #define MESSAGE_PANEL_OKBUTTON 0x00000002
92 
93 G_END_DECLS
void rcw_set_delete_confirm_mode(RemminaConnectionWindow *cnnwin, RemminaConnectionWindowOnDeleteConfirmMode mode)
Definition: rcw.c:4712
+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 #pragma once
38 
39 #include <gtk/gtk.h>
40 #include "remmina_file.h"
41 #include "remmina_message_panel.h"
42 
43 G_BEGIN_DECLS
44 
45 #define REMMINA_TYPE_CONNECTION_WINDOW (rcw_get_type())
46 #define RCW(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), REMMINA_TYPE_CONNECTION_WINDOW, RemminaConnectionWindow))
47 #define RCW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), REMMINA_TYPE_CONNECTION_WINDOW, RemminaConnectionWindowClass))
48 #define REMMINA_IS_CONNECTION_WINDOW(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), REMMINA_TYPE_CONNECTION_WINDOW))
49 #define REMMINA_IS_CONNECTION_WINDOW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), REMMINA_TYPE_CONNECTION_WINDOW))
50 #define RCW_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), REMMINA_TYPE_CONNECTION_WINDOW, RemminaConnectionWindowClass))
51 
52 typedef struct _RemminaConnectionWindowPriv RemminaConnectionWindowPriv;
53 
54 typedef struct _RemminaConnectionWindow {
55  GtkWindow window;
58 
60  GtkWindowClass parent_class;
61  void (*toolbar_place)(RemminaConnectionWindow *gp);
63 
65 
66 typedef enum {
70 
71 GType rcw_get_type(void)
72 G_GNUC_CONST;
73 
74 /* Open a new connection window for a .remmina file */
75 gboolean rcw_open_from_filename(const gchar *filename);
76 /* Open a new connection window for a given RemminaFile struct. The struct will be freed after the call */
77 void rcw_open_from_file(RemminaFile *remminafile);
80 GtkWidget *rcw_open_from_file_full(RemminaFile *remminafile, GCallback disconnect_cb, gpointer data, guint *handler);
81 GtkWindow* rcw_get_gtkwindow(RemminaConnectionObject *cnnobj);
83 
84 void rco_destroy_message_panel(RemminaConnectionObject *cnnobj, RemminaMessagePanel *mp);
85 void rco_show_message_panel(RemminaConnectionObject *cnnobj, RemminaMessagePanel *mp);
86 void rco_get_monitor_geometry(RemminaConnectionObject *cnnobj, GdkRectangle *sz);
87 
88 
89 
90 #define MESSAGE_PANEL_SPINNER 0x00000001
91 #define MESSAGE_PANEL_OKBUTTON 0x00000002
92 
93 G_END_DECLS
void rcw_set_delete_confirm_mode(RemminaConnectionWindow *cnnwin, RemminaConnectionWindowOnDeleteConfirmMode mode)
Definition: rcw.c:4725
typedefG_BEGIN_DECLS struct _RemminaFile RemminaFile
Definition: types.h:44
-
GtkWindow * rcw_get_gtkwindow(RemminaConnectionObject *cnnobj)
Definition: rcw.c:4703
-
GtkWidget * rcw_open_from_file_full(RemminaFile *remminafile, GCallback disconnect_cb, gpointer data, guint *handler)
Definition: rcw.c:4506
- -
void rco_destroy_message_panel(RemminaConnectionObject *cnnobj, RemminaMessagePanel *mp)
Deletes a RemminaMessagePanel from the current cnnobj and if it was visible, make visible the last re...
Definition: rcw.c:4722
+
GtkWindow * rcw_get_gtkwindow(RemminaConnectionObject *cnnobj)
Definition: rcw.c:4716
+
GtkWidget * rcw_open_from_file_full(RemminaFile *remminafile, GCallback disconnect_cb, gpointer data, guint *handler)
Definition: rcw.c:4519
+ +
void rco_destroy_message_panel(RemminaConnectionObject *cnnobj, RemminaMessagePanel *mp)
Deletes a RemminaMessagePanel from the current cnnobj and if it was visible, make visible the last re...
Definition: rcw.c:4735
RemminaConnectionWindowOnDeleteConfirmMode
Definition: rcw.h:66
struct _RemminaConnectionWindowPriv RemminaConnectionWindowPriv
Definition: rcw.h:52
struct _RemminaConnectionWindow RemminaConnectionWindow
-
void rcw_open_from_file(RemminaFile *remminafile)
Definition: rcw.c:4453
-
gboolean rcw_open_from_filename(const gchar *filename)
Definition: rcw.c:4405
+
void rcw_open_from_file(RemminaFile *remminafile)
Definition: rcw.c:4466
+
gboolean rcw_open_from_filename(const gchar *filename)
Definition: rcw.c:4418
GtkWindowClass parent_class
Definition: rcw.h:60
-
void rco_show_message_panel(RemminaConnectionObject *cnnobj, RemminaMessagePanel *mp)
Each cnnobj->page can have more than one RemminaMessagePanel, but 0 or 1 are visible.
Definition: rcw.c:4769
+
void rco_show_message_panel(RemminaConnectionObject *cnnobj, RemminaMessagePanel *mp)
Each cnnobj->page can have more than one RemminaMessagePanel, but 0 or 1 are visible.
Definition: rcw.c:4782
RemminaConnectionWindowPriv * priv
Definition: rcw.h:56
GType rcw_get_type(void) G_GNUC_CONST
-
gboolean rcw_delete(RemminaConnectionWindow *cnnwin)
Definition: rcw.c:648
+
gboolean rcw_delete(RemminaConnectionWindow *cnnwin)
Definition: rcw.c:650
struct _RemminaConnectionWindowClass RemminaConnectionWindowClass
-
RemminaConnectionWindow * cnnwin
Definition: rcw.c:163
+
RemminaConnectionWindow * cnnwin
Definition: rcw.c:164
-
GtkWidget * rcw_get_gtkviewport(RemminaConnectionObject *cnnobj)
Definition: rcw.c:4707
+
GtkWidget * rcw_get_gtkviewport(RemminaConnectionObject *cnnobj)
Definition: rcw.c:4720
GtkWindow window
Definition: rcw.h:55
-
void rco_get_monitor_geometry(RemminaConnectionObject *cnnobj, GdkRectangle *sz)
Definition: rcw.c:1009
+
void rco_get_monitor_geometry(RemminaConnectionObject *cnnobj, GdkRectangle *sz)
Definition: rcw.c:1011
diff --git a/public/remmina__chat__window_8c_source.html b/public/remmina__chat__window_8c_source.html index 9d430f7ec..07a68135e 100644 --- a/public/remmina__chat__window_8c_source.html +++ b/public/remmina__chat__window_8c_source.html @@ -99,7 +99,7 @@ $(document).ready(function(){initNavTree('remmina__chat__window_8c_source.html',
static gboolean remmina_chat_window_scroll_proc(RemminaChatWindow *window)
-
Definition: rcw.c:182
+
Definition: rcw.c:183
static void remmina_chat_window_clear_send_text(GtkWidget *widget, RemminaChatWindow *window)
void remmina_chat_window_receive(RemminaChatWindow *window, const gchar *name, const gchar *text)
GtkWidget * remmina_chat_window_new(GtkWindow *parent, const gchar *chat_with)
diff --git a/public/remmina__exec_8c_source.html b/public/remmina__exec_8c_source.html index 9c0989b22..e310d8426 100644 --- a/public/remmina__exec_8c_source.html +++ b/public/remmina__exec_8c_source.html @@ -98,7 +98,7 @@ $(document).ready(function(){initNavTree('remmina__exec_8c_source.html','');});
void remmina_widget_pool_register(GtkWidget *widget)
Definition: remmina_exec.h:59
-
gboolean rcw_delete(RemminaConnectionWindow *cnnwin)
Definition: rcw.c:648
+
gboolean rcw_delete(RemminaConnectionWindow *cnnwin)
Definition: rcw.c:650
Definition: remmina_exec.h:55
@@ -121,7 +121,7 @@ $(document).ready(function(){initNavTree('remmina__exec_8c_source.html','');});
gint remmina_widget_pool_count()
gchar * remmina_crypt_encrypt(const gchar *str)
Definition: remmina_crypt.c:93
-
void rcw_set_delete_confirm_mode(RemminaConnectionWindow *cnnwin, RemminaConnectionWindowOnDeleteConfirmMode mode)
Definition: rcw.c:4712
+
void rcw_set_delete_confirm_mode(RemminaConnectionWindow *cnnwin, RemminaConnectionWindowOnDeleteConfirmMode mode)
Definition: rcw.c:4725
void remmina_exec_command(RemminaCommandType command, const gchar *data)
Definition: remmina_exec.c:368
Definition: remmina_exec.h:49
Definition: remmina_exec.h:46
@@ -133,7 +133,7 @@ $(document).ready(function(){initNavTree('remmina__exec_8c_source.html','');});
void remmina_main_destroy()
Definition: remmina_main.c:193
RemminaFile * remmina_file_new(void)
Definition: remmina_file.c:93
-
void rcw_open_from_file(RemminaFile *remminafile)
Definition: rcw.c:4453
+
void rcw_open_from_file(RemminaFile *remminafile)
Definition: rcw.c:4466
Definition: remmina_exec.h:52
gint remmina_file_get_int(RemminaFile *remminafile, const gchar *setting, gint default_value)
Definition: remmina_file.c:603
void remmina_about_open(GtkWindow *parent)
Definition: remmina_about.c:44
@@ -144,7 +144,7 @@ $(document).ready(function(){initNavTree('remmina__exec_8c_source.html','');});
Definition: remmina_exec.h:53
Definition: rcw.h:68
-
gboolean rcw_open_from_filename(const gchar *filename)
Definition: rcw.c:4405
+
gboolean rcw_open_from_filename(const gchar *filename)
Definition: rcw.c:4418
static gboolean cb_closewidget(GtkWidget *widget, gpointer data)
Definition: remmina_exec.c:67
gboolean kioskmode
Definition: remmina.c:87
void remmina_application_condexit(RemminaCondExitType why)
Definition: remmina_exec.c:123
diff --git a/public/remmina__file__editor_8c_source.html b/public/remmina__file__editor_8c_source.html index eb4116467..0dd178d22 100644 --- a/public/remmina__file__editor_8c_source.html +++ b/public/remmina__file__editor_8c_source.html @@ -199,7 +199,7 @@ $(document).ready(function(){initNavTree('remmina__file__editor_8c_source.html',
static void remmina_file_editor_create_settings(RemminaFileEditor *gfe, GtkWidget *grid, const RemminaProtocolSetting *settings)
static void remmina_file_editor_on_save_connect(GtkWidget *button, RemminaFileEditor *gfe)
-
void rcw_open_from_file(RemminaFile *remminafile)
Definition: rcw.c:4453
+
void rcw_open_from_file(RemminaFile *remminafile)
Definition: rcw.c:4466
RemminaPref remmina_pref
Definition: rcw.c:79
void remmina_file_editor_file_save(RemminaFileEditor *gfe)
diff --git a/public/remmina__main_8c_source.html b/public/remmina__main_8c_source.html index 1bb52116c..1816182c1 100644 --- a/public/remmina__main_8c_source.html +++ b/public/remmina__main_8c_source.html @@ -254,7 +254,7 @@ $(document).ready(function(){initNavTree('remmina__main_8c_source.html','');});
void remmina_main_on_action_connection_external_tools(GSimpleAction *action, GVariant *param, gpointer data)
Definition: remmina_main.c:796
static void remmina_main_clear_selection_data(void)
Definition: remmina_main.c:261
-
void rcw_open_from_file(RemminaFile *remminafile)
Definition: rcw.c:4453
+
void rcw_open_from_file(RemminaFile *remminafile)
Definition: rcw.c:4466
void remmina_main_on_date_column_sort_clicked()
RemminaPref remmina_pref
Definition: rcw.c:79
gboolean remmina_main_file_list_on_key_press(GtkWidget *widget, GdkEventKey *event, gpointer user_data)
@@ -274,7 +274,7 @@ $(document).ready(function(){initNavTree('remmina__main_8c_source.html','');});
void remmina_main_on_action_connection_edit(GSimpleAction *action, GVariant *param, gpointer data)
Definition: remmina_main.c:946
void remmina_main_toggle_password_view(GtkWidget *widget, gpointer data)
GtkTreeModel * file_model_sort
Definition: remmina_main.h:92
-
gboolean rcw_open_from_filename(const gchar *filename)
Definition: rcw.c:4405
+
gboolean rcw_open_from_filename(const gchar *filename)
Definition: rcw.c:4418
static const gchar * supported_mime_types[]
Definition: remmina_main.c:87
Definition: remmina_main.c:76
void remmina_main_on_action_expand(GSimpleAction *action, GVariant *param, gpointer data)
diff --git a/public/remmina__masterthread__exec_8h_source.html b/public/remmina__masterthread__exec_8h_source.html index 72378a094..b8c326b8a 100644 --- a/public/remmina__masterthread__exec_8h_source.html +++ b/public/remmina__masterthread__exec_8h_source.html @@ -100,7 +100,7 @@ $(document).ready(function(){initNavTree('remmina__masterthread__exec_8h_source.
struct remmina_masterthread_exec_data::@12::@24 protocolwidget_panelshowlisten
-
Definition: rcw.c:162
+
Definition: rcw.c:163
gboolean complete
struct remmina_masterthread_exec_data RemminaMTExecData
struct remmina_masterthread_exec_data::@12::@18 ftp_client_update_task
diff --git a/public/remmina__message__panel_8c_source.html b/public/remmina__message__panel_8c_source.html index 7696039af..b2eb26f0b 100644 --- a/public/remmina__message__panel_8c_source.html +++ b/public/remmina__message__panel_8c_source.html @@ -122,7 +122,7 @@ $(document).ready(function(){initNavTree('remmina__message__panel_8c_source.html
Definition: types.h:160
-
Definition: rcw.c:182
+
Definition: rcw.c:183
void remmina_main_toggle_password_view(GtkWidget *widget, gpointer data)
void remmina_message_panel_setup_message(RemminaMessagePanel *mp, const gchar *message, RemminaMessagePanelCallback response_callback, gpointer response_callback_data)
gboolean remmina_message_panel_field_get_switch_state(RemminaMessagePanel *mp, int entryid)
diff --git a/public/remmina__plugin__manager_8c_source.html b/public/remmina__plugin__manager_8c_source.html index f04d8bdce..3bc1f4e6f 100644 --- a/public/remmina__plugin__manager_8c_source.html +++ b/public/remmina__plugin__manager_8c_source.html @@ -127,7 +127,7 @@ $(document).ready(function(){initNavTree('remmina__plugin__manager_8c_source.htm
G_BEGIN_DECLS typedef gboolean(* RemminaPluginFunc)(gchar *name, RemminaPlugin *plugin, gpointer data)
void remmina_plugin_manager_for_each_plugin(RemminaPluginType type, RemminaPluginFunc func, gpointer data)
gchar * remmina_protocol_widget_get_domain(RemminaProtocolWidget *gp)
-
GtkWidget * rcw_open_from_file_full(RemminaFile *remminafile, GCallback disconnect_cb, gpointer data, guint *handler)
Definition: rcw.c:4506
+
GtkWidget * rcw_open_from_file_full(RemminaFile *remminafile, GCallback disconnect_cb, gpointer data, guint *handler)
Definition: rcw.c:4519
void remmina_widget_pool_register(GtkWidget *widget)
void _remmina_critical(const gchar *fun, const gchar *fmt,...)
Definition: remmina_log.c:382
Definition: types.h:101
diff --git a/public/remmina__protocol__widget_8c_source.html b/public/remmina__protocol__widget_8c_source.html index 3b4a1b142..f985f4156 100644 --- a/public/remmina__protocol__widget_8c_source.html +++ b/public/remmina__protocol__widget_8c_source.html @@ -143,7 +143,7 @@ $(document).ready(function(){initNavTree('remmina__protocol__widget_8c_source.ht
gint multimon
void remmina_protocol_widget_register_hostkey(RemminaProtocolWidget *gp, GtkWidget *widget)
-
void rco_show_message_panel(RemminaConnectionObject *cnnobj, RemminaMessagePanel *mp)
Each cnnobj->page can have more than one RemminaMessagePanel, but 0 or 1 are visible.
Definition: rcw.c:4769
+
void rco_show_message_panel(RemminaConnectionObject *cnnobj, RemminaMessagePanel *mp)
Each cnnobj->page can have more than one RemminaMessagePanel, but 0 or 1 are visible.
Definition: rcw.c:4782
gboolean remmina_protocol_widget_start_reverse_tunnel(RemminaProtocolWidget *gp, gint local_port)
gchar * clientcert
gchar * remmina_protocol_widget_get_domain(RemminaProtocolWidget *gp)
@@ -151,7 +151,7 @@ $(document).ready(function(){initNavTree('remmina__protocol__widget_8c_source.ht
static void shutdown_loop(MpRunInfo *mpri)
gpointer destroy_func_callback_data
Definition: remmina_ssh.h:181
RemminaFile * remmina_file_dup_temp_protocol(RemminaFile *remminafile, const gchar *new_protocol)
Definition: remmina_file.c:899
-
GtkWidget * rcw_open_from_file_full(RemminaFile *remminafile, GCallback disconnect_cb, gpointer data, guint *handler)
Definition: rcw.c:4506
+
GtkWidget * rcw_open_from_file_full(RemminaFile *remminafile, GCallback disconnect_cb, gpointer data, guint *handler)
Definition: rcw.c:4519
void remmina_message_panel_setup_auth_x509(RemminaMessagePanel *mp, RemminaMessagePanelCallback response_callback, gpointer response_callback_data)
gint id
Definition: types.h:74
static int remmina_protocol_widget_dialog(enum panel_type dtype, RemminaProtocolWidget *gp, RemminaMessagePanelFlags pflags, const gchar *title, const gchar *default_username, const gchar *default_password, const gchar *default_domain, const gchar *strpasswordlabel)
@@ -174,10 +174,10 @@ $(document).ready(function(){initNavTree('remmina__protocol__widget_8c_source.ht
struct remmina_masterthread_exec_data::@12::@24 protocolwidget_panelshowlisten
void remmina_protocol_widget_call_feature_by_type(RemminaProtocolWidget *gp, RemminaProtocolFeatureType type, gint id)
void remmina_protocol_widget_lock_dynres(RemminaProtocolWidget *gp)
-
void rco_destroy_message_panel(RemminaConnectionObject *cnnobj, RemminaMessagePanel *mp)
Deletes a RemminaMessagePanel from the current cnnobj and if it was visible, make visible the last re...
Definition: rcw.c:4722
+
void rco_destroy_message_panel(RemminaConnectionObject *cnnobj, RemminaMessagePanel *mp)
Deletes a RemminaMessagePanel from the current cnnobj and if it was visible, make visible the last re...
Definition: rcw.c:4735
Definition: remmina_ssh.h:113
void remmina_protocol_widget_set_hostkey_func(RemminaProtocolWidget *gp, RemminaHostkeyFunc func)
-
Definition: rcw.c:162
+
Definition: rcw.c:163
gboolean bindlocalhost
Definition: remmina_ssh.h:172
gint remmina_protocol_widget_get_height(RemminaProtocolWidget *gp)
RemminaScaleMode remmina_protocol_widget_get_current_scale_mode(RemminaProtocolWidget *gp)
@@ -225,7 +225,7 @@ $(document).ready(function(){initNavTree('remmina__protocol__widget_8c_source.ht
void remmina_message_panel_field_set_filename(RemminaMessagePanel *mp, int entryid, const gchar *filename)
-
GtkWindow * rcw_get_gtkwindow(RemminaConnectionObject *cnnobj)
Definition: rcw.c:4703
+
GtkWindow * rcw_get_gtkwindow(RemminaConnectionObject *cnnobj)
Definition: rcw.c:4716
gboolean remmina_masterthread_exec_is_main_thread()
struct remmina_masterthread_exec_data::@12::@22 protocolwidget_mpdestroy
@@ -303,7 +303,7 @@ $(document).ready(function(){initNavTree('remmina__protocol__widget_8c_source.ht
gboolean(* close_connection)(RemminaProtocolWidget *gp)
Definition: plugin.h:81
void remmina_masterthread_exec_and_wait(RemminaMTExecData *d)
-
GtkWidget * rcw_get_gtkviewport(RemminaConnectionObject *cnnobj)
Definition: rcw.c:4707
+
GtkWidget * rcw_get_gtkviewport(RemminaConnectionObject *cnnobj)
Definition: rcw.c:4720
void(* RemminaMessagePanelCallback)(void *user_data, int button)
void remmina_protocol_widget_emit_signal(RemminaProtocolWidget *gp, const gchar *signal_name)
gchar * remmina_message_panel_field_get_string(RemminaMessagePanel *mp, int entryid)
@@ -342,7 +342,7 @@ $(document).ready(function(){initNavTree('remmina__protocol__widget_8c_source.ht
gpointer RemminaTunnelInitFunc
Definition: types.h:94
const gchar * name
Definition: plugin.h:67
RemminaHostkeyFunc hostkey_func
-
Definition: rcw.c:182
+
Definition: rcw.c:183
gchar * default_username
gchar * remmina_protocol_widget_get_password(RemminaProtocolWidget *gp)
gboolean remmina_public_get_modifier_for_keycode(GdkKeymap *keymap, guint16 keycode)
@@ -364,7 +364,7 @@ $(document).ready(function(){initNavTree('remmina__protocol__widget_8c_source.ht
gboolean has_error
void remmina_file_save(RemminaFile *remminafile)
Definition: remmina_file.c:730
static void remmina_protocol_widget_class_init(RemminaProtocolWidgetClass *klass)
-
void rco_get_monitor_geometry(RemminaConnectionObject *cnnobj, GdkRectangle *sz)
Definition: rcw.c:1009
+
void rco_get_monitor_geometry(RemminaConnectionObject *cnnobj, GdkRectangle *sz)
Definition: rcw.c:1011
gchar * clientkey
gboolean remmina_protocol_widget_plugin_receives_keystrokes(RemminaProtocolWidget *gp)
Check if the plugin accepts keystrokes.
diff --git a/public/remmina__protocol__widget_8h_source.html b/public/remmina__protocol__widget_8h_source.html index 64115bb82..9d842861f 100644 --- a/public/remmina__protocol__widget_8h_source.html +++ b/public/remmina__protocol__widget_8h_source.html @@ -107,7 +107,7 @@ $(document).ready(function(){initNavTree('remmina__protocol__widget_8h_source.ht
gboolean remmina_protocol_widget_ssh_exec(RemminaProtocolWidget *gp, gboolean wait, const gchar *fmt,...)
void remmina_protocol_widget_panel_show(RemminaProtocolWidget *gp)
void remmina_protocol_widget_save_cred(RemminaProtocolWidget *gp)
-
Definition: rcw.c:162
+
Definition: rcw.c:163
gboolean remmina_protocol_widget_is_closed(RemminaProtocolWidget *gp)
gboolean remmina_protocol_widget_start_reverse_tunnel(RemminaProtocolWidget *gp, gint local_port)
gboolean remmina_protocol_widget_query_feature_by_ref(RemminaProtocolWidget *gp, const RemminaProtocolFeature *feature)
diff --git a/public/struct___remmina_connection_object.html b/public/struct___remmina_connection_object.html index 345164231..a304f9ddf 100644 --- a/public/struct___remmina_connection_object.html +++ b/public/struct___remmina_connection_object.html @@ -114,7 +114,7 @@ Data Fields

Detailed Description

-

Definition at line 162 of file rcw.c.

+

Definition at line 163 of file rcw.c.

Field Documentation

◆ aspectframe

@@ -128,7 +128,7 @@ Data Fields
-

Definition at line 167 of file rcw.c.

+

Definition at line 168 of file rcw.c.

@@ -144,7 +144,7 @@ Data Fields
-

Definition at line 163 of file rcw.c.

+

Definition at line 164 of file rcw.c.

@@ -160,7 +160,7 @@ Data Fields
-

Definition at line 174 of file rcw.c.

+

Definition at line 175 of file rcw.c.

@@ -176,7 +176,7 @@ Data Fields
-

Definition at line 177 of file rcw.c.

+

Definition at line 178 of file rcw.c.

@@ -192,7 +192,7 @@ Data Fields
-

Definition at line 175 of file rcw.c.

+

Definition at line 176 of file rcw.c.

@@ -208,7 +208,7 @@ Data Fields
-

Definition at line 172 of file rcw.c.

+

Definition at line 173 of file rcw.c.

@@ -224,7 +224,7 @@ Data Fields
-

Definition at line 166 of file rcw.c.

+

Definition at line 167 of file rcw.c.

@@ -240,7 +240,7 @@ Data Fields
-

Definition at line 164 of file rcw.c.

+

Definition at line 165 of file rcw.c.

@@ -256,7 +256,7 @@ Data Fields
-

Definition at line 170 of file rcw.c.

+

Definition at line 171 of file rcw.c.

@@ -272,7 +272,7 @@ Data Fields
-

Definition at line 168 of file rcw.c.

+

Definition at line 169 of file rcw.c.

-- cgit v1.2.3