a712e568a1ea64ff48ede9167782bb0d6ace139c
[libguac-client-rdp.git] / src / guac_handlers.c
1
2 /* ***** BEGIN LICENSE BLOCK *****
3  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4  *
5  * The contents of this file are subject to the Mozilla Public License Version
6  * 1.1 (the "License"); you may not use this file except in compliance with
7  * the License. You may obtain a copy of the License at
8  * http://www.mozilla.org/MPL/
9  *
10  * Software distributed under the License is distributed on an "AS IS" basis,
11  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12  * for the specific language governing rights and limitations under the
13  * License.
14  *
15  * The Original Code is libguac-client-rdp.
16  *
17  * The Initial Developer of the Original Code is
18  * Michael Jumper.
19  * Portions created by the Initial Developer are Copyright (C) 2011
20  * the Initial Developer. All Rights Reserved.
21  *
22  * Contributor(s):
23  * Matt Hortman
24  * Jocelyn DELALANDE <j.delalande@ulteo.com> Ulteo SAS - http://www.ulteo.com
25  *
26  * Portions created by Ulteo SAS employees are Copyright (C) 2012 Ulteo SAS
27  *
28  * Alternatively, the contents of this file may be used under the terms of
29  * either the GNU General Public License Version 2 or later (the "GPL"), or
30  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
31  * in which case the provisions of the GPL or the LGPL are applicable instead
32  * of those above. If you wish to allow use of your version of this file only
33  * under the terms of either the GPL or the LGPL, and not to allow others to
34  * use your version of this file under the terms of the MPL, indicate your
35  * decision by deleting the provisions above and replace them with the notice
36  * and other provisions required by the GPL or the LGPL. If you do not delete
37  * the provisions above, a recipient may use your version of this file under
38  * the terms of any one of the MPL, the GPL or the LGPL.
39  *
40  * ***** END LICENSE BLOCK ***** */
41
42 #include <stdlib.h>
43 #include <string.h>
44
45 #include <sys/select.h>
46 #include <errno.h>
47
48 #include <freerdp/freerdp.h>
49 #include <freerdp/channels/channels.h>
50 #include <freerdp/input.h>
51 #include <freerdp/codec/color.h>
52 #include <freerdp/cache/cache.h>
53 #include <freerdp/utils/event.h>
54 #include <freerdp/plugins/cliprdr.h>
55
56 #include <guacamole/socket.h>
57 #include <guacamole/protocol.h>
58 #include <guacamole/client.h>
59 #include <guacamole/error.h>
60
61 #include "client.h"
62 #include "rdp_keymap.h"
63 #include "rdp_cliprdr.h"
64 #include "guac_handlers.h"
65
66 void __guac_rdp_update_keysyms(guac_client* client, const int* keysym_string, int from, int to);
67 int __guac_rdp_send_keysym(guac_client* client, int keysym, int pressed);
68 void __guac_rdp_send_altcode(guac_client* client, int altcode);
69
70
71 int rdp_guac_client_free_handler(guac_client* client) {
72
73     rdp_guac_client_data* guac_client_data =
74         (rdp_guac_client_data*) client->data;
75
76     freerdp* rdp_inst = guac_client_data->rdp_inst;
77     rdpChannels* channels = rdp_inst->context->channels;
78
79     /* Clean up RDP client */
80         freerdp_channels_close(channels, rdp_inst);
81         freerdp_channels_free(channels);
82         freerdp_disconnect(rdp_inst);
83     freerdp_clrconv_free(((rdp_freerdp_context*) rdp_inst->context)->clrconv);
84     cache_free(rdp_inst->context->cache);
85     freerdp_free(rdp_inst);
86
87     /* Free client data */
88     cairo_surface_destroy(guac_client_data->opaque_glyph_surface);
89     cairo_surface_destroy(guac_client_data->trans_glyph_surface);
90     free(guac_client_data->clipboard);
91     free(guac_client_data);
92
93     return 0;
94
95 }
96
97 int rdp_guac_client_handle_messages(guac_client* client) {
98
99     rdp_guac_client_data* guac_client_data = (rdp_guac_client_data*) client->data;
100     freerdp* rdp_inst = guac_client_data->rdp_inst;
101     rdpChannels* channels = rdp_inst->context->channels;
102
103     int index;
104     int max_fd, fd;
105     void* read_fds[32];
106     void* write_fds[32];
107     int read_count = 0;
108     int write_count = 0;
109     fd_set rfds, wfds;
110     RDP_EVENT* event;
111
112     struct timeval timeout = {
113         .tv_sec = 0,
114         .tv_usec = 250000
115     };
116
117     /* get rdp fds */
118     if (!freerdp_get_fds(rdp_inst, read_fds, &read_count, write_fds, &write_count)) {
119         guac_error = GUAC_STATUS_BAD_STATE;
120         guac_error_message = "Unable to read RDP file descriptors";
121         return 1;
122     }
123
124     /* get channel fds */
125     if (!freerdp_channels_get_fds(channels, rdp_inst, read_fds, &read_count, write_fds, &write_count)) {
126         guac_error = GUAC_STATUS_BAD_STATE;
127         guac_error_message = "Unable to read RDP channel file descriptors";
128         return 1;
129     }
130
131     /* Construct read fd_set */
132     max_fd = 0;
133     FD_ZERO(&rfds);
134     for (index = 0; index < read_count; index++) {
135         fd = (int)(long) (read_fds[index]);
136         if (fd > max_fd)
137             max_fd = fd;
138         FD_SET(fd, &rfds);
139     }
140
141     /* Construct write fd_set */
142     FD_ZERO(&wfds);
143     for (index = 0; index < write_count; index++) {
144         fd = (int)(long) (write_fds[index]);
145         if (fd > max_fd)
146             max_fd = fd;
147         FD_SET(fd, &wfds);
148     }
149
150     /* If no file descriptors, error */
151     if (max_fd == 0) {
152         guac_error = GUAC_STATUS_BAD_STATE;
153         guac_error_message = "No file descriptors";
154         return 1;
155     }
156
157     /* Otherwise, wait for file descriptors given */
158     if (select(max_fd + 1, &rfds, &wfds, NULL, &timeout) == -1) {
159         /* these are not really errors */
160         if (!((errno == EAGAIN) ||
161             (errno == EWOULDBLOCK) ||
162             (errno == EINPROGRESS) ||
163             (errno == EINTR))) /* signal occurred */
164         {
165             guac_error = GUAC_STATUS_SEE_ERRNO;
166             guac_error_message = "Error waiting for file descriptor";
167             return 1;
168         }
169     }
170
171     /* Check the libfreerdp fds */
172     if (!freerdp_check_fds(rdp_inst)) {
173         guac_error = GUAC_STATUS_BAD_STATE;
174         guac_error_message = "Error handling RDP file descriptors";
175         return 1;
176     }
177
178     /* Check channel fds */
179     if (!freerdp_channels_check_fds(channels, rdp_inst)) {
180         guac_error = GUAC_STATUS_BAD_STATE;
181         guac_error_message = "Error handling RDP channel file descriptors";
182         return 1;
183     }
184
185     /* Check for channel events */
186     event = freerdp_channels_pop_event(channels);
187     if (event) {
188
189         /* Handle clipboard events */
190         if (event->event_class == RDP_EVENT_CLASS_CLIPRDR)
191             guac_rdp_process_cliprdr_event(client, event);
192
193         freerdp_event_free(event);
194
195     }
196
197     /* Handle RDP disconnect */
198     if (freerdp_shall_disconnect(rdp_inst)) {
199         guac_error = GUAC_STATUS_NO_INPUT;
200         guac_error_message = "RDP server closed connection";
201         return 1;
202     }
203
204     /* Success */
205     return 0;
206
207 }
208
209 int rdp_guac_client_mouse_handler(guac_client* client, int x, int y, int mask) {
210
211     rdp_guac_client_data* guac_client_data = (rdp_guac_client_data*) client->data;
212     freerdp* rdp_inst = guac_client_data->rdp_inst;
213
214     /* If button mask unchanged, just send move event */
215     if (mask == guac_client_data->mouse_button_mask)
216         rdp_inst->input->MouseEvent(rdp_inst->input, PTR_FLAGS_MOVE, x, y);
217
218     /* Otherwise, send events describing button change */
219     else {
220
221         /* Mouse buttons which have JUST become released */
222         int released_mask =  guac_client_data->mouse_button_mask & ~mask;
223
224         /* Mouse buttons which have JUST become pressed */
225         int pressed_mask  = ~guac_client_data->mouse_button_mask &  mask;
226
227         /* Release event */
228         if (released_mask & 0x07) {
229
230             /* Calculate flags */
231             int flags = 0;
232             if (released_mask & 0x01) flags |= PTR_FLAGS_BUTTON1;
233             if (released_mask & 0x02) flags |= PTR_FLAGS_BUTTON3;
234             if (released_mask & 0x04) flags |= PTR_FLAGS_BUTTON2;
235
236             rdp_inst->input->MouseEvent(rdp_inst->input, flags, x, y);
237
238         }
239
240         /* Press event */
241         if (pressed_mask & 0x07) {
242
243             /* Calculate flags */
244             int flags = PTR_FLAGS_DOWN;
245             if (pressed_mask & 0x01) flags |= PTR_FLAGS_BUTTON1;
246             if (pressed_mask & 0x02) flags |= PTR_FLAGS_BUTTON3;
247             if (pressed_mask & 0x04) flags |= PTR_FLAGS_BUTTON2;
248             if (pressed_mask & 0x08) flags |= PTR_FLAGS_WHEEL | 0x78;
249             if (pressed_mask & 0x10) flags |= PTR_FLAGS_WHEEL | PTR_FLAGS_WHEEL_NEGATIVE | 0x88;
250
251             /* Send event */
252             rdp_inst->input->MouseEvent(rdp_inst->input, flags, x, y);
253
254         }
255
256         /* Scroll event */
257         if (pressed_mask & 0x18) {
258
259             /* Down */
260             if (pressed_mask & 0x08)
261                 rdp_inst->input->MouseEvent(
262                         rdp_inst->input,
263                         PTR_FLAGS_WHEEL | 0x78,
264                         x, y);
265
266             /* Up */
267             if (pressed_mask & 0x10)
268                 rdp_inst->input->MouseEvent(
269                         rdp_inst->input,
270                         PTR_FLAGS_WHEEL | PTR_FLAGS_WHEEL_NEGATIVE | 0x88,
271                         x, y);
272
273         }
274
275
276         guac_client_data->mouse_button_mask = mask;
277     }
278
279     return 0;
280 }
281
282 void __guac_rdp_send_altcode(guac_client* client, int altcode) {
283
284     rdp_guac_client_data* guac_client_data = (rdp_guac_client_data*) client->data;
285     freerdp* rdp_inst = guac_client_data->rdp_inst;
286     int i;
287
288     /* Lookup scancode for Alt */
289     int alt = GUAC_RDP_KEYSYM_LOOKUP(
290             guac_client_data->keymap,
291             0xFFE9 /* Alt_L */).scancode;
292
293     /* Release all pressed modifiers */
294     __guac_rdp_update_keysyms(client, GUAC_KEYSYMS_ALL_MODIFIERS, 1, 0);
295
296     /* Press Alt */
297     rdp_inst->input->KeyboardEvent(rdp_inst->input, KBD_FLAGS_DOWN, alt);
298
299     /* For each character in four-digit Alt-code ... */
300     for (i=0; i<4; i++) {
301
302         /* Get scancode of keypad digit */
303         int scancode = GUAC_RDP_KEYSYM_LOOKUP(
304                 guac_client_data->keymap,
305                 0xFFB0 + (altcode / 1000)
306         ).scancode;
307
308         /* Press and release digit */
309         rdp_inst->input->KeyboardEvent(rdp_inst->input, KBD_FLAGS_DOWN, scancode);
310         rdp_inst->input->KeyboardEvent(rdp_inst->input, KBD_FLAGS_RELEASE, scancode);
311
312         /* Shift digits left by one place */
313         altcode = (altcode * 10) % 10000;
314
315     }
316
317     /* Release Alt */
318     rdp_inst->input->KeyboardEvent(rdp_inst->input, KBD_FLAGS_RELEASE, alt);
319
320     /* Press all originally pressed modifiers */
321     __guac_rdp_update_keysyms(client, GUAC_KEYSYMS_ALL_MODIFIERS, 1, 1);
322
323 }
324
325 int __guac_rdp_send_keysym(guac_client* client, int keysym, int pressed) {
326
327     rdp_guac_client_data* guac_client_data = (rdp_guac_client_data*) client->data;
328     freerdp* rdp_inst = guac_client_data->rdp_inst;
329
330     /* If keysym can be in lookup table */
331     if (keysym <= 0xFFFF) {
332
333         /* Look up scancode mapping */
334         const guac_rdp_keysym_desc* keysym_desc =
335             &GUAC_RDP_KEYSYM_LOOKUP(guac_client_data->keymap, keysym);
336
337         /* If defined, send event */
338         if (keysym_desc->scancode != 0) {
339
340             /* If defined, send any prerequesite keys that must be set */
341             if (keysym_desc->set_keysyms != NULL)
342                 __guac_rdp_update_keysyms(client, keysym_desc->set_keysyms, 0, 1);
343
344             /* If defined, release any keys that must be cleared */
345             if (keysym_desc->clear_keysyms != NULL)
346                 __guac_rdp_update_keysyms(client, keysym_desc->clear_keysyms, 1, 0);
347
348             /* Send actual key */
349             rdp_inst->input->KeyboardEvent(
350                     rdp_inst->input,
351                     keysym_desc->flags
352                         | (pressed ? KBD_FLAGS_DOWN : KBD_FLAGS_RELEASE),
353                     keysym_desc->scancode);
354
355             guac_client_log_info(client, "Base flags are %d", keysym_desc->flags);
356
357             /* If defined, release any keys that were originally released */
358             if (keysym_desc->set_keysyms != NULL)
359                 __guac_rdp_update_keysyms(client, keysym_desc->set_keysyms, 0, 0);
360
361             /* If defined, send any keys that were originally set */
362             if (keysym_desc->clear_keysyms != NULL)
363                 __guac_rdp_update_keysyms(client, keysym_desc->clear_keysyms, 1, 1);
364
365             return 0;
366
367         }
368     }
369
370     /* Fall back to unicode events if undefined inside current keymap */
371
372     /* Only send when key pressed - Unicode events do not have DOWN/RELEASE flags */
373     if (pressed) {
374
375         /* Translate keysym into codepoint */
376         int codepoint;
377         if (keysym <= 0xFF)
378             codepoint = keysym;
379         else
380             codepoint = keysym & 0xFFFFFF;
381
382         guac_client_log_info(client, "Translated keysym 0x%x to U+%04X", keysym, codepoint);
383
384         /* Send Unicode event */
385         rdp_inst->input->UnicodeKeyboardEvent(
386                 rdp_inst->input,
387                 0, codepoint);
388     }
389     
390     else
391         guac_client_log_info(client, "Ignoring key release (Unicode event)");
392
393     return 0;
394 }
395
396 void __guac_rdp_update_keysyms(guac_client* client, const int* keysym_string, int from, int to) {
397
398     rdp_guac_client_data* guac_client_data = (rdp_guac_client_data*) client->data;
399     int keysym;
400
401     /* Send all keysyms in string, NULL terminated */
402     while ((keysym = *keysym_string) != 0) {
403
404         /* Get current keysym state */
405         int current_state = GUAC_RDP_KEYSYM_LOOKUP(guac_client_data->keysym_state, keysym);
406
407         /* If key is currently in given state, send event for changing it to specified "to" state */
408         if (current_state == from)
409             __guac_rdp_send_keysym(client, *keysym_string, to);
410
411         /* Next keysym */
412         keysym_string++;
413
414     }
415
416 }
417
418 int rdp_guac_client_key_handler(guac_client* client, int keysym, int pressed) {
419
420     rdp_guac_client_data* guac_client_data = (rdp_guac_client_data*) client->data;
421
422     /* Update keysym state */
423     GUAC_RDP_KEYSYM_LOOKUP(guac_client_data->keysym_state, keysym) = pressed;
424
425     return __guac_rdp_send_keysym(client, keysym, pressed);
426
427 }
428
429 int rdp_guac_client_clipboard_handler(guac_client* client, char* data) {
430
431     rdpChannels* channels = 
432         ((rdp_guac_client_data*) client->data)->rdp_inst->context->channels;
433
434     RDP_CB_FORMAT_LIST_EVENT* format_list =
435         (RDP_CB_FORMAT_LIST_EVENT*) freerdp_event_new(
436             RDP_EVENT_CLASS_CLIPRDR,
437             RDP_EVENT_TYPE_CB_FORMAT_LIST,
438             NULL, NULL);
439
440     /* Free existing data */
441     free(((rdp_guac_client_data*) client->data)->clipboard);
442
443     /* Store data in client */
444     ((rdp_guac_client_data*) client->data)->clipboard = strdup(data);
445
446     /* Notify server that text data is now available */
447     format_list->formats = (uint32*) malloc(sizeof(uint32));
448     format_list->formats[0] = CB_FORMAT_TEXT;
449     format_list->num_formats = 1;
450
451     freerdp_channels_send_event(channels, (RDP_EVENT*) format_list);
452
453     return 0;
454
455 }
456