1 /*
2 * Remmina - The GTK+ Remote Desktop Client
3 * Copyright (C) 2016-2021 Antenore Gatta, Giovanni Panozzo
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 * Boston, MA 02110-1301, USA.
19 *
20 * In addition, as a special exception, the copyright holders give
21 * permission to link the code of portions of this program with the
22 * OpenSSL library under certain conditions as described in each
23 * individual source file, and distribute linked combinations
24 * including the two.
25 * You must obey the GNU General Public License in all respects
26 * for all of the code used other than OpenSSL. * If you modify
27 * file(s) with this exception, you may extend this exception to your
28 * version of the file(s), but you are not obligated to do so. * If you
29 * do not wish to do so, delete this exception statement from your
30 * version. * If you delete this exception statement from all source
31 * files in the program, then also delete it here.
32 *
33 */
34
35 #include "gvnc_plugin_config.h"
36 #include "gvnc_plugin.h"
37
38 #include <vncdisplay.h>
39 #include <vncutil.h>
40 #include <vncaudiopulse.h>
41
42 #define GVNC_DEFAULT_PORT 5900
43
44 #ifndef VNC_CHECK_VERSION
45 # define VNC_CHECK_VERSION(a, b, c) 0
46 #endif
47 #if VNC_CHECK_VERSION(1, 2, 0)
48 # define HAVE_VNC_REMOTE_RESIZE
49 # define HAVE_VNC_POWER_CONTROL
50 #endif
51
52 enum {
53 GVNC_PLUGIN_FEATURE_PREF_VIEWONLY = 1,
54 GVNC_PLUGIN_FEATURE_DYNRESUPDATE,
55 GVNC_PLUGIN_FEATURE_PREF_DISABLECLIPBOARD,
56 GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTDEL,
57 GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTBACKSPACE,
58 GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTF1,
59 GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTF2,
60 GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTF3,
61 GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTF4,
62 GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTF5,
63 GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTF6,
64 GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTF7,
65 GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTF8,
66 GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTF9,
67 GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTF10,
68 GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTF11,
69 GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTF12,
70 #ifdef HAVE_VNC_POWER_CONTROL
71 GVNC_PLUGIN_FEATURE_TOOL_REBOOT,
72 GVNC_PLUGIN_FEATURE_TOOL_RESET,
73 GVNC_PLUGIN_FEATURE_TOOL_SHUTDOWN,
74 #endif
75 GVNC_PLUGIN_FEATURE_SCALE
76 };
77
78
79 static RemminaPluginService *remmina_plugin_service = NULL;
80 #define REMMINA_PLUGIN_DEBUG(fmt, ...) remmina_plugin_service->_remmina_debug(__func__, fmt, ## __VA_ARGS__)
81
str_replace(const gchar * string,const gchar * search,const gchar * replacement)82 gchar* str_replace(const gchar *string, const gchar *search, const gchar *replacement)
83 {
84 TRACE_CALL(__func__);
85 gchar *str, **arr;
86
87 g_return_val_if_fail(string != NULL, NULL);
88 g_return_val_if_fail(search != NULL, NULL);
89
90 if (replacement == NULL)
91 replacement = "";
92
93 arr = g_strsplit(string, search, -1);
94 if (arr != NULL && arr[0] != NULL)
95 str = g_strjoinv(replacement, arr);
96 else
97 str = g_strdup(string);
98
99 g_strfreev(arr);
100 return str;
101 }
102
103 /* Send a keystroke to the plugin window */
gvnc_plugin_keystroke(RemminaProtocolWidget * gp,const guint keystrokes[],const gint keylen)104 static void gvnc_plugin_keystroke(RemminaProtocolWidget *gp, const guint keystrokes[], const gint keylen)
105 {
106 TRACE_CALL(__func__);
107 GVncPluginData *gpdata = GET_PLUGIN_DATA(gp);
108
109 if (gpdata->vnc) {
110 vnc_display_send_keys(VNC_DISPLAY(gpdata->vnc),
111 keystrokes,
112 keylen);
113 }
114 }
115
116 #ifdef HAVE_VNC_POWER_CONTROL
gvnc_plugin_power_ctrl(RemminaProtocolWidget * gp,VncConnectionPowerAction action)117 static void gvnc_plugin_power_ctrl(RemminaProtocolWidget *gp, VncConnectionPowerAction action)
118 {
119 GVncPluginData *gpdata = GET_PLUGIN_DATA(gp);
120 VncConnection *conn = vnc_display_get_connection(VNC_DISPLAY(gpdata->vnc));
121
122 if (!vnc_connection_get_power_control(conn)) {
123 REMMINA_PLUGIN_DEBUG("Power control not available on this server");
124 return;
125 }
126 vnc_connection_power_control(conn, action);
127 }
128 #endif
129
130
gvnc_plugin_mouse_grab(GtkWidget * vncdisplay,RemminaProtocolWidget * gp)131 static void gvnc_plugin_mouse_grab(GtkWidget *vncdisplay, RemminaProtocolWidget *gp)
132 {
133 TRACE_CALL(__func__);
134 GVncPluginData *gpdata = GET_PLUGIN_DATA(gp);
135 VncGrabSequence *seq = vnc_display_get_grab_keys(VNC_DISPLAY(gpdata->vnc));
136 gchar *seqstr = vnc_grab_sequence_as_string(seq);
137
138 REMMINA_PLUGIN_DEBUG("Pointer grabbed: %s", seqstr);
139 }
140
gvnc_plugin_mouse_ungrab(GtkWidget * vncdisplay,RemminaProtocolWidget * gp)141 static void gvnc_plugin_mouse_ungrab(GtkWidget *vncdisplay, RemminaProtocolWidget *gp)
142 {
143 TRACE_CALL(__func__);
144 REMMINA_PLUGIN_DEBUG("Pointer ungrabbed");
145 }
146
gvnc_plugin_on_vnc_error(GtkWidget * vncdisplay G_GNUC_UNUSED,const gchar * msg,RemminaProtocolWidget * gp)147 static void gvnc_plugin_on_vnc_error(GtkWidget *vncdisplay G_GNUC_UNUSED, const gchar *msg, RemminaProtocolWidget *gp)
148 {
149 TRACE_CALL(__func__);
150 GVncPluginData *gpdata = GET_PLUGIN_DATA(gp);
151
152 REMMINA_PLUGIN_DEBUG("Error: %s\n", msg);
153 /* "vnc-error" is always followed by "vnc-disconnected",
154 * so save the error for that signal */
155 g_free(gpdata->error_msg);
156 gpdata->error_msg = g_strdup(msg);
157 }
158
159 static gboolean gvnc_plugin_get_screenshot(RemminaProtocolWidget *gp, RemminaPluginScreenshotData *rpsd) __attribute__ ((unused));
gvnc_plugin_get_screenshot(RemminaProtocolWidget * gp,RemminaPluginScreenshotData * rpsd)160 static gboolean gvnc_plugin_get_screenshot(RemminaProtocolWidget *gp, RemminaPluginScreenshotData *rpsd)
161 {
162 GVncPluginData *gpdata = GET_PLUGIN_DATA(gp);
163 //gsize szmem;
164 const VncPixelFormat *currentFormat;
165 //GError *err = NULL;
166
167 if (!gpdata)
168 return FALSE;
169
170 /* Get current pixel format for server */
171 currentFormat = vnc_connection_get_pixel_format(gpdata->conn);
172
173
174 GdkPixbuf *pix = vnc_display_get_pixbuf(VNC_DISPLAY(gpdata->vnc));
175
176 rpsd->width = gdk_pixbuf_get_width(pix);
177 rpsd->height = gdk_pixbuf_get_height(pix);
178 rpsd->bitsPerPixel = currentFormat->bits_per_pixel;
179 rpsd->bytesPerPixel = rpsd->bitsPerPixel / 8;
180 //szmem = gdk_pixbuf_get_byte_length(pix);
181
182 //szmem = rpsd->width * rpsd->height * rpsd->bytesPerPixel;
183
184 //REMMINA_PLUGIN_DEBUG("allocating %zu bytes for a full screenshot", szmem);
185 //REMMINA_PLUGIN_DEBUG("Calculated screenshot size: %zu", szcalc);
186 //rpsd->buffer = malloc(szmem);
187
188 //memcpy(rpsd->buffer, pix, szmem);
189 //gdk_pixbuf_save_to_buffer(pix, &rpsd->buffer, &szmem, "jpeg", &err, "quality", "100", NULL);
190
191
192 /* Returning TRUE instruct also the caller to deallocate rpsd->buffer */
193 return TRUE;
194 }
195
gvnc_plugin_paste_text(RemminaProtocolWidget * gp,const gchar * text)196 void gvnc_plugin_paste_text(RemminaProtocolWidget *gp, const gchar *text)
197 {
198 TRACE_CALL(__func__);
199 GVncPluginData *gpdata = GET_PLUGIN_DATA(gp);
200 gchar *out;
201 gsize a, b;
202 GError *error = NULL;
203
204 if (!gpdata) return;
205
206 out = g_convert_with_fallback(text, -1, "iso8859-1//TRANSLIT", "utf-8", NULL, &a, &b, &error);
207 if (out) {
208 REMMINA_PLUGIN_DEBUG("Pasting text");
209 vnc_display_client_cut_text(VNC_DISPLAY(gpdata->vnc), out);
210 g_free(out);
211 } else {
212 REMMINA_PLUGIN_DEBUG("Error pasting text: %s", error->message);
213 g_error_free(error);
214 }
215 }
216
gvnc_plugin_clipboard_cb(GtkClipboard * cb,GdkEvent * event,RemminaProtocolWidget * gp)217 static void gvnc_plugin_clipboard_cb(GtkClipboard *cb, GdkEvent *event, RemminaProtocolWidget *gp)
218 {
219 TRACE_CALL(__func__);
220 //GVncPluginData *gpdata = GET_PLUGIN_DATA(gp);
221 gchar *text;
222
223 REMMINA_PLUGIN_DEBUG("owner-change event received");
224
225 if (cb && gtk_clipboard_get_owner(cb) == (GObject *)gp)
226 return;
227
228 text = gtk_clipboard_wait_for_text(cb);
229 if (!text)
230 return;
231
232 gvnc_plugin_paste_text(gp, text);
233 g_free(text);
234 }
235
236
237 /* text was actually requested */
gvnc_plugin_clipboard_copy(GtkClipboard * clipboard G_GNUC_UNUSED,GtkSelectionData * data,guint info G_GNUC_UNUSED,RemminaProtocolWidget * gp)238 static void gvnc_plugin_clipboard_copy(GtkClipboard *clipboard G_GNUC_UNUSED, GtkSelectionData *data, guint info G_GNUC_UNUSED, RemminaProtocolWidget *gp)
239 {
240 TRACE_CALL(__func__);
241 GVncPluginData *gpdata = GET_PLUGIN_DATA(gp);
242
243 if (!gpdata) return;
244 gtk_selection_data_set_text(data, gpdata->clipstr, -1);
245 REMMINA_PLUGIN_DEBUG("Text copied");
246 }
247
gvnc_plugin_cut_text(VncDisplay * vnc G_GNUC_UNUSED,const gchar * text,RemminaProtocolWidget * gp)248 static void gvnc_plugin_cut_text(VncDisplay *vnc G_GNUC_UNUSED, const gchar *text, RemminaProtocolWidget *gp)
249 {
250 TRACE_CALL(__func__);
251 GVncPluginData *gpdata = GET_PLUGIN_DATA(gp);
252
253 REMMINA_PLUGIN_DEBUG("Got clipboard request for \"%s\"", text);
254
255 GtkClipboard *cb;
256 gsize a, b;
257 GtkTargetEntry targets[] = {
258 { g_strdup("UTF8_STRING"), 0, 0 },
259 { g_strdup("COMPOUND_TEXT"), 0, 0 },
260 { g_strdup("TEXT"), 0, 0 },
261 { g_strdup("STRING"), 0, 0 },
262 };
263
264 if (!text)
265 return;
266 g_free(gpdata->clipstr);
267 gpdata->clipstr = g_convert(text, -1, "utf-8", "iso8859-1", &a, &b, NULL);
268
269 if (gpdata->clipstr) {
270 cb = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD);
271
272 REMMINA_PLUGIN_DEBUG("setting clipboard with owner to owner %p", gp);
273 gtk_clipboard_set_with_owner(cb,
274 targets,
275 G_N_ELEMENTS(targets),
276 (GtkClipboardGetFunc)gvnc_plugin_clipboard_copy,
277 NULL,
278 G_OBJECT(gp));
279 }
280
281 //g_signal_emit_by_name(session, "session-cut-text", text);
282 }
283
284
gvnc_plugin_desktop_resize(GtkWidget * vncdisplay G_GNUC_UNUSED,int width,int height,RemminaProtocolWidget * gp)285 static void gvnc_plugin_desktop_resize(GtkWidget *vncdisplay G_GNUC_UNUSED, int width, int height, RemminaProtocolWidget *gp)
286 {
287 TRACE_CALL(__func__);
288 GVncPluginData *gpdata = GET_PLUGIN_DATA(gp);
289
290 REMMINA_PLUGIN_DEBUG("Remote desktop size changed to %dx%d\n", width, height);
291 REMMINA_PLUGIN_DEBUG("Desktop size is set to %dx%d\n", gpdata->width, gpdata->height);
292 remmina_plugin_service->protocol_plugin_set_width(gp, width);
293 remmina_plugin_service->protocol_plugin_set_height(gp, height);
294
295 remmina_plugin_service->protocol_plugin_desktop_resize(gp);
296 }
297
gvnc_plugin_on_bell(RemminaProtocolWidget * gp)298 static void gvnc_plugin_on_bell(RemminaProtocolWidget *gp)
299 {
300 TRACE_CALL(__func__);
301 REMMINA_PLUGIN_DEBUG("Bell message received");
302 //GVncPluginData *gpdata = GET_PLUGIN_DATA(gp);
303 RemminaFile *remminafile = remmina_plugin_service->protocol_plugin_get_file(gp);
304
305 if (remmina_plugin_service->file_get_int(remminafile, "disableserverbell", FALSE))
306 return;
307 GdkWindow *window = gtk_widget_get_window(GTK_WIDGET(gp));
308
309 if (window)
310 gdk_window_beep(window);
311 REMMINA_PLUGIN_DEBUG("Beep emitted");
312 }
gvnc_plugin_update_scale_mode(RemminaProtocolWidget * gp)313 static void gvnc_plugin_update_scale_mode(RemminaProtocolWidget *gp)
314 {
315 TRACE_CALL(__func__);
316
317 GVncPluginData *gpdata = GET_PLUGIN_DATA(gp);
318 RemminaScaleMode scaleMode = remmina_plugin_service->remmina_protocol_widget_get_current_scale_mode(gp);
319
320 #if 0
321 g_object_set(gpdata->display,
322 "scaling", (scaleMode == REMMINA_PROTOCOL_WIDGET_SCALE_MODE_SCALED),
323 "resize-guest", (scaleMode == REMMINA_PROTOCOL_WIDGET_SCALE_MODE_DYNRES),
324 NULL);
325 #endif
326 if (scaleMode == REMMINA_PROTOCOL_WIDGET_SCALE_MODE_SCALED || scaleMode == REMMINA_PROTOCOL_WIDGET_SCALE_MODE_DYNRES)
327 vnc_display_set_scaling(VNC_DISPLAY(gpdata->vnc), TRUE);
328 else
329 vnc_display_set_scaling(VNC_DISPLAY(gpdata->vnc), FALSE);
330
331 //width = remmina_plugin_service->protocol_plugin_get_width(gp);
332 //height = remmina_plugin_service->protocol_plugin_get_height(gp);
333 gpdata->width = vnc_display_get_width(VNC_DISPLAY(gpdata->vnc));
334 gpdata->height = vnc_display_get_height(VNC_DISPLAY(gpdata->vnc));
335
336 if (scaleMode != REMMINA_PROTOCOL_WIDGET_SCALE_MODE_NONE) {
337 /* In scaled mode, the VncDisplay will get its dimensions from its parent */
338 gtk_widget_set_size_request(GTK_WIDGET(gpdata->vnc), -1, -1);
339 } else {
340 /* In non scaled mode, the plugins forces dimensions of the VncDisplay */
341 #if 0
342 g_object_get(gpdata->display_channel,
343 "width", &width,
344 "height", &height,
345 NULL);
346 gtk_widget_set_size_request(GTK_WIDGET(gpdata->vnc), width, height);
347 #endif
348 gtk_widget_set_size_request(GTK_WIDGET(gpdata->vnc), gpdata->width, gpdata->height);
349 }
350 remmina_plugin_service->protocol_plugin_update_align(gp);
351 }
352
gvnc_plugin_query_feature(RemminaProtocolWidget * gp,const RemminaProtocolFeature * feature)353 static gboolean gvnc_plugin_query_feature(RemminaProtocolWidget *gp, const RemminaProtocolFeature *feature)
354 {
355 TRACE_CALL(__func__);
356
357 return TRUE;
358 }
359
gvnc_plugin_call_feature(RemminaProtocolWidget * gp,const RemminaProtocolFeature * feature)360 static void gvnc_plugin_call_feature(RemminaProtocolWidget *gp, const RemminaProtocolFeature *feature)
361 {
362 TRACE_CALL(__func__);
363
364 GVncPluginData *gpdata = GET_PLUGIN_DATA(gp);
365 RemminaFile *remminafile = remmina_plugin_service->protocol_plugin_get_file(gp);
366
367 guint keys[3];
368
369 switch (feature->id) {
370 case GVNC_PLUGIN_FEATURE_PREF_VIEWONLY:
371 gpdata->viewonly = remmina_plugin_service->file_get_int(remminafile, "viewonly", FALSE);
372 break;
373 case GVNC_PLUGIN_FEATURE_DYNRESUPDATE:
374 case GVNC_PLUGIN_FEATURE_SCALE:
375 gvnc_plugin_update_scale_mode(gp);
376 break;
377 case GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTDEL:
378 keys[0] = GDK_KEY_Control_L;
379 keys[1] = GDK_KEY_Alt_L;
380 keys[2] = GDK_KEY_Delete;
381 gvnc_plugin_keystroke(gp, keys, G_N_ELEMENTS(keys));
382 break;
383 case GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTBACKSPACE:
384 keys[0] = GDK_KEY_Control_L;
385 keys[1] = GDK_KEY_Alt_L;
386 keys[2] = GDK_KEY_BackSpace;
387 gvnc_plugin_keystroke(gp, keys, G_N_ELEMENTS(keys));
388 break;
389 case GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTF1:
390 keys[0] = GDK_KEY_Control_L;
391 keys[1] = GDK_KEY_Alt_L;
392 keys[2] = GDK_KEY_F1;
393 gvnc_plugin_keystroke(gp, keys, G_N_ELEMENTS(keys));
394 break;
395 case GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTF2:
396 keys[0] = GDK_KEY_Control_L;
397 keys[1] = GDK_KEY_Alt_L;
398 keys[2] = GDK_KEY_F2;
399 gvnc_plugin_keystroke(gp, keys, G_N_ELEMENTS(keys));
400 break;
401 case GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTF3:
402 keys[0] = GDK_KEY_Control_L;
403 keys[1] = GDK_KEY_Alt_L;
404 keys[2] = GDK_KEY_F3;
405 gvnc_plugin_keystroke(gp, keys, G_N_ELEMENTS(keys));
406 break;
407 case GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTF4:
408 keys[0] = GDK_KEY_Control_L;
409 keys[1] = GDK_KEY_Alt_L;
410 keys[2] = GDK_KEY_F4;
411 gvnc_plugin_keystroke(gp, keys, G_N_ELEMENTS(keys));
412 break;
413 case GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTF5:
414 keys[0] = GDK_KEY_Control_L;
415 keys[1] = GDK_KEY_Alt_L;
416 keys[2] = GDK_KEY_F5;
417 gvnc_plugin_keystroke(gp, keys, G_N_ELEMENTS(keys));
418 break;
419 case GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTF6:
420 keys[0] = GDK_KEY_Control_L;
421 keys[1] = GDK_KEY_Alt_L;
422 keys[2] = GDK_KEY_F6;
423 gvnc_plugin_keystroke(gp, keys, G_N_ELEMENTS(keys));
424 break;
425 case GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTF7:
426 keys[0] = GDK_KEY_Control_L;
427 keys[1] = GDK_KEY_Alt_L;
428 keys[2] = GDK_KEY_F7;
429 gvnc_plugin_keystroke(gp, keys, G_N_ELEMENTS(keys));
430 break;
431 case GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTF9:
432 keys[0] = GDK_KEY_Control_L;
433 keys[1] = GDK_KEY_Alt_L;
434 keys[2] = GDK_KEY_F9;
435 gvnc_plugin_keystroke(gp, keys, G_N_ELEMENTS(keys));
436 break;
437 case GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTF10:
438 keys[0] = GDK_KEY_Control_L;
439 keys[1] = GDK_KEY_Alt_L;
440 keys[2] = GDK_KEY_F10;
441 gvnc_plugin_keystroke(gp, keys, G_N_ELEMENTS(keys));
442 break;
443 case GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTF11:
444 keys[0] = GDK_KEY_Control_L;
445 keys[1] = GDK_KEY_Alt_L;
446 keys[2] = GDK_KEY_F11;
447 gvnc_plugin_keystroke(gp, keys, G_N_ELEMENTS(keys));
448 break;
449 case GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTF12:
450 keys[0] = GDK_KEY_Control_L;
451 keys[1] = GDK_KEY_Alt_L;
452 keys[2] = GDK_KEY_F12;
453 gvnc_plugin_keystroke(gp, keys, G_N_ELEMENTS(keys));
454 break;
455 #ifdef HAVE_VNC_POWER_CONTROL
456 case GVNC_PLUGIN_FEATURE_TOOL_REBOOT:
457 gvnc_plugin_power_ctrl(gp, VNC_CONNECTION_POWER_ACTION_REBOOT);
458 break;
459 case GVNC_PLUGIN_FEATURE_TOOL_RESET:
460 gvnc_plugin_power_ctrl(gp, VNC_CONNECTION_POWER_ACTION_RESET);
461 break;
462 case GVNC_PLUGIN_FEATURE_TOOL_SHUTDOWN:
463 gvnc_plugin_power_ctrl(gp, VNC_CONNECTION_POWER_ACTION_SHUTDOWN);
464 break;
465 #endif
466 default:
467 break;
468 }
469 }
470
gvnc_plugin_auth_unsupported(VncDisplay * vnc G_GNUC_UNUSED,unsigned int authType,RemminaProtocolWidget * gp)471 static void gvnc_plugin_auth_unsupported(VncDisplay *vnc G_GNUC_UNUSED, unsigned int authType, RemminaProtocolWidget *gp)
472 {
473 TRACE_CALL(__func__);
474 GVncPluginData *gpdata = GET_PLUGIN_DATA(gp);
475
476 g_clear_pointer(&gpdata->error_msg, g_free);
477 gchar *msg = g_strdup_printf(_("Unsupported authentication type %u"), authType);
478
479 remmina_plugin_service->protocol_plugin_set_error(gp, "%s", msg);
480 g_free(msg);
481 }
482
gvnc_plugin_auth_failure(VncDisplay * vnc G_GNUC_UNUSED,const gchar * reason,RemminaProtocolWidget * gp)483 static void gvnc_plugin_auth_failure(VncDisplay *vnc G_GNUC_UNUSED, const gchar *reason, RemminaProtocolWidget *gp)
484 {
485 TRACE_CALL(__func__);
486 GVncPluginData *gpdata = GET_PLUGIN_DATA(gp);
487
488 g_clear_pointer(&gpdata->error_msg, g_free);
489 gchar *msg = g_strdup_printf(_("Authentication failure: %s"), reason);
490
491 remmina_plugin_service->protocol_plugin_set_error(gp, "%s", msg);
492 g_free(msg);
493 }
494
gvnc_plugin_ask_auth(GtkWidget * vncdisplay,GValueArray * credList,RemminaProtocolWidget * gp)495 static gboolean gvnc_plugin_ask_auth(GtkWidget *vncdisplay, GValueArray *credList, RemminaProtocolWidget *gp)
496 {
497 TRACE_CALL(__func__);
498
499 gint ret;
500 gboolean disablepasswordstoring;
501 gchar *s_username = NULL, *s_password = NULL;
502 gboolean wantPassword = FALSE, wantUsername = FALSE;
503 int i;
504 gboolean save;
505
506
507 GVncPluginData *gpdata = GET_PLUGIN_DATA(gp);
508 RemminaFile *remminafile = remmina_plugin_service->protocol_plugin_get_file(gp);
509
510
511 GArray *credArray = g_array_sized_new(FALSE, TRUE, sizeof(GValue), credList->n_values);
512 g_array_set_clear_func(credArray, (GDestroyNotify) g_value_unset);
513 g_array_append_vals(credArray, credList->values, credList->n_values);
514
515 REMMINA_PLUGIN_DEBUG("Got credential request for %d credential(s)\n", credArray->len);
516
517 for (i = 0 ; i < credArray->len ; i++) {
518 GValue *cred = &g_array_index(credArray, GValue, i);
519 switch (g_value_get_enum(cred)) {
520 case VNC_DISPLAY_CREDENTIAL_USERNAME:
521 wantUsername = TRUE;
522 break;
523 case VNC_DISPLAY_CREDENTIAL_PASSWORD:
524 wantPassword = TRUE;
525 break;
526 case VNC_DISPLAY_CREDENTIAL_CLIENTNAME:
527 break;
528 default:
529 REMMINA_PLUGIN_DEBUG("Unsupported credential type %d", g_value_get_enum(cred));
530 vnc_display_close(VNC_DISPLAY(gpdata->vnc));
531 goto cleanup;
532 }
533 }
534 disablepasswordstoring = remmina_plugin_service->file_get_int(remminafile, "disablepasswordstoring", FALSE);
535
536 ret = remmina_plugin_service->protocol_plugin_init_auth(gp,
537 (disablepasswordstoring ? 0 : REMMINA_MESSAGE_PANEL_FLAG_SAVEPASSWORD)
538 | (wantUsername ? REMMINA_MESSAGE_PANEL_FLAG_USERNAME : 0),
539 _("Enter VNC authentication credentials"),
540 (wantUsername ? remmina_plugin_service->file_get_string(remminafile, "username") : NULL),
541 (wantPassword ? remmina_plugin_service->file_get_string(remminafile, "password") : NULL),
542 NULL,
543 NULL);
544 if (ret == GTK_RESPONSE_OK) {
545 s_username = remmina_plugin_service->protocol_plugin_init_get_username(gp);
546 s_password = remmina_plugin_service->protocol_plugin_init_get_password(gp);
547 remmina_plugin_service->file_set_string(remminafile, "username", s_username);
548
549 save = remmina_plugin_service->protocol_plugin_init_get_savepassword(gp);
550 if (save) {
551 // User has requested to save credentials. We put the password
552 // into remminafile->settings. It will be saved later, on successful connection, by
553 // rcw.c
554 remmina_plugin_service->file_set_string(remminafile, "password", s_password);
555 } else {
556 remmina_plugin_service->file_set_string(remminafile, "password", NULL);
557 }
558
559 for (i = 0 ; i < credArray->len ; i++) {
560 GValue *cred = &g_array_index(credArray, GValue, i);
561 switch (g_value_get_enum(cred)) {
562 case VNC_DISPLAY_CREDENTIAL_USERNAME:
563 if (!s_username ||
564 vnc_display_set_credential(VNC_DISPLAY(gpdata->vnc),
565 g_value_get_enum(cred),
566 s_username)) {
567 g_debug("Failed to set credential type %d", g_value_get_enum(cred));
568 vnc_display_close(VNC_DISPLAY(gpdata->vnc));
569 }
570 break;
571 case VNC_DISPLAY_CREDENTIAL_PASSWORD:
572 if (!s_password ||
573 vnc_display_set_credential(VNC_DISPLAY(gpdata->vnc),
574 g_value_get_enum(cred),
575 s_password)) {
576 g_debug("Failed to set credential type %d", g_value_get_enum(cred));
577 vnc_display_close(VNC_DISPLAY(gpdata->vnc));
578 }
579 break;
580 case VNC_DISPLAY_CREDENTIAL_CLIENTNAME:
581 if (vnc_display_set_credential(VNC_DISPLAY(gpdata->vnc),
582 g_value_get_enum(cred),
583 "remmina")) {
584 g_debug("Failed to set credential type %d", g_value_get_enum(cred));
585 vnc_display_close(VNC_DISPLAY(gpdata->vnc));
586 }
587 break;
588 default:
589 g_debug("Unsupported credential type %d", g_value_get_enum(cred));
590 vnc_display_close(VNC_DISPLAY(gpdata->vnc));
591 }
592 }
593
594 if (s_username) g_free(s_username);
595 if (s_password) g_free(s_password);
596
597 return TRUE;
598 } else {
599 return FALSE;
600 }
601
602 cleanup:
603 g_free(s_username);
604 g_free(s_password);
605
606 //g_object_set(gpdata->session, "password", s_password, NULL);
607 return TRUE;
608 }
609
gvnc_plugin_initialized(GtkWidget * vncdisplay,RemminaProtocolWidget * gp)610 static void gvnc_plugin_initialized(GtkWidget *vncdisplay, RemminaProtocolWidget *gp)
611 {
612 TRACE_CALL(__func__);
613
614 GVncPluginData *gpdata = GET_PLUGIN_DATA(gp);
615 RemminaFile *remminafile = remmina_plugin_service->protocol_plugin_get_file(gp);
616
617 REMMINA_PLUGIN_DEBUG("Connection initialized");
618 g_return_if_fail(gpdata != NULL);
619 REMMINA_PLUGIN_DEBUG("Presenting the window");
620
621 VncAudioFormat format = {
622 VNC_AUDIO_FORMAT_RAW_S32,
623 2,
624 44100,
625 };
626
627 REMMINA_PLUGIN_DEBUG("Gathering the VNC connection object");
628 gpdata->conn = vnc_display_get_connection(VNC_DISPLAY(gpdata->vnc));
629 g_return_if_fail(gpdata->conn != NULL);
630
631 if (remmina_plugin_service->file_get_int(remminafile, "enableaudio", FALSE)) {
632 REMMINA_PLUGIN_DEBUG("Setting up VNC audio channel");
633 if (vnc_connection_set_audio_format(gpdata->conn, &format))
634 REMMINA_PLUGIN_DEBUG("VNC audio format set");
635 else {
636 REMMINA_PLUGIN_DEBUG("VNC audio format returned an error");
637 return;
638 }
639
640 if (vnc_connection_set_audio(gpdata->conn, VNC_AUDIO(gpdata->pa)))
641 REMMINA_PLUGIN_DEBUG("VNC audio channel has been set");
642 else {
643 REMMINA_PLUGIN_DEBUG("VNC audio channel cannot be set");
644 return;
645 }
646 REMMINA_PLUGIN_DEBUG("Enabling audio");
647 if (vnc_connection_audio_enable(gpdata->conn))
648 REMMINA_PLUGIN_DEBUG("Audio enabled");
649 else
650 REMMINA_PLUGIN_DEBUG("Audio cannot be enabled");
651 }
652 gpdata->width = vnc_display_get_width(VNC_DISPLAY(gpdata->vnc));
653 gpdata->width = vnc_display_get_height(VNC_DISPLAY(gpdata->vnc));
654 }
655
gvnc_plugin_disconnected(VncDisplay * vnc G_GNUC_UNUSED,RemminaProtocolWidget * gp)656 static void gvnc_plugin_disconnected(VncDisplay *vnc G_GNUC_UNUSED, RemminaProtocolWidget *gp)
657 {
658 TRACE_CALL(__func__);
659 //GVncPluginData *gpdata = GET_PLUGIN_DATA(gp);
660
661 REMMINA_PLUGIN_DEBUG("[%s] Plugin disconnected", PLUGIN_NAME);
662 }
gvnc_plugin_close_connection(RemminaProtocolWidget * gp)663 static gboolean gvnc_plugin_close_connection(RemminaProtocolWidget *gp)
664 {
665 TRACE_CALL(__func__);
666 GVncPluginData *gpdata = GET_PLUGIN_DATA(gp);
667
668 REMMINA_PLUGIN_DEBUG("[%s] Plugin close connection", PLUGIN_NAME);
669
670 if (gpdata) {
671 if (gpdata->error_msg) g_free(gpdata->error_msg);
672 if (gpdata->vnc)
673 vnc_display_close(VNC_DISPLAY(gpdata->vnc));
674 //g_object_unref(gpdata->vnc);
675 }
676
677 /* Remove instance->context from gp object data to avoid double free */
678 g_object_steal_data(G_OBJECT(gp), "plugin-data");
679 remmina_plugin_service->protocol_plugin_signal_connection_closed(gp);
680 return FALSE;
681 }
682
gvnc_plugin_init(RemminaProtocolWidget * gp)683 static void gvnc_plugin_init(RemminaProtocolWidget *gp)
684 {
685 TRACE_CALL(__func__);
686 GVncPluginData *gpdata;
687 //VncGrabSequence *seq;
688
689 GtkClipboard *cb;
690
691 gpdata = g_new0(GVncPluginData, 1);
692 g_object_set_data_full(G_OBJECT(gp), "plugin-data", gpdata, g_free);
693
694 gpdata->pa = NULL;
695
696 REMMINA_PLUGIN_DEBUG("[%s] Plugin init", PLUGIN_NAME);
697
698 RemminaFile *remminafile = remmina_plugin_service->protocol_plugin_get_file(gp);
699
700 if (remmina_plugin_service->file_get_int(remminafile, "gvncdebug", FALSE))
701 vnc_util_set_debug(TRUE);
702
703 gpdata->vnc = vnc_display_new();
704 if (remmina_plugin_service->file_get_int(remminafile, "enableaudio", FALSE))
705 gpdata->pa = vnc_audio_pulse_new();
706
707
708 g_signal_connect(gpdata->vnc, "vnc-auth-credential",
709 G_CALLBACK(gvnc_plugin_ask_auth), gp);
710 g_signal_connect(gpdata->vnc, "vnc-auth-failure",
711 G_CALLBACK(gvnc_plugin_auth_failure), gp);
712 g_signal_connect(gpdata->vnc, "vnc-auth-unsupported",
713 G_CALLBACK(gvnc_plugin_auth_unsupported), gp);
714 g_signal_connect(gpdata->vnc, "vnc-disconnected",
715 G_CALLBACK(gvnc_plugin_disconnected), gp);
716 g_signal_connect(gpdata->vnc, "vnc-initialized",
717 G_CALLBACK(gvnc_plugin_initialized), gp);
718 g_signal_connect(gpdata->vnc, "vnc-desktop-resize",
719 G_CALLBACK(gvnc_plugin_desktop_resize), gp);
720 g_signal_connect(gpdata->vnc, "vnc-bell",
721 G_CALLBACK(gvnc_plugin_on_bell), gp);
722 g_signal_connect(gpdata->vnc, "vnc-error",
723 G_CALLBACK(gvnc_plugin_on_vnc_error), gp);
724 g_signal_connect(gpdata->vnc, "vnc-pointer-grab",
725 G_CALLBACK(gvnc_plugin_mouse_grab), gp);
726 g_signal_connect(gpdata->vnc, "vnc-pointer-ungrab",
727 G_CALLBACK(gvnc_plugin_mouse_ungrab), gp);
728 g_signal_connect(gpdata->vnc, "vnc-server-cut-text",
729 G_CALLBACK(gvnc_plugin_cut_text), gp);
730 //seq = vnc_grab_sequence_new_from_string ("Control_R");
731 //vnc_display_set_grab_keys(VNC_DISPLAY(gpdata->vnc), seq);
732 //vnc_grab_sequence_free(seq);
733
734 /* Setup the clipboard */
735 cb = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD);
736 gpdata->signal_clipboard = g_signal_connect(cb,
737 "owner-change",
738 G_CALLBACK(gvnc_plugin_clipboard_cb),
739 gp);
740 }
741
gvnc_plugin_open_connection(RemminaProtocolWidget * gp)742 static gboolean gvnc_plugin_open_connection(RemminaProtocolWidget *gp)
743 {
744 TRACE_CALL(__func__);
745
746 gint port;
747 gchar *host = NULL, *tunnel = NULL;
748 GVncPluginData *gpdata = GET_PLUGIN_DATA(gp);
749 RemminaFile *remminafile = remmina_plugin_service->protocol_plugin_get_file(gp);
750
751 REMMINA_PLUGIN_DEBUG("[%s] Plugin open connection", PLUGIN_NAME);
752
753 gpdata->box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
754 gtk_container_add(GTK_CONTAINER(gp), gpdata->box);
755 gtk_widget_set_hexpand(GTK_WIDGET(gpdata->vnc), TRUE);
756 gtk_widget_set_vexpand(GTK_WIDGET(gpdata->vnc), TRUE);
757 gtk_container_add(GTK_CONTAINER(gpdata->box), gpdata->vnc);
758
759 remmina_plugin_service->protocol_plugin_register_hostkey(gp, gpdata->vnc);
760
761
762 const gchar *address = remmina_plugin_service->file_get_string(remminafile, "server");
763 if(strstr(g_strdup(address), "unix:///") != NULL) {
764 REMMINA_PLUGIN_DEBUG("address contain unix:// -> %s", address);
765 gchar *val = str_replace (address, "unix://", "");
766 REMMINA_PLUGIN_DEBUG("address after cleaning = %s", val);
767 gint fd = remmina_plugin_service->open_unix_sock(val);
768 REMMINA_PLUGIN_DEBUG("Unix socket fd: %d", fd);
769 gpdata->fd = fd;
770 g_free(val);
771
772 } else {
773 /* Setup SSH tunnel if needed */
774 tunnel = remmina_plugin_service->protocol_plugin_start_direct_tunnel(gp, GVNC_DEFAULT_PORT, FALSE);
775 if (!tunnel)
776 return FALSE;
777 remmina_plugin_service->get_server_port(tunnel,
778 GVNC_DEFAULT_PORT,
779 &host,
780 &port);
781 }
782
783
784
785 gpdata->depth_profile = remmina_plugin_service->file_get_int(remminafile, "depth_profile", 24);
786 gpdata->viewonly = remmina_plugin_service->file_get_int(remminafile, "viewonly", FALSE);
787 vnc_display_set_depth(VNC_DISPLAY(gpdata->vnc), gpdata->depth_profile);
788 if (gpdata->fd > 0)
789 vnc_display_open_fd(VNC_DISPLAY(gpdata->vnc), gpdata->fd);
790 else
791 vnc_display_open_host(VNC_DISPLAY(gpdata->vnc), host, g_strdup_printf("%d", port));
792 gpdata->lossy_encoding = remmina_plugin_service->file_get_int(remminafile, "lossy_encoding", FALSE);
793 vnc_display_set_lossy_encoding(VNC_DISPLAY(gpdata->vnc), gpdata->shared);
794 vnc_display_set_shared_flag(VNC_DISPLAY(gpdata->vnc), gpdata->shared);
795
796 if(host) g_free(host);
797 if(tunnel) g_free(tunnel);
798
799 /* TRUE Conflict with remmina? */
800 vnc_display_set_keyboard_grab(VNC_DISPLAY(gpdata->vnc), FALSE);
801 vnc_display_set_read_only(VNC_DISPLAY(gpdata->vnc), gpdata->viewonly);
802 /* TRUE Conflict with remmina? */
803 vnc_display_set_pointer_grab(VNC_DISPLAY(gpdata->vnc), FALSE);
804 vnc_display_set_pointer_local(VNC_DISPLAY(gpdata->vnc), TRUE);
805
806 vnc_display_set_force_size(VNC_DISPLAY(gpdata->vnc), FALSE);
807 vnc_display_set_scaling(VNC_DISPLAY(gpdata->vnc), TRUE);
808 #ifdef HAVE_VNC_REMOTE_RESIZE
809 vnc_display_set_allow_resize(VNC_DISPLAY(gpdata->vnc), TRUE);
810 vnc_display_set_zoom_level(VNC_DISPLAY(gpdata->vnc), opt_zoom);
811 #endif
812 remmina_plugin_service->protocol_plugin_signal_connection_opened(gp);
813 gtk_widget_show_all(gpdata->box);
814 return TRUE;
815 }
816
817 /* Array of key/value pairs for color depths */
818 static gpointer colordepth_list[] =
819 {
820 "0", N_("Use server settings"),
821 "1", N_("True colour (24 bits)"),
822 "2", N_("High colour (16 bits)"),
823 "3", N_("Low colour (8 bits)"),
824 "4", N_("Ultra low colour (3 bits)"),
825 NULL
826 };
827 /* Array of RemminaProtocolSetting for basic settings.
828 * Each item is composed by:
829 * a) RemminaProtocolSettingType for setting type
830 * b) Setting name
831 * c) Setting description
832 * d) Compact disposition
833 * e) Values for REMMINA_PROTOCOL_SETTING_TYPE_SELECT or REMMINA_PROTOCOL_SETTING_TYPE_COMBO
834 * f) Setting Tooltip
835 */
836 static const RemminaProtocolSetting gvnc_plugin_basic_settings[] =
837 {
838 { REMMINA_PROTOCOL_SETTING_TYPE_SERVER, "server", NULL, FALSE, NULL, NULL },
839 { REMMINA_PROTOCOL_SETTING_TYPE_PASSWORD, "password", N_("VNC password"), FALSE, NULL, NULL },
840 { REMMINA_PROTOCOL_SETTING_TYPE_SELECT, "depth_profile", N_("Colour depth"), FALSE, colordepth_list, NULL },
841 { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "lossy_encoding", N_("Use JPEG Compression"), TRUE, NULL, N_("This might not work on all VNC servers") },
842 { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "gvncdebug", N_("Enable GTK-VNC debug"), FALSE, NULL, NULL },
843 { REMMINA_PROTOCOL_SETTING_TYPE_END, NULL, NULL, FALSE, NULL, NULL }
844 };
845
846 /* Array of RemminaProtocolSetting for advanced settings.
847 * Each item is composed by:
848 * a) RemminaProtocolSettingType for setting type
849 * b) Setting name
850 * c) Setting description
851 * d) Compact disposition
852 * e) Values for REMMINA_PROTOCOL_SETTING_TYPE_SELECT or REMMINA_PROTOCOL_SETTING_TYPE_COMBO
853 * f) Setting Tooltip
854 */
855 static const RemminaProtocolSetting gvnc_plugin_advanced_settings[] =
856 {
857 { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "disableclipboard", N_("No clipboard sync"), TRUE, NULL, NULL },
858 { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "disablepasswordstoring", N_("Forget passwords after use"), FALSE, NULL, NULL },
859 { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "disableserverbell", N_("Ignore remote bell messages"), TRUE, NULL, NULL },
860 { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "enableaudio", N_("Enable audio channel"), FALSE, NULL, NULL },
861 { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "viewonly", N_("View only"), TRUE, NULL, NULL },
862 { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "shared", N_("Shared connection"), TRUE, NULL, N_("If the server should try to share the desktop by leaving other clients connected") },
863 { REMMINA_PROTOCOL_SETTING_TYPE_END, NULL, NULL, TRUE, NULL, NULL }
864 };
865
866 /* Array for available features.
867 * The last element of the array must be REMMINA_PROTOCOL_FEATURE_TYPE_END. */
868 static const RemminaProtocolFeature gvnc_plugin_features[] =
869 {
870 { REMMINA_PROTOCOL_FEATURE_TYPE_PREF, GVNC_PLUGIN_FEATURE_PREF_VIEWONLY, GINT_TO_POINTER(REMMINA_PROTOCOL_FEATURE_PREF_CHECK), "viewonly", N_("View only") },
871 { REMMINA_PROTOCOL_FEATURE_TYPE_PREF, GVNC_PLUGIN_FEATURE_PREF_DISABLECLIPBOARD, GINT_TO_POINTER(REMMINA_PROTOCOL_FEATURE_PREF_CHECK), "disableclipboard", N_("No clipboard sync") },
872 { REMMINA_PROTOCOL_FEATURE_TYPE_TOOL, GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTDEL, N_("Send Ctrl+Alt+_Del"), NULL, NULL },
873 { REMMINA_PROTOCOL_FEATURE_TYPE_TOOL, GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTBACKSPACE, N_("Send Ctrl+Alt+_Backspace"), NULL, NULL },
874 { REMMINA_PROTOCOL_FEATURE_TYPE_TOOL, GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTF1, N_("Send Ctrl+Alt+_F1"), NULL, NULL },
875 { REMMINA_PROTOCOL_FEATURE_TYPE_TOOL, GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTF2, N_("Send Ctrl+Alt+_F2"), NULL, NULL },
876 { REMMINA_PROTOCOL_FEATURE_TYPE_TOOL, GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTF3, N_("Send Ctrl+Alt+_F3"), NULL, NULL },
877 { REMMINA_PROTOCOL_FEATURE_TYPE_TOOL, GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTF4, N_("Send Ctrl+Alt+_F4"), NULL, NULL },
878 { REMMINA_PROTOCOL_FEATURE_TYPE_TOOL, GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTF5, N_("Send Ctrl+Alt+_F5"), NULL, NULL },
879 { REMMINA_PROTOCOL_FEATURE_TYPE_TOOL, GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTF6, N_("Send Ctrl+Alt+_F6"), NULL, NULL },
880 { REMMINA_PROTOCOL_FEATURE_TYPE_TOOL, GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTF7, N_("Send Ctrl+Alt+_F7"), NULL, NULL },
881 { REMMINA_PROTOCOL_FEATURE_TYPE_TOOL, GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTF8, N_("Send Ctrl+Alt+_F8"), NULL, NULL },
882 { REMMINA_PROTOCOL_FEATURE_TYPE_TOOL, GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTF9, N_("Send Ctrl+Alt+_F9"), NULL, NULL },
883 { REMMINA_PROTOCOL_FEATURE_TYPE_TOOL, GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTF10, N_("Send Ctrl+Alt+_F10"), NULL, NULL },
884 { REMMINA_PROTOCOL_FEATURE_TYPE_TOOL, GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTF11, N_("Send Ctrl+Alt+_F11"), NULL, NULL },
885 { REMMINA_PROTOCOL_FEATURE_TYPE_TOOL, GVNC_PLUGIN_FEATURE_TOOL_SENDCTRLALTF12, N_("Send Ctrl+Alt+_F12"), NULL, NULL },
886 #ifdef HAVE_VNC_POWER_CONTROL
887 { REMMINA_PROTOCOL_FEATURE_TYPE_TOOL, GVNC_PLUGIN_FEATURE_TOOL_REBOOT, N_("Reboot remote host"), NULL, NULL },
888 { REMMINA_PROTOCOL_FEATURE_TYPE_TOOL, GVNC_PLUGIN_FEATURE_TOOL_RESET, N_("Reset remote host (hard reboot)"), NULL, NULL },
889 { REMMINA_PROTOCOL_FEATURE_TYPE_TOOL, GVNC_PLUGIN_FEATURE_TOOL_SHUTDOWN, N_("Shutdown remote host"), NULL, NULL },
890 #endif
891 { REMMINA_PROTOCOL_FEATURE_TYPE_DYNRESUPDATE, GVNC_PLUGIN_FEATURE_DYNRESUPDATE, NULL, NULL, NULL },
892 { REMMINA_PROTOCOL_FEATURE_TYPE_SCALE, GVNC_PLUGIN_FEATURE_SCALE, NULL, NULL, NULL },
893 { REMMINA_PROTOCOL_FEATURE_TYPE_END, 0, NULL, NULL, NULL }
894 };
895
896 /* Protocol plugin definition and features */
897 static RemminaProtocolPlugin remmina_plugin = {
898 REMMINA_PLUGIN_TYPE_PROTOCOL, // Type
899 PLUGIN_NAME, // Name
900 PLUGIN_DESCRIPTION, // Description
901 GETTEXT_PACKAGE, // Translation domain
902 PLUGIN_VERSION, // Version number
903 PLUGIN_APPICON, // Icon for normal connection
904 PLUGIN_APPICON, // Icon for SSH connection
905 gvnc_plugin_basic_settings, // Array for basic settings
906 gvnc_plugin_advanced_settings, // Array for advanced settings
907 REMMINA_PROTOCOL_SSH_SETTING_TUNNEL, // SSH settings type
908 gvnc_plugin_features, // Array for available features
909 gvnc_plugin_init, // Plugin initialization
910 gvnc_plugin_open_connection, // Plugin open connection
911 gvnc_plugin_close_connection, // Plugin close connection
912 gvnc_plugin_query_feature, // Query for available features
913 gvnc_plugin_call_feature, // Call a feature
914 gvnc_plugin_keystroke, // Send a keystroke
915 NULL, // No screenshot support available
916 //gvnc_plugin_get_screenshot, // No screenshot support available
917 NULL, // RCW map event
918 NULL // RCW unmap event
919 };
920
remmina_plugin_entry(RemminaPluginService * service)921 G_MODULE_EXPORT gboolean remmina_plugin_entry(RemminaPluginService *service)
922 {
923 TRACE_CALL(__func__);
924 remmina_plugin_service = service;
925
926 bindtextdomain(GETTEXT_PACKAGE, REMMINA_RUNTIME_LOCALEDIR);
927 bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
928
929 if (!service->register_plugin((RemminaPlugin *)&remmina_plugin))
930 return FALSE;
931
932 return TRUE;
933 }
934