Remmina - The GTK+ Remote Desktop Client  v1.4.34
Remmina is a remote desktop client written in GTK+, aiming to be useful for system administrators and travellers, who need to work with lots of remote computers in front of either large monitors or tiny netbooks. Remmina supports multiple network protocols in an integrated and consistent user interface. Currently RDP, VNC, NX, XDMCP and SSH are supported.
vnc_plugin.c
Go to the documentation of this file.
1 /*
2  * Remmina - The GTK+ Remote Desktop Client
3  * Copyright (C) 2010-2011 Vic Lee
4  * Copyright (C) 2014-2015 Antenore Gatta, Fabio Castelli, Giovanni Panozzo
5  * Copyright (C) 2016-2022 Antenore Gatta, Giovanni Panozzo
6  * Copyright (C) 2022-2023 Antenore Gatta, Giovanni Panozzo, Hiroyuki Tanaka
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin Street, Fifth Floor,
21  * Boston, MA 02110-1301, USA.
22  *
23  * In addition, as a special exception, the copyright holders give
24  * permission to link the code of portions of this program with the
25  * OpenSSL library under certain conditions as described in each
26  * individual source file, and distribute linked combinations
27  * including the two.
28  * You must obey the GNU General Public License in all respects
29  * for all of the code used other than OpenSSL. * If you modify
30  * file(s) with this exception, you may extend this exception to your
31  * version of the file(s), but you are not obligated to do so. * If you
32  * do not wish to do so, delete this exception statement from your
33  * version. * If you delete this exception statement from all source
34  * files in the program, then also delete it here.
35  *
36  */
37 
38 #include <gmodule.h>
39 #include "vnc_plugin.h"
40 #include <rfb/rfbclient.h>
41 
42 #define REMMINA_PLUGIN_VNC_FEATURE_PREF_QUALITY 1
43 #define REMMINA_PLUGIN_VNC_FEATURE_PREF_VIEWONLY 2
44 #define REMMINA_PLUGIN_VNC_FEATURE_PREF_DISABLESERVERINPUT 3
45 #define REMMINA_PLUGIN_VNC_FEATURE_TOOL_REFRESH 4
46 #define REMMINA_PLUGIN_VNC_FEATURE_TOOL_CHAT 5
47 #define REMMINA_PLUGIN_VNC_FEATURE_SCALE 6
48 #define REMMINA_PLUGIN_VNC_FEATURE_UNFOCUS 7
49 #define REMMINA_PLUGIN_VNC_FEATURE_TOOL_SENDCTRLALTDEL 8
50 #define REMMINA_PLUGIN_VNC_FEATURE_PREF_COLOR 9
51 #define REMMINA_PLUGIN_VNC_FEATURE_DYNRESUPDATE 10
52 
53 #define VNC_DEFAULT_PORT 5900
54 
55 #define GET_PLUGIN_DATA(gp) (RemminaPluginVncData *)g_object_get_data(G_OBJECT(gp), "plugin-data")
56 
58 
59 static int dot_cursor_x_hot = 2;
60 static int dot_cursor_y_hot = 2;
61 static const gchar *dot_cursor_xpm[] =
62 { "5 5 3 1", " c None", ". c #000000", "+ c #FFFFFF", " ... ", ".+++.", ".+ +.", ".+++.", " ... " };
63 
64 
65 #define LOCK_BUFFER(t) if (t) { CANCEL_DEFER } pthread_mutex_lock(&gpdata->buffer_mutex);
66 #define UNLOCK_BUFFER(t) pthread_mutex_unlock(&gpdata->buffer_mutex); if (t) { CANCEL_ASYNC }
67 
70  GtkWidget * widget;
71  gint x, y, width, height;
73  gboolean scale;
74 
75  /* Mutex for thread synchronization */
76  pthread_mutex_t mu;
77  /* Flag to catch cancellations */
78  gboolean cancelled;
79 };
80 
81 static gboolean onMainThread_cb(struct onMainThread_cb_data *d)
82 {
83  TRACE_CALL(__func__);
84  if (!d->cancelled) {
85  switch (d->func) {
86  case FUNC_UPDATE_SCALE:
88  break;
89  }
90  pthread_mutex_unlock(&d->mu);
91  } else {
92  /* thread has been cancelled, so we must free d memory here */
93  g_free(d);
94  }
95  return G_SOURCE_REMOVE;
96 }
97 
98 
99 static void onMainThread_cleanup_handler(gpointer data)
100 {
101  TRACE_CALL(__func__);
102  struct onMainThread_cb_data *d = data;
103 
104  d->cancelled = TRUE;
105 }
106 
107 
109 {
110  TRACE_CALL(__func__);
111  d->cancelled = FALSE;
112  pthread_cleanup_push(onMainThread_cleanup_handler, d);
113  pthread_mutex_init(&d->mu, NULL);
114  pthread_mutex_lock(&d->mu);
115  gdk_threads_add_idle((GSourceFunc)onMainThread_cb, (gpointer)d);
116 
117  pthread_mutex_lock(&d->mu);
118 
119  pthread_cleanup_pop(0);
120  pthread_mutex_unlock(&d->mu);
121  pthread_mutex_destroy(&d->mu);
122 }
123 
128 static gboolean check_for_endianness()
129 {
130  unsigned int x = 1;
131  char *c = (char *)&x;
132 
133  return (int)*c;
134 }
135 
136 static void remmina_plugin_vnc_event_push(RemminaProtocolWidget *gp, gint event_type, gpointer p1, gpointer p2, gpointer p3)
137 {
138  TRACE_CALL(__func__);
139  RemminaPluginVncData *gpdata = GET_PLUGIN_DATA(gp);
140  RemminaPluginVncEvent *event;
141 
142  event = g_new(RemminaPluginVncEvent, 1);
143  event->event_type = event_type;
144  switch (event_type) {
146  event->event_data.key.keyval = GPOINTER_TO_UINT(p1);
147  event->event_data.key.pressed = GPOINTER_TO_INT(p2);
148  break;
150  event->event_data.pointer.x = GPOINTER_TO_INT(p1);
151  event->event_data.pointer.y = GPOINTER_TO_INT(p2);
152  event->event_data.pointer.button_mask = GPOINTER_TO_INT(p3);
153  break;
156  event->event_data.text.text = g_strdup((char *)p1);
157  break;
158  default:
159  break;
160  }
161 
162  pthread_mutex_lock(&gpdata->vnc_event_queue_mutex);
163  g_queue_push_tail(gpdata->vnc_event_queue, event);
164  pthread_mutex_unlock(&gpdata->vnc_event_queue_mutex);
165 
166  if (write(gpdata->vnc_event_pipe[1], "\0", 1)) {
167  /* Ignore */
168  }
169 }
170 
172 {
173  TRACE_CALL(__func__);
174  switch (event->event_type) {
177  g_free(event->event_data.text.text);
178  break;
179  default:
180  break;
181  }
182  g_free(event);
183 }
184 
186 {
187  TRACE_CALL(__func__);
188  RemminaPluginVncData *gpdata = GET_PLUGIN_DATA(gp);
189  RemminaPluginVncEvent *event;
190 
191  /* This is called from main thread after plugin thread has
192  * been closed, so no queue locking is necessary here */
193  while ((event = g_queue_pop_head(gpdata->vnc_event_queue)) != NULL)
195 }
196 
197 static void remmina_plugin_vnc_scale_area(RemminaProtocolWidget *gp, gint *x, gint *y, gint *w, gint *h)
198 {
199  TRACE_CALL(__func__);
200  RemminaPluginVncData *gpdata = GET_PLUGIN_DATA(gp);
201  GtkAllocation widget_allocation;
202  gint width, height;
203  gint sx, sy, sw, sh;
204 
205  if (gpdata->rgb_buffer == NULL)
206  return;
207 
210 
211  gtk_widget_get_allocation(GTK_WIDGET(gp), &widget_allocation);
212 
213  if (widget_allocation.width == width && widget_allocation.height == height)
214  return; /* Same size, no scaling */
215 
216  /* We have to extend the scaled region 2 scaled pixels, to avoid gaps */
217  sx = MIN(MAX(0, (*x) * widget_allocation.width / width - widget_allocation.width / width - 2), widget_allocation.width - 1);
218  sy = MIN(MAX(0, (*y) * widget_allocation.height / height - widget_allocation.height / height - 2), widget_allocation.height - 1);
219  sw = MIN(widget_allocation.width - sx, (*w) * widget_allocation.width / width + widget_allocation.width / width + 4);
220  sh = MIN(widget_allocation.height - sy, (*h) * widget_allocation.height / height + widget_allocation.height / height + 4);
221 
222  *x = sx;
223  *y = sy;
224  *w = sw;
225  *h = sh;
226 }
227 
229 {
230  TRACE_CALL(__func__);
231  /* This function can be called from a non main thread */
232 
233  RemminaPluginVncData *gpdata;
234  gint width, height;
235 
237  struct onMainThread_cb_data *d;
238  d = (struct onMainThread_cb_data *)g_malloc(sizeof(struct onMainThread_cb_data));
239  d->func = FUNC_UPDATE_SCALE;
240  d->gp = gp;
241  d->scale = scale;
243  g_free(d);
244  return;
245  }
246 
247  gpdata = GET_PLUGIN_DATA(gp);
248 
251  if (scale)
252  /* In scaled mode, drawing_area will get its dimensions from its parent */
253  gtk_widget_set_size_request(GTK_WIDGET(gpdata->drawing_area), -1, -1);
254  else
255  /* In non scaled mode, the plugins forces dimensions of drawing area */
256  gtk_widget_set_size_request(GTK_WIDGET(gpdata->drawing_area), width, height);
257 
259 }
260 
262 {
263  TRACE_CALL(__func__);
264  RemminaPluginVncData *gpdata = GET_PLUGIN_DATA(gp);
265  GdkCursor *cur;
266 
267  LOCK_BUFFER(FALSE);
268  gpdata->queuecursor_handler = 0;
269 
270  if (gpdata->queuecursor_surface) {
271  cur = gdk_cursor_new_from_surface(gdk_display_get_default(), gpdata->queuecursor_surface, gpdata->queuecursor_x,
272  gpdata->queuecursor_y);
273  gdk_window_set_cursor(gtk_widget_get_window(gpdata->drawing_area), cur);
274  g_object_unref(cur);
275  cairo_surface_destroy(gpdata->queuecursor_surface);
276  gpdata->queuecursor_surface = NULL;
277  } else {
278  gdk_window_set_cursor(gtk_widget_get_window(gpdata->drawing_area), NULL);
279  }
280  UNLOCK_BUFFER(FALSE);
281 
282  return FALSE;
283 }
284 
285 static void remmina_plugin_vnc_queuecursor(RemminaProtocolWidget *gp, cairo_surface_t *surface, gint x, gint y)
286 {
287  TRACE_CALL(__func__);
288  RemminaPluginVncData *gpdata = GET_PLUGIN_DATA(gp);
289 
290  if (gpdata->queuecursor_surface)
291  cairo_surface_destroy(gpdata->queuecursor_surface);
292  gpdata->queuecursor_surface = surface;
293  gpdata->queuecursor_x = x;
294  gpdata->queuecursor_y = y;
295  if (!gpdata->queuecursor_handler)
296  gpdata->queuecursor_handler = IDLE_ADD((GSourceFunc)remmina_plugin_vnc_setcursor, gp);
297 }
298 
299 typedef struct _RemminaKeyVal {
300  guint keyval;
301  guint16 keycode;
303 
304 /***************************** LibVNCClient related codes *********************************/
305 
307 { rfbNoAuth, rfbVncAuth, rfbMSLogon, 0 };
308 
310 {
311  RemminaPluginVncEvent *event;
312 
313  CANCEL_DEFER;
314  pthread_mutex_lock(&gpdata->vnc_event_queue_mutex);
315 
316  event = g_queue_pop_head(gpdata->vnc_event_queue);
317 
318  pthread_mutex_unlock(&gpdata->vnc_event_queue_mutex);
319  CANCEL_ASYNC;
320 
321  return event;
322 }
323 
325 {
326  TRACE_CALL(__func__);
327  RemminaPluginVncEvent *event;
328  RemminaPluginVncData *gpdata = GET_PLUGIN_DATA(gp);
329  rfbClient *cl;
330  gchar buf[100];
331 
332  cl = (rfbClient *)gpdata->client;
333  while ((event = remmina_plugin_vnc_event_queue_pop_head(gpdata)) != NULL) {
334  if (cl) {
335  switch (event->event_type) {
337  SendKeyEvent(cl, event->event_data.key.keyval, event->event_data.key.pressed);
338  break;
340  SendPointerEvent(cl, event->event_data.pointer.x, event->event_data.pointer.y,
341  event->event_data.pointer.button_mask);
342  break;
344  if (event->event_data.text.text) {
345  rfbClientLog("sending clipboard text '%s'\n", event->event_data.text.text);
346  SendClientCutText(cl, event->event_data.text.text, strlen(event->event_data.text.text));
347  }
348  break;
350  TextChatOpen(cl);
351  break;
353  TextChatSend(cl, event->event_data.text.text);
354  break;
356  TextChatClose(cl);
357  TextChatFinish(cl);
358  break;
359  default:
360  rfbClientLog("Ignoring VNC event: 0x%x\n", event->event_type);
361  break;
362  }
363  }
365  }
366  if (read(gpdata->vnc_event_pipe[0], buf, sizeof(buf))) {
367  /* Ignore */
368  }
369 }
370 
373  gchar * text;
374  gint textlen;
376 
377 static void remmina_plugin_vnc_update_quality(rfbClient *cl, gint quality)
378 {
379  TRACE_CALL(__func__);
380  RemminaProtocolWidget *gp = rfbClientGetClientData(cl, NULL);
381  RemminaFile *remminafile;
382  gchar *enc = NULL;
383 
390  switch (quality) {
391  case 9:
392  cl->appData.useBGR233 = 0;
393  cl->appData.encodingsString = "copyrect zlib hextile raw";
394  cl->appData.compressLevel = 1;
395  cl->appData.qualityLevel = 9;
396  break;
397  case 2:
398  cl->appData.useBGR233 = 0;
399  cl->appData.encodingsString = "tight zrle ultra copyrect hextile zlib corre rre raw";
400  cl->appData.compressLevel = 2;
401  cl->appData.qualityLevel = 7;
402  break;
403  case 1:
404  cl->appData.useBGR233 = 0;
405  cl->appData.encodingsString = "tight zrle ultra copyrect hextile zlib corre rre raw";
406  cl->appData.compressLevel = 3;
407  cl->appData.qualityLevel = 5;
408  break;
409  case 0:
410  default:
411  // bpp8 and tight encoding is not supported in libvnc
412  cl->appData.useBGR233 = 1;
413  cl->appData.encodingsString = "copyrect zrle ultra zlib hextile corre rre raw";
414  cl->appData.qualityLevel = 1;
415  break;
416  }
418  enc = g_strdup(remmina_plugin_service->file_get_string(remminafile, "encodings"));
419  if (enc) {
420  cl->appData.encodingsString = g_strdup(enc);
421  g_free (enc), enc = NULL;
422  }
423  gboolean tight = remmina_plugin_service->file_get_int (remminafile, "tightencoding", FALSE);
424  if (tight) {
425  if (!g_strrstr ( g_strdup(cl->appData.encodingsString), "tight\0")) {
426  cl->appData.encodingsString = g_strdup_printf("%s %s", "tight", g_strdup(cl->appData.encodingsString));
427  }
428  }
429 
430  REMMINA_PLUGIN_DEBUG("Quality: %d", quality);
431  REMMINA_PLUGIN_DEBUG("Encodings: %s", cl->appData.encodingsString);
432 }
433 
434 static void remmina_plugin_vnc_update_colordepth(rfbClient *cl, gint colordepth)
435 {
436  TRACE_CALL(__func__);
437 
438  cl->format.depth = colordepth;
439  cl->appData.requestedDepth = colordepth;
440 
441  cl->format.trueColour = 1;
442  cl->format.bigEndian = check_for_endianness()?FALSE:TRUE;
443 
444  switch (colordepth) {
445  case 8:
446  cl->format.depth = 8;
447  cl->format.bitsPerPixel = 8;
448  cl->format.blueMax = 3;
449  cl->format.blueShift = 6;
450  cl->format.greenMax = 7;
451  cl->format.greenShift = 3;
452  cl->format.redMax = 7;
453  cl->format.redShift = 0;
454  break;
455  case 16:
456  cl->format.depth = 15;
457  cl->format.bitsPerPixel = 16;
458  cl->format.redShift = 11;
459  cl->format.greenShift = 6;
460  cl->format.blueShift = 1;
461  cl->format.redMax = 31;
462  cl->format.greenMax = 31;
463  cl->format.blueMax = 31;
464  break;
465  case 32:
466  default:
467  cl->format.depth = 24;
468  cl->format.bitsPerPixel = 32;
469  cl->format.blueShift = 0;
470  cl->format.redShift = 16;
471  cl->format.greenShift = 8;
472  cl->format.blueMax = 0xff;
473  cl->format.redMax = 0xff;
474  cl->format.greenMax = 0xff;
475  break;
476  }
477 
478  rfbClientLog("colordepth = %d\n", colordepth);
479  rfbClientLog("format.depth = %d\n", cl->format.depth);
480  rfbClientLog("format.bitsPerPixel = %d\n", cl->format.bitsPerPixel);
481  rfbClientLog("format.blueShift = %d\n", cl->format.blueShift);
482  rfbClientLog("format.redShift = %d\n", cl->format.redShift);
483  rfbClientLog("format.trueColour = %d\n", cl->format.trueColour);
484  rfbClientLog("format.greenShift = %d\n", cl->format.greenShift);
485  rfbClientLog("format.blueMax = %d\n", cl->format.blueMax);
486  rfbClientLog("format.redMax = %d\n", cl->format.redMax);
487  rfbClientLog("format.greenMax = %d\n", cl->format.greenMax);
488  rfbClientLog("format.bigEndian = %d\n", cl->format.bigEndian);
489 }
490 
491 static rfbBool remmina_plugin_vnc_rfb_allocfb(rfbClient *cl)
492 {
493  TRACE_CALL(__func__);
494  RemminaProtocolWidget *gp = rfbClientGetClientData(cl, NULL);
495  RemminaPluginVncData *gpdata = GET_PLUGIN_DATA(gp);
496  gint width, height, depth, size;
497  gboolean scale;
498  cairo_surface_t *new_surface, *old_surface;
499 
500  width = cl->width;
501  height = cl->height;
502  depth = cl->format.bitsPerPixel;
503  size = width * height * (depth / 8);
504 
505  new_surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height);
506  if (cairo_surface_status(new_surface) != CAIRO_STATUS_SUCCESS)
507  return FALSE;
508  old_surface = gpdata->rgb_buffer;
509 
510  LOCK_BUFFER(TRUE);
511 
514 
515  gpdata->rgb_buffer = new_surface;
516 
517  if (gpdata->vnc_buffer)
518  g_free(gpdata->vnc_buffer);
519  gpdata->vnc_buffer = (guchar *)g_malloc(size);
520  cl->frameBuffer = gpdata->vnc_buffer;
521 
522  UNLOCK_BUFFER(TRUE);
523 
524  if (old_surface)
525  cairo_surface_destroy(old_surface);
526 
529 
530  /* Notify window of change so that scroll border can be hidden or shown if needed */
532 
533  /* Refresh the client’s updateRect - bug in xvncclient */
534  cl->updateRect.w = width;
535  cl->updateRect.h = height;
536 
537  return TRUE;
538 }
539 
540 static gint remmina_plugin_vnc_bits(gint n)
541 {
542  TRACE_CALL(__func__);
543  gint b = 0;
544 
545  while (n) {
546  b++;
547  n >>= 1;
548  }
549  return b ? b : 1;
550 }
551 
553 {
554  TRACE_CALL(__func__);
555  RemminaPluginVncData *gpdata = GET_PLUGIN_DATA(gp);
556  gint x, y, w, h;
557 
558  if (GTK_IS_WIDGET(gp) && gpdata->connected) {
559  LOCK_BUFFER(FALSE);
560  x = gpdata->queuedraw_x;
561  y = gpdata->queuedraw_y;
562  w = gpdata->queuedraw_w;
563  h = gpdata->queuedraw_h;
564  gpdata->queuedraw_handler = 0;
565  UNLOCK_BUFFER(FALSE);
566 
567  gtk_widget_queue_draw_area(GTK_WIDGET(gp), x, y, w, h);
568  }
569  return FALSE;
570 }
571 
572 static void remmina_plugin_vnc_queue_draw_area(RemminaProtocolWidget *gp, gint x, gint y, gint w, gint h)
573 {
574  TRACE_CALL(__func__);
575  RemminaPluginVncData *gpdata = GET_PLUGIN_DATA(gp);
576  gint nx2, ny2, ox2, oy2;
577 
578  LOCK_BUFFER(TRUE);
579  if (gpdata->queuedraw_handler) {
580  nx2 = x + w;
581  ny2 = y + h;
582  ox2 = gpdata->queuedraw_x + gpdata->queuedraw_w;
583  oy2 = gpdata->queuedraw_y + gpdata->queuedraw_h;
584  gpdata->queuedraw_x = MIN(gpdata->queuedraw_x, x);
585  gpdata->queuedraw_y = MIN(gpdata->queuedraw_y, y);
586  gpdata->queuedraw_w = MAX(ox2, nx2) - gpdata->queuedraw_x;
587  gpdata->queuedraw_h = MAX(oy2, ny2) - gpdata->queuedraw_y;
588  } else {
589  gpdata->queuedraw_x = x;
590  gpdata->queuedraw_y = y;
591  gpdata->queuedraw_w = w;
592  gpdata->queuedraw_h = h;
593  gpdata->queuedraw_handler = IDLE_ADD((GSourceFunc)remmina_plugin_vnc_queue_draw_area_real, gp);
594  }
595  UNLOCK_BUFFER(TRUE);
596 }
597 
598 static void remmina_plugin_vnc_rfb_fill_buffer(rfbClient *cl, guchar *dest, gint dest_rowstride, guchar *src,
599  gint src_rowstride, guchar *mask, gint w, gint h)
600 {
601  TRACE_CALL(__func__);
602  guchar *srcptr;
603  gint bytesPerPixel;
604  guint32 src_pixel;
605  gint ix, iy;
606  gint i;
607  guchar c;
608  gint rs, gs, bs, rm, gm, bm, rl, gl, bl, rr, gr, br;
609  gint r;
610  guint32 *destptr;
611 
612  union {
613  struct {
614  guchar a, r, g, b;
615  } colors;
616  guint32 argb;
617  } dst_pixel;
618 
619  bytesPerPixel = cl->format.bitsPerPixel / 8;
620  switch (cl->format.bitsPerPixel) {
621  case 32:
622  /* The following codes fill in the Alpha channel swap red/green value */
623  for (iy = 0; iy < h; iy++) {
624  destptr = (guint32 *)(dest + iy * dest_rowstride);
625  srcptr = src + iy * src_rowstride;
626  for (ix = 0; ix < w; ix++) {
627  if (!mask || *mask++) {
628  dst_pixel.colors.a = 0xff;
629  dst_pixel.colors.r = *(srcptr + 2);
630  dst_pixel.colors.g = *(srcptr + 1);
631  dst_pixel.colors.b = *srcptr;
632  *destptr++ = ntohl(dst_pixel.argb);
633  } else {
634  *destptr++ = 0;
635  }
636  srcptr += 4;
637  }
638  }
639  break;
640  default:
641  rm = cl->format.redMax;
642  gm = cl->format.greenMax;
643  bm = cl->format.blueMax;
644  rr = remmina_plugin_vnc_bits(rm);
645  gr = remmina_plugin_vnc_bits(gm);
646  br = remmina_plugin_vnc_bits(bm);
647  rl = 8 - rr;
648  gl = 8 - gr;
649  bl = 8 - br;
650  rs = cl->format.redShift;
651  gs = cl->format.greenShift;
652  bs = cl->format.blueShift;
653  for (iy = 0; iy < h; iy++) {
654  destptr = (guint32 *)(dest + iy * dest_rowstride);
655  srcptr = src + iy * src_rowstride;
656  for (ix = 0; ix < w; ix++) {
657  src_pixel = 0;
658  for (i = 0; i < bytesPerPixel; i++)
659  src_pixel += (*srcptr++) << (8 * i);
660 
661  if (!mask || *mask++) {
662  dst_pixel.colors.a = 0xff;
663  c = (guchar)((src_pixel >> rs) & rm) << rl;
664  for (r = rr; r < 8; r *= 2)
665  c |= c >> r;
666  dst_pixel.colors.r = c;
667  c = (guchar)((src_pixel >> gs) & gm) << gl;
668  for (r = gr; r < 8; r *= 2)
669  c |= c >> r;
670  dst_pixel.colors.g = c;
671  c = (guchar)((src_pixel >> bs) & bm) << bl;
672  for (r = br; r < 8; r *= 2)
673  c |= c >> r;
674  dst_pixel.colors.b = c;
675  *destptr++ = ntohl(dst_pixel.argb);
676  } else {
677  *destptr++ = 0;
678  }
679  }
680  }
681  break;
682  }
683 }
684 
685 static void remmina_plugin_vnc_rfb_updatefb(rfbClient *cl, int x, int y, int w, int h)
686 {
687  TRACE_CALL(__func__);
688  RemminaProtocolWidget *gp = rfbClientGetClientData(cl, NULL);
689  RemminaPluginVncData *gpdata = GET_PLUGIN_DATA(gp);
690  gint bytesPerPixel;
691  gint rowstride;
692  gint width;
693 
694  LOCK_BUFFER(TRUE);
695 
696  if (w >= 1 || h >= 1) {
698  bytesPerPixel = cl->format.bitsPerPixel / 8;
699  rowstride = cairo_image_surface_get_stride(gpdata->rgb_buffer);
700  cairo_surface_flush(gpdata->rgb_buffer);
701  remmina_plugin_vnc_rfb_fill_buffer(cl, cairo_image_surface_get_data(gpdata->rgb_buffer) + y * rowstride + x * 4,
702  rowstride, gpdata->vnc_buffer + ((y * width + x) * bytesPerPixel), width * bytesPerPixel, NULL,
703  w, h);
704  cairo_surface_mark_dirty(gpdata->rgb_buffer);
705  }
706 
708  remmina_plugin_vnc_scale_area(gp, &x, &y, &w, &h);
709 
710  UNLOCK_BUFFER(TRUE);
711 
712  remmina_plugin_vnc_queue_draw_area(gp, x, y, w, h);
713 }
714 
715 static void remmina_plugin_vnc_rfb_finished(rfbClient *cl) __attribute__ ((unused));
716 static void remmina_plugin_vnc_rfb_finished(rfbClient *cl)
717 {
718  TRACE_CALL(__func__);
719  REMMINA_PLUGIN_DEBUG("FinishedFrameBufferUpdate");
720 }
721 
722 static void remmina_plugin_vnc_rfb_led_state(rfbClient *cl, int value, int pad)
723 {
724  TRACE_CALL(__func__);
725  REMMINA_PLUGIN_DEBUG("Led state - value: %d, pad: %d", value, pad);
726 }
727 
729 {
730  TRACE_CALL(__func__);
731  RemminaProtocolWidget *gp = param->gp;
732  RemminaPluginVncData *gpdata = GET_PLUGIN_DATA(gp);
733  GDateTime *t;
734  glong diff;
735  const char *cur_charset;
736  gchar *text;
737  gsize br, bw;
738 
739  if (GTK_IS_WIDGET(gp) && gpdata->connected) {
740  t = g_date_time_new_now_utc();
741  diff = g_date_time_difference(t, gpdata->clipboard_timer) / 100000; // tenth of second
742  if (diff >= 10) {
743  g_date_time_unref(gpdata->clipboard_timer);
744  gpdata->clipboard_timer = t;
745  /* Convert text from VNC latin-1 to current GTK charset (usually UTF-8) */
746  g_get_charset(&cur_charset);
747  text = g_convert_with_fallback(param->text, param->textlen, cur_charset, "ISO-8859-1", "?", &br, &bw, NULL);
748  gtk_clipboard_set_text(gtk_clipboard_get(GDK_SELECTION_CLIPBOARD), text, bw);
749  g_free(text);
750  } else {
751  g_date_time_unref(t);
752  }
753  }
754  g_free(param->text);
755  g_free(param);
756  return FALSE;
757 }
758 
759 static void remmina_plugin_vnc_rfb_cuttext(rfbClient *cl, const char *text, int textlen)
760 {
761  TRACE_CALL(__func__);
763 
764  param = g_new(RemminaPluginVncCuttextParam, 1);
765  param->gp = (RemminaProtocolWidget *)rfbClientGetClientData(cl, NULL);
766  param->text = g_malloc(textlen);
767  memcpy(param->text, text, textlen);
768  param->textlen = textlen;
769  IDLE_ADD((GSourceFunc)remmina_plugin_vnc_queue_cuttext, param);
770 }
771 
772 static char *
774 {
775  TRACE_CALL(__func__);
776  RemminaProtocolWidget *gp = rfbClientGetClientData(cl, NULL);
777  RemminaPluginVncData *gpdata = GET_PLUGIN_DATA(gp);
778  RemminaFile *remminafile;
779  gchar *pwd = NULL;
780 
781  gpdata->auth_called = TRUE;
783 
784  if (gpdata->auth_first)
785  pwd = g_strdup(remmina_plugin_service->file_get_string(remminafile, "password"));
786  if (!pwd) {
787  gboolean save;
788  gint ret;
789  gboolean disablepasswordstoring = remmina_plugin_service->file_get_int(remminafile, "disablepasswordstoring", FALSE);
791  (disablepasswordstoring ? 0 : REMMINA_MESSAGE_PANEL_FLAG_SAVEPASSWORD),
792  _("Enter VNC password"),
793  NULL,
794  remmina_plugin_service->file_get_string(remminafile, "password"),
795  NULL,
796  NULL);
797  if (ret != GTK_RESPONSE_OK) {
798  gpdata->connected = FALSE;
799  return NULL;
800  }
803  if (save)
804  remmina_plugin_service->file_set_string(remminafile, "password", pwd);
805  else
806  remmina_plugin_service->file_set_string(remminafile, "password", NULL);
807  }
808  return pwd;
809 }
810 
811 static rfbCredential *
812 remmina_plugin_vnc_rfb_credential(rfbClient *cl, int credentialType)
813 {
814  TRACE_CALL(__func__);
815  rfbCredential *cred;
816  RemminaProtocolWidget *gp = rfbClientGetClientData(cl, NULL);
817  RemminaPluginVncData *gpdata = GET_PLUGIN_DATA(gp);
818  RemminaFile *remminafile;
819  gint ret;
820  gchar *s1, *s2;
821  gboolean disablepasswordstoring;
822 
823  gpdata->auth_called = TRUE;
825 
826  cred = g_new0(rfbCredential, 1);
827 
828  switch (credentialType) {
829  case rfbCredentialTypeUser:
830 
831  s1 = g_strdup(remmina_plugin_service->file_get_string(remminafile, "username"));
832 
833  s2 = g_strdup(remmina_plugin_service->file_get_string(remminafile, "password"));
834 
835  if (gpdata->auth_first && s1 && s2) {
836  cred->userCredential.username = s1;
837  cred->userCredential.password = s2;
838  } else {
839  g_free(s1);
840  g_free(s2);
841 
842  disablepasswordstoring = remmina_plugin_service->file_get_int(remminafile, "disablepasswordstoring", FALSE);
845  _("Enter VNC authentication credentials"),
846  remmina_plugin_service->file_get_string(remminafile, "username"),
847  remmina_plugin_service->file_get_string(remminafile, "password"),
848  NULL,
849  NULL);
850  if (ret == GTK_RESPONSE_OK) {
852  cred->userCredential.username = remmina_plugin_service->protocol_plugin_init_get_username(gp);
853  cred->userCredential.password = remmina_plugin_service->protocol_plugin_init_get_password(gp);
854  if (save) {
855  remmina_plugin_service->file_set_string(remminafile, "username", cred->userCredential.username);
856  remmina_plugin_service->file_set_string(remminafile, "password", cred->userCredential.password);
857  } else {
858  remmina_plugin_service->file_set_string(remminafile, "username", NULL);
859  remmina_plugin_service->file_set_string(remminafile, "password", NULL);
860  }
861  } else {
862  g_free(cred);
863  cred = NULL;
864  gpdata->connected = FALSE;
865  }
866  }
867  break;
868 
869  case rfbCredentialTypeX509:
870  if (gpdata->auth_first &&
871  remmina_plugin_service->file_get_string(remminafile, "cacert")) {
872  cred->x509Credential.x509CACertFile = g_strdup(remmina_plugin_service->file_get_string(remminafile, "cacert"));
873  cred->x509Credential.x509CACrlFile = g_strdup(remmina_plugin_service->file_get_string(remminafile, "cacrl"));
874  cred->x509Credential.x509ClientCertFile = g_strdup(remmina_plugin_service->file_get_string(remminafile, "clientcert"));
875  cred->x509Credential.x509ClientKeyFile = g_strdup(remmina_plugin_service->file_get_string(remminafile, "clientkey"));
876  } else {
878 
879  if (ret == GTK_RESPONSE_OK) {
880  cred->x509Credential.x509CACertFile = remmina_plugin_service->protocol_plugin_init_get_cacert(gp);
881  cred->x509Credential.x509CACrlFile = remmina_plugin_service->protocol_plugin_init_get_cacrl(gp);
882  cred->x509Credential.x509ClientCertFile = remmina_plugin_service->protocol_plugin_init_get_clientcert(gp);
883  cred->x509Credential.x509ClientKeyFile = remmina_plugin_service->protocol_plugin_init_get_clientkey(gp);
884  } else {
885  g_free(cred);
886  cred = NULL;
887  gpdata->connected = FALSE;
888  }
889  }
890  break;
891 
892  default:
893  g_free(cred);
894  cred = NULL;
895  break;
896  }
897  return cred;
898 }
899 
900 static void remmina_plugin_vnc_rfb_cursor_shape(rfbClient *cl, int xhot, int yhot, int width, int height, int bytesPerPixel)
901 {
902  TRACE_CALL(__func__);
903  RemminaProtocolWidget *gp = rfbClientGetClientData(cl, NULL);
904  RemminaPluginVncData *gpdata = GET_PLUGIN_DATA(gp);
905 
906  if (!gtk_widget_get_window(GTK_WIDGET(gp)))
907  return;
908 
909  if (width && height) {
910  gint stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, width);
911  guchar *data = g_malloc(stride * height);
912  remmina_plugin_vnc_rfb_fill_buffer(cl, data, stride, cl->rcSource,
913  width * cl->format.bitsPerPixel / 8, cl->rcMask, width, height);
914  cairo_surface_t *surface = cairo_image_surface_create_for_data(data, CAIRO_FORMAT_ARGB32, width, height, stride);
915  if (cairo_surface_status(surface) != CAIRO_STATUS_SUCCESS) {
916  g_free(data);
917  return;
918  }
919  if (cairo_surface_set_user_data(surface, NULL, NULL, g_free) != CAIRO_STATUS_SUCCESS) {
920  g_free(data);
921  return;
922  }
923 
924  LOCK_BUFFER(TRUE);
925  remmina_plugin_vnc_queuecursor(gp, surface, xhot, yhot);
926  UNLOCK_BUFFER(TRUE);
927  }
928 }
929 
930 static void remmina_plugin_vnc_rfb_bell(rfbClient *cl)
931 {
932  TRACE_CALL(__func__);
933  REMMINA_PLUGIN_DEBUG("Bell message received");
935  RemminaFile *remminafile;
936  GdkWindow *window;
937 
938  gp = (RemminaProtocolWidget *)(rfbClientGetClientData(cl, NULL));
940 
941  if (remmina_plugin_service->file_get_int(remminafile, "disableserverbell", FALSE))
942  return;
943 
944  window = gtk_widget_get_window(GTK_WIDGET(gp));
945 
946  if (window)
947  gdk_window_beep(window);
948  REMMINA_PLUGIN_DEBUG("Beep emitted");
949 }
950 
951 /* Translate known VNC messages. It’s for intltool only, not for gcc */
952 #ifdef __DO_NOT_COMPILE_ME__
953 N_("Unable to connect to VNC server")
954 N_("Couldn’t convert '%s' to host address")
955 N_("VNC connection failed: %s")
956 N_("Your connection has been rejected.")
957 #endif
959 #define MAX_ERROR_LENGTH 1000
960 static gchar vnc_error[MAX_ERROR_LENGTH + 1];
961 static gboolean vnc_encryption_disable_requested;
962 
963 static void remmina_plugin_vnc_rfb_output(const char *format, ...)
964 {
965  TRACE_CALL(__func__);
966  gchar *f, *p, *ff;
967 
968  if (!rfbEnableClientLogging)
969  return;
970 
971  va_list args;
972  va_start(args, format);
973  /* eliminate the last \n */
974  f = g_strdup(format);
975  if (f[strlen(f) - 1] == '\n') f[strlen(f) - 1] = '\0';
976 
977  if (g_strcmp0(f, "VNC connection failed: %s") == 0) {
978  p = va_arg(args, gchar *);
979  g_snprintf(vnc_error, MAX_ERROR_LENGTH, _(f), _(p));
980  } else if (g_strcmp0(f, "The VNC server requested an unknown authentication method. %s") == 0) {
981  p = va_arg(args, gchar *);
982  if (vnc_encryption_disable_requested) {
983  ff = g_strconcat(_("The VNC server requested an unknown authentication method. %s"),
984  ". ",
985  _("Please retry after turning on encryption for this profile."),
986  NULL);
987  g_snprintf(vnc_error, MAX_ERROR_LENGTH, ff, p);
988  g_free(ff);
989  } else {
990  g_snprintf(vnc_error, MAX_ERROR_LENGTH, _(f), p);
991  }
992  } else {
993  g_vsnprintf(vnc_error, MAX_ERROR_LENGTH, _(f), args);
994  }
995  g_free(f);
996  va_end(args);
997 
998  REMMINA_PLUGIN_DEBUG("VNC returned: %s", vnc_error);
999 }
1000 
1001 static void remmina_plugin_vnc_chat_on_send(RemminaProtocolWidget *gp, const gchar *text)
1002 {
1003  TRACE_CALL(__func__);
1004  gchar *ptr;
1005 
1006  /* Need to add a line-feed for UltraVNC */
1007  ptr = g_strdup_printf("%s\n", text);
1009  g_free(ptr);
1010 }
1011 
1013 {
1014  TRACE_CALL(__func__);
1016 }
1017 
1018 /* Send CTRL+ALT+DEL keys keystrokes to the plugin drawing_area widget */
1020 {
1021  TRACE_CALL(__func__);
1022  guint keys[] = { GDK_KEY_Control_L, GDK_KEY_Alt_L, GDK_KEY_Delete };
1023  RemminaPluginVncData *gpdata = GET_PLUGIN_DATA(gp);
1024 
1026  keys, G_N_ELEMENTS(keys), GDK_KEY_PRESS | GDK_KEY_RELEASE);
1027 }
1028 
1030 {
1031  TRACE_CALL(__func__);
1033  return FALSE;
1034 }
1035 
1037 {
1038  TRACE_CALL(__func__);
1039  RemminaPluginVncData *gpdata = GET_PLUGIN_DATA(gp);
1040  rfbClient *cl;
1041 
1042  cl = (rfbClient *)gpdata->client;
1043 
1047  return FALSE;
1048 }
1049 
1050 static void remmina_plugin_vnc_rfb_chat(rfbClient *cl, int value, char *text)
1051 {
1052  TRACE_CALL(__func__);
1054 
1055  gp = (RemminaProtocolWidget *)(rfbClientGetClientData(cl, NULL));
1056  switch (value) {
1057  case rfbTextChatOpen:
1058  IDLE_ADD((GSourceFunc)remmina_plugin_vnc_open_chat, gp);
1059  break;
1060  case rfbTextChatClose:
1061  /* Do nothing… but wait for the next rfbTextChatFinished signal */
1062  break;
1063  case rfbTextChatFinished:
1064  IDLE_ADD((GSourceFunc)remmina_plugin_vnc_close_chat, gp);
1065  break;
1066  default:
1067  /* value is the text length */
1069  break;
1070  }
1071 }
1072 
1074 {
1075  TRACE_CALL(__func__);
1076  RemminaPluginVncData *gpdata = GET_PLUGIN_DATA(gp);
1077  fd_set fds;
1078 
1083  gpdata->listen_sock = ListenAtTcpPort(cl->listenPort);
1084  if (gpdata->listen_sock < 0)
1085  return FALSE;
1086 
1088 
1090 
1091  FD_ZERO(&fds);
1092  if (gpdata->listen_sock >= 0)
1093  FD_SET(gpdata->listen_sock, &fds);
1094 
1095  select(gpdata->listen_sock + 1, &fds, NULL, NULL, NULL);
1096 
1097  if (!FD_ISSET(gpdata->listen_sock, &fds)) {
1098  close(gpdata->listen_sock);
1099  gpdata->listen_sock = -1;
1100  return FALSE;
1101  }
1102 
1103  if (FD_ISSET(gpdata->listen_sock, &fds))
1104  cl->sock = AcceptTcpConnection(gpdata->listen_sock);
1105  if (cl->sock >= 0) {
1106  close(gpdata->listen_sock);
1107  gpdata->listen_sock = -1;
1108  }
1109  if (cl->sock < 0 || !SetNonBlocking(cl->sock))
1110  return FALSE;
1111 
1112  return TRUE;
1113 }
1114 
1115 
1117 {
1118  TRACE_CALL(__func__);
1119  RemminaPluginVncData *gpdata = GET_PLUGIN_DATA(gp);
1120  gint ret;
1121  gint i;
1122  rfbClient *cl;
1123  fd_set fds;
1124  struct timeval timeout;
1125 
1126  if (!gpdata->connected) {
1127  gpdata->running = FALSE;
1128  return FALSE;
1129  }
1130 
1131  cl = (rfbClient *)gpdata->client;
1132 
1133  /*
1134  * Do not explicitly wait while data is on the buffer, see:
1135  * - https://jira.glyptodon.com/browse/GUAC-1056
1136  * - https://jira.glyptodon.com/browse/GUAC-1056?focusedCommentId=14348&page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel#comment-14348
1137  * - https://github.com/apache/guacamole-server/blob/67680bd2d51e7949453f0f7ffc7f4234a1136715/src/protocols/vnc/vnc.c#L155
1138  */
1139  if (cl->buffered)
1140  goto handle_buffered;
1141 
1142  timeout.tv_sec = 10;
1143  timeout.tv_usec = 0;
1144  FD_ZERO(&fds);
1145  FD_SET(cl->sock, &fds);
1146  FD_SET(gpdata->vnc_event_pipe[0], &fds);
1147  ret = select(MAX(cl->sock, gpdata->vnc_event_pipe[0]) + 1, &fds, NULL, NULL, &timeout);
1148 
1149  /* Sometimes it returns <0 when opening a modal dialog in other window. Absolutely weird */
1150  /* So we continue looping anyway */
1151  if (ret <= 0)
1152  return TRUE;
1153 
1154  if (FD_ISSET(gpdata->vnc_event_pipe[0], &fds))
1156  if (FD_ISSET(cl->sock, &fds)) {
1157  i = WaitForMessage(cl, 500);
1158  if (i < 0)
1159  return TRUE;
1160 handle_buffered:
1161  if (!HandleRFBServerMessage(cl)) {
1162  gpdata->running = FALSE;
1165  return FALSE;
1166  }
1167  }
1168 
1169  return TRUE;
1170 }
1171 
1173 {
1174  TRACE_CALL(__func__);
1175  RemminaPluginVncData *gpdata = GET_PLUGIN_DATA(gp);
1176  RemminaFile *remminafile;
1177  rfbClient *cl = NULL;
1178  gchar *host;
1179  gchar *s = NULL;
1180 
1182  gpdata->running = TRUE;
1183 
1184  rfbClientLog = rfbClientErr = remmina_plugin_vnc_rfb_output;
1185 
1186  gint colordepth = remmina_plugin_service->file_get_int(remminafile, "colordepth", 32);
1187  gint quality = remmina_plugin_service->file_get_int(remminafile, "quality", 9);
1188 
1189  while (gpdata->connected) {
1190  gpdata->auth_called = FALSE;
1191 
1192  host = remmina_plugin_service->protocol_plugin_start_direct_tunnel(gp, VNC_DEFAULT_PORT, TRUE);
1193 
1194  if (host == NULL) {
1195  REMMINA_PLUGIN_DEBUG("host is null");
1196  gpdata->connected = FALSE;
1197  break;
1198  }
1199 
1200  /* int bitsPerSample,int samplesPerPixel, int bytesPerPixel */
1201  switch (colordepth) {
1202  case 8:
1203  cl = rfbGetClient(2, 3, 1);
1204  break;
1205  case 15:
1206  case 16:
1207  cl = rfbGetClient(5, 3, 2);
1208  break;
1209  case 24:
1210  cl = rfbGetClient(6, 3, 3);
1211  break;
1212  case 32:
1213  default:
1214  cl = rfbGetClient(8, 3, 4);
1215  break;
1216  }
1217  REMMINA_PLUGIN_DEBUG("Color depth: %d", colordepth);
1218  cl->MallocFrameBuffer = remmina_plugin_vnc_rfb_allocfb;
1219  cl->canHandleNewFBSize = TRUE;
1220  cl->GetPassword = remmina_plugin_vnc_rfb_password;
1221  cl->GetCredential = remmina_plugin_vnc_rfb_credential;
1222  cl->GotFrameBufferUpdate = remmina_plugin_vnc_rfb_updatefb;
1233  cl->HandleKeyboardLedState = remmina_plugin_vnc_rfb_led_state;
1234  cl->GotXCutText = (
1235  remmina_plugin_service->file_get_int(remminafile, "disableclipboard", FALSE) ?
1237  cl->GotCursorShape = remmina_plugin_vnc_rfb_cursor_shape;
1238  cl->Bell = remmina_plugin_vnc_rfb_bell;
1239  cl->HandleTextChat = remmina_plugin_vnc_rfb_chat;
1245  rfbClientSetClientData(cl, NULL, gp);
1246 
1247  if (host[0] == '\0') {
1248  cl->serverHost = g_strdup(host);
1249  cl->listenSpecified = TRUE;
1250  if (remmina_plugin_service->file_get_int(remminafile, "ssh_tunnel_enabled", FALSE))
1251  /* When we use reverse tunnel, the local port does not really matter.
1252  * Hardcode a default port just in case the remote port is customized
1253  * to a privilege port then we will have problem listening. */
1254  cl->listenPort = 5500;
1255  else
1256  cl->listenPort = remmina_plugin_service->file_get_int(remminafile, "listenport", 5500);
1257 
1259  } else {
1260  if (strstr(host, "unix://") == host) {
1261  cl->serverHost = g_strdup(host + strlen("unix://"));
1262  cl->serverPort = 0;
1263  } else {
1264  remmina_plugin_service->get_server_port(host, VNC_DEFAULT_PORT, &s, &cl->serverPort);
1265  cl->serverHost = g_strdup(s);
1266  g_free(s);
1267  /* Support short-form (:0, :1) */
1268  if (cl->serverPort < 100)
1269  cl->serverPort += VNC_DEFAULT_PORT;
1270  }
1271  }
1272  g_free(host);
1273  host = NULL;
1274 
1275  if (cl->serverHost && strstr(cl->serverHost, "unix://") != cl->serverHost && remmina_plugin_service->file_get_string(remminafile, "proxy")) {
1277  remmina_plugin_service->file_get_string(remminafile, "server"),
1278  VNC_DEFAULT_PORT,
1279  &cl->destHost,
1280  &cl->destPort);
1282  remmina_plugin_service->file_get_string(remminafile, "proxy"),
1283  VNC_DEFAULT_PORT,
1284  &cl->serverHost,
1285  &cl->serverPort);
1286  REMMINA_PLUGIN_DEBUG("cl->serverHost: %s", cl->serverHost);
1287  REMMINA_PLUGIN_DEBUG("cl->serverPort: %d", cl->serverPort);
1288  REMMINA_PLUGIN_DEBUG("cl->destHost: %s", cl->destHost);
1289  REMMINA_PLUGIN_DEBUG("cl->destPort: %d", cl->destPort);
1290  }
1291 
1292  cl->appData.useRemoteCursor = (
1293  remmina_plugin_service->file_get_int(remminafile, "showcursor", FALSE) ? FALSE : TRUE);
1294 
1295  remmina_plugin_vnc_update_quality(cl, quality);
1296  remmina_plugin_vnc_update_colordepth(cl, colordepth);
1297  if ((cl->format.depth == 8) && (quality == 9))
1298  cl->appData.encodingsString = "copyrect zlib hextile raw";
1299  else if ((cl->format.depth == 8) && (quality == 2))
1300  cl->appData.encodingsString = "zrle ultra copyrect hextile zlib corre rre raw";
1301  else if ((cl->format.depth == 8) && (quality == 1))
1302  cl->appData.encodingsString = "zrle ultra copyrect hextile zlib corre rre raw";
1303  else if ((cl->format.depth == 8) && (quality == 0))
1304  cl->appData.encodingsString = "zrle ultra copyrect hextile zlib corre rre raw";
1305  SetFormatAndEncodings(cl);
1306 
1307  if (remmina_plugin_service->file_get_int(remminafile, "disableencryption", FALSE)) {
1308  vnc_encryption_disable_requested = TRUE;
1309  SetClientAuthSchemes(cl, remmina_plugin_vnc_no_encrypt_auth_types, -1);
1310  } else {
1311  vnc_encryption_disable_requested = FALSE;
1312  }
1313 
1314  if (rfbInitClient(cl, NULL, NULL)) {
1315  REMMINA_PLUGIN_DEBUG("Client initialization successfull");
1316  break;
1317  } else {
1318  REMMINA_PLUGIN_DEBUG("Client initialization failed");
1319  }
1320 
1321  /* If the authentication is not called, it has to be a fatal error and must quit */
1322  if (!gpdata->auth_called) {
1323  REMMINA_PLUGIN_DEBUG("Client not authenticated");
1324  gpdata->connected = FALSE;
1325  break;
1326  }
1327 
1328  /* vnc4server reports "already in use" after authentication. Workaround here */
1329  if (strstr(vnc_error, "The server is already in use")) {
1330  gpdata->connected = FALSE;
1331  gpdata->auth_called = FALSE;
1332  break;
1333  }
1334  /* Don't assume authentication failed for known network-related errors in
1335  libvncclient/sockets.c. */
1336  if (strstr(vnc_error, "read (") || strstr(vnc_error, "select\n") ||
1337  strstr(vnc_error, "write\n") || strstr(vnc_error, "Connection timed out")) {
1338  gpdata->connected = FALSE;
1339  gpdata->auth_called = FALSE;
1340  break;
1341  }
1342 
1343  /* Otherwise, it’s a password error. Try to clear saved password if any */
1344  remmina_plugin_service->file_set_string(remminafile, "password", NULL);
1345 
1346  if (!gpdata->connected)
1347  break;
1348 
1350 
1351  /* It’s safer to sleep a while before reconnect */
1352  sleep(2);
1353 
1354  gpdata->auth_first = FALSE;
1355  }
1356 
1357  if (!gpdata->connected) {
1358  REMMINA_PLUGIN_DEBUG("Client not connected with error: %s", vnc_error);
1359  if (cl && !gpdata->auth_called && !(remmina_plugin_service->protocol_plugin_has_error(gp)))
1360  remmina_plugin_service->protocol_plugin_set_error(gp, "%s", vnc_error);
1361  gpdata->running = FALSE;
1362 
1364 
1365  return FALSE;
1366  }
1367 
1368  REMMINA_PLUGIN_DEBUG("Client connected");
1370 
1371  gpdata->client = cl;
1372 
1374 
1375  if (remmina_plugin_service->file_get_int(remminafile, "disableserverinput", FALSE))
1376  PermitServerInput(cl, 1);
1377 
1378  if (gpdata->thread) {
1379  while (remmina_plugin_vnc_main_loop(gp)) {
1380  }
1381  gpdata->running = FALSE;
1382  } else {
1383  IDLE_ADD((GSourceFunc)remmina_plugin_vnc_main_loop, gp);
1384  }
1385 
1386  return FALSE;
1387 }
1388 
1389 
1390 static gpointer
1392 {
1393  TRACE_CALL(__func__);
1394  pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
1395 
1396  CANCEL_ASYNC
1398  return NULL;
1399 }
1400 
1401 
1403 {
1404  GtkAllocation widget_allocation;
1406 
1408  gtk_widget_get_allocation(widget, &widget_allocation);
1409  result.x = x * remmina_plugin_service->protocol_plugin_get_width(gp) / widget_allocation.width;
1410  result.y = y * remmina_plugin_service->protocol_plugin_get_height(gp) / widget_allocation.height;
1411  } else {
1412  result.x = x;
1413  result.y = y;
1414  }
1415 
1416  return result;
1417 }
1418 
1419 static gboolean remmina_plugin_vnc_on_motion(GtkWidget *widget, GdkEventMotion *event, RemminaProtocolWidget *gp)
1420 {
1421  TRACE_CALL(__func__);
1422  RemminaPluginVncData *gpdata = GET_PLUGIN_DATA(gp);
1423  RemminaFile *remminafile;
1424  RemminaPluginVncCoordinates coordinates;
1425 
1426  if (!gpdata->connected || !gpdata->client)
1427  return FALSE;
1429  if (remmina_plugin_service->file_get_int(remminafile, "viewonly", FALSE))
1430  return FALSE;
1431 
1432  coordinates = remmina_plugin_vnc_scale_coordinates(widget, gp, event->x, event->y);
1433  remmina_plugin_vnc_event_push(gp, REMMINA_PLUGIN_VNC_EVENT_POINTER, GINT_TO_POINTER(coordinates.x), GINT_TO_POINTER(coordinates.y),
1434  GINT_TO_POINTER(gpdata->button_mask));
1435  return TRUE;
1436 }
1437 
1438 static gboolean remmina_plugin_vnc_on_button(GtkWidget *widget, GdkEventButton *event, RemminaProtocolWidget *gp)
1439 {
1440  TRACE_CALL(__func__);
1441  RemminaPluginVncData *gpdata = GET_PLUGIN_DATA(gp);
1442  RemminaFile *remminafile;
1443  RemminaPluginVncCoordinates coordinates;
1444  gint mask;
1445 
1446  if (!gpdata->connected || !gpdata->client)
1447  return FALSE;
1449  if (remmina_plugin_service->file_get_int(remminafile, "viewonly", FALSE))
1450  return FALSE;
1451 
1452  /* We only accept 3 buttons */
1453  if (event->button < 1 || event->button > 3)
1454  return FALSE;
1455  /* We bypass 2button-press and 3button-press events */
1456  if (event->type != GDK_BUTTON_PRESS && event->type != GDK_BUTTON_RELEASE)
1457  return TRUE;
1458 
1459  mask = (1 << (event->button - 1));
1460  gpdata->button_mask = (event->type == GDK_BUTTON_PRESS ? (gpdata->button_mask | mask) :
1461  (gpdata->button_mask & (0xff - mask)));
1462 
1463  coordinates = remmina_plugin_vnc_scale_coordinates(widget, gp, event->x, event->y);
1464  remmina_plugin_vnc_event_push(gp, REMMINA_PLUGIN_VNC_EVENT_POINTER, GINT_TO_POINTER(coordinates.x), GINT_TO_POINTER(coordinates.y),
1465  GINT_TO_POINTER(gpdata->button_mask));
1466  return TRUE;
1467 }
1468 
1469 static gint delta_to_mask(float delta, float *accum, gint mask_plus, gint mask_minus)
1470 {
1471  *accum += delta;
1472  if (*accum >= 1.0) {
1473  *accum = 0.0;
1474  return mask_plus;
1475  } else if (*accum <= -1.0) {
1476  *accum = 0.0;
1477  return mask_minus;
1478  }
1479  return 0;
1480 }
1481 
1482 static gboolean remmina_plugin_vnc_on_scroll(GtkWidget *widget, GdkEventScroll *event, RemminaProtocolWidget *gp)
1483 {
1484  TRACE_CALL(__func__);
1485  RemminaPluginVncData *gpdata = GET_PLUGIN_DATA(gp);
1486  RemminaFile *remminafile;
1487  RemminaPluginVncCoordinates coordinates;
1488  gint mask;
1489 
1490  if (!gpdata->connected || !gpdata->client)
1491  return FALSE;
1493  if (remmina_plugin_service->file_get_int(remminafile, "viewonly", FALSE))
1494  return FALSE;
1495 
1496  switch (event->direction) {
1497  case GDK_SCROLL_UP:
1498  mask = (1 << 3);
1499  gpdata->scroll_y_accumulator = 0;
1500  break;
1501  case GDK_SCROLL_DOWN:
1502  mask = (1 << 4);
1503  gpdata->scroll_y_accumulator = 0;
1504  break;
1505  case GDK_SCROLL_LEFT:
1506  mask = (1 << 5);
1507  gpdata->scroll_x_accumulator = 0;
1508  break;
1509  case GDK_SCROLL_RIGHT:
1510  mask = (1 << 6);
1511  gpdata->scroll_x_accumulator = 0;
1512  break;
1513 #if GTK_CHECK_VERSION(3, 4, 0)
1514  case GDK_SCROLL_SMOOTH:
1515  /* RFB does not seems to support SMOOTH scroll, so we accumulate GTK delta requested
1516  * up to 1.0 and then send a normal RFB wheel scroll when the accumulator reaches 1.0 */
1517  mask = delta_to_mask(event->delta_y, &(gpdata->scroll_y_accumulator), (1 << 4), (1 << 3));
1518  mask |= delta_to_mask(event->delta_x, &(gpdata->scroll_x_accumulator), (1 << 6), (1 << 5));
1519  if (!mask)
1520  return FALSE;
1521  break;
1522 #endif
1523  default:
1524  return FALSE;
1525  }
1526 
1527  coordinates = remmina_plugin_vnc_scale_coordinates(widget, gp, event->x, event->y);
1528  remmina_plugin_vnc_event_push(gp, REMMINA_PLUGIN_VNC_EVENT_POINTER, GINT_TO_POINTER(coordinates.x), GINT_TO_POINTER(coordinates.y),
1529  GINT_TO_POINTER(mask | gpdata->button_mask));
1530  remmina_plugin_vnc_event_push(gp, REMMINA_PLUGIN_VNC_EVENT_POINTER, GINT_TO_POINTER(coordinates.x), GINT_TO_POINTER(coordinates.y),
1531  GINT_TO_POINTER(gpdata->button_mask));
1532 
1533  return TRUE;
1534 }
1535 
1537 {
1538  TRACE_CALL(__func__);
1539  RemminaPluginVncData *gpdata = GET_PLUGIN_DATA(gp);
1540  RemminaKeyVal *k;
1541  gint i;
1542 
1543  if (!gpdata)
1544  return;
1545 
1546  if (keycode == 0) {
1547  /* Send all release key events for previously pressed keys */
1548  for (i = 0; i < gpdata->pressed_keys->len; i++) {
1549  k = g_ptr_array_index(gpdata->pressed_keys, i);
1551  GINT_TO_POINTER(FALSE), NULL);
1552  g_free(k);
1553  }
1554  g_ptr_array_set_size(gpdata->pressed_keys, 0);
1555  } else {
1556  /* Unregister the keycode only */
1557  for (i = 0; i < gpdata->pressed_keys->len; i++) {
1558  k = g_ptr_array_index(gpdata->pressed_keys, i);
1559  if (k->keycode == keycode) {
1560  g_free(k);
1561  g_ptr_array_remove_index_fast(gpdata->pressed_keys, i);
1562  break;
1563  }
1564  }
1565  }
1566 }
1567 
1568 static gboolean remmina_plugin_vnc_on_key(GtkWidget *widget, GdkEventKey *event, RemminaProtocolWidget *gp)
1569 {
1570  TRACE_CALL(__func__);
1571  RemminaPluginVncData *gpdata = GET_PLUGIN_DATA(gp);
1572  RemminaFile *remminafile;
1573  RemminaKeyVal *k;
1574  guint event_keyval;
1575  guint keyval;
1576 
1577  if (!gpdata->connected || !gpdata->client)
1578  return FALSE;
1580  if (remmina_plugin_service->file_get_int(remminafile, "viewonly", FALSE))
1581  return FALSE;
1582 
1583  gpdata->scroll_x_accumulator = 0;
1584  gpdata->scroll_y_accumulator = 0;
1585 
1586  /* When sending key release, try first to find out a previously sent keyval
1587  * to workaround bugs like https://bugs.freedesktop.org/show_bug.cgi?id=7430 */
1588 
1589  event_keyval = event->keyval;
1590  if (event->type == GDK_KEY_RELEASE) {
1591  for (int i = 0; i < gpdata->pressed_keys->len; i++) {
1592  k = g_ptr_array_index(gpdata->pressed_keys, i);
1593  if (k->keycode == event->hardware_keycode) {
1594  event_keyval = k->keyval;
1595  break;
1596  }
1597  }
1598  }
1599 
1601  event_keyval);
1602 
1603  remmina_plugin_vnc_event_push(gp, REMMINA_PLUGIN_VNC_EVENT_KEY, GUINT_TO_POINTER(keyval),
1604  GINT_TO_POINTER(event->type == GDK_KEY_PRESS ? TRUE : FALSE), NULL);
1605 
1606  /* Register/unregister the pressed key */
1607  if (event->type == GDK_KEY_PRESS) {
1608  k = g_new(RemminaKeyVal, 1);
1609  k->keyval = keyval;
1610  k->keycode = event->hardware_keycode;
1611  g_ptr_array_add(gpdata->pressed_keys, k);
1612  } else {
1613  remmina_plugin_vnc_release_key(gp, event->hardware_keycode);
1614  }
1615  return TRUE;
1616 }
1617 
1618 static void remmina_plugin_vnc_on_cuttext_request(GtkClipboard *clipboard, const gchar *text, RemminaProtocolWidget *gp)
1619 {
1620  TRACE_CALL(__func__);
1621  RemminaPluginVncData *gpdata = GET_PLUGIN_DATA(gp);
1622  GDateTime *t;
1623  glong diff;
1624  gsize br, bw;
1625  gchar *latin1_text;
1626  const char *cur_charset;
1627 
1628  if (text) {
1629  /* A timer (1 second) to avoid clipboard "loopback": text cut out from VNC won’t paste back into VNC */
1630  t = g_date_time_new_now_utc();
1631  diff = g_date_time_difference(t, gpdata->clipboard_timer) / 100000; // tenth of second
1632  if (diff < 10)
1633  return;
1634  g_date_time_unref(gpdata->clipboard_timer);
1635  gpdata->clipboard_timer = t;
1636  /* Convert text from current charset to latin-1 before sending to remote server.
1637  * See RFC6143 7.5.6 */
1638  g_get_charset(&cur_charset);
1639  latin1_text = g_convert_with_fallback(text, -1, "ISO-8859-1", cur_charset, "?", &br, &bw, NULL);
1640  remmina_plugin_vnc_event_push(gp, REMMINA_PLUGIN_VNC_EVENT_CUTTEXT, (gpointer)latin1_text, NULL, NULL);
1641  g_free(latin1_text);
1642  }
1643 }
1644 
1645 static void remmina_plugin_vnc_on_cuttext(GtkClipboard *clipboard, GdkEvent *event, RemminaProtocolWidget *gp)
1646 {
1647  TRACE_CALL(__func__);
1648  RemminaPluginVncData *gpdata = GET_PLUGIN_DATA(gp);
1649  RemminaFile *remminafile;
1650 
1651  if (!gpdata->connected || !gpdata->client)
1652  return;
1654  if (remmina_plugin_service->file_get_int(remminafile, "viewonly", FALSE))
1655  return;
1656 
1657  gtk_clipboard_request_text(clipboard, (GtkClipboardTextReceivedFunc)remmina_plugin_vnc_on_cuttext_request, gp);
1658 }
1659 
1661 {
1662  TRACE_CALL(__func__);
1663  RemminaFile *remminafile;
1664  GdkCursor *cursor;
1665  GdkPixbuf *pixbuf;
1666 
1668 
1669  if (remmina_plugin_service->file_get_int(remminafile, "showcursor", FALSE)) {
1670  /* Hide local cursor (show a small dot instead) */
1671  pixbuf = gdk_pixbuf_new_from_xpm_data(dot_cursor_xpm);
1672  cursor = gdk_cursor_new_from_pixbuf(gdk_display_get_default(), pixbuf, dot_cursor_x_hot, dot_cursor_y_hot);
1673  g_object_unref(pixbuf);
1674  gdk_window_set_cursor(gtk_widget_get_window(GTK_WIDGET(gp)), cursor);
1675  g_object_unref(cursor);
1676  }
1677 }
1678 
1679 /******************************************************************************************/
1680 
1682 {
1683  TRACE_CALL(__func__);
1684  RemminaPluginVncData *gpdata = GET_PLUGIN_DATA(gp);
1685  RemminaFile *remminafile;
1686 
1688 
1689  gpdata->connected = TRUE;
1690  gchar *server;
1691  gint port;
1692  const gchar* raw_server;
1693 
1695 
1696  g_signal_connect(G_OBJECT(gp), "realize", G_CALLBACK(remmina_plugin_vnc_on_realize), NULL);
1697  g_signal_connect(G_OBJECT(gpdata->drawing_area), "motion-notify-event", G_CALLBACK(remmina_plugin_vnc_on_motion), gp);
1698  g_signal_connect(G_OBJECT(gpdata->drawing_area), "button-press-event", G_CALLBACK(remmina_plugin_vnc_on_button), gp);
1699  g_signal_connect(G_OBJECT(gpdata->drawing_area), "button-release-event", G_CALLBACK(remmina_plugin_vnc_on_button), gp);
1700  g_signal_connect(G_OBJECT(gpdata->drawing_area), "scroll-event", G_CALLBACK(remmina_plugin_vnc_on_scroll), gp);
1701  g_signal_connect(G_OBJECT(gpdata->drawing_area), "key-press-event", G_CALLBACK(remmina_plugin_vnc_on_key), gp);
1702  g_signal_connect(G_OBJECT(gpdata->drawing_area), "key-release-event", G_CALLBACK(remmina_plugin_vnc_on_key), gp);
1703 
1704  if (!remmina_plugin_service->file_get_int(remminafile, "disableclipboard", FALSE))
1705  gpdata->clipboard_handler = g_signal_connect(G_OBJECT(gtk_clipboard_get(GDK_SELECTION_CLIPBOARD)),
1706  "owner-change", G_CALLBACK(remmina_plugin_vnc_on_cuttext), gp);
1707 
1708 
1709  if (pthread_create(&gpdata->thread, NULL, remmina_plugin_vnc_main_thread, gp)) {
1710  /* I don’t think this will ever happen… */
1711  g_print("Could not initialize pthread. Falling back to non-thread mode…\n");
1712  g_timeout_add(0, (GSourceFunc)remmina_plugin_vnc_main, gp);
1713  gpdata->thread = 0;
1714  }
1715 
1716  raw_server = remmina_plugin_service->file_get_string(remminafile, "server");
1717 
1718  if (raw_server && strstr(raw_server, "unix://") == raw_server) {
1719  REMMINA_PLUGIN_AUDIT(_("Connected to %s via VNC"), raw_server);
1720  } else {
1722  VNC_DEFAULT_PORT,
1723  &server,
1724  &port);
1725 
1726  REMMINA_PLUGIN_AUDIT(_("Connected to %s:%d via VNC"), server, port);
1727  g_free(server), server = NULL;
1728  }
1729 #if LIBVNCSERVER_CHECK_VERSION_VERSION(0, 9, 14)
1731 #endif
1732  return TRUE;
1733 }
1734 
1736 {
1737  TRACE_CALL(__func__);
1738  RemminaPluginVncData *gpdata = GET_PLUGIN_DATA(gp);
1739 
1740  gchar *server;
1741  gint port;
1742 
1745  VNC_DEFAULT_PORT,
1746  &server,
1747  &port);
1748 
1749  REMMINA_PLUGIN_AUDIT(_("Disconnected from %s:%d via VNC"), server, port);
1750  g_free(server), server = NULL;
1751 
1752  /* wait until the running attribute is set to false by the VNC thread */
1753  if (gpdata->running)
1754  return TRUE;
1755 
1756  /* unregister the clipboard monitor */
1757  if (gpdata->clipboard_handler) {
1758  g_signal_handler_disconnect(G_OBJECT(gtk_clipboard_get(GDK_SELECTION_CLIPBOARD)), gpdata->clipboard_handler);
1759  gpdata->clipboard_handler = 0;
1760  }
1761 
1762  if (gpdata->queuecursor_handler) {
1763  g_source_remove(gpdata->queuecursor_handler);
1764  gpdata->queuecursor_handler = 0;
1765  }
1766  if (gpdata->queuecursor_surface) {
1767  cairo_surface_destroy(gpdata->queuecursor_surface);
1768  gpdata->queuecursor_surface = NULL;
1769  }
1770 
1771  if (gpdata->queuedraw_handler) {
1772  g_source_remove(gpdata->queuedraw_handler);
1773  gpdata->queuedraw_handler = 0;
1774  }
1775  if (gpdata->listen_sock >= 0)
1776  close(gpdata->listen_sock);
1777  if (gpdata->client) {
1778  rfbClientCleanup((rfbClient *)gpdata->client);
1779  gpdata->client = NULL;
1780  }
1781  if (gpdata->rgb_buffer) {
1782  cairo_surface_destroy(gpdata->rgb_buffer);
1783  gpdata->rgb_buffer = NULL;
1784  }
1785  if (gpdata->vnc_buffer) {
1786  g_free(gpdata->vnc_buffer);
1787  gpdata->vnc_buffer = NULL;
1788  }
1789  g_ptr_array_free(gpdata->pressed_keys, TRUE);
1790  g_date_time_unref(gpdata->clipboard_timer);
1792  g_queue_free(gpdata->vnc_event_queue);
1793  pthread_mutex_destroy(&gpdata->vnc_event_queue_mutex);
1794  close(gpdata->vnc_event_pipe[0]);
1795  close(gpdata->vnc_event_pipe[1]);
1796 
1797 
1798  pthread_mutex_destroy(&gpdata->buffer_mutex);
1800 
1801  return FALSE;
1802 }
1803 
1805 {
1806  TRACE_CALL(__func__);
1807  RemminaPluginVncData *gpdata = GET_PLUGIN_DATA(gp);
1808 
1809  gpdata->connected = FALSE;
1810 
1811  if (gpdata->thread) {
1812  pthread_cancel(gpdata->thread);
1813  if (gpdata->thread) pthread_join(gpdata->thread, NULL);
1814  gpdata->running = FALSE;
1816  } else {
1817  g_timeout_add(200, (GSourceFunc)remmina_plugin_vnc_close_connection_timeout, gp);
1818  }
1819 
1820  return FALSE;
1821 }
1822 
1824 {
1825  TRACE_CALL(__func__);
1826  RemminaPluginVncData *gpdata = GET_PLUGIN_DATA(gp);
1827  switch (feature->id) {
1828  case REMMINA_PLUGIN_VNC_FEATURE_PREF_DISABLESERVERINPUT:
1829  return SupportsClient2Server((rfbClient *)(gpdata->client), rfbSetServerInput) ? TRUE : FALSE;
1830  case REMMINA_PLUGIN_VNC_FEATURE_TOOL_CHAT:
1831  return SupportsClient2Server((rfbClient *)(gpdata->client), rfbTextChat) ? TRUE : FALSE;
1832  default:
1833  return TRUE;
1834  }
1835 }
1836 
1838 {
1839  TRACE_CALL(__func__);
1840  RemminaPluginVncData *gpdata = GET_PLUGIN_DATA(gp);
1841  RemminaFile *remminafile;
1842  rfbClient* client;
1843  uint8_t previous_bpp;
1845  switch (feature->id) {
1846  case REMMINA_PLUGIN_VNC_FEATURE_PREF_QUALITY:
1847  remmina_plugin_vnc_update_quality((rfbClient *)(gpdata->client),
1848  remmina_plugin_service->file_get_int(remminafile, "quality", 9));
1849  remmina_plugin_vnc_update_colordepth((rfbClient *)(gpdata->client),
1850  remmina_plugin_service->file_get_int(remminafile, "colordepth", 32));
1851  SetFormatAndEncodings((rfbClient *)(gpdata->client));
1852  break;
1853  case REMMINA_PLUGIN_VNC_FEATURE_PREF_COLOR:
1854  client = (rfbClient *)(gpdata->client);
1855  previous_bpp = client->format.bitsPerPixel;
1857  remmina_plugin_service->file_get_int(remminafile, "colordepth", 32));
1858  SetFormatAndEncodings(client);
1859  //Need to clear away old and reallocate if we're increasing bpp
1860  if (client->format.bitsPerPixel > previous_bpp){
1861  remmina_plugin_vnc_rfb_allocfb((rfbClient *)(gpdata->client));
1862  SendFramebufferUpdateRequest((rfbClient *)(gpdata->client), 0, 0,
1865  }
1866  break;
1867  case REMMINA_PLUGIN_VNC_FEATURE_PREF_VIEWONLY:
1868  break;
1869  case REMMINA_PLUGIN_VNC_FEATURE_PREF_DISABLESERVERINPUT:
1870  PermitServerInput((rfbClient *)(gpdata->client),
1871  remmina_plugin_service->file_get_int(remminafile, "disableserverinput", FALSE) ? 1 : 0);
1872  break;
1873  case REMMINA_PLUGIN_VNC_FEATURE_UNFOCUS:
1875  break;
1876  case REMMINA_PLUGIN_VNC_FEATURE_SCALE:
1877  remmina_plugin_vnc_update_scale(gp, remmina_plugin_service->file_get_int(remminafile, "scale", FALSE));
1878  break;
1879  case REMMINA_PLUGIN_VNC_FEATURE_TOOL_REFRESH:
1880  SendFramebufferUpdateRequest((rfbClient *)(gpdata->client), 0, 0,
1883  break;
1884  case REMMINA_PLUGIN_VNC_FEATURE_TOOL_CHAT:
1886  break;
1887  case REMMINA_PLUGIN_VNC_FEATURE_TOOL_SENDCTRLALTDEL:
1889  break;
1890  default:
1891  break;
1892  }
1893 }
1894 
1895 /* Send a keystroke to the plugin window */
1896 static void remmina_plugin_vnc_keystroke(RemminaProtocolWidget *gp, const guint keystrokes[], const gint keylen)
1897 {
1898  TRACE_CALL(__func__);
1899  RemminaPluginVncData *gpdata = GET_PLUGIN_DATA(gp);
1900 
1902  keystrokes, keylen, GDK_KEY_PRESS | GDK_KEY_RELEASE);
1903  return;
1904 }
1905 
1906 #if LIBVNCSERVER_CHECK_VERSION_VERSION(0, 9, 14)
1907 static gboolean remmina_plugin_vnc_on_size_allocate(GtkWidget *widget, GtkAllocation *alloc, RemminaProtocolWidget *gp)
1908 {
1909  TRACE_CALL(__func__);
1911  RemminaPluginVncData *gpdata = GET_PLUGIN_DATA(gp);
1912 
1913  if (scale_mode == REMMINA_PROTOCOL_WIDGET_SCALE_MODE_DYNRES){
1914  char str[1024];
1915  sprintf(str, "DEBUG: %d x %d", alloc->width, alloc->height);
1916  TRACE_CALL(str);
1917  if (gpdata->client){
1918  SendExtDesktopSize(gpdata->client, alloc->width, alloc->height);
1919  }
1920  }
1921  return TRUE;
1922 }
1923 #endif
1924 
1925 static gboolean remmina_plugin_vnc_on_draw(GtkWidget *widget, cairo_t *context, RemminaProtocolWidget *gp)
1926 {
1927  TRACE_CALL(__func__);
1928  RemminaPluginVncData *gpdata = GET_PLUGIN_DATA(gp);
1929  cairo_surface_t *surface;
1930  gint width, height;
1931  GtkAllocation widget_allocation;
1932 
1933  LOCK_BUFFER(FALSE);
1934 
1935  surface = gpdata->rgb_buffer;
1936  if (!surface) {
1937  UNLOCK_BUFFER(FALSE);
1938  return FALSE;
1939  }
1940 
1943 
1945  gtk_widget_get_allocation(widget, &widget_allocation);
1946  cairo_scale(context,
1947  (double)widget_allocation.width / width,
1948  (double)widget_allocation.height / height);
1949  }
1950 
1951  cairo_rectangle(context, 0, 0, width, height);
1952  cairo_set_source_surface(context, surface, 0, 0);
1953  cairo_fill(context);
1954 
1955  UNLOCK_BUFFER(FALSE);
1956  return TRUE;
1957 }
1958 
1960 {
1961  TRACE_CALL(__func__);
1962  RemminaPluginVncData *gpdata;
1963  gint flags;
1964  gdouble aspect_ratio;
1965 
1966  gpdata = g_new0(RemminaPluginVncData, 1);
1967  g_object_set_data_full(G_OBJECT(gp), "plugin-data", gpdata, g_free);
1968 
1969  gboolean disable_smooth_scrolling = FALSE;
1971 
1972  disable_smooth_scrolling = remmina_plugin_service->file_get_int(remminafile, "disablesmoothscrolling", FALSE);
1973  REMMINA_PLUGIN_DEBUG("Disable smooth scrolling is set to %d", disable_smooth_scrolling);
1974 
1975  gpdata->drawing_area = gtk_drawing_area_new();
1976  gtk_widget_show(gpdata->drawing_area);
1977 
1978  aspect_ratio = remmina_plugin_service->file_get_double(remminafile, "aspect_ratio", 0);
1979  if (aspect_ratio > 0){
1980  GtkWidget* aspectframe = gtk_aspect_frame_new(NULL, 0, 0, aspect_ratio, FALSE);
1981 
1982  gtk_frame_set_shadow_type(GTK_FRAME(aspectframe), GTK_SHADOW_NONE);
1983  gtk_widget_show(aspectframe);
1984  gtk_container_add(GTK_CONTAINER(aspectframe), gpdata->drawing_area);
1985  gtk_container_add(GTK_CONTAINER(gp), aspectframe);
1986  }
1987  else{
1988  gtk_container_add(GTK_CONTAINER(gp), gpdata->drawing_area);
1989  }
1990 
1991  gtk_widget_add_events(
1992  gpdata->drawing_area,
1993  GDK_POINTER_MOTION_MASK | GDK_BUTTON_PRESS_MASK
1994  | GDK_BUTTON_RELEASE_MASK | GDK_KEY_PRESS_MASK
1995  | GDK_KEY_RELEASE_MASK
1996  | GDK_SCROLL_MASK);
1997  gtk_widget_set_can_focus(gpdata->drawing_area, TRUE);
1998 
1999  if (!disable_smooth_scrolling) {
2000  REMMINA_PLUGIN_DEBUG("Adding GDK_SMOOTH_SCROLL_MASK");
2001  gtk_widget_add_events(gpdata->drawing_area, GDK_SMOOTH_SCROLL_MASK);
2002  }
2003 
2004 
2005  g_signal_connect(G_OBJECT(gpdata->drawing_area), "draw", G_CALLBACK(remmina_plugin_vnc_on_draw), gp);
2006 #if LIBVNCSERVER_CHECK_VERSION_VERSION(0, 9, 14)
2007  g_signal_connect(G_OBJECT(gpdata->drawing_area), "size-allocate", G_CALLBACK(remmina_plugin_vnc_on_size_allocate), gp);
2008 #endif
2009  gpdata->auth_first = TRUE;
2010  gpdata->clipboard_timer = g_date_time_new_now_utc();
2011  gpdata->listen_sock = -1;
2012  gpdata->pressed_keys = g_ptr_array_new();
2013  gpdata->vnc_event_queue = g_queue_new();
2014  pthread_mutex_init(&gpdata->vnc_event_queue_mutex, NULL);
2015  if (pipe(gpdata->vnc_event_pipe)) {
2016  g_print("Error creating pipes.\n");
2017  gpdata->vnc_event_pipe[0] = 0;
2018  gpdata->vnc_event_pipe[1] = 0;
2019  }
2020  flags = fcntl(gpdata->vnc_event_pipe[0], F_GETFL, 0);
2021  fcntl(gpdata->vnc_event_pipe[0], F_SETFL, flags | O_NONBLOCK);
2022 
2023  pthread_mutex_init(&gpdata->buffer_mutex, NULL);
2024 }
2025 
2026 /* Array of key/value pairs for color depths */
2027 static gpointer colordepth_list[] =
2028 {
2029  "32", N_("True colour (32 bpp)"),
2030  "16", N_("High colour (16 bpp)"),
2031  "8", N_("256 colours (8 bpp)"),
2032  NULL
2033 };
2034 
2035 /* Array of key/value pairs for quality selection */
2036 static gpointer quality_list[] =
2037 {
2038  "2", N_("Good"),
2039  "9", N_("Best (slowest)"),
2040  "1", N_("Medium"),
2041  "0", N_("Poor (fastest)"),
2042  NULL
2043 };
2044 
2045 static gchar repeater_tooltip[] =
2046  N_("Connect to VNC using a repeater:\n"
2047  " • The server field must contain the repeater ID, e.g. ID:123456789\n"
2048  " • The repeater field have to be set to the repeater IP and port, like:\n"
2049  " 10.10.10.12:5901\n"
2050  " • From the remote VNC server, you will connect to\n"
2051  " the repeater, e.g. with x11vnc:\n"
2052  " x11vnc -connect repeater=ID:123456789+10.10.10.12:5500");
2053 
2054 static gchar vnciport_tooltip[] =
2055  N_("Listening for remote VNC connection:\n"
2056  " • The “Listen on port” field is the port Remmina will listen to,\n"
2057  " e.g. 8888\n"
2058  " • From the remote VNC server, you will connect to\n"
2059  " Remmina, e.g. with x11vnc:\n"
2060  " x11vnc -display :0 -connect 192.168.1.36:8888");
2061 
2062 static gchar aspect_ratio_tooltip[] =
2063  N_("Lock the aspect ratio when dynamic resolution is enabled:\n"
2064  "\n"
2065  " • The aspect ratio should be entered as a decimal number, e.g. 1.777\n"
2066  " • 16:9 corresponds roughly to 1.7777, 4:3 corresponds roughly to 1.333\n"
2067  " • The default value of 0 does not enforce any aspect ratio");
2068 
2069 static gchar vncencodings_tooltip[] =
2070  N_("Overriding the pre-set VNC encoding quality:\n"
2071  "\n"
2072  " • “Poor (fastest)” sets encoding to “copyrect zlib hextile raw”\n"
2073  " • “Medium” sets encoding to “tight zrle ultra copyrect hextile zlib corre rre raw”\n"
2074  " • “Good” sets encoding to “tight zrle ultra copyrect hextile zlib corre rre raw”\n"
2075  " • “Best (slowest)” sets encoding to “copyrect zrle ultra zlib hextile corre rre raw”");
2076 
2077 /* Array of RemminaProtocolSetting for basic settings.
2078  * Each item is composed by:
2079  * a) RemminaProtocolSettingType for setting type
2080  * b) Setting name
2081  * c) Setting description
2082  * d) Compact disposition
2083  * e) Values for REMMINA_PROTOCOL_SETTING_TYPE_SELECT or REMMINA_PROTOCOL_SETTING_TYPE_COMBO
2084  * f) Setting tooltip
2085  * g) Validation data pointer, will be passed to the validation callback method.
2086  * h) Validation callback method (Can be NULL. Every entry will be valid then.)
2087  * use following prototype:
2088  * gboolean mysetting_validator_method(gpointer key, gpointer value,
2089  * gpointer validator_data);
2090  * gpointer key is a gchar* containing the setting's name,
2091  * gpointer value contains the value which should be validated,
2092  * gpointer validator_data contains your passed data.
2093  */
2095 {
2096  { REMMINA_PROTOCOL_SETTING_TYPE_SERVER, "server", NULL, FALSE, "_rfb._tcp", NULL, NULL, NULL },
2097  { REMMINA_PROTOCOL_SETTING_TYPE_TEXT, "proxy", N_("Repeater"), FALSE, NULL, repeater_tooltip, NULL, NULL },
2098  { REMMINA_PROTOCOL_SETTING_TYPE_TEXT, "username", N_("Username"), FALSE, NULL, NULL, NULL, NULL },
2099  { REMMINA_PROTOCOL_SETTING_TYPE_PASSWORD, "password", N_("User password"), FALSE, NULL, NULL, NULL, NULL },
2100  { REMMINA_PROTOCOL_SETTING_TYPE_SELECT, "colordepth", N_("Colour depth"), FALSE, colordepth_list, NULL, NULL, NULL },
2101  { REMMINA_PROTOCOL_SETTING_TYPE_SELECT, "quality", N_("Quality"), FALSE, quality_list, NULL, NULL, NULL },
2102  { REMMINA_PROTOCOL_SETTING_TYPE_KEYMAP, "keymap", NULL, FALSE, NULL, NULL, NULL, NULL },
2103  { REMMINA_PROTOCOL_SETTING_TYPE_END, NULL, NULL, FALSE, NULL, NULL, NULL, NULL }
2104 };
2105 
2106 // Same as above.
2108 {
2109  { REMMINA_PROTOCOL_SETTING_TYPE_TEXT, "listenport", N_("Listen on port"), FALSE, NULL, vnciport_tooltip, NULL, NULL},
2110  { REMMINA_PROTOCOL_SETTING_TYPE_TEXT, "username", N_("Username"), FALSE, NULL, NULL, NULL, NULL},
2111  { REMMINA_PROTOCOL_SETTING_TYPE_PASSWORD, "password", N_("User password"), FALSE, NULL, NULL, NULL, NULL},
2112  { REMMINA_PROTOCOL_SETTING_TYPE_SELECT, "colordepth", N_("Colour depth"), FALSE, colordepth_list, NULL, NULL, NULL},
2113  { REMMINA_PROTOCOL_SETTING_TYPE_SELECT, "quality", N_("Quality"), FALSE, quality_list, NULL, NULL, NULL},
2114  { REMMINA_PROTOCOL_SETTING_TYPE_KEYMAP, "keymap", NULL, FALSE, NULL, NULL, NULL, NULL},
2115  { REMMINA_PROTOCOL_SETTING_TYPE_END, NULL, NULL, FALSE, NULL, NULL, NULL, NULL}
2116 };
2117 
2118 /* Array of RemminaProtocolSetting for advanced settings.
2119  * Each item is composed by:
2120  * a) RemminaProtocolSettingType for setting type
2121  * b) Setting name
2122  * c) Setting description
2123  * d) Compact disposition
2124  * e) Values for REMMINA_PROTOCOL_SETTING_TYPE_SELECT or REMMINA_PROTOCOL_SETTING_TYPE_COMBO
2125  * f) Setting Tooltip
2126  */
2128 {
2129  { REMMINA_PROTOCOL_SETTING_TYPE_TEXT, "encodings", N_("Override pre-set VNC encodings"), FALSE, NULL, vncencodings_tooltip },
2130  { REMMINA_PROTOCOL_SETTING_TYPE_TEXT, "aspect_ratio", N_("Dynamic resolution enforced aspec ratio"), FALSE, NULL, aspect_ratio_tooltip },
2131  { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "tightencoding", N_("Force tight encoding"), TRUE, NULL, N_("Enabling this may help when the remote desktop looks scrambled") },
2132  { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "disablesmoothscrolling", N_("Disable smooth scrolling"), FALSE, NULL, NULL },
2133  { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "disablepasswordstoring", N_("Forget passwords after use"), TRUE, NULL, NULL },
2134  { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "disableserverbell", N_("Ignore remote bell messages"), FALSE, NULL, NULL },
2135  { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "disableserverinput", N_("Prevent local interaction on the server"), TRUE, NULL, NULL },
2136  { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "showcursor", N_("Show remote cursor"), FALSE, NULL, NULL },
2137  { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "disableclipboard", N_("Turn off clipboard sync"), TRUE, NULL, NULL },
2138  { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "disableencryption", N_("Turn off encryption"), FALSE, NULL, NULL },
2139  { REMMINA_PROTOCOL_SETTING_TYPE_CHECK, "viewonly", N_("View only"), TRUE, NULL, NULL },
2140  { REMMINA_PROTOCOL_SETTING_TYPE_END, NULL, NULL, FALSE, NULL, NULL }
2141 };
2142 
2143 /* Array for available features.
2144  * The last element of the array must be REMMINA_PROTOCOL_FEATURE_TYPE_END. */
2146 {
2147  { REMMINA_PROTOCOL_FEATURE_TYPE_PREF, REMMINA_PLUGIN_VNC_FEATURE_PREF_QUALITY, GINT_TO_POINTER(REMMINA_PROTOCOL_FEATURE_PREF_RADIO), "quality",
2148  quality_list },
2149  { REMMINA_PROTOCOL_FEATURE_TYPE_PREF, REMMINA_PLUGIN_VNC_FEATURE_PREF_COLOR, GINT_TO_POINTER(REMMINA_PROTOCOL_FEATURE_PREF_RADIO), "colordepth",
2150  colordepth_list },
2151  { REMMINA_PROTOCOL_FEATURE_TYPE_PREF, REMMINA_PLUGIN_VNC_FEATURE_PREF_VIEWONLY, GINT_TO_POINTER(REMMINA_PROTOCOL_FEATURE_PREF_CHECK), "viewonly",
2152  N_("View only") },
2153  { REMMINA_PROTOCOL_FEATURE_TYPE_PREF, REMMINA_PLUGIN_VNC_FEATURE_PREF_DISABLESERVERINPUT, GINT_TO_POINTER(REMMINA_PROTOCOL_FEATURE_PREF_CHECK), "disableserverinput",N_("Prevent local interaction on the server") },
2154  { REMMINA_PROTOCOL_FEATURE_TYPE_TOOL, REMMINA_PLUGIN_VNC_FEATURE_TOOL_REFRESH, N_("Refresh"), NULL, NULL },
2155  { REMMINA_PROTOCOL_FEATURE_TYPE_TOOL, REMMINA_PLUGIN_VNC_FEATURE_TOOL_CHAT, N_("Open Chat…"), "face-smile", NULL },
2156  { REMMINA_PROTOCOL_FEATURE_TYPE_TOOL, REMMINA_PLUGIN_VNC_FEATURE_TOOL_SENDCTRLALTDEL, N_("Send Ctrl+Alt+Delete"), NULL, NULL },
2157  { REMMINA_PROTOCOL_FEATURE_TYPE_SCALE, REMMINA_PLUGIN_VNC_FEATURE_SCALE, NULL, NULL, NULL },
2158  { REMMINA_PROTOCOL_FEATURE_TYPE_UNFOCUS, REMMINA_PLUGIN_VNC_FEATURE_UNFOCUS, NULL, NULL, NULL },
2159 #if LIBVNCSERVER_CHECK_VERSION_VERSION(0, 9, 14)
2160  { REMMINA_PROTOCOL_FEATURE_TYPE_DYNRESUPDATE, REMMINA_PLUGIN_VNC_FEATURE_DYNRESUPDATE, NULL, NULL, NULL },
2161 #endif
2162  { REMMINA_PROTOCOL_FEATURE_TYPE_END, 0, NULL, NULL, NULL }
2163 };
2164 
2165 /* Protocol plugin definition and features */
2167 {
2169  VNC_PLUGIN_NAME, // Name
2170  VNC_PLUGIN_DESCRIPTION, // Description
2171  GETTEXT_PACKAGE, // Translation domain
2172  VNC_PLUGIN_VERSION, // Version number
2173  VNC_PLUGIN_APPICON, // Icon for normal connection
2174  VNC_PLUGIN_SSH_APPICON, // Icon for SSH connection
2175  remmina_plugin_vnc_basic_settings, // Array for basic settings
2176  remmina_plugin_vnc_advanced_settings, // Array for advanced settings
2177  REMMINA_PROTOCOL_SSH_SETTING_TUNNEL, // SSH settings type
2178  remmina_plugin_vnc_features, // Array for available features
2179  remmina_plugin_vnc_init, // Plugin initialization
2180  remmina_plugin_vnc_open_connection, // Plugin open connection
2181  remmina_plugin_vnc_close_connection, // Plugin close connection
2182  remmina_plugin_vnc_query_feature, // Query for available features
2183  remmina_plugin_vnc_call_feature, // Call a feature
2184  remmina_plugin_vnc_keystroke // Send a keystroke
2185 };
2186 
2187 /* Protocol plugin definition and features */
2189 {
2191  VNCI_PLUGIN_NAME, // Name
2192  VNCI_PLUGIN_DESCRIPTION, // Description
2193  GETTEXT_PACKAGE, // Translation domain
2194  VERSION, // Version number
2195  VNCI_PLUGIN_APPICON, // Icon for normal connection
2196  VNCI_PLUGIN_SSH_APPICON, // Icon for SSH connection
2197  remmina_plugin_vnci_basic_settings, // Array for basic settings
2198  remmina_plugin_vnc_advanced_settings, // Array for advanced settings
2199  REMMINA_PROTOCOL_SSH_SETTING_REVERSE_TUNNEL, // SSH settings type
2200  remmina_plugin_vnc_features, // Array for available features
2201  remmina_plugin_vnc_init, // Plugin initialization
2202  remmina_plugin_vnc_open_connection, // Plugin open connection
2203  remmina_plugin_vnc_close_connection, // Plugin close connection
2204  remmina_plugin_vnc_query_feature, // Query for available features
2205  remmina_plugin_vnc_call_feature, // Call a feature
2206  remmina_plugin_vnc_keystroke, // Send a keystroke
2207  NULL, // No screenshot support available
2208  NULL, // RCW map event
2209  NULL // RCW unmap event
2210 };
2211 
2212 G_MODULE_EXPORT gboolean
2214 {
2215  TRACE_CALL(__func__);
2216  remmina_plugin_service = service;
2217 
2218  bindtextdomain(GETTEXT_PACKAGE, REMMINA_RUNTIME_LOCALEDIR);
2219  bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
2220 
2222  return FALSE;
2223 
2225  return FALSE;
2226 
2227  return TRUE;
2228 }
@ REMMINA_PLUGIN_TYPE_PROTOCOL
Definition: plugin.h:48
guint16 keycode
Definition: vnc_plugin.c:301
guint(* pref_keymap_get_keyval)(const gchar *keymap, guint keyval)
Definition: plugin.h:249
gint(* file_get_int)(RemminaFile *remminafile, const gchar *setting, gint default_value)
Definition: plugin.h:238
RemminaFile *(* protocol_plugin_get_file)(RemminaProtocolWidget *gp)
Definition: plugin.h:194
void(* protocol_plugin_set_height)(RemminaProtocolWidget *gp, gint height)
Definition: plugin.h:187
void(* protocol_plugin_init_show_listen)(RemminaProtocolWidget *gp, gint port)
Definition: plugin.h:220
void(* protocol_plugin_send_keys_signals)(GtkWidget *widget, const guint *keyvals, int length, GdkEventType action)
Definition: plugin.h:228
void(* file_set_string)(RemminaFile *remminafile, const gchar *setting, const gchar *value)
Definition: plugin.h:234
gboolean(* protocol_plugin_is_closed)(RemminaProtocolWidget *gp)
Definition: plugin.h:193
gboolean(* register_plugin)(RemminaPlugin *plugin)
Definition: plugin.h:182
gint(* protocol_plugin_init_authx509)(RemminaProtocolWidget *gp)
Definition: plugin.h:214
void(* protocol_plugin_init_show_retry)(RemminaProtocolWidget *gp)
Definition: plugin.h:221
const gchar *(* file_get_string)(RemminaFile *remminafile, const gchar *setting)
Definition: plugin.h:235
void(* get_server_port)(const gchar *server, gint defaultport, gchar **host, gint *port)
Definition: plugin.h:265
void(* protocol_plugin_desktop_resize)(RemminaProtocolWidget *gp)
Definition: plugin.h:206
void(* protocol_plugin_set_error)(RemminaProtocolWidget *gp, const gchar *fmt,...)
Definition: plugin.h:192
gchar *(* protocol_plugin_init_get_clientkey)(RemminaProtocolWidget *gp)
Definition: plugin.h:218
gdouble(* file_get_double)(RemminaFile *remminafile, const gchar *setting, gdouble default_value)
Definition: plugin.h:239
gchar *(* protocol_plugin_init_get_clientcert)(RemminaProtocolWidget *gp)
Definition: plugin.h:217
void(* protocol_plugin_chat_close)(RemminaProtocolWidget *gp)
Definition: plugin.h:226
void(* protocol_plugin_chat_receive)(RemminaProtocolWidget *gp, const gchar *text)
Definition: plugin.h:227
gchar *(* protocol_plugin_init_get_cacrl)(RemminaProtocolWidget *gp)
Definition: plugin.h:216
gboolean(* is_main_thread)(void)
Definition: plugin.h:266
gint(* protocol_plugin_init_auth)(RemminaProtocolWidget *gp, RemminaMessagePanelFlags pflags, const gchar *title, const gchar *default_username, const gchar *default_password, const gchar *default_domain, const gchar *password_prompt)
Definition: plugin.h:207
gchar *(* protocol_plugin_init_get_cacert)(RemminaProtocolWidget *gp)
Definition: plugin.h:215
void(* protocol_plugin_unlock_dynres)(RemminaProtocolWidget *gp)
Definition: plugin.h:205
gint(* protocol_plugin_get_height)(RemminaProtocolWidget *gp)
Definition: plugin.h:186
void(* protocol_plugin_update_align)(RemminaProtocolWidget *gp)
Definition: plugin.h:203
void(* protocol_plugin_signal_connection_opened)(RemminaProtocolWidget *gp)
Definition: plugin.h:202
void(* protocol_plugin_signal_connection_closed)(RemminaProtocolWidget *gp)
Definition: plugin.h:201
void(* protocol_plugin_chat_open)(RemminaProtocolWidget *gp, const gchar *name, void(*on_send)(RemminaProtocolWidget *gp, const gchar *text), void(*on_destroy)(RemminaProtocolWidget *gp))
Definition: plugin.h:225
void(* protocol_plugin_set_width)(RemminaProtocolWidget *gp, gint width)
Definition: plugin.h:185
gboolean(* protocol_plugin_has_error)(RemminaProtocolWidget *gp)
Definition: plugin.h:191
void(* protocol_plugin_register_hostkey)(RemminaProtocolWidget *gp, GtkWidget *widget)
Definition: plugin.h:196
void(* protocol_plugin_init_save_cred)(RemminaProtocolWidget *gp)
Definition: plugin.h:219
gchar *(* protocol_plugin_init_get_username)(RemminaProtocolWidget *gp)
Definition: plugin.h:210
gboolean(* protocol_plugin_start_reverse_tunnel)(RemminaProtocolWidget *gp, gint local_port)
Definition: plugin.h:198
gchar *(* protocol_plugin_init_get_password)(RemminaProtocolWidget *gp)
Definition: plugin.h:211
gint(* protocol_plugin_get_width)(RemminaProtocolWidget *gp)
Definition: plugin.h:184
gchar *(* protocol_plugin_start_direct_tunnel)(RemminaProtocolWidget *gp, gint default_port, gboolean port_plus)
Definition: plugin.h:197
RemminaScaleMode(* remmina_protocol_widget_get_current_scale_mode)(RemminaProtocolWidget *gp)
Definition: plugin.h:188
gboolean(* protocol_plugin_init_get_savepassword)(RemminaProtocolWidget *gp)
Definition: plugin.h:213
RemminaProtocolWidget * gp
Definition: vnc_plugin.c:372
GPtrArray * pressed_keys
Definition: vnc_plugin.h:109
GQueue * vnc_event_queue
Definition: vnc_plugin.h:112
cairo_surface_t * rgb_buffer
Definition: vnc_plugin.h:92
cairo_surface_t * queuecursor_surface
Definition: vnc_plugin.h:100
pthread_mutex_t vnc_event_queue_mutex
Definition: vnc_plugin.h:111
pthread_mutex_t buffer_mutex
Definition: vnc_plugin.h:116
GDateTime * clipboard_timer
Definition: vnc_plugin.h:98
GtkWidget * drawing_area
Definition: vnc_plugin.h:90
struct _RemminaPluginVncEvent::@63::@65 pointer
union _RemminaPluginVncEvent::@63 event_data
struct _RemminaPluginVncEvent::@63::@64 key
enum onMainThread_cb_data::@61 func
pthread_mutex_t mu
Definition: vnc_plugin.c:76
RemminaProtocolWidget * gp
Definition: vnc_plugin.c:72
GtkWidget * widget
Definition: vnc_plugin.c:70
typedefG_BEGIN_DECLS struct _RemminaFile RemminaFile
Definition: types.h:44
@ REMMINA_MESSAGE_PANEL_FLAG_USERNAME
Definition: types.h:157
@ REMMINA_MESSAGE_PANEL_FLAG_SAVEPASSWORD
Definition: types.h:160
@ REMMINA_PROTOCOL_SSH_SETTING_REVERSE_TUNNEL
Definition: types.h:132
@ REMMINA_PROTOCOL_SSH_SETTING_TUNNEL
Definition: types.h:130
@ REMMINA_PROTOCOL_FEATURE_TYPE_UNFOCUS
Definition: types.h:50
@ REMMINA_PROTOCOL_FEATURE_TYPE_DYNRESUPDATE
Definition: types.h:52
@ REMMINA_PROTOCOL_FEATURE_TYPE_PREF
Definition: types.h:48
@ REMMINA_PROTOCOL_FEATURE_TYPE_END
Definition: types.h:47
@ REMMINA_PROTOCOL_FEATURE_TYPE_TOOL
Definition: types.h:49
@ REMMINA_PROTOCOL_FEATURE_TYPE_SCALE
Definition: types.h:51
@ REMMINA_PROTOCOL_SETTING_TYPE_SELECT
Definition: types.h:108
@ REMMINA_PROTOCOL_SETTING_TYPE_CHECK
Definition: types.h:110
@ REMMINA_PROTOCOL_SETTING_TYPE_SERVER
Definition: types.h:100
@ REMMINA_PROTOCOL_SETTING_TYPE_TEXT
Definition: types.h:106
@ REMMINA_PROTOCOL_SETTING_TYPE_END
Definition: types.h:98
@ REMMINA_PROTOCOL_SETTING_TYPE_KEYMAP
Definition: types.h:104
@ REMMINA_PROTOCOL_SETTING_TYPE_PASSWORD
Definition: types.h:101
RemminaScaleMode
Definition: types.h:142
@ REMMINA_PROTOCOL_WIDGET_SCALE_MODE_DYNRES
Definition: types.h:145
@ REMMINA_PROTOCOL_WIDGET_SCALE_MODE_NONE
Definition: types.h:143
static int dot_cursor_y_hot
Definition: vnc_plugin.c:60
static gchar aspect_ratio_tooltip[]
Definition: vnc_plugin.c:2062
static gboolean remmina_plugin_vnc_main_loop(RemminaProtocolWidget *gp)
Definition: vnc_plugin.c:1116
static void remmina_plugin_vnc_rfb_fill_buffer(rfbClient *cl, guchar *dest, gint dest_rowstride, guchar *src, gint src_rowstride, guchar *mask, gint w, gint h)
Definition: vnc_plugin.c:598
static gpointer remmina_plugin_vnc_main_thread(gpointer data)
Definition: vnc_plugin.c:1391
static void remmina_plugin_vnc_on_realize(RemminaProtocolWidget *gp, gpointer data)
Definition: vnc_plugin.c:1660
static void remmina_plugin_vnc_rfb_chat(rfbClient *cl, int value, char *text)
Definition: vnc_plugin.c:1050
static void remmina_plugin_vnc_update_scale(RemminaProtocolWidget *gp, gboolean scale)
Definition: vnc_plugin.c:228
struct _RemminaKeyVal RemminaKeyVal
struct _RemminaPluginVncCuttextParam RemminaPluginVncCuttextParam
static gchar vnciport_tooltip[]
Definition: vnc_plugin.c:2054
static RemminaProtocolPlugin remmina_plugin_vnci
Definition: vnc_plugin.c:2188
static gboolean remmina_plugin_vnc_open_connection(RemminaProtocolWidget *gp)
Definition: vnc_plugin.c:1681
static void remmina_plugin_vnc_event_free_all(RemminaProtocolWidget *gp)
Definition: vnc_plugin.c:185
static RemminaPluginVncCoordinates remmina_plugin_vnc_scale_coordinates(GtkWidget *widget, RemminaProtocolWidget *gp, gint x, gint y)
Definition: vnc_plugin.c:1402
static void onMainThread_schedule_callback_and_wait(struct onMainThread_cb_data *d)
Definition: vnc_plugin.c:108
static void remmina_plugin_vnc_release_key(RemminaProtocolWidget *gp, guint16 keycode)
Definition: vnc_plugin.c:1536
static void remmina_plugin_vnc_rfb_led_state(rfbClient *cl, int value, int pad)
Definition: vnc_plugin.c:722
static gboolean remmina_plugin_vnc_on_motion(GtkWidget *widget, GdkEventMotion *event, RemminaProtocolWidget *gp)
Definition: vnc_plugin.c:1419
static gboolean remmina_plugin_vnc_open_chat(RemminaProtocolWidget *gp)
Definition: vnc_plugin.c:1036
static gboolean remmina_plugin_vnc_on_button(GtkWidget *widget, GdkEventButton *event, RemminaProtocolWidget *gp)
Definition: vnc_plugin.c:1438
static gboolean remmina_plugin_vnc_on_key(GtkWidget *widget, GdkEventKey *event, RemminaProtocolWidget *gp)
Definition: vnc_plugin.c:1568
static gboolean remmina_plugin_vnc_incoming_connection(RemminaProtocolWidget *gp, rfbClient *cl)
Definition: vnc_plugin.c:1073
static gboolean remmina_plugin_vnc_on_draw(GtkWidget *widget, cairo_t *context, RemminaProtocolWidget *gp)
Definition: vnc_plugin.c:1925
G_MODULE_EXPORT gboolean remmina_plugin_entry(RemminaPluginService *service)
Definition: vnc_plugin.c:2213
static void remmina_plugin_vnc_init(RemminaProtocolWidget *gp)
Definition: vnc_plugin.c:1959
static gboolean remmina_plugin_vnc_close_connection_timeout(RemminaProtocolWidget *gp)
Definition: vnc_plugin.c:1735
static void onMainThread_cleanup_handler(gpointer data)
Definition: vnc_plugin.c:99
static gboolean remmina_plugin_vnc_close_chat(RemminaProtocolWidget *gp)
Definition: vnc_plugin.c:1029
static gchar repeater_tooltip[]
Definition: vnc_plugin.c:2045
static gint delta_to_mask(float delta, float *accum, gint mask_plus, gint mask_minus)
Definition: vnc_plugin.c:1469
static void remmina_plugin_vnc_call_feature(RemminaProtocolWidget *gp, const RemminaProtocolFeature *feature)
Definition: vnc_plugin.c:1837
static gpointer quality_list[]
Definition: vnc_plugin.c:2036
static gboolean remmina_plugin_vnc_query_feature(RemminaProtocolWidget *gp, const RemminaProtocolFeature *feature)
Definition: vnc_plugin.c:1823
static gboolean remmina_plugin_vnc_on_scroll(GtkWidget *widget, GdkEventScroll *event, RemminaProtocolWidget *gp)
Definition: vnc_plugin.c:1482
static void remmina_plugin_vnc_update_colordepth(rfbClient *cl, gint colordepth)
Definition: vnc_plugin.c:434
static gboolean check_for_endianness()
Function check_for_endianness() returns 1, if architecture is little endian, 0 in case of big endian.
Definition: vnc_plugin.c:128
static void remmina_plugin_vnc_on_cuttext(GtkClipboard *clipboard, GdkEvent *event, RemminaProtocolWidget *gp)
Definition: vnc_plugin.c:1645
static gpointer colordepth_list[]
Definition: vnc_plugin.c:2027
static void remmina_plugin_vnc_rfb_cursor_shape(rfbClient *cl, int xhot, int yhot, int width, int height, int bytesPerPixel)
Definition: vnc_plugin.c:900
static gboolean remmina_plugin_vnc_close_connection(RemminaProtocolWidget *gp)
Definition: vnc_plugin.c:1804
static void remmina_plugin_vnc_queuecursor(RemminaProtocolWidget *gp, cairo_surface_t *surface, gint x, gint y)
Definition: vnc_plugin.c:285
static const RemminaProtocolSetting remmina_plugin_vnc_advanced_settings[]
Definition: vnc_plugin.c:2127
static gboolean remmina_plugin_vnc_main(RemminaProtocolWidget *gp)
Definition: vnc_plugin.c:1172
static void remmina_plugin_vnc_event_push(RemminaProtocolWidget *gp, gint event_type, gpointer p1, gpointer p2, gpointer p3)
Definition: vnc_plugin.c:136
static gboolean remmina_plugin_vnc_queue_draw_area_real(RemminaProtocolWidget *gp)
Definition: vnc_plugin.c:552
static const RemminaProtocolSetting remmina_plugin_vnc_basic_settings[]
Definition: vnc_plugin.c:2094
static rfbBool remmina_plugin_vnc_rfb_allocfb(rfbClient *cl)
Definition: vnc_plugin.c:491
static RemminaPluginService * remmina_plugin_service
Definition: vnc_plugin.c:57
static RemminaProtocolPlugin remmina_plugin_vnc
Definition: vnc_plugin.c:2166
static gchar vncencodings_tooltip[]
Definition: vnc_plugin.c:2069
static void remmina_plugin_vnc_on_cuttext_request(GtkClipboard *clipboard, const gchar *text, RemminaProtocolWidget *gp)
Definition: vnc_plugin.c:1618
static gboolean onMainThread_cb(struct onMainThread_cb_data *d)
Definition: vnc_plugin.c:81
static void remmina_plugin_vnc_update_quality(rfbClient *cl, gint quality)
Definition: vnc_plugin.c:377
static gboolean remmina_plugin_vnc_queue_cuttext(RemminaPluginVncCuttextParam *param)
Definition: vnc_plugin.c:728
static gboolean remmina_plugin_vnc_on_size_allocate(GtkWidget *widget, GtkAllocation *alloc, RemminaProtocolWidget *gp)
Definition: vnc_plugin.c:1907
static const uint32_t remmina_plugin_vnc_no_encrypt_auth_types[]
Definition: vnc_plugin.c:306
static void remmina_plugin_vnc_keystroke(RemminaProtocolWidget *gp, const guint keystrokes[], const gint keylen)
Definition: vnc_plugin.c:1896
static void remmina_plugin_vnc_rfb_finished(rfbClient *cl) __attribute__((unused))
Definition: vnc_plugin.c:716
static void remmina_plugin_vnc_chat_on_send(RemminaProtocolWidget *gp, const gchar *text)
Definition: vnc_plugin.c:1001
static rfbCredential * remmina_plugin_vnc_rfb_credential(rfbClient *cl, int credentialType)
Definition: vnc_plugin.c:812
static char * remmina_plugin_vnc_rfb_password(rfbClient *cl)
Definition: vnc_plugin.c:773
N_("Unable to connect to VNC server")
Definition: vnc_plugin.c:953
static const RemminaProtocolFeature remmina_plugin_vnc_features[]
Definition: vnc_plugin.c:2145
static void remmina_plugin_vnc_scale_area(RemminaProtocolWidget *gp, gint *x, gint *y, gint *w, gint *h)
Definition: vnc_plugin.c:197
static const RemminaProtocolSetting remmina_plugin_vnci_basic_settings[]
Definition: vnc_plugin.c:2107
gboolean remmina_plugin_vnc_setcursor(RemminaProtocolWidget *gp)
Definition: vnc_plugin.c:261
static void remmina_plugin_vnc_send_ctrlaltdel(RemminaProtocolWidget *gp)
Definition: vnc_plugin.c:1019
static void remmina_plugin_vnc_chat_on_destroy(RemminaProtocolWidget *gp)
Definition: vnc_plugin.c:1012
static void remmina_plugin_vnc_rfb_cuttext(rfbClient *cl, const char *text, int textlen)
Definition: vnc_plugin.c:759
static const gchar * dot_cursor_xpm[]
Definition: vnc_plugin.c:61
static void remmina_plugin_vnc_queue_draw_area(RemminaProtocolWidget *gp, gint x, gint y, gint w, gint h)
Definition: vnc_plugin.c:572
static RemminaPluginVncEvent * remmina_plugin_vnc_event_queue_pop_head(RemminaPluginVncData *gpdata)
Definition: vnc_plugin.c:309
static void remmina_plugin_vnc_rfb_bell(rfbClient *cl)
Definition: vnc_plugin.c:930
static void remmina_plugin_vnc_event_free(RemminaPluginVncEvent *event)
Definition: vnc_plugin.c:171
static void remmina_plugin_vnc_process_vnc_event(RemminaProtocolWidget *gp)
Definition: vnc_plugin.c:324
static int dot_cursor_x_hot
Definition: vnc_plugin.c:59
static gint remmina_plugin_vnc_bits(gint n)
Definition: vnc_plugin.c:540
static void remmina_plugin_vnc_rfb_updatefb(rfbClient *cl, int x, int y, int w, int h)
Definition: vnc_plugin.c:685
@ REMMINA_PLUGIN_VNC_EVENT_CUTTEXT
Definition: vnc_plugin.h:125
@ REMMINA_PLUGIN_VNC_EVENT_CHAT_SEND
Definition: vnc_plugin.h:127
@ REMMINA_PLUGIN_VNC_EVENT_POINTER
Definition: vnc_plugin.h:124
@ REMMINA_PLUGIN_VNC_EVENT_CHAT_CLOSE
Definition: vnc_plugin.h:128
@ REMMINA_PLUGIN_VNC_EVENT_KEY
Definition: vnc_plugin.h:123
@ REMMINA_PLUGIN_VNC_EVENT_CHAT_OPEN
Definition: vnc_plugin.h:126