Remmina - The GTK+ Remote Desktop Client  v1.4.31
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.
rdp_event.c
Go to the documentation of this file.
1 /*
2  * Remmina - The GTK Remote Desktop Client
3  * Copyright (C) 2010 Jay Sorg
4  * Copyright (C) 2010-2011 Vic Lee
5  * Copyright (C) 2011 Marc-Andre Moreau <marcandre.moreau@gmail.com>
6  * Copyright (C) 2014-2015 Antenore Gatta, Fabio Castelli, Giovanni Panozzo
7  * Copyright (C) 2016-2022 Antenore Gatta, Giovanni Panozzo
8  * Copyright (C) 2022-2023 Antenore Gatta, Giovanni Panozzo, Hiroyuki Tanaka
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin Street, Fifth Floor,
23  * Boston, MA 02110-1301, USA.
24  *
25  * In addition, as a special exception, the copyright holders give
26  * permission to link the code of portions of this program with the
27  * OpenSSL library under certain conditions as described in each
28  * individual source file, and distribute linked combinations
29  * including the two.
30  * You must obey the GNU General Public License in all respects
31  * for all of the code used other than OpenSSL. * If you modify
32  * file(s) with this exception, you may extend this exception to your
33  * version of the file(s), but you are not obligated to do so. * If you
34  * do not wish to do so, delete this exception statement from your
35  * version. * If you delete this exception statement from all source
36  * files in the program, then also delete it here.
37  *
38  */
39 
40 #include "rdp_cliprdr.h"
41 #include "rdp_event.h"
42 #include "rdp_monitor.h"
43 #include "rdp_settings.h"
44 #include <gdk/gdkkeysyms.h>
45 #ifdef GDK_WINDOWING_X11
46 #include <cairo/cairo-xlib.h>
47 #else
48 #include <cairo/cairo.h>
49 #endif
50 #include <freerdp/locale/keyboard.h>
51 
53 {
54  TRACE_CALL(__func__);
55  rfContext *rfi = GET_PLUGIN_DATA(gp);
56  rdpGdi *gdi;
57 
58  if (rfi == NULL)
59  return false;
60 
62  int do_suppress = !remmina_plugin_service->file_get_int(remminafile, "no-suppress", FALSE);
63 
64  if (do_suppress) {
65  gdi = ((rdpContext *)rfi)->gdi;
66 
67  REMMINA_PLUGIN_DEBUG("Map event received, disabling TS_SUPPRESS_OUTPUT_PDU ");
68  gdi_send_suppress_output(gdi, FALSE);
69  }
70 
71  return FALSE;
72 }
73 
75 {
76  TRACE_CALL(__func__);
77  rfContext *rfi = GET_PLUGIN_DATA(gp);
78  rdpGdi *gdi;
79 
80  if (rfi == NULL)
81  return false;
82 
83  GtkWidget *toplevel = gtk_widget_get_toplevel(GTK_WIDGET(gp));
84  GdkWindow *window = gtk_widget_get_window(toplevel);
85 
86  if (gdk_window_get_fullscreen_mode(window) == GDK_FULLSCREEN_ON_ALL_MONITORS) {
87  REMMINA_PLUGIN_DEBUG("Unmap event received, but cannot enable TS_SUPPRESS_OUTPUT_PDU when in fullscreen");
88  return FALSE;
89  }
90 
92  int do_suppress = !remmina_plugin_service->file_get_int(remminafile, "no-suppress", FALSE);
93 
94  if (do_suppress) {
95  gdi = ((rdpContext *)rfi)->gdi;
96 
97  REMMINA_PLUGIN_DEBUG("Unmap event received, enabling TS_SUPPRESS_OUTPUT_PDU ");
98  gdi_send_suppress_output(gdi, TRUE);
99  }
100 
101  return FALSE;
102 }
103 
104 static gboolean remmina_rdp_event_on_focus_in(GtkWidget *widget, GdkEventKey *event, RemminaProtocolWidget *gp)
105 {
106  TRACE_CALL(__func__);
107 
108  rfContext *rfi = GET_PLUGIN_DATA(gp);
109  rdpInput *input;
110  GdkModifierType state;
111 
112 #if GTK_CHECK_VERSION(3, 20, 0)
113  GdkSeat *seat;
114 #else
115  GdkDeviceManager *manager;
116 #endif
117  GdkDevice *keyboard = NULL;
118 
119  const gchar *wname = gtk_widget_get_name(gtk_widget_get_toplevel(widget));
120  REMMINA_PLUGIN_DEBUG("Top level name is: %s", wname);
121 
122  if (!rfi || !rfi->connected || rfi->is_reconnecting)
123  return FALSE;
124 
125  input = rfi->instance->input;
126  UINT32 toggle_keys_state = 0;
127 
128 #if GTK_CHECK_VERSION(3, 20, 0)
129  seat = gdk_display_get_default_seat(gdk_display_get_default());
130  keyboard = gdk_seat_get_pointer(seat);
131 #else
132  manager = gdk_display_get_device_manager(gdk_display_get_default());
133  keyboard = gdk_device_manager_get_client_pointer(manager);
134 #endif
135  gdk_window_get_device_position(gdk_get_default_root_window(), keyboard, NULL, NULL, &state);
136 
137  if (state & GDK_LOCK_MASK)
138  toggle_keys_state |= KBD_SYNC_CAPS_LOCK;
139  if (state & GDK_MOD2_MASK)
140  toggle_keys_state |= KBD_SYNC_NUM_LOCK;
141  if (state & GDK_MOD5_MASK)
142  toggle_keys_state |= KBD_SYNC_SCROLL_LOCK;
143 
144  input->SynchronizeEvent(input, toggle_keys_state);
145  input->KeyboardEvent(input, KBD_FLAGS_RELEASE, 0x0F);
146 
147  return FALSE;
148 }
149 
151 {
152  TRACE_CALL(__func__);
153  rfContext *rfi = GET_PLUGIN_DATA(gp);
154  RemminaPluginRdpEvent *event;
155 
156  /* Called by the main GTK thread to send an event to the libfreerdp thread */
157 
158  if (!rfi || !rfi->connected || rfi->is_reconnecting)
159  return;
160 
161  if (rfi->event_queue) {
162 #if GLIB_CHECK_VERSION(2,67,3)
163  event = g_memdup2(e, sizeof(RemminaPluginRdpEvent));
164 #else
165  event = g_memdup(e, sizeof(RemminaPluginRdpEvent));
166 #endif
167  g_async_queue_push(rfi->event_queue, event);
168 
169  if (write(rfi->event_pipe[1], "\0", 1)) {
170  }
171  }
172 }
173 
175 {
176  TRACE_CALL(__func__);
177  rfContext *rfi = GET_PLUGIN_DATA(gp);
178  RemminaPluginRdpEvent rdp_event = { 0 };
179  int i;
180 
181  /* Send all release key events for previously pressed keys */
182  for (i = 0; i < rfi->pressed_keys->len; i++) {
183  rdp_event = g_array_index(rfi->pressed_keys, RemminaPluginRdpEvent, i);
184  if ((rdp_event.type == REMMINA_RDP_EVENT_TYPE_SCANCODE ||
186  rdp_event.key_event.up == false) {
187  rdp_event.key_event.up = true;
188  remmina_rdp_event_event_push(gp, &rdp_event);
189  }
190  }
191 
192  g_array_set_size(rfi->pressed_keys, 0);
193 }
194 
196 {
197  TRACE_CALL(__func__);
198  gint i;
199  rfContext *rfi = GET_PLUGIN_DATA(gp);
200  RemminaPluginRdpEvent rdp_event_2 = { 0 };
201 
203 
204  if ((rdp_event.type == REMMINA_RDP_EVENT_TYPE_SCANCODE ||
206  rdp_event.key_event.up) {
207  /* Unregister the keycode only */
208  for (i = 0; i < rfi->pressed_keys->len; i++) {
209  rdp_event_2 = g_array_index(rfi->pressed_keys, RemminaPluginRdpEvent, i);
210 
211  if (rdp_event_2.key_event.key_code == rdp_event.key_event.key_code &&
212  rdp_event_2.key_event.unicode_code == rdp_event.key_event.unicode_code &&
213  rdp_event_2.key_event.extended == rdp_event.key_event.extended) {
214  g_array_remove_index_fast(rfi->pressed_keys, i);
215  break;
216  }
217  }
218  }
219 }
220 
222 {
223  TRACE_CALL(__func__);
224  rfContext *rfi = GET_PLUGIN_DATA(gp);
225 
226  if (!rdp_event.key_event.key_code)
227  return;
228 
229  if (rdp_event.key_event.up)
230  remmina_rdp_event_release_key(gp, rdp_event);
231  else
232  g_array_append_val(rfi->pressed_keys, rdp_event);
233 }
234 
235 
236 static void remmina_rdp_event_scale_area(RemminaProtocolWidget *gp, gint *x, gint *y, gint *w, gint *h)
237 {
238  TRACE_CALL(__func__);
239  gint width, height;
240  gint sx, sy, sw, sh;
241  rfContext *rfi = GET_PLUGIN_DATA(gp);
242 
243  if (!rfi || !rfi->connected || rfi->is_reconnecting || !rfi->surface)
244  return;
245 
248 
249  if ((width == 0) || (height == 0))
250  return;
251 
252  if ((rfi->scale_width == width) && (rfi->scale_height == height)) {
253  /* Same size, just copy the pixels */
254  *x = MIN(MAX(0, *x), width - 1);
255  *y = MIN(MAX(0, *y), height - 1);
256  *w = MIN(width - *x, *w);
257  *h = MIN(height - *y, *h);
258  return;
259  }
260 
261  /* We have to extend the scaled region one scaled pixel, to avoid gaps */
262 
263  sx = MIN(MAX(0, (*x) * rfi->scale_width / width
264  - rfi->scale_width / width - 2), rfi->scale_width - 1);
265 
266  sy = MIN(MAX(0, (*y) * rfi->scale_height / height
267  - rfi->scale_height / height - 2), rfi->scale_height - 1);
268 
269  sw = MIN(rfi->scale_width - sx, (*w) * rfi->scale_width / width
270  + rfi->scale_width / width + 4);
271 
272  sh = MIN(rfi->scale_height - sy, (*h) * rfi->scale_height / height
273  + rfi->scale_height / height + 4);
274 
275  *x = sx;
276  *y = sy;
277  *w = sw;
278  *h = sh;
279 }
280 
282 {
283  TRACE_CALL(__func__);
284  rfContext *rfi = GET_PLUGIN_DATA(gp);
285  gint x, y, w, h, i;
286 
287  for (i = 0; i < ui->reg.ninvalid; i++) {
288  x = ui->reg.ureg[i].x;
289  y = ui->reg.ureg[i].y;
290  w = ui->reg.ureg[i].w;
291  h = ui->reg.ureg[i].h;
292 
294  remmina_rdp_event_scale_area(gp, &x, &y, &w, &h);
295 
296  gtk_widget_queue_draw_area(rfi->drawing_area, x, y, w, h);
297  }
298  g_free(ui->reg.ureg);
299 }
300 
301 void remmina_rdp_event_update_rect(RemminaProtocolWidget *gp, gint x, gint y, gint w, gint h)
302 {
303  TRACE_CALL(__func__);
304  rfContext *rfi = GET_PLUGIN_DATA(gp);
305 
307  remmina_rdp_event_scale_area(gp, &x, &y, &w, &h);
308 
309  gtk_widget_queue_draw_area(rfi->drawing_area, x, y, w, h);
310 }
311 
313 {
314  TRACE_CALL(__func__);
315  GtkAllocation a;
316  gint rdwidth, rdheight;
317  gint gpwidth, gpheight;
318  rfContext *rfi = GET_PLUGIN_DATA(gp);
319 
320  gtk_widget_get_allocation(GTK_WIDGET(gp), &a);
321  gpwidth = a.width;
322  gpheight = a.height;
323 
325  if ((gpwidth > 1) && (gpheight > 1)) {
328 
329  rfi->scale_width = gpwidth;
330  rfi->scale_height = gpheight;
331 
332  rfi->scale_x = (gdouble)rfi->scale_width / (gdouble)rdwidth;
333  rfi->scale_y = (gdouble)rfi->scale_height / (gdouble)rdheight;
334  }
335  } else {
336  rfi->scale_width = 0;
337  rfi->scale_height = 0;
338  rfi->scale_x = 0;
339  rfi->scale_y = 0;
340  }
341 }
342 
343 static gboolean remmina_rdp_event_on_draw(GtkWidget *widget, cairo_t *context, RemminaProtocolWidget *gp)
344 {
345  TRACE_CALL(__func__);
346  rfContext *rfi = GET_PLUGIN_DATA(gp);
347  guint width, height;
348  gchar *msg;
349  cairo_text_extents_t extents;
350 
351  if (!rfi || !rfi->connected)
352  return FALSE;
353 
354 
355  if (rfi->is_reconnecting) {
356  /* FreeRDP is reconnecting, just show a message to the user */
357 
358  width = gtk_widget_get_allocated_width(widget);
359  height = gtk_widget_get_allocated_height(widget);
360 
361  /* Draw text */
362  msg = g_strdup_printf(_("Reconnection attempt %d of %d…"),
364 
365  cairo_select_font_face(context, "Sans", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD);
366  cairo_set_font_size(context, 24);
367  cairo_set_source_rgb(context, 0.9, 0.9, 0.9);
368  cairo_text_extents(context, msg, &extents);
369  cairo_move_to(context, (width - (extents.width + extents.x_bearing)) / 2, (height - (extents.height + extents.y_bearing)) / 2);
370  cairo_show_text(context, msg);
371  g_free(msg);
372  } else {
373  /* Standard drawing: We copy the surface from RDP */
374 
375  if (!rfi->surface)
376  return FALSE;
377 
379  cairo_scale(context, rfi->scale_x, rfi->scale_y);
380 
381  cairo_surface_flush(rfi->surface);
382  cairo_set_source_surface(context, rfi->surface, 0, 0);
383  cairo_surface_mark_dirty(rfi->surface);
384 
385  cairo_set_operator(context, CAIRO_OPERATOR_SOURCE); // Ignore alpha channel from FreeRDP
386  cairo_paint(context);
387  }
388 
389  return TRUE;
390 }
391 
393 {
394  TRACE_CALL(__func__);
395  rfContext *rfi = GET_PLUGIN_DATA(gp);
396 
397  RemminaPluginRdpEvent rdp_event = { 0 };
398  GtkAllocation a;
399  gint desktopOrientation, desktopScaleFactor, deviceScaleFactor;
400 
401  RemminaFile *remminafile;
402 
403  if (!rfi || !rfi->connected || rfi->is_reconnecting)
404  return FALSE;
405 
407 
409  return FALSE;
410 
412  gint gpwidth, gpheight, prevwidth, prevheight;
413 
414  gchar *monitorids = NULL;
415  guint32 maxwidth = 0;
416  guint32 maxheight = 0;
417 
418  remmina_rdp_monitor_get(rfi, &monitorids, &maxwidth, &maxheight);
419 
420  REMMINA_PLUGIN_DEBUG("Sending preconfigured monitor layout");
421  if (rfi->dispcontext && rfi->dispcontext->SendMonitorLayout) {
422  remmina_rdp_settings_get_orientation_scale_prefs(&desktopOrientation, &desktopScaleFactor, &deviceScaleFactor);
423  gtk_widget_get_allocation(GTK_WIDGET(gp), &a);
424  gpwidth = a.width;
425  gpheight = a.height;
428 
429  if ((gpwidth != prevwidth || gpheight != prevheight) && gpwidth >= 200 && gpheight >= 200) {
430  if (rfi->rdpgfxchan) {
431  /* Workaround for FreeRDP issue #5417 */
432  if (gpwidth < AVC_MIN_DESKTOP_WIDTH)
433  gpwidth = AVC_MIN_DESKTOP_WIDTH;
434  if (gpheight < AVC_MIN_DESKTOP_HEIGHT)
435  gpheight = AVC_MIN_DESKTOP_HEIGHT;
436  }
438  if (remmina_plugin_service->file_get_int(remminafile, "multimon", FALSE)) {
439  const rdpMonitor *base = freerdp_settings_get_pointer(rfi->settings, FreeRDP_MonitorDefArray);
440  for (gint i = 0; i < freerdp_settings_get_uint32(rfi->settings, FreeRDP_MonitorCount); ++i) {
441  const rdpMonitor *current = &base[i];
442  REMMINA_PLUGIN_DEBUG("Sending display layout n° %d", i);
443  rdp_event.monitor_layout.Flags = current->is_primary;
444  REMMINA_PLUGIN_DEBUG("EVNT MON LAYOUT - Flags: %i", rdp_event.monitor_layout.Flags);
445  rdp_event.monitor_layout.Left = current->x;
446  REMMINA_PLUGIN_DEBUG("EVNT MON LAYOUT - Left: %i", rdp_event.monitor_layout.Left);
447  rdp_event.monitor_layout.Top = current->y;
448  REMMINA_PLUGIN_DEBUG("EVNT MON LAYOUT - Top: %i", rdp_event.monitor_layout.Top);
449  rdp_event.monitor_layout.width = current->width;
450  REMMINA_PLUGIN_DEBUG("EVNT MON LAYOUT - width: %i", rdp_event.monitor_layout.width);
451  rdp_event.monitor_layout.height = current->height;
452  REMMINA_PLUGIN_DEBUG("EVNT MON LAYOUT - height: %i", rdp_event.monitor_layout.height);
453  rdp_event.monitor_layout.physicalWidth = current->attributes.physicalWidth;
454  REMMINA_PLUGIN_DEBUG("EVNT MON LAYOUT - physicalWidth: %i", rdp_event.monitor_layout.physicalWidth);
455  rdp_event.monitor_layout.physicalHeight = current->attributes.physicalHeight;
456  REMMINA_PLUGIN_DEBUG("EVNT MON LAYOUT - PhysicalHeight: %i", rdp_event.monitor_layout.physicalHeight);
457  if (current->attributes.orientation)
458  rdp_event.monitor_layout.desktopOrientation = current->attributes.orientation;
459  else
460  rdp_event.monitor_layout.desktopOrientation = rdp_event.monitor_layout.desktopOrientation;
461  REMMINA_PLUGIN_DEBUG("EVNT MON LAYOUT - desktopOrientation: %i", rdp_event.monitor_layout.desktopOrientation);
462  rdp_event.monitor_layout.desktopScaleFactor = rdp_event.monitor_layout.desktopScaleFactor;
463  REMMINA_PLUGIN_DEBUG("EVNT MON LAYOUT - ScaleFactorflag: %i", rdp_event.monitor_layout.desktopScaleFactor);
464  rdp_event.monitor_layout.deviceScaleFactor = rdp_event.monitor_layout.deviceScaleFactor;
465  }
466  remmina_rdp_event_event_push(gp, &rdp_event);
467  } else {
468  rdp_event.monitor_layout.width = gpwidth;
469  rdp_event.monitor_layout.height = gpheight;
470  rdp_event.monitor_layout.desktopOrientation = desktopOrientation;
471  rdp_event.monitor_layout.desktopScaleFactor = desktopScaleFactor;
472  rdp_event.monitor_layout.deviceScaleFactor = deviceScaleFactor;
473  remmina_rdp_event_event_push(gp, &rdp_event);
474  }
475  }
476  }
477 
478  g_free(monitorids);
479 
480  return FALSE;
481 }
482 
484 {
485  TRACE_CALL(__func__);
486  rfContext *rfi = GET_PLUGIN_DATA(gp);
487 
488  if (!rfi || !rfi->connected || rfi->is_reconnecting)
489  return;
491  g_source_remove(rfi->delayed_monitor_layout_handler);
493  }
495  rfi->delayed_monitor_layout_handler = g_timeout_add(500, (GSourceFunc)remmina_rdp_event_delayed_monitor_layout, gp);
496 }
497 
498 static gboolean remmina_rdp_event_on_configure(GtkWidget *widget, GdkEventConfigure *event, RemminaProtocolWidget *gp)
499 {
500  TRACE_CALL(__func__);
501  /* Called when gp changes its size or position */
502 
503  rfContext *rfi = GET_PLUGIN_DATA(gp);
504 
505  if (!rfi || !rfi->connected || rfi->is_reconnecting)
506  return FALSE;
507 
509 
510  /* If the scaler is not active, schedule a delayed remote resolution change */
512 
513 
514  return FALSE;
515 }
516 
517 static void remmina_rdp_event_translate_pos(RemminaProtocolWidget *gp, int ix, int iy, UINT16 *ox, UINT16 *oy)
518 {
519  TRACE_CALL(__func__);
520  rfContext *rfi = GET_PLUGIN_DATA(gp);
521 
522  /*
523  * Translate a position from local window coordinates (ix,iy) to
524  * RDP coordinates and put result on (*ox,*uy)
525  * */
526 
527  if (!rfi || !rfi->connected || rfi->is_reconnecting)
528  return;
529 
530  if ((rfi->scale == REMMINA_PROTOCOL_WIDGET_SCALE_MODE_SCALED) && (rfi->scale_width >= 1) && (rfi->scale_height >= 1)) {
531  *ox = (UINT16)(ix * remmina_plugin_service->protocol_plugin_get_width(gp) / rfi->scale_width);
532  *oy = (UINT16)(iy * remmina_plugin_service->protocol_plugin_get_height(gp) / rfi->scale_height);
533  } else {
534  *ox = (UINT16)ix;
535  *oy = (UINT16)iy;
536  }
537 }
538 
539 static void remmina_rdp_event_reverse_translate_pos_reverse(RemminaProtocolWidget *gp, int ix, int iy, int *ox, int *oy)
540 {
541  TRACE_CALL(__func__);
542  rfContext *rfi = GET_PLUGIN_DATA(gp);
543 
544  /*
545  * Translate a position from RDP coordinates (ix,iy) to
546  * local window coordinates and put result on (*ox,*uy)
547  * */
548 
549  if (!rfi || !rfi->connected || rfi->is_reconnecting)
550  return;
551 
552  if ((rfi->scale == REMMINA_PROTOCOL_WIDGET_SCALE_MODE_SCALED) && (rfi->scale_width >= 1) && (rfi->scale_height >= 1)) {
555  } else {
556  *ox = ix;
557  *oy = iy;
558  }
559 }
560 
561 static gboolean remmina_rdp_event_on_motion(GtkWidget *widget, GdkEventMotion *event, RemminaProtocolWidget *gp)
562 {
563  TRACE_CALL(__func__);
564  RemminaPluginRdpEvent rdp_event = { 0 };
565  RemminaFile *remminafile;
566 
568  if (remmina_plugin_service->file_get_int(remminafile, "viewonly", FALSE))
569  return FALSE;
570 
572  rdp_event.mouse_event.flags = PTR_FLAGS_MOVE;
573  rdp_event.mouse_event.extended = FALSE;
574 
575  remmina_rdp_event_translate_pos(gp, event->x, event->y, &rdp_event.mouse_event.x, &rdp_event.mouse_event.y);
576  remmina_rdp_event_event_push(gp, &rdp_event);
577 
578  return TRUE;
579 }
580 
581 static gboolean remmina_rdp_event_on_button(GtkWidget *widget, GdkEventButton *event, RemminaProtocolWidget *gp)
582 {
583  TRACE_CALL(__func__);
584  gint flag;
585  gboolean extended = FALSE;
586  RemminaPluginRdpEvent rdp_event = { 0 };
587  gint primary, secondary;
588 
589  RemminaFile *remminafile;
590 
592  if (remmina_plugin_service->file_get_int(remminafile, "viewonly", FALSE))
593  return FALSE;
594 
595  /* We bypass 2button-press and 3button-press events */
596  if ((event->type != GDK_BUTTON_PRESS) && (event->type != GDK_BUTTON_RELEASE))
597  return TRUE;
598 
599  flag = 0;
600 
601  if (remmina_plugin_service->file_get_int(remminafile, "left-handed", FALSE)) {
602  primary = PTR_FLAGS_BUTTON2;
603  secondary = PTR_FLAGS_BUTTON1;
604  } else {
605  primary = PTR_FLAGS_BUTTON1;
606  secondary = PTR_FLAGS_BUTTON2;
607  }
608 
609  switch (event->button) {
610  case 1:
611  flag |= primary;
612  break;
613  case 2:
614  flag |= PTR_FLAGS_BUTTON3;
615  break;
616  case 3:
617  flag |= secondary;
618  break;
619  case 8: /* back */
620  case 97: /* Xming */
621  extended = TRUE;
622  flag |= PTR_XFLAGS_BUTTON1;
623  break;
624  case 9: /* forward */
625  case 112: /* Xming */
626  extended = TRUE;
627  flag |= PTR_XFLAGS_BUTTON2;
628  break;
629  default:
630  return FALSE;
631  }
632 
633  if (event->type == GDK_BUTTON_PRESS) {
634  if (extended)
635  flag |= PTR_XFLAGS_DOWN;
636  else
637  flag |= PTR_FLAGS_DOWN;
638  }
639 
641  remmina_rdp_event_translate_pos(gp, event->x, event->y, &rdp_event.mouse_event.x, &rdp_event.mouse_event.y);
642 
643  if (flag != 0) {
644  rdp_event.mouse_event.flags = flag;
645  rdp_event.mouse_event.extended = extended;
646  remmina_rdp_event_event_push(gp, &rdp_event);
647  }
648 
649  return TRUE;
650 }
651 
652 static gboolean remmina_rdp_event_on_scroll(GtkWidget *widget, GdkEventScroll *event, RemminaProtocolWidget *gp)
653 {
654  TRACE_CALL(__func__);
655  gint flag;
656  RemminaPluginRdpEvent rdp_event = { 0 };
657  float windows_delta;
658  RemminaFile *remminafile;
659 
661  if (remmina_plugin_service->file_get_int(remminafile, "viewonly", FALSE))
662  return FALSE;
663 
664  flag = 0;
666 
667  /* See [MS-RDPBCGR] TS_POINTER_EVENT and WM_MOUSEWHEEL message */
668 
669  switch (event->direction) {
670  case GDK_SCROLL_UP:
671  flag = PTR_FLAGS_WHEEL | 0x0078; // 120 is one scroll unit defined in WM_MOUSEWHEEL
672  break;
673 
674  case GDK_SCROLL_DOWN:
675  flag = PTR_FLAGS_WHEEL | 0x0188; // -120 (one scroll unit) in 9 bits two's complement
676  break;
677 
678 #if GTK_CHECK_VERSION(3, 4, 0)
679  case GDK_SCROLL_SMOOTH:
680 
681  if (event->delta_y == 0.0)
682  return FALSE;
683 
684  windows_delta = event->delta_y * -120;
685 
686  if (windows_delta > 255)
687  windows_delta = 255;
688  if (windows_delta < -256)
689  windows_delta = -256;
690 
691  flag = PTR_FLAGS_WHEEL | ((short)windows_delta & WheelRotationMask);
692 
693  break;
694 #endif
695 
696  default:
697  return FALSE;
698  }
699 
700  rdp_event.mouse_event.flags = flag;
701  rdp_event.mouse_event.extended = FALSE;
702  remmina_rdp_event_translate_pos(gp, event->x, event->y, &rdp_event.mouse_event.x, &rdp_event.mouse_event.y);
703  remmina_rdp_event_event_push(gp, &rdp_event);
704 
705  return TRUE;
706 }
707 
708 static void remmina_rdp_event_init_keymap(rfContext *rfi, const gchar *strmap)
709 {
710  long int v1, v2;
711  const char *s;
712  char *endptr;
714 
715  if (strmap == NULL || strmap[0] == 0) {
716  rfi->keymap = NULL;
717  return;
718  }
719  s = strmap;
720  rfi->keymap = g_array_new(FALSE, TRUE, sizeof(RemminaPluginRdpKeymapEntry));
721  while (1) {
722  v1 = strtol(s, &endptr, 10);
723  if (endptr == s) break;
724  s = endptr;
725  if (*s != ':') break;
726  s++;
727  v2 = strtol(s, &endptr, 10);
728  if (endptr == s) break;
729  s = endptr;
730  ke.orig_keycode = v1 & 0x7fffffff;
731  ke.translated_keycode = v2 & 0x7fffffff;
732  g_array_append_val(rfi->keymap, ke);
733  if (*s != ',') break;
734  s++;
735  }
736  if (rfi->keymap->len == 0) {
737  g_array_unref(rfi->keymap);
738  rfi->keymap = NULL;
739  }
740 }
741 
742 static gboolean remmina_rdp_event_on_key(GtkWidget *widget, GdkEventKey *event, RemminaProtocolWidget *gp)
743 {
744  TRACE_CALL(__func__);
745  guint32 unicode_keyval;
746  guint16 hardware_keycode;
747  rfContext *rfi = GET_PLUGIN_DATA(gp);
748  RemminaPluginRdpEvent rdp_event;
750  RemminaFile *remminafile;
751  DWORD scancode = 0;
752  int ik;
753 
754  if (!rfi || !rfi->connected || rfi->is_reconnecting)
755  return FALSE;
756 
758  if (remmina_plugin_service->file_get_int(remminafile, "viewonly", FALSE))
759  return FALSE;
760 
761 #ifdef ENABLE_GTK_INSPECTOR_KEY
762  /* GTK inspector key is propagated up. Disabled by default.
763  * enable it by defining ENABLE_GTK_INSPECTOR_KEY */
764  if ((event->state & GDK_CONTROL_MASK) != 0 && (event->keyval == GDK_KEY_I || event->keyval == GDK_KEY_D))
765  return FALSE;
766 
767 #endif
768 
770  rdp_event.key_event.up = (event->type == GDK_KEY_PRESS ? false : true);
771  rdp_event.key_event.extended = false;
772 
773  switch (event->keyval) {
774  case GDK_KEY_Pause:
775  /*
776  * See https://msdn.microsoft.com/en-us/library/cc240584.aspx
777  * 2.2.8.1.1.3.1.1.1 Keyboard Event (TS_KEYBOARD_EVENT)
778  * for pause key management
779  */
780  rdp_event.key_event.key_code = 0x1D;
781  rdp_event.key_event.up = false;
782  remmina_rdp_event_event_push(gp, &rdp_event);
783  rdp_event.key_event.key_code = 0x45;
784  rdp_event.key_event.up = false;
785  remmina_rdp_event_event_push(gp, &rdp_event);
786  rdp_event.key_event.key_code = 0x1D;
787  rdp_event.key_event.up = true;
788  remmina_rdp_event_event_push(gp, &rdp_event);
789  rdp_event.key_event.key_code = 0x45;
790  rdp_event.key_event.up = true;
791  remmina_rdp_event_event_push(gp, &rdp_event);
792  break;
793 
794  default:
795  if (!rfi->use_client_keymap) {
796  hardware_keycode = event->hardware_keycode;
797  if (rfi->keymap) {
798  for (ik = 0; ik < rfi->keymap->len; ik++) {
799  kep = &g_array_index(rfi->keymap, RemminaPluginRdpKeymapEntry, ik);
800  if (hardware_keycode == kep->orig_keycode) {
801  hardware_keycode = kep->translated_keycode;
802  break;
803  }
804  }
805  }
806  scancode = freerdp_keyboard_get_rdp_scancode_from_x11_keycode(hardware_keycode);
807  if (scancode) {
808  rdp_event.key_event.key_code = scancode & 0xFF;
809  rdp_event.key_event.extended = scancode & 0x100;
810  remmina_rdp_event_event_push(gp, &rdp_event);
811  keypress_list_add(gp, rdp_event);
812  }
813  } else {
814  unicode_keyval = gdk_keyval_to_unicode(event->keyval);
815  /* Decide when whe should send a keycode or a Unicode character.
816  * - All non char keys (Shift, Alt, Super) should be sent as keycode
817  * - Space should be sent as keycode (see issue #1364)
818  * - All special keys (F1-F10, numeric pad, Home/End/Arrows/PgUp/PgDn/Insert/Delete) keycode
819  * - All key pressed while Ctrl or Alt or Super is down are not decoded by gdk_keyval_to_unicode(), so send it as keycode
820  * - All keycodes not translatable to unicode chars, as keycode
821  * - The rest as Unicode char
822  */
823  if (event->keyval >= 0xfe00 || // Arrows, Shift, Alt, Fn, num keypad…
824  event->hardware_keycode == 0x41 || // Spacebar
825  unicode_keyval == 0 || // Impossible to translate
826  (event->state & (GDK_MOD1_MASK | GDK_CONTROL_MASK | GDK_SUPER_MASK)) != 0 // A modifier not recognized by gdk_keyval_to_unicode()
827  ) {
828  scancode = freerdp_keyboard_get_rdp_scancode_from_x11_keycode(event->hardware_keycode);
829  rdp_event.key_event.key_code = scancode & 0xFF;
830  rdp_event.key_event.extended = scancode & 0x100;
831  if (rdp_event.key_event.key_code) {
832  remmina_rdp_event_event_push(gp, &rdp_event);
833  keypress_list_add(gp, rdp_event);
834  }
835  } else {
837  rdp_event.key_event.unicode_code = unicode_keyval;
838  rdp_event.key_event.extended = false;
839  remmina_rdp_event_event_push(gp, &rdp_event);
840  keypress_list_add(gp, rdp_event);
841  }
842  }
843  break;
844  }
845 
846  return TRUE;
847 }
848 
849 gboolean remmina_rdp_event_on_clipboard(GtkClipboard *gtkClipboard, GdkEvent *event, RemminaProtocolWidget *gp)
850 {
851  /* Signal handler for GTK clipboard owner-change */
852  TRACE_CALL(__func__);
853  RemminaPluginRdpEvent rdp_event = { 0 };
854  CLIPRDR_FORMAT_LIST *pFormatList;
855  GObject *new_owner;
856 
857  /* Usually "owner-change" is fired when a user presses "COPY" on the client
858  * OR when this plugin calls gtk_clipboard_set_with_owner()
859  * after receiving a RDP server format list in remmina_rdp_cliprdr_server_format_list()
860  * In the latter case, we must ignore owner change */
861 
862  REMMINA_PLUGIN_DEBUG("gp=%p: owner-change event received", gp);
863 
864  rfContext *rfi = GET_PLUGIN_DATA(gp);
865 
866  if (rfi)
868 
869  new_owner = gtk_clipboard_get_owner(gtkClipboard);
870  if (new_owner != (GObject *)gp) {
871  /* To do: avoid this when the new owner is another remmina protocol widget of
872  * the same remmina application */
873  REMMINA_PLUGIN_DEBUG("gp=%p owner-change: new owner is different than me: new=%p me=%p",
874  gp, new_owner, gp);
875 
876  REMMINA_PLUGIN_DEBUG("gp=%p owner-change: new owner is not me: Sending local clipboard format list to server.",
877  gp, new_owner, gp);
880  rdp_event.clipboard_formatlist.pFormatList = pFormatList;
881  remmina_rdp_event_event_push(gp, &rdp_event);
882  } else {
883  REMMINA_PLUGIN_DEBUG(" ... but I'm the owner!");
884  }
885  return TRUE;
886 }
887 
889 {
890  TRACE_CALL(__func__);
891  gchar *s;
892  gint flags;
893  rfContext *rfi = GET_PLUGIN_DATA(gp);
894  GtkClipboard *clipboard;
895  RemminaFile *remminafile;
896 
897  gboolean disable_smooth_scrolling = FALSE;
898 
899  if (!rfi) return;
901 
902  /* we get first the global preferences */
903  s = remmina_plugin_service->pref_get_value("rdp_disable_smooth_scrolling");
904  disable_smooth_scrolling = (s && s[0] == '1' ? TRUE : FALSE);
905  g_free(s), s = NULL;
906  /* Otherwise we use the connection profile specific setting */
907  disable_smooth_scrolling = remmina_plugin_service->file_get_int(remminafile, "disable-smooth-scrolling", disable_smooth_scrolling);
908 
909  REMMINA_PLUGIN_DEBUG("Disable smooth scrolling is set to %d", disable_smooth_scrolling);
910 
911  rfi->drawing_area = gtk_drawing_area_new();
912  gtk_widget_show(rfi->drawing_area);
913  gtk_container_add(GTK_CONTAINER(gp), rfi->drawing_area);
914 
915  gtk_widget_add_events(rfi->drawing_area, GDK_POINTER_MOTION_MASK
916  | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
917  | GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK
918  | GDK_SCROLL_MASK | GDK_FOCUS_CHANGE_MASK);
919 
920  if (!disable_smooth_scrolling) {
921  REMMINA_PLUGIN_DEBUG("Adding GDK_SMOOTH_SCROLL_MASK");
922  gtk_widget_add_events(rfi->drawing_area, GDK_SMOOTH_SCROLL_MASK);
923  }
924 
925  gtk_widget_set_can_focus(rfi->drawing_area, TRUE);
926 
928 
929  s = remmina_plugin_service->pref_get_value("rdp_use_client_keymap");
930  rfi->use_client_keymap = (s && s[0] == '1' ? TRUE : FALSE);
931  g_free(s), s = NULL;
932 
933  /* Read special keymap from profile file, if exists */
935 
936  if (rfi->use_client_keymap && rfi->keymap)
937  fprintf(stderr, "RDP profile error: you cannot define both rdp_map_hardware_keycode and have 'Use client keyboard mapping' enabled\n");
938 
939  g_signal_connect(G_OBJECT(rfi->drawing_area), "draw",
940  G_CALLBACK(remmina_rdp_event_on_draw), gp);
941  g_signal_connect(G_OBJECT(rfi->drawing_area), "configure-event",
942  G_CALLBACK(remmina_rdp_event_on_configure), gp);
943  g_signal_connect(G_OBJECT(rfi->drawing_area), "motion-notify-event",
944  G_CALLBACK(remmina_rdp_event_on_motion), gp);
945  g_signal_connect(G_OBJECT(rfi->drawing_area), "button-press-event",
946  G_CALLBACK(remmina_rdp_event_on_button), gp);
947  g_signal_connect(G_OBJECT(rfi->drawing_area), "button-release-event",
948  G_CALLBACK(remmina_rdp_event_on_button), gp);
949  g_signal_connect(G_OBJECT(rfi->drawing_area), "scroll-event",
950  G_CALLBACK(remmina_rdp_event_on_scroll), gp);
951  g_signal_connect(G_OBJECT(rfi->drawing_area), "key-press-event",
952  G_CALLBACK(remmina_rdp_event_on_key), gp);
953  g_signal_connect(G_OBJECT(rfi->drawing_area), "key-release-event",
954  G_CALLBACK(remmina_rdp_event_on_key), gp);
955  g_signal_connect(G_OBJECT(rfi->drawing_area), "focus-in-event",
956  G_CALLBACK(remmina_rdp_event_on_focus_in), gp);
963  //g_signal_connect(G_OBJECT(gtk_widget_get_toplevel(rfi->drawing_area)), "map-event",
964  // G_CALLBACK(remmina_rdp_event_on_map), gp);
965  //g_signal_connect(G_OBJECT(gtk_widget_get_toplevel(rfi->drawing_area)), "unmap-event",
966  // G_CALLBACK(remmina_rdp_event_on_unmap), gp);
967 
968  if (!remmina_plugin_service->file_get_int(remminafile, "disableclipboard", FALSE)) {
969  clipboard = gtk_widget_get_clipboard(rfi->drawing_area, GDK_SELECTION_CLIPBOARD);
970  rfi->clipboard.clipboard_handler = g_signal_connect(clipboard, "owner-change", G_CALLBACK(remmina_rdp_event_on_clipboard), gp);
971  }
972 
973  rfi->pressed_keys = g_array_new(FALSE, TRUE, sizeof(RemminaPluginRdpEvent));
974  rfi->event_queue = g_async_queue_new_full(g_free);
975  rfi->ui_queue = g_async_queue_new();
976  pthread_mutex_init(&rfi->ui_queue_mutex, NULL);
977 
978  if (pipe(rfi->event_pipe)) {
979  g_print("Error creating pipes.\n");
980  rfi->event_pipe[0] = -1;
981  rfi->event_pipe[1] = -1;
982  rfi->event_handle = NULL;
983  } else {
984  flags = fcntl(rfi->event_pipe[0], F_GETFL, 0);
985  fcntl(rfi->event_pipe[0], F_SETFL, flags | O_NONBLOCK);
986  rfi->event_handle = CreateFileDescriptorEvent(NULL, FALSE, FALSE, rfi->event_pipe[0], WINPR_FD_READ);
987  if (!rfi->event_handle)
988  g_print("CreateFileDescriptorEvent() failed\n");
989  }
990 
991  rfi->object_table = g_hash_table_new_full(NULL, NULL, NULL, g_free);
992 
993  rfi->display = gdk_display_get_default();
994 
995 #if GTK_CHECK_VERSION(3, 22, 0)
996  GdkVisual *visual = gdk_screen_get_system_visual(
997  gdk_display_get_default_screen(rfi->display));
998  rfi->bpp = gdk_visual_get_depth(visual);
999 #else
1000  rfi->bpp = gdk_visual_get_best_depth();
1001 #endif
1002 }
1003 
1005 {
1006  TRACE_CALL(__func__);
1007 
1008  switch (obj->type) {
1010  free(obj->nocodec.bitmap);
1011  break;
1012 
1013  default:
1014  break;
1015  }
1016 
1017  g_free(obj);
1018 }
1019 
1021 {
1022  TRACE_CALL(__func__);
1023  rfContext *rfi = GET_PLUGIN_DATA(gp);
1025 
1026  if (!rfi) return;
1027 
1028  /* unregister the clipboard monitor */
1029  if (rfi->clipboard.clipboard_handler) {
1030  g_signal_handler_disconnect(G_OBJECT(gtk_widget_get_clipboard(rfi->drawing_area, GDK_SELECTION_CLIPBOARD)), rfi->clipboard.clipboard_handler);
1031  rfi->clipboard.clipboard_handler = 0;
1032  }
1033  if (rfi->delayed_monitor_layout_handler) {
1034  g_source_remove(rfi->delayed_monitor_layout_handler);
1036  }
1037  if (rfi->ui_handler) {
1038  g_source_remove(rfi->ui_handler);
1039  rfi->ui_handler = 0;
1040  }
1041  while ((ui = (RemminaPluginRdpUiObject *)g_async_queue_try_pop(rfi->ui_queue)) != NULL)
1043  if (rfi->surface) {
1044  cairo_surface_mark_dirty(rfi->surface);
1045  cairo_surface_destroy(rfi->surface);
1046  rfi->surface = NULL;
1047  }
1048 
1049  g_hash_table_destroy(rfi->object_table);
1050 
1051  g_array_free(rfi->pressed_keys, TRUE);
1052  if (rfi->keymap) {
1053  g_array_free(rfi->keymap, TRUE);
1054  rfi->keymap = NULL;
1055  }
1056  g_async_queue_unref(rfi->event_queue);
1057  rfi->event_queue = NULL;
1058  g_async_queue_unref(rfi->ui_queue);
1059  rfi->ui_queue = NULL;
1060  pthread_mutex_destroy(&rfi->ui_queue_mutex);
1061 
1062  if (rfi->event_handle) {
1063  CloseHandle(rfi->event_handle);
1064  rfi->event_handle = NULL;
1065  }
1066 
1067  close(rfi->event_pipe[0]);
1068  close(rfi->event_pipe[1]);
1069 }
1070 
1072 {
1073  int stride;
1074  rdpGdi *gdi;
1075 
1076  if (!rfi)
1077  return;
1078 
1079  gdi = ((rdpContext *)rfi)->gdi;
1080 
1081  if (!gdi)
1082  return;
1083 
1084  if (rfi->surface) {
1085  cairo_surface_mark_dirty(rfi->surface);
1086  cairo_surface_destroy(rfi->surface);
1087  rfi->surface = NULL;
1088  }
1089  stride = cairo_format_stride_for_width(rfi->cairo_format, gdi->width);
1090  rfi->surface = cairo_image_surface_create_for_data((unsigned char *)gdi->primary_buffer, rfi->cairo_format, gdi->width, gdi->height, stride);
1091  cairo_surface_flush(rfi->surface);
1092 }
1093 
1095 {
1096  TRACE_CALL(__func__);
1097  gint width, height;
1098  rdpGdi *gdi;
1099  rfContext *rfi = GET_PLUGIN_DATA(gp);
1100 
1103 
1104  gdi = ((rdpContext *)rfi)->gdi;
1105 
1107 
1108  /* See if we also must rellocate rfi->surface with different width and height,
1109  * this usually happens after a DesktopResize RDP event*/
1110 
1111  if (rfi->surface && (cairo_image_surface_get_width(rfi->surface) != gdi->width ||
1112  cairo_image_surface_get_height(rfi->surface) != gdi->height)) {
1113  /* Destroys and recreate rfi->surface with new width and height */
1114  cairo_surface_mark_dirty(rfi->surface);
1115  cairo_surface_destroy(rfi->surface);
1116  rfi->surface = NULL;
1118  } else if (rfi->surface == NULL) {
1120  }
1121 
1122  /* Send gdi->width and gdi->height obtained from remote server to gp plugin,
1123  * so they will be saved when closing connection */
1124  if (width != gdi->width)
1126  if (height != gdi->height)
1128 
1130 
1132  /* In scaled mode and autores mode, drawing_area will get its dimensions from its parent */
1133  gtk_widget_set_size_request(rfi->drawing_area, -1, -1);
1134  else
1135  /* In non scaled mode, the plugins forces dimensions of drawing area */
1136  gtk_widget_set_size_request(rfi->drawing_area, width, height);
1138 }
1139 
1141 {
1142  TRACE_CALL(__func__);
1143  rfContext *rfi = GET_PLUGIN_DATA(gp);
1144  rdpGdi *gdi;
1145 
1146  gdi = ((rdpContext *)rfi)->gdi;
1147 
1148  gtk_widget_realize(rfi->drawing_area);
1149 
1151  gtk_widget_queue_draw_area(rfi->drawing_area, 0, 0, gdi->width, gdi->height);
1152 
1154 
1156  const gchar *host = freerdp_settings_get_string (rfi->settings, FreeRDP_ServerHostname);
1157  // TRANSLATORS: the placeholder may be either an IP/FQDN or a server hostname
1158  REMMINA_PLUGIN_AUDIT(_("Connected to %s via RDP"), host);
1159 }
1160 
1162 {
1163  TRACE_CALL(__func__);
1164  rfContext *rfi = GET_PLUGIN_DATA(gp);
1165 
1166  gdk_window_invalidate_rect(gtk_widget_get_window(rfi->drawing_area), NULL, TRUE);
1167 }
1168 
1170 {
1171  TRACE_CALL(__func__);
1172  GdkPixbuf *pixbuf;
1173  rfContext *rfi = GET_PLUGIN_DATA(gp);
1174  rdpPointer *pointer = (rdpPointer *)ui->cursor.pointer;
1175  cairo_surface_t *surface;
1176  UINT8 *data = malloc(pointer->width * pointer->height * 4);
1177 
1178  if (!freerdp_image_copy_from_pointer_data(
1179  (BYTE *)data, PIXEL_FORMAT_BGRA32,
1180  pointer->width * 4, 0, 0, pointer->width, pointer->height,
1181  pointer->xorMaskData, pointer->lengthXorMask,
1182  pointer->andMaskData, pointer->lengthAndMask,
1183  pointer->xorBpp, &(ui->cursor.context->gdi->palette))) {
1184  free(data);
1185  return FALSE;
1186  }
1187 
1188  surface = cairo_image_surface_create_for_data(data, CAIRO_FORMAT_ARGB32, pointer->width, pointer->height, cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, pointer->width));
1189  cairo_surface_flush(surface);
1190  pixbuf = gdk_pixbuf_get_from_surface(surface, 0, 0, pointer->width, pointer->height);
1191  cairo_surface_mark_dirty(surface);
1192  cairo_surface_destroy(surface);
1193  free(data);
1194  ((rfPointer *)ui->cursor.pointer)->cursor = gdk_cursor_new_from_pixbuf(rfi->display, pixbuf, pointer->xPos, pointer->yPos);
1195  g_object_unref(pixbuf);
1196 
1197  return TRUE;
1198 }
1199 
1201 {
1202  TRACE_CALL(__func__);
1203  g_object_unref(ui->cursor.pointer->cursor);
1204  ui->cursor.pointer->cursor = NULL;
1205 }
1206 
1208 {
1209  TRACE_CALL(__func__);
1210  GdkWindow *w, *nw;
1211  gint nx, ny, wx, wy;
1212 
1213 #if GTK_CHECK_VERSION(3, 20, 0)
1214  GdkSeat *seat;
1215 #else
1216  GdkDeviceManager *manager;
1217 #endif
1218  GdkDevice *dev;
1219  rfContext *rfi = GET_PLUGIN_DATA(gp);
1220 
1221  if (rfi == NULL)
1222  return FALSE;
1223 
1224  w = gtk_widget_get_window(rfi->drawing_area);
1225 #if GTK_CHECK_VERSION(3, 20, 0)
1226  seat = gdk_display_get_default_seat(gdk_display_get_default());
1227  dev = gdk_seat_get_pointer(seat);
1228 #else
1229  manager = gdk_display_get_device_manager(gdk_display_get_default());
1230  dev = gdk_device_manager_get_client_pointer(manager);
1231 #endif
1232 
1233  nw = gdk_device_get_window_at_position(dev, NULL, NULL);
1234 
1235  if (nw == w) {
1236  nx = 0;
1237  ny = 0;
1239  gdk_window_get_root_coords(w, nx, ny, &wx, &wy);
1240  gdk_device_warp(dev, gdk_window_get_screen(w), wx, wy);
1241  }
1242  return TRUE;
1243 }
1244 
1246 {
1247  TRACE_CALL(__func__);
1248  rfContext *rfi = GET_PLUGIN_DATA(gp);
1249 
1250  switch (ui->cursor.type) {
1252  ui->retval = remmina_rdp_event_create_cursor(gp, ui) ? 1 : 0;
1253  break;
1254 
1257  break;
1258 
1260  gdk_window_set_cursor(gtk_widget_get_window(rfi->drawing_area), ui->cursor.pointer->cursor);
1261  ui->retval = 1;
1262  break;
1263 
1265  ui->retval = remmina_rdp_event_set_pointer_position(gp, ui->pos.x, ui->pos.y) ? 1 : 0;
1266  break;
1267 
1269  gdk_window_set_cursor(gtk_widget_get_window(rfi->drawing_area),
1270  gdk_cursor_new_for_display(gdk_display_get_default(),
1271  GDK_BLANK_CURSOR));
1272  ui->retval = 1;
1273  break;
1274 
1276  gdk_window_set_cursor(gtk_widget_get_window(rfi->drawing_area), NULL);
1277  ui->retval = 1;
1278  break;
1279  }
1280 }
1281 
1283 {
1284  TRACE_CALL(__func__);
1286 }
1287 
1289 {
1290  TRACE_CALL(__func__);
1291  rfContext *rfi = GET_PLUGIN_DATA(gp);
1292 
1293  if (!rfi || !rfi->connected || rfi->is_reconnecting)
1294  return;
1296 }
1297 
1299 {
1300  TRACE_CALL(__func__);
1301  rfContext *rfi = GET_PLUGIN_DATA(gp);
1302 
1303  cairo_surface_mark_dirty(rfi->surface);
1304  cairo_surface_destroy(rfi->surface);
1305  rfi->surface = NULL;
1306 }
1307 
1309 {
1310  TRACE_CALL(__func__);
1311  switch (ui->event.type) {
1314  break;
1317  break;
1318  }
1319 }
1320 
1322 {
1323  TRACE_CALL(__func__);
1324  switch (ui->type) {
1327  break;
1328 
1331  break;
1332 
1335  break;
1336 
1337  case REMMINA_RDP_UI_CURSOR:
1338  remmina_rdp_event_cursor(gp, ui);
1339  break;
1340 
1343  break;
1344 
1345  case REMMINA_RDP_UI_EVENT:
1347  break;
1348 
1349  default:
1350  break;
1351  }
1352 }
1353 
1355 {
1356  TRACE_CALL(__func__);
1357 
1358  rfContext *rfi = GET_PLUGIN_DATA(gp);
1360 
1361  pthread_mutex_lock(&rfi->ui_queue_mutex);
1362  ui = (RemminaPluginRdpUiObject *)g_async_queue_try_pop(rfi->ui_queue);
1363  if (ui) {
1364  pthread_mutex_lock(&ui->sync_wait_mutex);
1365  if (!rfi->thread_cancelled)
1367  // Should we signal the caller thread to unlock ?
1368  if (ui->sync) {
1369  ui->complete = TRUE;
1370  pthread_cond_signal(&ui->sync_wait_cond);
1371  pthread_mutex_unlock(&ui->sync_wait_mutex);
1372  } else {
1374  }
1375 
1376  pthread_mutex_unlock(&rfi->ui_queue_mutex);
1377  return TRUE;
1378  } else {
1379  rfi->ui_handler = 0;
1380  pthread_mutex_unlock(&rfi->ui_queue_mutex);
1381  return FALSE;
1382  }
1383 }
1384 
1386 {
1387  TRACE_CALL(__func__);
1388  rfContext *rfi = GET_PLUGIN_DATA(gp);
1389  gboolean ui_sync_save;
1390  int oldcanceltype;
1391 
1392  if (!rfi || rfi->thread_cancelled)
1393  return;
1394 
1397  return;
1398  }
1399 
1400  pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &oldcanceltype);
1401 
1402  pthread_mutex_lock(&rfi->ui_queue_mutex);
1403 
1404  ui_sync_save = ui->sync;
1405  ui->complete = FALSE;
1406 
1407  if (ui_sync_save) {
1408  pthread_mutex_init(&ui->sync_wait_mutex, NULL);
1409  pthread_cond_init(&ui->sync_wait_cond, NULL);
1410  }
1411 
1412  ui->complete = FALSE;
1413 
1414  g_async_queue_push(rfi->ui_queue, ui);
1415 
1416  if (!rfi->ui_handler)
1417  rfi->ui_handler = IDLE_ADD((GSourceFunc)remmina_rdp_event_process_ui_queue, gp);
1418 
1419  if (ui_sync_save) {
1420  /* Wait for main thread function completion before returning */
1421  pthread_mutex_lock(&ui->sync_wait_mutex);
1422  pthread_mutex_unlock(&rfi->ui_queue_mutex);
1423  while (!ui->complete)
1424  pthread_cond_wait(&ui->sync_wait_cond, &ui->sync_wait_mutex);
1425  pthread_cond_destroy(&ui->sync_wait_cond);
1426  pthread_mutex_destroy(&ui->sync_wait_mutex);
1427  } else {
1428  pthread_mutex_unlock(&rfi->ui_queue_mutex);
1429  }
1430  pthread_setcanceltype(oldcanceltype, NULL);
1431 }
1432 
1434 {
1435  ui->sync = FALSE;
1437 }
1438 
1440 {
1441  TRACE_CALL(__func__);
1442  int retval;
1443 
1444  ui->sync = TRUE;
1446  retval = ui->retval;
1448  return retval;
1449 }
1450 
1452 {
1453  TRACE_CALL(__func__);
1454  void *rp;
1455 
1456  ui->sync = TRUE;
1458  rp = ui->retptr;
1460  return rp;
1461 }
struct remmina_plugin_rdp_event::@42::@45 mouse_event
gboolean thread_cancelled
Definition: rdp_plugin.h:332
static void remmina_rdp_event_free_cursor(RemminaProtocolWidget *gp, RemminaPluginRdpUiObject *ui)
Definition: rdp_event.c:1200
gboolean is_reconnecting
Definition: rdp_plugin.h:344
gulong clipboard_handler
Definition: rdp_plugin.h:140
static void remmina_rdp_event_create_cairo_surface(rfContext *rfi)
Definition: rdp_event.c:1071
RemminaScaleMode(* remmina_protocol_widget_get_current_scale_mode)(RemminaProtocolWidget *gp)
Definition: plugin.h:172
static void remmina_rdp_event_process_event(RemminaProtocolWidget *gp, RemminaPluginRdpUiObject *ui)
Definition: rdp_event.c:1308
guint delayed_monitor_layout_handler
Definition: rdp_plugin.h:360
struct remmina_plugin_rdp_ui_object::@50::@53 cursor
static void remmina_rdp_event_update_scale_factor(RemminaProtocolWidget *gp)
Definition: rdp_event.c:312
gint event_pipe[2]
Definition: rdp_plugin.h:381
gdouble scale_y
Definition: rdp_plugin.h:359
gboolean remmina_rdp_event_on_unmap(RemminaProtocolWidget *gp)
Definition: rdp_event.c:74
typedefG_BEGIN_DECLS struct _RemminaFile RemminaFile
Definition: types.h:44
cairo_surface_t * surface
Definition: rdp_plugin.h:366
gboolean rdpgfxchan
Definition: rdp_plugin.h:341
struct remmina_plugin_rdp_ui_object::@50::@52 reg
void(* protocol_plugin_set_width)(RemminaProtocolWidget *gp, gint width)
Definition: plugin.h:169
guint ui_handler
Definition: rdp_plugin.h:377
void remmina_rdp_settings_get_orientation_scale_prefs(int *desktopOrientation, int *desktopScaleFactor, int *deviceScaleFactor)
Definition: rdp_settings.c:707
void remmina_rdp_clipboard_abort_client_format_data_request(rfContext *rfi)
Definition: rdp_cliprdr.c:947
GdkDisplay * display
Definition: rdp_plugin.h:364
void * remmina_rdp_event_queue_ui_sync_retptr(RemminaProtocolWidget *gp, RemminaPluginRdpUiObject *ui)
Definition: rdp_event.c:1451
struct remmina_plugin_rdp_event::@42::@49 monitor_layout
GAsyncQueue * ui_queue
Definition: rdp_plugin.h:375
void remmina_rdp_event_update_rect(RemminaProtocolWidget *gp, gint x, gint y, gint w, gint h)
Definition: rdp_event.c:301
pthread_mutex_t sync_wait_mutex
Definition: rdp_plugin.h:269
int reconnect_nattempt
Definition: rdp_plugin.h:352
static gboolean remmina_rdp_event_process_ui_queue(RemminaProtocolWidget *gp)
Definition: rdp_event.c:1354
static BOOL remmina_rdp_event_create_cursor(RemminaProtocolWidget *gp, RemminaPluginRdpUiObject *ui)
Definition: rdp_event.c:1169
static void remmina_rdp_event_release_key(RemminaProtocolWidget *gp, RemminaPluginRdpEvent rdp_event)
Definition: rdp_event.c:195
static void remmina_rdp_event_queue_ui(RemminaProtocolWidget *gp, RemminaPluginRdpUiObject *ui)
Definition: rdp_event.c:1385
gint(* protocol_plugin_get_width)(RemminaProtocolWidget *gp)
Definition: plugin.h:168
gdouble scale_x
Definition: rdp_plugin.h:358
gint(* file_get_int)(RemminaFile *remminafile, const gchar *setting, gint default_value)
Definition: plugin.h:222
static gboolean remmina_rdp_event_on_key(GtkWidget *widget, GdkEventKey *event, RemminaProtocolWidget *gp)
Definition: rdp_event.c:742
void remmina_rdp_event_free_event(RemminaProtocolWidget *gp, RemminaPluginRdpUiObject *obj)
Definition: rdp_event.c:1004
static gboolean remmina_rdp_event_on_motion(GtkWidget *widget, GdkEventMotion *event, RemminaProtocolWidget *gp)
Definition: rdp_event.c:561
void(* protocol_plugin_update_align)(RemminaProtocolWidget *gp)
Definition: plugin.h:187
void(* protocol_plugin_signal_connection_opened)(RemminaProtocolWidget *gp)
Definition: plugin.h:186
void remmina_rdp_event_process_clipboard(RemminaProtocolWidget *gp, RemminaPluginRdpUiObject *ui)
Definition: rdp_cliprdr.c:914
int reconnect_maxattempts
Definition: rdp_plugin.h:351
static void remmina_rdp_ui_event_update_scale(RemminaProtocolWidget *gp, RemminaPluginRdpUiObject *ui)
Definition: rdp_event.c:1282
static gboolean remmina_rdp_event_on_focus_in(GtkWidget *widget, GdkEventKey *event, RemminaProtocolWidget *gp)
Definition: rdp_event.c:104
RemminaPluginRdpEventType type
Definition: rdp_plugin.h:191
void remmina_rdp_event_update_regions(RemminaProtocolWidget *gp, RemminaPluginRdpUiObject *ui)
Definition: rdp_event.c:281
static gboolean remmina_rdp_event_delayed_monitor_layout(RemminaProtocolWidget *gp)
Definition: rdp_event.c:392
static RemminaPluginService * remmina_plugin_service
unsigned translated_keycode
Definition: rdp_plugin.h:316
GtkWidget * drawing_area
Definition: rdp_plugin.h:355
cairo_format_t cairo_format
Definition: rdp_plugin.h:367
static gboolean remmina_rdp_event_on_configure(GtkWidget *widget, GdkEventConfigure *event, RemminaProtocolWidget *gp)
Definition: rdp_event.c:498
int remmina_rdp_event_queue_ui_sync_retint(RemminaProtocolWidget *gp, RemminaPluginRdpUiObject *ui)
Definition: rdp_event.c:1439
void remmina_rdp_event_init(RemminaProtocolWidget *gp)
Definition: rdp_event.c:888
gboolean use_client_keymap
Definition: rdp_plugin.h:361
static gboolean remmina_rdp_event_on_scroll(GtkWidget *widget, GdkEventScroll *event, RemminaProtocolWidget *gp)
Definition: rdp_event.c:652
gchar *(* pref_get_value)(const gchar *key)
Definition: plugin.h:227
static void remmina_rdp_event_reconnect_progress(RemminaProtocolWidget *gp, RemminaPluginRdpUiObject *ui)
Definition: rdp_event.c:1161
GArray * keymap
Definition: rdp_plugin.h:386
unsigned orig_keycode
Definition: rdp_plugin.h:315
static void remmina_rdp_event_scale_area(RemminaProtocolWidget *gp, gint *x, gint *y, gint *w, gint *h)
Definition: rdp_event.c:236
void remmina_rdp_event_queue_ui_async(RemminaProtocolWidget *gp, RemminaPluginRdpUiObject *ui)
Definition: rdp_event.c:1433
static gboolean remmina_rdp_event_on_button(GtkWidget *widget, GdkEventButton *event, RemminaProtocolWidget *gp)
Definition: rdp_event.c:581
struct remmina_plugin_rdp_event::@42::@44 key_event
struct remmina_plugin_rdp_event::@42::@46 clipboard_formatlist
gint(* protocol_plugin_get_height)(RemminaProtocolWidget *gp)
Definition: plugin.h:170
void remmina_rdp_event_send_delayed_monitor_layout(RemminaProtocolWidget *gp)
Definition: rdp_event.c:483
GAsyncQueue * event_queue
Definition: rdp_plugin.h:380
static void remmina_rdp_event_release_all_keys(RemminaProtocolWidget *gp)
Definition: rdp_event.c:174
CLIPRDR_FORMAT_LIST * remmina_rdp_cliprdr_get_client_format_list(RemminaProtocolWidget *gp)
Definition: rdp_cliprdr.c:709
pthread_cond_t sync_wait_cond
Definition: rdp_plugin.h:270
rfClipboard clipboard
Definition: rdp_plugin.h:384
static void remmina_rdp_event_init_keymap(rfContext *rfi, const gchar *strmap)
Definition: rdp_event.c:708
HANDLE event_handle
Definition: rdp_plugin.h:382
static void remmina_rdp_event_translate_pos(RemminaProtocolWidget *gp, int ix, int iy, UINT16 *ox, UINT16 *oy)
Definition: rdp_event.c:517
static void remmina_rdp_ui_event_destroy_cairo_surface(RemminaProtocolWidget *gp, RemminaPluginRdpUiObject *ui)
Definition: rdp_event.c:1298
void remmina_rdp_event_uninit(RemminaProtocolWidget *gp)
Definition: rdp_event.c:1020
RemminaFile *(* protocol_plugin_get_file)(RemminaProtocolWidget *gp)
Definition: plugin.h:178
GHashTable * object_table
Definition: rdp_plugin.h:373
gboolean remmina_rdp_event_on_clipboard(GtkClipboard *gtkClipboard, GdkEvent *event, RemminaProtocolWidget *gp)
Definition: rdp_event.c:849
static void remmina_rdp_event_reverse_translate_pos_reverse(RemminaProtocolWidget *gp, int ix, int iy, int *ox, int *oy)
Definition: rdp_event.c:539
static void remmina_rdp_event_process_ui_event(RemminaProtocolWidget *gp, RemminaPluginRdpUiObject *ui)
Definition: rdp_event.c:1321
static void keypress_list_add(RemminaProtocolWidget *gp, RemminaPluginRdpEvent rdp_event)
Definition: rdp_event.c:221
gboolean connected
Definition: rdp_plugin.h:343
RemminaScaleMode scale
Definition: rdp_plugin.h:330
freerdp * instance
Definition: rdp_plugin.h:327
DispClientContext * dispcontext
Definition: rdp_plugin.h:335
static BOOL remmina_rdp_event_set_pointer_position(RemminaProtocolWidget *gp, gint x, gint y)
Definition: rdp_event.c:1207
void(* protocol_plugin_register_hostkey)(RemminaProtocolWidget *gp, GtkWidget *widget)
Definition: plugin.h:180
static void remmina_rdp_event_connected(RemminaProtocolWidget *gp, RemminaPluginRdpUiObject *ui)
Definition: rdp_event.c:1140
GArray * pressed_keys
Definition: rdp_plugin.h:379
void remmina_rdp_event_update_scale(RemminaProtocolWidget *gp)
Definition: rdp_event.c:1094
rdpSettings * settings
Definition: rdp_plugin.h:326
gint scale_height
Definition: rdp_plugin.h:357
void(* protocol_plugin_set_height)(RemminaProtocolWidget *gp, gint height)
Definition: plugin.h:171
Definition: rdp_plugin.h:314
void remmina_rdp_event_unfocus(RemminaProtocolWidget *gp)
Definition: rdp_event.c:1288
struct remmina_plugin_rdp_ui_object::@50::@55 nocodec
struct remmina_plugin_rdp_ui_object::@50::@57 event
void remmina_rdp_monitor_get(rfContext *rfi, gchar **monitorids, guint32 *maxwidth, guint32 *maxheight)
Set the MonitorIDs, the maxwidth and maxheight.
Definition: rdp_monitor.c:68
void remmina_rdp_event_event_push(RemminaProtocolWidget *gp, const RemminaPluginRdpEvent *e)
Definition: rdp_event.c:150
static void remmina_rdp_event_cursor(RemminaProtocolWidget *gp, RemminaPluginRdpUiObject *ui)
Definition: rdp_event.c:1245
gboolean remmina_rdp_event_on_map(RemminaProtocolWidget *gp)
Definition: rdp_event.c:52
struct remmina_plugin_rdp_ui_object::@50::@58 pos
gboolean(* is_main_thread)(void)
Definition: plugin.h:250
static gboolean remmina_rdp_event_on_draw(GtkWidget *widget, cairo_t *context, RemminaProtocolWidget *gp)
Definition: rdp_event.c:343
gint scale_width
Definition: rdp_plugin.h:356
pthread_mutex_t ui_queue_mutex
Definition: rdp_plugin.h:376
RemminaPluginRdpUiType type
Definition: rdp_plugin.h:266