Remove now unused altcode function.
[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
69
70 int rdp_guac_client_free_handler(guac_client* client) {
71
72     rdp_guac_client_data* guac_client_data =
73         (rdp_guac_client_data*) client->data;
74
75     freerdp* rdp_inst = guac_client_data->rdp_inst;
76     rdpChannels* channels = rdp_inst->context->channels;
77
78     /* Clean up RDP client */
79         freerdp_channels_close(channels, rdp_inst);
80         freerdp_channels_free(channels);
81         freerdp_disconnect(rdp_inst);
82     freerdp_clrconv_free(((rdp_freerdp_context*) rdp_inst->context)->clrconv);
83     cache_free(rdp_inst->context->cache);
84     freerdp_free(rdp_inst);
85
86     /* Free client data */
87     cairo_surface_destroy(guac_client_data->opaque_glyph_surface);
88     cairo_surface_destroy(guac_client_data->trans_glyph_surface);
89     free(guac_client_data->clipboard);
90     free(guac_client_data);
91
92     return 0;
93
94 }
95
96 int rdp_guac_client_handle_messages(guac_client* client) {
97
98     rdp_guac_client_data* guac_client_data = (rdp_guac_client_data*) client->data;
99     freerdp* rdp_inst = guac_client_data->rdp_inst;
100     rdpChannels* channels = rdp_inst->context->channels;
101
102     int index;
103     int max_fd, fd;
104     void* read_fds[32];
105     void* write_fds[32];
106     int read_count = 0;
107     int write_count = 0;
108     fd_set rfds, wfds;
109     RDP_EVENT* event;
110
111     struct timeval timeout = {
112         .tv_sec = 0,
113         .tv_usec = 250000
114     };
115
116     /* get rdp fds */
117     if (!freerdp_get_fds(rdp_inst, read_fds, &read_count, write_fds, &write_count)) {
118         guac_error = GUAC_STATUS_BAD_STATE;
119         guac_error_message = "Unable to read RDP file descriptors";
120         return 1;
121     }
122
123     /* get channel fds */
124     if (!freerdp_channels_get_fds(channels, rdp_inst, read_fds, &read_count, write_fds, &write_count)) {
125         guac_error = GUAC_STATUS_BAD_STATE;
126         guac_error_message = "Unable to read RDP channel file descriptors";
127         return 1;
128     }
129
130     /* Construct read fd_set */
131     max_fd = 0;
132     FD_ZERO(&rfds);
133     for (index = 0; index < read_count; index++) {
134         fd = (int)(long) (read_fds[index]);
135         if (fd > max_fd)
136             max_fd = fd;
137         FD_SET(fd, &rfds);
138     }
139
140     /* Construct write fd_set */
141     FD_ZERO(&wfds);
142     for (index = 0; index < write_count; index++) {
143         fd = (int)(long) (write_fds[index]);
144         if (fd > max_fd)
145             max_fd = fd;
146         FD_SET(fd, &wfds);
147     }
148
149     /* If no file descriptors, error */
150     if (max_fd == 0) {
151         guac_error = GUAC_STATUS_BAD_STATE;
152         guac_error_message = "No file descriptors";
153         return 1;
154     }
155
156     /* Otherwise, wait for file descriptors given */
157     if (select(max_fd + 1, &rfds, &wfds, NULL, &timeout) == -1) {
158         /* these are not really errors */
159         if (!((errno == EAGAIN) ||
160             (errno == EWOULDBLOCK) ||
161             (errno == EINPROGRESS) ||
162             (errno == EINTR))) /* signal occurred */
163         {
164             guac_error = GUAC_STATUS_SEE_ERRNO;
165             guac_error_message = "Error waiting for file descriptor";
166             return 1;
167         }
168     }
169
170     /* Check the libfreerdp fds */
171     if (!freerdp_check_fds(rdp_inst)) {
172         guac_error = GUAC_STATUS_BAD_STATE;
173         guac_error_message = "Error handling RDP file descriptors";
174         return 1;
175     }
176
177     /* Check channel fds */
178     if (!freerdp_channels_check_fds(channels, rdp_inst)) {
179         guac_error = GUAC_STATUS_BAD_STATE;
180         guac_error_message = "Error handling RDP channel file descriptors";
181         return 1;
182     }
183
184     /* Check for channel events */
185     event = freerdp_channels_pop_event(channels);
186     if (event) {
187
188         /* Handle clipboard events */
189         if (event->event_class == RDP_EVENT_CLASS_CLIPRDR)
190             guac_rdp_process_cliprdr_event(client, event);
191
192         freerdp_event_free(event);
193
194     }
195
196     /* Handle RDP disconnect */
197     if (freerdp_shall_disconnect(rdp_inst)) {
198         guac_error = GUAC_STATUS_NO_INPUT;
199         guac_error_message = "RDP server closed connection";
200         return 1;
201     }
202
203     /* Success */
204     return 0;
205
206 }
207
208 int rdp_guac_client_mouse_handler(guac_client* client, int x, int y, int mask) {
209
210     rdp_guac_client_data* guac_client_data = (rdp_guac_client_data*) client->data;
211     freerdp* rdp_inst = guac_client_data->rdp_inst;
212
213     /* If button mask unchanged, just send move event */
214     if (mask == guac_client_data->mouse_button_mask)
215         rdp_inst->input->MouseEvent(rdp_inst->input, PTR_FLAGS_MOVE, x, y);
216
217     /* Otherwise, send events describing button change */
218     else {
219
220         /* Mouse buttons which have JUST become released */
221         int released_mask =  guac_client_data->mouse_button_mask & ~mask;
222
223         /* Mouse buttons which have JUST become pressed */
224         int pressed_mask  = ~guac_client_data->mouse_button_mask &  mask;
225
226         /* Release event */
227         if (released_mask & 0x07) {
228
229             /* Calculate flags */
230             int flags = 0;
231             if (released_mask & 0x01) flags |= PTR_FLAGS_BUTTON1;
232             if (released_mask & 0x02) flags |= PTR_FLAGS_BUTTON3;
233             if (released_mask & 0x04) flags |= PTR_FLAGS_BUTTON2;
234
235             rdp_inst->input->MouseEvent(rdp_inst->input, flags, x, y);
236
237         }
238
239         /* Press event */
240         if (pressed_mask & 0x07) {
241
242             /* Calculate flags */
243             int flags = PTR_FLAGS_DOWN;
244             if (pressed_mask & 0x01) flags |= PTR_FLAGS_BUTTON1;
245             if (pressed_mask & 0x02) flags |= PTR_FLAGS_BUTTON3;
246             if (pressed_mask & 0x04) flags |= PTR_FLAGS_BUTTON2;
247             if (pressed_mask & 0x08) flags |= PTR_FLAGS_WHEEL | 0x78;
248             if (pressed_mask & 0x10) flags |= PTR_FLAGS_WHEEL | PTR_FLAGS_WHEEL_NEGATIVE | 0x88;
249
250             /* Send event */
251             rdp_inst->input->MouseEvent(rdp_inst->input, flags, x, y);
252
253         }
254
255         /* Scroll event */
256         if (pressed_mask & 0x18) {
257
258             /* Down */
259             if (pressed_mask & 0x08)
260                 rdp_inst->input->MouseEvent(
261                         rdp_inst->input,
262                         PTR_FLAGS_WHEEL | 0x78,
263                         x, y);
264
265             /* Up */
266             if (pressed_mask & 0x10)
267                 rdp_inst->input->MouseEvent(
268                         rdp_inst->input,
269                         PTR_FLAGS_WHEEL | PTR_FLAGS_WHEEL_NEGATIVE | 0x88,
270                         x, y);
271
272         }
273
274
275         guac_client_data->mouse_button_mask = mask;
276     }
277
278     return 0;
279 }
280
281
282 int __guac_rdp_send_keysym(guac_client* client, int keysym, int pressed) {
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
287     /* If keysym can be in lookup table */
288     if (keysym <= 0xFFFF) {
289
290         /* Look up scancode mapping */
291         const guac_rdp_keysym_desc* keysym_desc =
292             &GUAC_RDP_KEYSYM_LOOKUP(guac_client_data->keymap, keysym);
293
294         /* If defined, send event */
295         if (keysym_desc->scancode != 0) {
296
297             /* If defined, send any prerequesite keys that must be set */
298             if (keysym_desc->set_keysyms != NULL)
299                 __guac_rdp_update_keysyms(client, keysym_desc->set_keysyms, 0, 1);
300
301             /* If defined, release any keys that must be cleared */
302             if (keysym_desc->clear_keysyms != NULL)
303                 __guac_rdp_update_keysyms(client, keysym_desc->clear_keysyms, 1, 0);
304
305             /* Send actual key */
306             rdp_inst->input->KeyboardEvent(
307                     rdp_inst->input,
308                     keysym_desc->flags
309                         | (pressed ? KBD_FLAGS_DOWN : KBD_FLAGS_RELEASE),
310                     keysym_desc->scancode);
311
312             guac_client_log_info(client, "Base flags are %d", keysym_desc->flags);
313
314             /* If defined, release any keys that were originally released */
315             if (keysym_desc->set_keysyms != NULL)
316                 __guac_rdp_update_keysyms(client, keysym_desc->set_keysyms, 0, 0);
317
318             /* If defined, send any keys that were originally set */
319             if (keysym_desc->clear_keysyms != NULL)
320                 __guac_rdp_update_keysyms(client, keysym_desc->clear_keysyms, 1, 1);
321
322             return 0;
323
324         }
325     }
326
327     /* Fall back to unicode events if undefined inside current keymap */
328
329     /* Only send when key pressed - Unicode events do not have DOWN/RELEASE flags */
330     if (pressed) {
331
332         /* Translate keysym into codepoint */
333         int codepoint;
334         if (keysym <= 0xFF)
335             codepoint = keysym;
336         else
337             codepoint = keysym & 0xFFFFFF;
338
339         guac_client_log_info(client, "Translated keysym 0x%x to U+%04X", keysym, codepoint);
340
341         /* Send Unicode event */
342         rdp_inst->input->UnicodeKeyboardEvent(
343                 rdp_inst->input,
344                 0, codepoint);
345     }
346     
347     else
348         guac_client_log_info(client, "Ignoring key release (Unicode event)");
349
350     return 0;
351 }
352
353 void __guac_rdp_update_keysyms(guac_client* client, const int* keysym_string, int from, int to) {
354
355     rdp_guac_client_data* guac_client_data = (rdp_guac_client_data*) client->data;
356     int keysym;
357
358     /* Send all keysyms in string, NULL terminated */
359     while ((keysym = *keysym_string) != 0) {
360
361         /* Get current keysym state */
362         int current_state = GUAC_RDP_KEYSYM_LOOKUP(guac_client_data->keysym_state, keysym);
363
364         /* If key is currently in given state, send event for changing it to specified "to" state */
365         if (current_state == from)
366             __guac_rdp_send_keysym(client, *keysym_string, to);
367
368         /* Next keysym */
369         keysym_string++;
370
371     }
372
373 }
374
375 int rdp_guac_client_key_handler(guac_client* client, int keysym, int pressed) {
376
377     rdp_guac_client_data* guac_client_data = (rdp_guac_client_data*) client->data;
378
379     /* Update keysym state */
380     GUAC_RDP_KEYSYM_LOOKUP(guac_client_data->keysym_state, keysym) = pressed;
381
382     return __guac_rdp_send_keysym(client, keysym, pressed);
383
384 }
385
386 int rdp_guac_client_clipboard_handler(guac_client* client, char* data) {
387
388     rdpChannels* channels = 
389         ((rdp_guac_client_data*) client->data)->rdp_inst->context->channels;
390
391     RDP_CB_FORMAT_LIST_EVENT* format_list =
392         (RDP_CB_FORMAT_LIST_EVENT*) freerdp_event_new(
393             RDP_EVENT_CLASS_CLIPRDR,
394             RDP_EVENT_TYPE_CB_FORMAT_LIST,
395             NULL, NULL);
396
397     /* Free existing data */
398     free(((rdp_guac_client_data*) client->data)->clipboard);
399
400     /* Store data in client */
401     ((rdp_guac_client_data*) client->data)->clipboard = strdup(data);
402
403     /* Notify server that text data is now available */
404     format_list->formats = (uint32*) malloc(sizeof(uint32));
405     format_list->formats[0] = CB_FORMAT_TEXT;
406     format_list->num_formats = 1;
407
408     freerdp_channels_send_event(channels, (RDP_EVENT*) format_list);
409
410     return 0;
411
412 }
413