Remmina - The GTK+ Remote Desktop Client  v1.4.33
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  rdp_event_2.key_event.extended1 == rdp_event.key_event.extended1) {
215  g_array_remove_index_fast(rfi->pressed_keys, i);
216  break;
217  }
218  }
219  }
220 }
221 
223 {
224  TRACE_CALL(__func__);
225  rfContext *rfi = GET_PLUGIN_DATA(gp);
226 
227  if (!rdp_event.key_event.key_code)
228  return;
229 
230  if (rdp_event.key_event.up)
231  remmina_rdp_event_release_key(gp, rdp_event);
232  else
233  g_array_append_val(rfi->pressed_keys, rdp_event);
234 }
235 
236 
237 static void remmina_rdp_event_scale_area(RemminaProtocolWidget *gp, gint *x, gint *y, gint *w, gint *h)
238 {
239  TRACE_CALL(__func__);
240  gint width, height;
241  gint sx, sy, sw, sh;
242  rfContext *rfi = GET_PLUGIN_DATA(gp);
243 
244  if (!rfi || !rfi->connected || rfi->is_reconnecting || !rfi->surface)
245  return;
246 
249 
250  if ((width == 0) || (height == 0))
251  return;
252 
253  if ((rfi->scale_width == width) && (rfi->scale_height == height)) {
254  /* Same size, just copy the pixels */
255  *x = MIN(MAX(0, *x), width - 1);
256  *y = MIN(MAX(0, *y), height - 1);
257  *w = MIN(width - *x, *w);
258  *h = MIN(height - *y, *h);
259  return;
260  }
261 
262  /* We have to extend the scaled region one scaled pixel, to avoid gaps */
263 
264  sx = MIN(MAX(0, (*x) * rfi->scale_width / width
265  - rfi->scale_width / width - 2), rfi->scale_width - 1);
266 
267  sy = MIN(MAX(0, (*y) * rfi->scale_height / height
268  - rfi->scale_height / height - 2), rfi->scale_height - 1);
269 
270  sw = MIN(rfi->scale_width - sx, (*w) * rfi->scale_width / width
271  + rfi->scale_width / width + 4);
272 
273  sh = MIN(rfi->scale_height - sy, (*h) * rfi->scale_height / height
274  + rfi->scale_height / height + 4);
275 
276  *x = sx;
277  *y = sy;
278  *w = sw;
279  *h = sh;
280 }
281 
283 {
284  TRACE_CALL(__func__);
285  rfContext *rfi = GET_PLUGIN_DATA(gp);
286  gint x, y, w, h, i;
287 
288  for (i = 0; i < ui->reg.ninvalid; i++) {
289  x = ui->reg.ureg[i].x;
290  y = ui->reg.ureg[i].y;
291  w = ui->reg.ureg[i].w;
292  h = ui->reg.ureg[i].h;
293 
295  remmina_rdp_event_scale_area(gp, &x, &y, &w, &h);
296 
297  gtk_widget_queue_draw_area(rfi->drawing_area, x, y, w, h);
298  }
299  g_free(ui->reg.ureg);
300 }
301 
302 void remmina_rdp_event_update_rect(RemminaProtocolWidget *gp, gint x, gint y, gint w, gint h)
303 {
304  TRACE_CALL(__func__);
305  rfContext *rfi = GET_PLUGIN_DATA(gp);
306 
308  remmina_rdp_event_scale_area(gp, &x, &y, &w, &h);
309 
310  gtk_widget_queue_draw_area(rfi->drawing_area, x, y, w, h);
311 }
312 
314 {
315  TRACE_CALL(__func__);
316  GtkAllocation a;
317  gint rdwidth, rdheight;
318  gint gpwidth, gpheight;
319  rfContext *rfi = GET_PLUGIN_DATA(gp);
320 
321  gtk_widget_get_allocation(GTK_WIDGET(gp), &a);
322  gpwidth = a.width;
323  gpheight = a.height;
324 
326  if ((gpwidth > 1) && (gpheight > 1)) {
329 
330  rfi->scale_width = gpwidth;
331  rfi->scale_height = gpheight;
332 
333  rfi->scale_x = (gdouble)rfi->scale_width / (gdouble)rdwidth;
334  rfi->scale_y = (gdouble)rfi->scale_height / (gdouble)rdheight;
335  }
336  } else {
337  rfi->scale_width = 0;
338  rfi->scale_height = 0;
339  rfi->scale_x = 0;
340  rfi->scale_y = 0;
341  }
342 }
343 
344 static gboolean remmina_rdp_event_on_draw(GtkWidget *widget, cairo_t *context, RemminaProtocolWidget *gp)
345 {
346  TRACE_CALL(__func__);
347  rfContext *rfi = GET_PLUGIN_DATA(gp);
348  guint width, height;
349  gchar *msg;
350  cairo_text_extents_t extents;
351 
352  if (!rfi || !rfi->connected)
353  return FALSE;
354 
355 
356  if (rfi->is_reconnecting) {
357  /* FreeRDP is reconnecting, just show a message to the user */
358 
359  width = gtk_widget_get_allocated_width(widget);
360  height = gtk_widget_get_allocated_height(widget);
361 
362  /* Draw text */
363  msg = g_strdup_printf(_("Reconnection attempt %d of %d…"),
365 
366  cairo_select_font_face(context, "Sans", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD);
367  cairo_set_font_size(context, 24);
368  cairo_set_source_rgb(context, 0.9, 0.9, 0.9);
369  cairo_text_extents(context, msg, &extents);
370  cairo_move_to(context, (width - (extents.width + extents.x_bearing)) / 2, (height - (extents.height + extents.y_bearing)) / 2);
371  cairo_show_text(context, msg);
372  g_free(msg);
373  } else {
374  /* Standard drawing: We copy the surface from RDP */
375 
376  if (!rfi->surface)
377  return FALSE;
378 
380  cairo_scale(context, rfi->scale_x, rfi->scale_y);
381 
382  cairo_surface_flush(rfi->surface);
383  cairo_set_source_surface(context, rfi->surface, 0, 0);
384  cairo_surface_mark_dirty(rfi->surface);
385 
386  cairo_set_operator(context, CAIRO_OPERATOR_SOURCE); // Ignore alpha channel from FreeRDP
387  cairo_paint(context);
388  }
389 
390  return TRUE;
391 }
392 
394 {
395  TRACE_CALL(__func__);
396  rfContext *rfi = GET_PLUGIN_DATA(gp);
397 
398  RemminaPluginRdpEvent rdp_event = { 0 };
399  GtkAllocation a;
400  gint desktopOrientation, desktopScaleFactor, deviceScaleFactor;
401 
402  RemminaFile *remminafile;
403 
404  if (!rfi || !rfi->connected || rfi->is_reconnecting)
405  return FALSE;
406 
408 
410  return FALSE;
411 
413  gint gpwidth, gpheight, prevwidth, prevheight;
414 
415  gchar *monitorids = NULL;
416  guint32 maxwidth = 0;
417  guint32 maxheight = 0;
418 
419  remmina_rdp_monitor_get(rfi, &monitorids, &maxwidth, &maxheight);
420 
421  REMMINA_PLUGIN_DEBUG("Sending preconfigured monitor layout");
422  if (rfi->dispcontext && rfi->dispcontext->SendMonitorLayout) {
423  remmina_rdp_settings_get_orientation_scale_prefs(&desktopOrientation, &desktopScaleFactor, &deviceScaleFactor);
424  gtk_widget_get_allocation(GTK_WIDGET(gp), &a);
425  gpwidth = a.width;
426  gpheight = a.height;
429 
430  if ((gpwidth != prevwidth || gpheight != prevheight) && gpwidth >= 200 && gpheight >= 200) {
431  if (rfi->rdpgfxchan) {
432  /* Workaround for FreeRDP issue #5417 */
433  if (gpwidth < AVC_MIN_DESKTOP_WIDTH)
434  gpwidth = AVC_MIN_DESKTOP_WIDTH;
435  if (gpheight < AVC_MIN_DESKTOP_HEIGHT)
436  gpheight = AVC_MIN_DESKTOP_HEIGHT;
437  }
439  if (remmina_plugin_service->file_get_int(remminafile, "multimon", FALSE)) {
440  const rdpMonitor *base = freerdp_settings_get_pointer(rfi->settings, FreeRDP_MonitorDefArray);
441  for (gint i = 0; i < freerdp_settings_get_uint32(rfi->settings, FreeRDP_MonitorCount); ++i) {
442  const rdpMonitor *current = &base[i];
443  REMMINA_PLUGIN_DEBUG("Sending display layout n° %d", i);
444  rdp_event.monitor_layout.Flags = current->is_primary;
445  REMMINA_PLUGIN_DEBUG("EVNT MON LAYOUT - Flags: %i", rdp_event.monitor_layout.Flags);
446  rdp_event.monitor_layout.Left = current->x;
447  REMMINA_PLUGIN_DEBUG("EVNT MON LAYOUT - Left: %i", rdp_event.monitor_layout.Left);
448  rdp_event.monitor_layout.Top = current->y;
449  REMMINA_PLUGIN_DEBUG("EVNT MON LAYOUT - Top: %i", rdp_event.monitor_layout.Top);
450  rdp_event.monitor_layout.width = current->width;
451  REMMINA_PLUGIN_DEBUG("EVNT MON LAYOUT - width: %i", rdp_event.monitor_layout.width);
452  rdp_event.monitor_layout.height = current->height;
453  REMMINA_PLUGIN_DEBUG("EVNT MON LAYOUT - height: %i", rdp_event.monitor_layout.height);
454  rdp_event.monitor_layout.physicalWidth = current->attributes.physicalWidth;
455  REMMINA_PLUGIN_DEBUG("EVNT MON LAYOUT - physicalWidth: %i", rdp_event.monitor_layout.physicalWidth);
456  rdp_event.monitor_layout.physicalHeight = current->attributes.physicalHeight;
457  REMMINA_PLUGIN_DEBUG("EVNT MON LAYOUT - PhysicalHeight: %i", rdp_event.monitor_layout.physicalHeight);
458  if (current->attributes.orientation)
459  rdp_event.monitor_layout.desktopOrientation = current->attributes.orientation;
460  else
461  rdp_event.monitor_layout.desktopOrientation = rdp_event.monitor_layout.desktopOrientation;
462  REMMINA_PLUGIN_DEBUG("EVNT MON LAYOUT - desktopOrientation: %i", rdp_event.monitor_layout.desktopOrientation);
463  rdp_event.monitor_layout.desktopScaleFactor = rdp_event.monitor_layout.desktopScaleFactor;
464  REMMINA_PLUGIN_DEBUG("EVNT MON LAYOUT - ScaleFactorflag: %i", rdp_event.monitor_layout.desktopScaleFactor);
465  rdp_event.monitor_layout.deviceScaleFactor = rdp_event.monitor_layout.deviceScaleFactor;
466  }
467  remmina_rdp_event_event_push(gp, &rdp_event);
468  } else {
469  rdp_event.monitor_layout.width = gpwidth;
470  rdp_event.monitor_layout.height = gpheight;
471  rdp_event.monitor_layout.desktopOrientation = desktopOrientation;
472  rdp_event.monitor_layout.desktopScaleFactor = desktopScaleFactor;
473  rdp_event.monitor_layout.deviceScaleFactor = deviceScaleFactor;
474  remmina_rdp_event_event_push(gp, &rdp_event);
475  }
476  }
477  }
478 
479  g_free(monitorids);
480 
481  return FALSE;
482 }
483 
485 {
486  TRACE_CALL(__func__);
487  rfContext *rfi = GET_PLUGIN_DATA(gp);
488 
489  if (!rfi || !rfi->connected || rfi->is_reconnecting)
490  return;
492  g_source_remove(rfi->delayed_monitor_layout_handler);
494  }
496  rfi->delayed_monitor_layout_handler = g_timeout_add(500, (GSourceFunc)remmina_rdp_event_delayed_monitor_layout, gp);
497 }
498 
499 static gboolean remmina_rdp_event_on_configure(GtkWidget *widget, GdkEventConfigure *event, RemminaProtocolWidget *gp)
500 {
501  TRACE_CALL(__func__);
502  /* Called when gp changes its size or position */
503 
504  rfContext *rfi = GET_PLUGIN_DATA(gp);
505 
506  if (!rfi || !rfi->connected || rfi->is_reconnecting)
507  return FALSE;
508 
510 
511  /* If the scaler is not active, schedule a delayed remote resolution change */
513 
514 
515  return FALSE;
516 }
517 
518 static void remmina_rdp_event_translate_pos(RemminaProtocolWidget *gp, int ix, int iy, UINT16 *ox, UINT16 *oy)
519 {
520  TRACE_CALL(__func__);
521  rfContext *rfi = GET_PLUGIN_DATA(gp);
522 
523  /*
524  * Translate a position from local window coordinates (ix,iy) to
525  * RDP coordinates and put result on (*ox,*uy)
526  * */
527 
528  if (!rfi || !rfi->connected || rfi->is_reconnecting)
529  return;
530 
531  if ((rfi->scale == REMMINA_PROTOCOL_WIDGET_SCALE_MODE_SCALED) && (rfi->scale_width >= 1) && (rfi->scale_height >= 1)) {
532  *ox = (UINT16)(ix * remmina_plugin_service->protocol_plugin_get_width(gp) / rfi->scale_width);
533  *oy = (UINT16)(iy * remmina_plugin_service->protocol_plugin_get_height(gp) / rfi->scale_height);
534  } else {
535  *ox = (UINT16)ix;
536  *oy = (UINT16)iy;
537  }
538 }
539 
540 static void remmina_rdp_event_reverse_translate_pos_reverse(RemminaProtocolWidget *gp, int ix, int iy, int *ox, int *oy)
541 {
542  TRACE_CALL(__func__);
543  rfContext *rfi = GET_PLUGIN_DATA(gp);
544 
545  /*
546  * Translate a position from RDP coordinates (ix,iy) to
547  * local window coordinates and put result on (*ox,*uy)
548  * */
549 
550  if (!rfi || !rfi->connected || rfi->is_reconnecting)
551  return;
552 
553  if ((rfi->scale == REMMINA_PROTOCOL_WIDGET_SCALE_MODE_SCALED) && (rfi->scale_width >= 1) && (rfi->scale_height >= 1)) {
556  } else {
557  *ox = ix;
558  *oy = iy;
559  }
560 }
561 
563  TRACE_CALL(__func__);
564  RemminaPluginRdpEvent rdp_event = { 0 };
565  RemminaFile *remminafile;
566  rfContext *rfi = GET_PLUGIN_DATA(gp);
567 
569  if (remmina_plugin_service->file_get_int(remminafile, "viewonly", FALSE))
570  return;
571 
573  rdp_event.mouse_event.flags = PTR_FLAGS_MOVE;
574  rdp_event.mouse_event.extended = FALSE;
575  rdp_event.mouse_event.x = rfi->last_x;
576  rdp_event.mouse_event.y = rfi->last_y;
577  remmina_rdp_event_event_push(gp, &rdp_event);
578 }
579 
580 static gboolean remmina_rdp_event_on_motion(GtkWidget *widget, GdkEventMotion *event, RemminaProtocolWidget *gp)
581 {
582  TRACE_CALL(__func__);
583  RemminaPluginRdpEvent rdp_event = { 0 };
584  RemminaFile *remminafile;
585  rfContext *rfi = GET_PLUGIN_DATA(gp);
586 
588  if (remmina_plugin_service->file_get_int(remminafile, "viewonly", FALSE))
589  return FALSE;
590 
592  rdp_event.mouse_event.flags = PTR_FLAGS_MOVE;
593  rdp_event.mouse_event.extended = FALSE;
594 
595  remmina_rdp_event_translate_pos(gp, event->x, event->y, &rdp_event.mouse_event.x, &rdp_event.mouse_event.y);
596  if (rfi != NULL){
597  rfi->last_x = rdp_event.mouse_event.x;
598  rfi->last_y = rdp_event.mouse_event.y;
599  }
600 
601  remmina_rdp_event_event_push(gp, &rdp_event);
602 
603  return TRUE;
604 }
605 
606 static gboolean remmina_rdp_event_on_button(GtkWidget *widget, GdkEventButton *event, RemminaProtocolWidget *gp)
607 {
608  TRACE_CALL(__func__);
609  gint flag;
610  gboolean extended = FALSE;
611  RemminaPluginRdpEvent rdp_event = { 0 };
612  gint primary, secondary;
613 
614  RemminaFile *remminafile;
615 
617  if (remmina_plugin_service->file_get_int(remminafile, "viewonly", FALSE))
618  return FALSE;
619 
620  /* We bypass 2button-press and 3button-press events */
621  if ((event->type != GDK_BUTTON_PRESS) && (event->type != GDK_BUTTON_RELEASE))
622  return TRUE;
623 
624  flag = 0;
625 
626  if (remmina_plugin_service->file_get_int(remminafile, "left-handed", FALSE)) {
627  primary = PTR_FLAGS_BUTTON2;
628  secondary = PTR_FLAGS_BUTTON1;
629  } else {
630  primary = PTR_FLAGS_BUTTON1;
631  secondary = PTR_FLAGS_BUTTON2;
632  }
633 
634  switch (event->button) {
635  case 1:
636  flag |= primary;
637  break;
638  case 2:
639  flag |= PTR_FLAGS_BUTTON3;
640  break;
641  case 3:
642  flag |= secondary;
643  break;
644  case 8: /* back */
645  case 97: /* Xming */
646  extended = TRUE;
647  flag |= PTR_XFLAGS_BUTTON1;
648  break;
649  case 9: /* forward */
650  case 112: /* Xming */
651  extended = TRUE;
652  flag |= PTR_XFLAGS_BUTTON2;
653  break;
654  default:
655  return FALSE;
656  }
657 
658  if (event->type == GDK_BUTTON_PRESS) {
659  if (extended)
660  flag |= PTR_XFLAGS_DOWN;
661  else
662  flag |= PTR_FLAGS_DOWN;
663  }
664 
666  remmina_rdp_event_translate_pos(gp, event->x, event->y, &rdp_event.mouse_event.x, &rdp_event.mouse_event.y);
667 
668  if (flag != 0) {
669  rdp_event.mouse_event.flags = flag;
670  rdp_event.mouse_event.extended = extended;
671  remmina_rdp_event_event_push(gp, &rdp_event);
672  }
673 
674  return TRUE;
675 }
676 
677 static gboolean remmina_rdp_event_on_scroll(GtkWidget *widget, GdkEventScroll *event, RemminaProtocolWidget *gp)
678 {
679  TRACE_CALL(__func__);
680  gint flag;
681  RemminaPluginRdpEvent rdp_event = { 0 };
682  float windows_delta;
683  RemminaFile *remminafile;
684 
686  if (remmina_plugin_service->file_get_int(remminafile, "viewonly", FALSE))
687  return FALSE;
688 
689  flag = 0;
691 
692  /* See [MS-RDPBCGR] TS_POINTER_EVENT and WM_MOUSEWHEEL message */
693 
694  switch (event->direction) {
695  case GDK_SCROLL_UP:
696  flag = PTR_FLAGS_WHEEL | 0x0078; // 120 is one scroll unit defined in WM_MOUSEWHEEL
697  break;
698 
699  case GDK_SCROLL_DOWN:
700  flag = PTR_FLAGS_WHEEL | 0x0188; // -120 (one scroll unit) in 9 bits two's complement
701  break;
702 
703 #if GTK_CHECK_VERSION(3, 4, 0)
704  case GDK_SCROLL_SMOOTH:
705 
706  if (event->delta_y == 0.0)
707  return FALSE;
708 
709  windows_delta = event->delta_y * -120;
710 
711  if (windows_delta > 255)
712  windows_delta = 255;
713  if (windows_delta < -256)
714  windows_delta = -256;
715 
716  flag = PTR_FLAGS_WHEEL | ((short)windows_delta & WheelRotationMask);
717 
718  break;
719 #endif
720 
721  default:
722  return FALSE;
723  }
724 
725  rdp_event.mouse_event.flags = flag;
726  rdp_event.mouse_event.extended = FALSE;
727  remmina_rdp_event_translate_pos(gp, event->x, event->y, &rdp_event.mouse_event.x, &rdp_event.mouse_event.y);
728  remmina_rdp_event_event_push(gp, &rdp_event);
729 
730  return TRUE;
731 }
732 
733 static void remmina_rdp_event_init_keymap(rfContext *rfi, const gchar *strmap)
734 {
735  long int v1, v2;
736  const char *s;
737  char *endptr;
739 
740  if (strmap == NULL || strmap[0] == 0) {
741  rfi->keymap = NULL;
742  return;
743  }
744  s = strmap;
745  rfi->keymap = g_array_new(FALSE, TRUE, sizeof(RemminaPluginRdpKeymapEntry));
746  while (1) {
747  v1 = strtol(s, &endptr, 10);
748  if (endptr == s) break;
749  s = endptr;
750  if (*s != ':') break;
751  s++;
752  v2 = strtol(s, &endptr, 10);
753  if (endptr == s) break;
754  s = endptr;
755  ke.orig_keycode = v1 & 0x7fffffff;
756  ke.translated_keycode = v2 & 0x7fffffff;
757  g_array_append_val(rfi->keymap, ke);
758  if (*s != ',') break;
759  s++;
760  }
761  if (rfi->keymap->len == 0) {
762  g_array_unref(rfi->keymap);
763  rfi->keymap = NULL;
764  }
765 }
766 
767 static gboolean remmina_rdp_event_on_key(GtkWidget *widget, GdkEventKey *event, RemminaProtocolWidget *gp)
768 {
769  TRACE_CALL(__func__);
770  guint32 unicode_keyval;
771  guint16 hardware_keycode;
772  rfContext *rfi = GET_PLUGIN_DATA(gp);
773  RemminaPluginRdpEvent rdp_event;
775  RemminaFile *remminafile;
776  DWORD scancode = 0;
777  int ik;
778 
779  if (!rfi || !rfi->connected || rfi->is_reconnecting)
780  return FALSE;
781 
783  if (remmina_plugin_service->file_get_int(remminafile, "viewonly", FALSE))
784  return FALSE;
785 
786 #ifdef ENABLE_GTK_INSPECTOR_KEY
787  /* GTK inspector key is propagated up. Disabled by default.
788  * enable it by defining ENABLE_GTK_INSPECTOR_KEY */
789  if ((event->state & GDK_CONTROL_MASK) != 0 && (event->keyval == GDK_KEY_I || event->keyval == GDK_KEY_D))
790  return FALSE;
791 
792 #endif
793 
795  rdp_event.key_event.up = (event->type == GDK_KEY_PRESS ? false : true);
796  rdp_event.key_event.extended = false;
797  rdp_event.key_event.extended1 = false;
798 
799  switch (event->keyval) {
800  case GDK_KEY_Pause:
801  /*
802  * See https://msdn.microsoft.com/en-us/library/cc240584.aspx
803  * 2.2.8.1.1.3.1.1.1 Keyboard Event (TS_KEYBOARD_EVENT)
804  * for pause key management
805  */
806  rdp_event.key_event.key_code = 0x1D;
807  rdp_event.key_event.up = false;
808  rdp_event.key_event.extended1 = TRUE;
809  remmina_rdp_event_event_push(gp, &rdp_event);
810  rdp_event.key_event.key_code = 0x45;
811  rdp_event.key_event.up = false;
812  rdp_event.key_event.extended1 = FALSE;
813  remmina_rdp_event_event_push(gp, &rdp_event);
814  rdp_event.key_event.key_code = 0x1D;
815  rdp_event.key_event.up = true;
816  rdp_event.key_event.extended1 = TRUE;
817  remmina_rdp_event_event_push(gp, &rdp_event);
818  rdp_event.key_event.key_code = 0x45;
819  rdp_event.key_event.up = true;
820  rdp_event.key_event.extended1 = FALSE;
821  remmina_rdp_event_event_push(gp, &rdp_event);
822  break;
823 
824  default:
825  if (!rfi->use_client_keymap) {
826  hardware_keycode = event->hardware_keycode;
827  if (rfi->keymap) {
828  for (ik = 0; ik < rfi->keymap->len; ik++) {
829  kep = &g_array_index(rfi->keymap, RemminaPluginRdpKeymapEntry, ik);
830  if (hardware_keycode == kep->orig_keycode) {
831  hardware_keycode = kep->translated_keycode;
832  break;
833  }
834  }
835  }
836  scancode = freerdp_keyboard_get_rdp_scancode_from_x11_keycode(hardware_keycode);
837  if (scancode) {
838  rdp_event.key_event.key_code = scancode & 0xFF;
839  rdp_event.key_event.extended = scancode & 0x100;
840  rdp_event.key_event.extended1 = FALSE;
841  remmina_rdp_event_event_push(gp, &rdp_event);
842  keypress_list_add(gp, rdp_event);
843  }
844  } else {
845  unicode_keyval = gdk_keyval_to_unicode(event->keyval);
846  /* Decide when whe should send a keycode or a Unicode character.
847  * - All non char keys (Shift, Alt, Super) should be sent as keycode
848  * - Space should be sent as keycode (see issue #1364)
849  * - All special keys (F1-F10, numeric pad, Home/End/Arrows/PgUp/PgDn/Insert/Delete) keycode
850  * - All key pressed while Ctrl or Alt or Super is down are not decoded by gdk_keyval_to_unicode(), so send it as keycode
851  * - All keycodes not translatable to unicode chars, as keycode
852  * - The rest as Unicode char
853  */
854  if (event->keyval >= 0xfe00 || // Arrows, Shift, Alt, Fn, num keypad…
855  event->hardware_keycode == 0x41 || // Spacebar
856  unicode_keyval == 0 || // Impossible to translate
857  (event->state & (GDK_MOD1_MASK | GDK_CONTROL_MASK | GDK_SUPER_MASK)) != 0 // A modifier not recognized by gdk_keyval_to_unicode()
858  ) {
859  scancode = freerdp_keyboard_get_rdp_scancode_from_x11_keycode(event->hardware_keycode);
860  rdp_event.key_event.key_code = scancode & 0xFF;
861  rdp_event.key_event.extended1 = FALSE;
862  if (rdp_event.key_event.key_code) {
863  remmina_rdp_event_event_push(gp, &rdp_event);
864  keypress_list_add(gp, rdp_event);
865  }
866  } else {
868  rdp_event.key_event.unicode_code = unicode_keyval;
869  rdp_event.key_event.extended = false;
870  rdp_event.key_event.extended1 = FALSE;
871  remmina_rdp_event_event_push(gp, &rdp_event);
872  keypress_list_add(gp, rdp_event);
873  }
874  }
875  break;
876  }
877 
878  return TRUE;
879 }
880 
881 gboolean remmina_rdp_event_on_clipboard(GtkClipboard *gtkClipboard, GdkEvent *event, RemminaProtocolWidget *gp)
882 {
883  /* Signal handler for GTK clipboard owner-change */
884  TRACE_CALL(__func__);
885  RemminaPluginRdpEvent rdp_event = { 0 };
886  CLIPRDR_FORMAT_LIST *pFormatList;
887  GObject *new_owner;
888 
889  /* Usually "owner-change" is fired when a user presses "COPY" on the client
890  * OR when this plugin calls gtk_clipboard_set_with_owner()
891  * after receiving a RDP server format list in remmina_rdp_cliprdr_server_format_list()
892  * In the latter case, we must ignore owner change */
893 
894  REMMINA_PLUGIN_DEBUG("gp=%p: owner-change event received", gp);
895 
896  rfContext *rfi = GET_PLUGIN_DATA(gp);
897 
898  if (rfi)
900 
901  new_owner = gtk_clipboard_get_owner(gtkClipboard);
902  if (new_owner != (GObject *)gp) {
903  /* To do: avoid this when the new owner is another remmina protocol widget of
904  * the same remmina application */
905  REMMINA_PLUGIN_DEBUG("gp=%p owner-change: new owner is different than me: new=%p me=%p",
906  gp, new_owner, gp);
907 
908  REMMINA_PLUGIN_DEBUG("gp=%p owner-change: new owner is not me: Sending local clipboard format list to server.",
909  gp, new_owner, gp);
912  rdp_event.clipboard_formatlist.pFormatList = pFormatList;
913  remmina_rdp_event_event_push(gp, &rdp_event);
914  } else {
915  REMMINA_PLUGIN_DEBUG(" ... but I'm the owner!");
916  }
917  return TRUE;
918 }
919 
921 {
922  TRACE_CALL(__func__);
923  gchar *s;
924  gint flags;
925  rfContext *rfi = GET_PLUGIN_DATA(gp);
926  GtkClipboard *clipboard;
927  RemminaFile *remminafile;
928 
929  gboolean disable_smooth_scrolling = FALSE;
930 
931  if (!rfi) return;
933 
934  /* we get first the global preferences */
935  s = remmina_plugin_service->pref_get_value("rdp_disable_smooth_scrolling");
936  disable_smooth_scrolling = (s && s[0] == '1' ? TRUE : FALSE);
937  g_free(s), s = NULL;
938  /* Otherwise we use the connection profile specific setting */
939  disable_smooth_scrolling = remmina_plugin_service->file_get_int(remminafile, "disable-smooth-scrolling", disable_smooth_scrolling);
940 
941  REMMINA_PLUGIN_DEBUG("Disable smooth scrolling is set to %d", disable_smooth_scrolling);
942 
943  rfi->drawing_area = gtk_drawing_area_new();
944  gtk_widget_show(rfi->drawing_area);
945  gtk_container_add(GTK_CONTAINER(gp), rfi->drawing_area);
946 
947  gtk_widget_add_events(rfi->drawing_area, GDK_POINTER_MOTION_MASK
948  | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
949  | GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK
950  | GDK_SCROLL_MASK | GDK_FOCUS_CHANGE_MASK);
951 
952  if (!disable_smooth_scrolling) {
953  REMMINA_PLUGIN_DEBUG("Adding GDK_SMOOTH_SCROLL_MASK");
954  gtk_widget_add_events(rfi->drawing_area, GDK_SMOOTH_SCROLL_MASK);
955  }
956 
957  gtk_widget_set_can_focus(rfi->drawing_area, TRUE);
958 
960 
961  s = remmina_plugin_service->pref_get_value("rdp_use_client_keymap");
962  rfi->use_client_keymap = (s && s[0] == '1' ? TRUE : FALSE);
963  g_free(s), s = NULL;
964 
965  /* Read special keymap from profile file, if exists */
967 
968  if (rfi->use_client_keymap && rfi->keymap)
969  fprintf(stderr, "RDP profile error: you cannot define both rdp_map_hardware_keycode and have 'Use client keyboard mapping' enabled\n");
970 
971  g_signal_connect(G_OBJECT(rfi->drawing_area), "draw",
972  G_CALLBACK(remmina_rdp_event_on_draw), gp);
973  g_signal_connect(G_OBJECT(rfi->drawing_area), "configure-event",
974  G_CALLBACK(remmina_rdp_event_on_configure), gp);
975  g_signal_connect(G_OBJECT(rfi->drawing_area), "motion-notify-event",
976  G_CALLBACK(remmina_rdp_event_on_motion), gp);
977  g_signal_connect(G_OBJECT(rfi->drawing_area), "button-press-event",
978  G_CALLBACK(remmina_rdp_event_on_button), gp);
979  g_signal_connect(G_OBJECT(rfi->drawing_area), "button-release-event",
980  G_CALLBACK(remmina_rdp_event_on_button), gp);
981  g_signal_connect(G_OBJECT(rfi->drawing_area), "scroll-event",
982  G_CALLBACK(remmina_rdp_event_on_scroll), gp);
983  g_signal_connect(G_OBJECT(rfi->drawing_area), "key-press-event",
984  G_CALLBACK(remmina_rdp_event_on_key), gp);
985  g_signal_connect(G_OBJECT(rfi->drawing_area), "key-release-event",
986  G_CALLBACK(remmina_rdp_event_on_key), gp);
987  g_signal_connect(G_OBJECT(rfi->drawing_area), "focus-in-event",
988  G_CALLBACK(remmina_rdp_event_on_focus_in), gp);
995  //g_signal_connect(G_OBJECT(gtk_widget_get_toplevel(rfi->drawing_area)), "map-event",
996  // G_CALLBACK(remmina_rdp_event_on_map), gp);
997  //g_signal_connect(G_OBJECT(gtk_widget_get_toplevel(rfi->drawing_area)), "unmap-event",
998  // G_CALLBACK(remmina_rdp_event_on_unmap), gp);
999 
1000  if (!remmina_plugin_service->file_get_int(remminafile, "disableclipboard", FALSE)) {
1001  clipboard = gtk_widget_get_clipboard(rfi->drawing_area, GDK_SELECTION_CLIPBOARD);
1002  rfi->clipboard.clipboard_handler = g_signal_connect(clipboard, "owner-change", G_CALLBACK(remmina_rdp_event_on_clipboard), gp);
1003  }
1004 
1005  rfi->pressed_keys = g_array_new(FALSE, TRUE, sizeof(RemminaPluginRdpEvent));
1006  rfi->event_queue = g_async_queue_new_full(g_free);
1007  rfi->ui_queue = g_async_queue_new();
1008  pthread_mutex_init(&rfi->ui_queue_mutex, NULL);
1009 
1010  if (pipe(rfi->event_pipe)) {
1011  g_print("Error creating pipes.\n");
1012  rfi->event_pipe[0] = -1;
1013  rfi->event_pipe[1] = -1;
1014  rfi->event_handle = NULL;
1015  } else {
1016  flags = fcntl(rfi->event_pipe[0], F_GETFL, 0);
1017  fcntl(rfi->event_pipe[0], F_SETFL, flags | O_NONBLOCK);
1018  rfi->event_handle = CreateFileDescriptorEvent(NULL, FALSE, FALSE, rfi->event_pipe[0], WINPR_FD_READ);
1019  if (!rfi->event_handle)
1020  g_print("CreateFileDescriptorEvent() failed\n");
1021  }
1022 
1023  rfi->object_table = g_hash_table_new_full(NULL, NULL, NULL, g_free);
1024 
1025  rfi->display = gdk_display_get_default();
1026 
1027 #if GTK_CHECK_VERSION(3, 22, 0)
1028  GdkVisual *visual = gdk_screen_get_system_visual(
1029  gdk_display_get_default_screen(rfi->display));
1030  rfi->bpp = gdk_visual_get_depth(visual);
1031 #else
1032  rfi->bpp = gdk_visual_get_best_depth();
1033 #endif
1034 }
1035 
1037 {
1038  TRACE_CALL(__func__);
1039 
1040  switch (obj->type) {
1042  free(obj->nocodec.bitmap);
1043  break;
1044 
1045  default:
1046  break;
1047  }
1048 
1049  g_free(obj);
1050 }
1051 
1053 {
1054  TRACE_CALL(__func__);
1055  rfContext *rfi = GET_PLUGIN_DATA(gp);
1057 
1058  if (!rfi) return;
1059 
1060  /* unregister the clipboard monitor */
1061  if (rfi->clipboard.clipboard_handler) {
1062  g_signal_handler_disconnect(G_OBJECT(gtk_widget_get_clipboard(rfi->drawing_area, GDK_SELECTION_CLIPBOARD)), rfi->clipboard.clipboard_handler);
1063  rfi->clipboard.clipboard_handler = 0;
1064  }
1065  if (rfi->delayed_monitor_layout_handler) {
1066  g_source_remove(rfi->delayed_monitor_layout_handler);
1068  }
1069  if (rfi->ui_handler) {
1070  g_source_remove(rfi->ui_handler);
1071  rfi->ui_handler = 0;
1072  }
1073  while ((ui = (RemminaPluginRdpUiObject *)g_async_queue_try_pop(rfi->ui_queue)) != NULL)
1075  if (rfi->surface) {
1076  cairo_surface_mark_dirty(rfi->surface);
1077  cairo_surface_destroy(rfi->surface);
1078  rfi->surface = NULL;
1079  }
1080 
1081  g_hash_table_destroy(rfi->object_table);
1082 
1083  g_array_free(rfi->pressed_keys, TRUE);
1084  if (rfi->keymap) {
1085  g_array_free(rfi->keymap, TRUE);
1086  rfi->keymap = NULL;
1087  }
1088  g_async_queue_unref(rfi->event_queue);
1089  rfi->event_queue = NULL;
1090  g_async_queue_unref(rfi->ui_queue);
1091  rfi->ui_queue = NULL;
1092  pthread_mutex_destroy(&rfi->ui_queue_mutex);
1093 
1094  if (rfi->event_handle) {
1095  CloseHandle(rfi->event_handle);
1096  rfi->event_handle = NULL;
1097  }
1098 
1099  close(rfi->event_pipe[0]);
1100  close(rfi->event_pipe[1]);
1101 }
1102 
1104 {
1105  int stride;
1106  rdpGdi *gdi;
1107 
1108  if (!rfi)
1109  return;
1110 
1111  gdi = ((rdpContext *)rfi)->gdi;
1112 
1113  if (!gdi)
1114  return;
1115 
1116  if (rfi->surface) {
1117  cairo_surface_mark_dirty(rfi->surface);
1118  cairo_surface_destroy(rfi->surface);
1119  rfi->surface = NULL;
1120  }
1121  stride = cairo_format_stride_for_width(rfi->cairo_format, gdi->width);
1122  rfi->surface = cairo_image_surface_create_for_data((unsigned char *)gdi->primary_buffer, rfi->cairo_format, gdi->width, gdi->height, stride);
1123  cairo_surface_flush(rfi->surface);
1124 }
1125 
1127 {
1128  TRACE_CALL(__func__);
1129  gint width, height;
1130  rdpGdi *gdi;
1131  rfContext *rfi = GET_PLUGIN_DATA(gp);
1132 
1135 
1136  gdi = ((rdpContext *)rfi)->gdi;
1137 
1139 
1140  /* See if we also must rellocate rfi->surface with different width and height,
1141  * this usually happens after a DesktopResize RDP event*/
1142 
1143  if (rfi->surface && (cairo_image_surface_get_width(rfi->surface) != gdi->width ||
1144  cairo_image_surface_get_height(rfi->surface) != gdi->height)) {
1145  /* Destroys and recreate rfi->surface with new width and height */
1146  cairo_surface_mark_dirty(rfi->surface);
1147  cairo_surface_destroy(rfi->surface);
1148  rfi->surface = NULL;
1150  } else if (rfi->surface == NULL) {
1152  }
1153 
1154  /* Send gdi->width and gdi->height obtained from remote server to gp plugin,
1155  * so they will be saved when closing connection */
1156  if (width != gdi->width)
1158  if (height != gdi->height)
1160 
1162 
1164  /* In scaled mode and autores mode, drawing_area will get its dimensions from its parent */
1165  gtk_widget_set_size_request(rfi->drawing_area, -1, -1);
1166  else
1167  /* In non scaled mode, the plugins forces dimensions of drawing area */
1168  gtk_widget_set_size_request(rfi->drawing_area, width, height);
1170 }
1171 
1173 {
1174  TRACE_CALL(__func__);
1175  rfContext *rfi = GET_PLUGIN_DATA(gp);
1176  rdpGdi *gdi;
1177 
1178  gdi = ((rdpContext *)rfi)->gdi;
1179 
1180  gtk_widget_realize(rfi->drawing_area);
1181 
1183  gtk_widget_queue_draw_area(rfi->drawing_area, 0, 0, gdi->width, gdi->height);
1184 
1186 
1188  const gchar *host = freerdp_settings_get_string (rfi->settings, FreeRDP_ServerHostname);
1189  // TRANSLATORS: the placeholder may be either an IP/FQDN or a server hostname
1190  REMMINA_PLUGIN_AUDIT(_("Connected to %s via RDP"), host);
1191 }
1192 
1194 {
1195  TRACE_CALL(__func__);
1196  rfContext *rfi = GET_PLUGIN_DATA(gp);
1197 
1198  gdk_window_invalidate_rect(gtk_widget_get_window(rfi->drawing_area), NULL, TRUE);
1199 }
1200 
1202 {
1203  TRACE_CALL(__func__);
1204  GdkPixbuf *pixbuf;
1205  rfContext *rfi = GET_PLUGIN_DATA(gp);
1206  rdpPointer *pointer = (rdpPointer *)ui->cursor.pointer;
1207  cairo_surface_t *surface;
1208  UINT8 *data = malloc(pointer->width * pointer->height * 4);
1209 
1210  if (!freerdp_image_copy_from_pointer_data(
1211  (BYTE *)data, PIXEL_FORMAT_BGRA32,
1212  pointer->width * 4, 0, 0, pointer->width, pointer->height,
1213  pointer->xorMaskData, pointer->lengthXorMask,
1214  pointer->andMaskData, pointer->lengthAndMask,
1215  pointer->xorBpp, &(ui->cursor.context->gdi->palette))) {
1216  free(data);
1217  return FALSE;
1218  }
1219 
1220  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));
1221  cairo_surface_flush(surface);
1222  pixbuf = gdk_pixbuf_get_from_surface(surface, 0, 0, pointer->width, pointer->height);
1223  cairo_surface_mark_dirty(surface);
1224  cairo_surface_destroy(surface);
1225  free(data);
1226  ((rfPointer *)ui->cursor.pointer)->cursor = gdk_cursor_new_from_pixbuf(rfi->display, pixbuf, pointer->xPos, pointer->yPos);
1227  g_object_unref(pixbuf);
1228 
1229  return TRUE;
1230 }
1231 
1233 {
1234  TRACE_CALL(__func__);
1235  g_object_unref(ui->cursor.pointer->cursor);
1236  ui->cursor.pointer->cursor = NULL;
1237 }
1238 
1240 {
1241  TRACE_CALL(__func__);
1242  GdkWindow *w, *nw;
1243  gint nx, ny, wx, wy;
1244 
1245 #if GTK_CHECK_VERSION(3, 20, 0)
1246  GdkSeat *seat;
1247 #else
1248  GdkDeviceManager *manager;
1249 #endif
1250  GdkDevice *dev;
1251  rfContext *rfi = GET_PLUGIN_DATA(gp);
1252 
1253  if (rfi == NULL)
1254  return FALSE;
1255 
1256  w = gtk_widget_get_window(rfi->drawing_area);
1257 #if GTK_CHECK_VERSION(3, 20, 0)
1258  seat = gdk_display_get_default_seat(gdk_display_get_default());
1259  dev = gdk_seat_get_pointer(seat);
1260 #else
1261  manager = gdk_display_get_device_manager(gdk_display_get_default());
1262  dev = gdk_device_manager_get_client_pointer(manager);
1263 #endif
1264 
1265  nw = gdk_device_get_window_at_position(dev, NULL, NULL);
1266 
1267  if (nw == w) {
1268  nx = 0;
1269  ny = 0;
1271  gdk_window_get_root_coords(w, nx, ny, &wx, &wy);
1272  gdk_device_warp(dev, gdk_window_get_screen(w), wx, wy);
1273  }
1274  return TRUE;
1275 }
1276 
1278 {
1279  TRACE_CALL(__func__);
1280  rfContext *rfi = GET_PLUGIN_DATA(gp);
1281 
1282  switch (ui->cursor.type) {
1284  ui->retval = remmina_rdp_event_create_cursor(gp, ui) ? 1 : 0;
1285  break;
1286 
1289  break;
1290 
1292  gdk_window_set_cursor(gtk_widget_get_window(rfi->drawing_area), ui->cursor.pointer->cursor);
1293  ui->retval = 1;
1294  break;
1295 
1297  ui->retval = remmina_rdp_event_set_pointer_position(gp, ui->pos.x, ui->pos.y) ? 1 : 0;
1298  break;
1299 
1301  gdk_window_set_cursor(gtk_widget_get_window(rfi->drawing_area),
1302  gdk_cursor_new_for_display(gdk_display_get_default(),
1303  GDK_BLANK_CURSOR));
1304  ui->retval = 1;
1305  break;
1306 
1308  gdk_window_set_cursor(gtk_widget_get_window(rfi->drawing_area), NULL);
1309  ui->retval = 1;
1310  break;
1311  }
1312 }
1313 
1315 {
1316  TRACE_CALL(__func__);
1318 }
1319 
1321 {
1322  TRACE_CALL(__func__);
1323  rfContext *rfi = GET_PLUGIN_DATA(gp);
1324 
1325  if (!rfi || !rfi->connected || rfi->is_reconnecting)
1326  return;
1328 }
1329 
1331 {
1332  TRACE_CALL(__func__);
1333  rfContext *rfi = GET_PLUGIN_DATA(gp);
1334 
1335  cairo_surface_mark_dirty(rfi->surface);
1336  cairo_surface_destroy(rfi->surface);
1337  rfi->surface = NULL;
1338 }
1339 
1341 {
1342  TRACE_CALL(__func__);
1343  switch (ui->event.type) {
1346  break;
1349  break;
1350  }
1351 }
1352 
1354 {
1355  TRACE_CALL(__func__);
1356  switch (ui->type) {
1359  break;
1360 
1363  break;
1364 
1367  break;
1368 
1369  case REMMINA_RDP_UI_CURSOR:
1370  remmina_rdp_event_cursor(gp, ui);
1371  break;
1372 
1375  break;
1376 
1377  case REMMINA_RDP_UI_EVENT:
1379  break;
1380 
1381  default:
1382  break;
1383  }
1384 }
1385 
1387 {
1388  TRACE_CALL(__func__);
1389 
1390  rfContext *rfi = GET_PLUGIN_DATA(gp);
1392 
1393  pthread_mutex_lock(&rfi->ui_queue_mutex);
1394  ui = (RemminaPluginRdpUiObject *)g_async_queue_try_pop(rfi->ui_queue);
1395  if (ui) {
1396  pthread_mutex_lock(&ui->sync_wait_mutex);
1397  if (!rfi->thread_cancelled)
1399  // Should we signal the caller thread to unlock ?
1400  if (ui->sync) {
1401  ui->complete = TRUE;
1402  pthread_cond_signal(&ui->sync_wait_cond);
1403  pthread_mutex_unlock(&ui->sync_wait_mutex);
1404  } else {
1406  }
1407 
1408  pthread_mutex_unlock(&rfi->ui_queue_mutex);
1409  return TRUE;
1410  } else {
1411  rfi->ui_handler = 0;
1412  pthread_mutex_unlock(&rfi->ui_queue_mutex);
1413  return FALSE;
1414  }
1415 }
1416 
1418 {
1419  TRACE_CALL(__func__);
1420  rfContext *rfi = GET_PLUGIN_DATA(gp);
1421  gboolean ui_sync_save;
1422  int oldcanceltype;
1423 
1424  if (!rfi || rfi->thread_cancelled)
1425  return;
1426 
1429  return;
1430  }
1431 
1432  pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &oldcanceltype);
1433 
1434  pthread_mutex_lock(&rfi->ui_queue_mutex);
1435 
1436  ui_sync_save = ui->sync;
1437  ui->complete = FALSE;
1438 
1439  if (ui_sync_save) {
1440  pthread_mutex_init(&ui->sync_wait_mutex, NULL);
1441  pthread_cond_init(&ui->sync_wait_cond, NULL);
1442  }
1443 
1444  ui->complete = FALSE;
1445 
1446  g_async_queue_push(rfi->ui_queue, ui);
1447 
1448  if (!rfi->ui_handler)
1449  rfi->ui_handler = IDLE_ADD((GSourceFunc)remmina_rdp_event_process_ui_queue, gp);
1450 
1451  if (ui_sync_save) {
1452  /* Wait for main thread function completion before returning */
1453  pthread_mutex_lock(&ui->sync_wait_mutex);
1454  pthread_mutex_unlock(&rfi->ui_queue_mutex);
1455  while (!ui->complete)
1456  pthread_cond_wait(&ui->sync_wait_cond, &ui->sync_wait_mutex);
1457  pthread_cond_destroy(&ui->sync_wait_cond);
1458  pthread_mutex_destroy(&ui->sync_wait_mutex);
1459  } else {
1460  pthread_mutex_unlock(&rfi->ui_queue_mutex);
1461  }
1462  pthread_setcanceltype(oldcanceltype, NULL);
1463 }
1464 
1466 {
1467  ui->sync = FALSE;
1469 }
1470 
1472 {
1473  TRACE_CALL(__func__);
1474  int retval;
1475 
1476  ui->sync = TRUE;
1478  retval = ui->retval;
1480  return retval;
1481 }
1482 
1484 {
1485  TRACE_CALL(__func__);
1486  void *rp;
1487 
1488  ui->sync = TRUE;
1490  rp = ui->retptr;
1492  return rp;
1493 }
struct remmina_plugin_rdp_event::@42::@45 mouse_event
gboolean thread_cancelled
Definition: rdp_plugin.h:333
static void remmina_rdp_event_free_cursor(RemminaProtocolWidget *gp, RemminaPluginRdpUiObject *ui)
Definition: rdp_event.c:1232
gboolean is_reconnecting
Definition: rdp_plugin.h:345
gulong clipboard_handler
Definition: rdp_plugin.h:140
static void remmina_rdp_event_create_cairo_surface(rfContext *rfi)
Definition: rdp_event.c:1103
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:1340
guint delayed_monitor_layout_handler
Definition: rdp_plugin.h:361
struct remmina_plugin_rdp_ui_object::@50::@53 cursor
static void remmina_rdp_event_update_scale_factor(RemminaProtocolWidget *gp)
Definition: rdp_event.c:313
gint event_pipe[2]
Definition: rdp_plugin.h:382
gdouble scale_y
Definition: rdp_plugin.h:360
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:367
gboolean rdpgfxchan
Definition: rdp_plugin.h:342
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:378
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:365
void * remmina_rdp_event_queue_ui_sync_retptr(RemminaProtocolWidget *gp, RemminaPluginRdpUiObject *ui)
Definition: rdp_event.c:1483
struct remmina_plugin_rdp_event::@42::@49 monitor_layout
GAsyncQueue * ui_queue
Definition: rdp_plugin.h:376
void remmina_rdp_event_update_rect(RemminaProtocolWidget *gp, gint x, gint y, gint w, gint h)
Definition: rdp_event.c:302
pthread_mutex_t sync_wait_mutex
Definition: rdp_plugin.h:270
int reconnect_nattempt
Definition: rdp_plugin.h:353
static gboolean remmina_rdp_event_process_ui_queue(RemminaProtocolWidget *gp)
Definition: rdp_event.c:1386
static BOOL remmina_rdp_event_create_cursor(RemminaProtocolWidget *gp, RemminaPluginRdpUiObject *ui)
Definition: rdp_event.c:1201
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:1417
gint(* protocol_plugin_get_width)(RemminaProtocolWidget *gp)
Definition: plugin.h:168
gdouble scale_x
Definition: rdp_plugin.h:359
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:767
void remmina_rdp_event_free_event(RemminaProtocolWidget *gp, RemminaPluginRdpUiObject *obj)
Definition: rdp_event.c:1036
static gboolean remmina_rdp_event_on_motion(GtkWidget *widget, GdkEventMotion *event, RemminaProtocolWidget *gp)
Definition: rdp_event.c:580
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:352
static void remmina_rdp_ui_event_update_scale(RemminaProtocolWidget *gp, RemminaPluginRdpUiObject *ui)
Definition: rdp_event.c:1314
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
UINT16 last_x
Definition: rdp_plugin.h:384
void remmina_rdp_event_update_regions(RemminaProtocolWidget *gp, RemminaPluginRdpUiObject *ui)
Definition: rdp_event.c:282
static gboolean remmina_rdp_event_delayed_monitor_layout(RemminaProtocolWidget *gp)
Definition: rdp_event.c:393
static RemminaPluginService * remmina_plugin_service
unsigned translated_keycode
Definition: rdp_plugin.h:317
GtkWidget * drawing_area
Definition: rdp_plugin.h:356
cairo_format_t cairo_format
Definition: rdp_plugin.h:368
static gboolean remmina_rdp_event_on_configure(GtkWidget *widget, GdkEventConfigure *event, RemminaProtocolWidget *gp)
Definition: rdp_event.c:499
int remmina_rdp_event_queue_ui_sync_retint(RemminaProtocolWidget *gp, RemminaPluginRdpUiObject *ui)
Definition: rdp_event.c:1471
void remmina_rdp_event_init(RemminaProtocolWidget *gp)
Definition: rdp_event.c:920
gboolean use_client_keymap
Definition: rdp_plugin.h:362
static gboolean remmina_rdp_event_on_scroll(GtkWidget *widget, GdkEventScroll *event, RemminaProtocolWidget *gp)
Definition: rdp_event.c:677
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:1193
GArray * keymap
Definition: rdp_plugin.h:389
UINT16 last_y
Definition: rdp_plugin.h:385
unsigned orig_keycode
Definition: rdp_plugin.h:316
static void remmina_rdp_event_scale_area(RemminaProtocolWidget *gp, gint *x, gint *y, gint *w, gint *h)
Definition: rdp_event.c:237
void remmina_rdp_event_queue_ui_async(RemminaProtocolWidget *gp, RemminaPluginRdpUiObject *ui)
Definition: rdp_event.c:1465
static gboolean remmina_rdp_event_on_button(GtkWidget *widget, GdkEventButton *event, RemminaProtocolWidget *gp)
Definition: rdp_event.c:606
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:484
GAsyncQueue * event_queue
Definition: rdp_plugin.h:381
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:271
rfClipboard clipboard
Definition: rdp_plugin.h:387
static void remmina_rdp_event_init_keymap(rfContext *rfi, const gchar *strmap)
Definition: rdp_event.c:733
HANDLE event_handle
Definition: rdp_plugin.h:383
static void remmina_rdp_event_translate_pos(RemminaProtocolWidget *gp, int ix, int iy, UINT16 *ox, UINT16 *oy)
Definition: rdp_event.c:518
static void remmina_rdp_ui_event_destroy_cairo_surface(RemminaProtocolWidget *gp, RemminaPluginRdpUiObject *ui)
Definition: rdp_event.c:1330
void remmina_rdp_event_uninit(RemminaProtocolWidget *gp)
Definition: rdp_event.c:1052
void remmina_rdp_mouse_jitter(RemminaProtocolWidget *gp)
Definition: rdp_event.c:562
RemminaFile *(* protocol_plugin_get_file)(RemminaProtocolWidget *gp)
Definition: plugin.h:178
GHashTable * object_table
Definition: rdp_plugin.h:374
gboolean remmina_rdp_event_on_clipboard(GtkClipboard *gtkClipboard, GdkEvent *event, RemminaProtocolWidget *gp)
Definition: rdp_event.c:881
static void remmina_rdp_event_reverse_translate_pos_reverse(RemminaProtocolWidget *gp, int ix, int iy, int *ox, int *oy)
Definition: rdp_event.c:540
static void remmina_rdp_event_process_ui_event(RemminaProtocolWidget *gp, RemminaPluginRdpUiObject *ui)
Definition: rdp_event.c:1353
static void keypress_list_add(RemminaProtocolWidget *gp, RemminaPluginRdpEvent rdp_event)
Definition: rdp_event.c:222
gboolean connected
Definition: rdp_plugin.h:344
RemminaScaleMode scale
Definition: rdp_plugin.h:331
freerdp * instance
Definition: rdp_plugin.h:328
DispClientContext * dispcontext
Definition: rdp_plugin.h:336
static BOOL remmina_rdp_event_set_pointer_position(RemminaProtocolWidget *gp, gint x, gint y)
Definition: rdp_event.c:1239
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:1172
GArray * pressed_keys
Definition: rdp_plugin.h:380
void remmina_rdp_event_update_scale(RemminaProtocolWidget *gp)
Definition: rdp_event.c:1126
rdpSettings * settings
Definition: rdp_plugin.h:327
gint scale_height
Definition: rdp_plugin.h:358
void(* protocol_plugin_set_height)(RemminaProtocolWidget *gp, gint height)
Definition: plugin.h:171
Definition: rdp_plugin.h:315
void remmina_rdp_event_unfocus(RemminaProtocolWidget *gp)
Definition: rdp_event.c:1320
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:1277
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:344
gint scale_width
Definition: rdp_plugin.h:357
pthread_mutex_t ui_queue_mutex
Definition: rdp_plugin.h:377
RemminaPluginRdpUiType type
Definition: rdp_plugin.h:267