Fixed stub, added rudimentary mouse support, support for clip, coloring of text.
[libguac-client-rdp.git] / src / rdp_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  *
24  * Alternatively, the contents of this file may be used under the terms of
25  * either the GNU General Public License Version 2 or later (the "GPL"), or
26  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27  * in which case the provisions of the GPL or the LGPL are applicable instead
28  * of those above. If you wish to allow use of your version of this file only
29  * under the terms of either the GPL or the LGPL, and not to allow others to
30  * use your version of this file under the terms of the MPL, indicate your
31  * decision by deleting the provisions above and replace them with the notice
32  * and other provisions required by the GPL or the LGPL. If you do not delete
33  * the provisions above, a recipient may use your version of this file under
34  * the terms of any one of the MPL, the GPL or the LGPL.
35  *
36  * ***** END LICENSE BLOCK ***** */
37
38 #include <stdio.h>
39 #include <stdlib.h>
40
41 #include <cairo/cairo.h>
42
43 #include <guacamole/log.h>
44 #include <guacamole/guacio.h>
45 #include <guacamole/client.h>
46 #include <guacamole/protocol.h>
47
48 #include <freerdp/freerdp.h>
49
50 #include "rdp_handlers.h"
51 #include "rdp_client.h"
52
53
54 void guac_rdp_convert_color(int depth, int color, guac_rdp_color* comp) {
55
56     switch (depth) {
57
58         case 24:
59             comp->red   = (color >> 16) & 0xFF;
60             comp->green = (color >>  8) & 0xFF;
61             comp->blue  = (color      ) & 0xFF;
62             break;
63
64         case 16:
65             comp->red   = ((color >> 8) & 0xF8) | ((color >> 13) & 0x07);
66             comp->green = ((color >> 3) & 0xFC) | ((color >>  9) & 0x03);
67             comp->blue  = ((color << 3) & 0xF8) | ((color >>  2) & 0x07);
68             break;
69
70         default: /* The Magenta of Failure */
71             comp->red   = 0xFF;
72             comp->green = 0x00;
73             comp->blue  = 0xFF;
74     }
75
76 }
77
78 void guac_rdp_ui_error(rdpInst* inst, const char* text) {
79
80     guac_client* client = (guac_client*) inst->param1;
81     GUACIO* io = client->io;
82
83     guac_send_error(io, text);
84     guac_flush(io);
85
86 }
87
88 void guac_rdp_ui_warning(rdpInst* inst, const char* text) {
89     guac_log_info("guac_rdp_ui_warning: %s\n", text);
90 }
91
92 void guac_rdp_ui_unimpl(rdpInst* inst, const char* text) {
93     guac_log_info("guac_rdp_ui_unimpl: %s\n", text);
94 }
95
96 void guac_rdp_ui_begin_update(rdpInst* inst) {
97     /* UNUSED */
98 }
99
100 void guac_rdp_ui_end_update(rdpInst* inst) {
101     guac_client* client = (guac_client*) inst->param1;
102     GUACIO* io = client->io;
103     guac_flush(io);
104 }
105
106 void guac_rdp_ui_desktop_save(rdpInst* inst, int offset, int x, int y, int cx, int cy) {
107     guac_log_info("guac_rdp_ui_desktop_save: STUB\n");
108 }
109
110 void guac_rdp_ui_desktop_restore(rdpInst* inst, int offset, int x, int y, int cx, int cy) {
111     guac_log_info("guac_rdp_ui_desktop_restore: STUB\n");
112 }
113
114
115 RD_HBITMAP guac_rdp_ui_create_bitmap(rdpInst* inst, int width, int height, uint8* data) {
116
117     /* Allocate buffer */
118     guac_client* client = (guac_client*) inst->param1;
119     GUACIO* io = client->io; 
120     guac_layer* buffer = guac_client_alloc_buffer(client);
121
122     int x, y;
123     int stride;
124     int bpp = inst->settings->server_depth / 8;
125     unsigned char* image_buffer;
126     unsigned char* image_buffer_row;
127
128     cairo_surface_t* surface;
129
130     /* Init Cairo buffer */
131     stride = cairo_format_stride_for_width(CAIRO_FORMAT_RGB24, width);
132     image_buffer = malloc(height*stride);
133     image_buffer_row = image_buffer;
134
135     /* Copy image data from image data to buffer */
136     for (y = 0; y<height; y++) {
137
138         unsigned int*  image_buffer_current;
139         
140         /* Get current buffer row, advance to next */
141         image_buffer_current  = (unsigned int*) image_buffer_row;
142         image_buffer_row     += stride;
143
144         for (x = 0; x<width; x++) {
145
146             unsigned char red, green, blue;
147             unsigned int v;
148
149             switch (bpp) {
150                 case 3:
151                     blue  = *((unsigned char*) data++);
152                     green = *((unsigned char*) data++);
153                     red   = *((unsigned char*) data++);
154                     break;
155
156                 case 2:
157                     v  = *((unsigned char*) data++);
158                     v |= *((unsigned char*) data++) << 8;
159
160                     red   = ((v >> 8) & 0xF8) | ((v >> 13) & 0x07);
161                     green = ((v >> 3) & 0xFC) | ((v >>  9) & 0x03);
162                     blue  = ((v << 3) & 0xF8) | ((v >>  2) & 0x07);
163                     break;
164
165                 default: /* The Magenta of Failure */
166                     red   = 0xFF;
167                     green = 0x00;
168                     blue  = 0xFF;
169             }
170
171             /* Output RGB */
172             *(image_buffer_current++) = (red << 16) | (green << 8) | blue;
173
174         }
175     }
176
177     surface = cairo_image_surface_create_for_data(image_buffer, CAIRO_FORMAT_RGB24, width, height, stride);
178     guac_send_png(io, GUAC_COMP_OVER, buffer, 0, 0, surface);
179
180     /* Free surface */
181     cairo_surface_destroy(surface);
182     free(image_buffer);
183
184     return (RD_HBITMAP) buffer;
185
186 }
187
188 void guac_rdp_ui_paint_bitmap(rdpInst* inst, int x, int y, int cx, int cy, int width, int height, uint8* data) {
189     guac_log_info("guac_rdp_ui_paint_bitmap: STUB\n");
190 }
191
192 void guac_rdp_ui_destroy_bitmap(rdpInst* inst, RD_HBITMAP bmp) {
193
194     /* Free buffer */
195     guac_client* client = (guac_client*) inst->param1;
196     guac_client_free_buffer(client, (guac_layer*) bmp);
197
198 }
199
200 void guac_rdp_ui_line(rdpInst* inst, uint8 opcode, int startx, int starty, int endx, int endy, RD_PEN* pen) {
201     guac_log_info("guac_rdp_ui_line: STUB\n");
202 }
203
204 void guac_rdp_ui_rect(rdpInst* inst, int x, int y, int cx, int cy, int color) {
205
206     guac_client* client = (guac_client*) inst->param1;
207     GUACIO* io = client->io;
208
209     unsigned char red, green, blue;
210
211     switch (inst->settings->server_depth) {
212         case 24:
213             red   = (color >> 16) & 0xFF;
214             green = (color >>  8) & 0xFF;
215             blue  = (color      ) & 0xFF;
216             break;
217
218         case 16:
219             red   = ((color >> 8) & 0xF8) | ((color >> 13) & 0x07);
220             green = ((color >> 3) & 0xFC) | ((color >>  9) & 0x03);
221             blue  = ((color << 3) & 0xF8) | ((color >>  2) & 0x07);
222             break;
223
224         default: /* The Magenta of Failure */
225             red   = 0xFF;
226             green = 0x00;
227             blue  = 0xFF;
228     }
229
230     /* Send rectangle */
231     guac_send_rect(io, GUAC_COMP_OVER, GUAC_DEFAULT_LAYER,
232             x, y, cx, cy,
233             red, green, blue, 0xFF);
234
235 }
236
237 void guac_rdp_ui_polygon(rdpInst* inst, uint8 opcode, uint8 fillmode, RD_POINT* point, int npoints, RD_BRUSH* brush, int bgcolor, int fgcolor) {
238     guac_log_info("guac_rdp_ui_polygon: STUB\n");
239 }
240
241 void guac_rdp_ui_polyline(rdpInst* inst, uint8 opcode, RD_POINT* points, int npoints, RD_PEN* pen) {
242     guac_log_info("guac_rdp_ui_polyline: STUB\n");
243 }
244
245 void guac_rdp_ui_ellipse(rdpInst* inst, uint8 opcode, uint8 fillmode, int x, int y, int cx, int cy, RD_BRUSH*  brush, int bgcolor, int fgcolor) {
246     guac_log_info("guac_rdp_ui_ellipse: STUB\n");
247 }
248
249 void guac_rdp_ui_start_draw_glyphs(rdpInst* inst, int bgcolor, int fgcolor) {
250
251     guac_client* client = (guac_client*) inst->param1;
252     rdp_guac_client_data* guac_client_data = (rdp_guac_client_data*) client->data;
253
254     guac_rdp_convert_color(
255             inst->settings->server_depth,
256             bgcolor,
257             &(guac_client_data->background));
258
259     guac_rdp_convert_color(
260             inst->settings->server_depth,
261             fgcolor,
262             &(guac_client_data->foreground));
263
264 }
265
266 void guac_rdp_ui_draw_glyph(rdpInst* inst, int x, int y, int width, int height, RD_HGLYPH glyph) {
267
268     guac_client* client = (guac_client*) inst->param1;
269     rdp_guac_client_data* guac_client_data = (rdp_guac_client_data*) client->data;
270     GUACIO* io = client->io;
271
272     /* NOTE: Originally: Stencil=SRC, FG=ATOP, BG=RATOP */
273     /* Temporarily removed BG drawing... */
274
275     /* Stencil */
276     guac_send_copy(io,
277             (guac_layer*) glyph, 0, 0, width, height,
278             GUAC_COMP_ROUT, GUAC_DEFAULT_LAYER, x, y);
279
280     /* Foreground */
281     guac_send_rect(io, GUAC_COMP_RATOP, GUAC_DEFAULT_LAYER,
282             x, y, width, height,
283             guac_client_data->foreground.red,
284             guac_client_data->foreground.green,
285             guac_client_data->foreground.blue,
286             255);
287
288     /* Background */
289     /*guac_send_rect(io, GUAC_COMP_RATOP, GUAC_DEFAULT_LAYER,
290             x, y, width, height,
291             guac_client_data->background.red,
292             guac_client_data->background.green,
293             guac_client_data->background.blue,
294             255);*/
295
296 }
297
298 void guac_rdp_ui_end_draw_glyphs(rdpInst* inst, int x, int y, int cx, int cy) {
299     /* UNUSED */
300 }
301
302 uint32 guac_rdp_ui_get_toggle_keys_state(rdpInst* inst) {
303     guac_log_info("guac_rdp_ui_get_toggle_keys_state: STUB\n");
304     return 0;
305 }
306
307 void guac_rdp_ui_bell(rdpInst* inst) {
308     guac_log_info("guac_rdp_ui_bell: STUB\n");
309 }
310
311 void guac_rdp_ui_destblt(rdpInst* inst, uint8 opcode, int x, int y, int cx, int cy) {
312     guac_log_info("guac_rdp_ui_destblt: STUB\n");
313 }
314
315 void guac_rdp_ui_patblt(rdpInst* inst, uint8 opcode, int x, int y, int cx, int cy, RD_BRUSH* brush, int bgcolor, int fgcolor) {
316     guac_log_info("guac_rdp_ui_patblt: STUB\n");
317 }
318
319 void guac_rdp_ui_screenblt(rdpInst* inst, uint8 opcode, int x, int y, int cx, int cy, int srcx, int srcy) {
320     guac_log_info("guac_rdp_ui_screenblt: STUB\n");
321 }
322
323 void guac_rdp_ui_memblt(rdpInst* inst, uint8 opcode, int x, int y, int width, int height, RD_HBITMAP src, int srcx, int srcy) {
324
325     guac_client* client = (guac_client*) inst->param1;
326     GUACIO* io = client->io;
327
328     if (opcode != 204)
329         guac_log_info("guac_rdp_ui_memblt: opcode=%i, index=%i\n", opcode,
330                 ((guac_layer*) src)->index);
331
332     guac_send_copy(io,
333             (guac_layer*) src, srcx, srcy, width, height,
334             GUAC_COMP_OVER, GUAC_DEFAULT_LAYER, x, y);
335
336 }
337
338 void guac_rdp_ui_triblt(rdpInst* inst, uint8 opcode, int x, int y, int cx, int cy, RD_HBITMAP src, int srcx, int srcy, RD_BRUSH* brush, int bgcolor,  int fgcolor) {
339     guac_log_info("guac_rdp_ui_triblt: STUB\n");
340 }
341
342 RD_HGLYPH guac_rdp_ui_create_glyph(rdpInst* inst, int width, int height, uint8* data) {
343
344     /* Allocate buffer */
345     guac_client* client = (guac_client*) inst->param1;
346     GUACIO* io = client->io;
347     guac_layer* glyph = guac_client_alloc_buffer(client);
348
349     int x, y, i;
350     int stride;
351     unsigned char* image_buffer;
352     unsigned char* image_buffer_row;
353
354     cairo_surface_t* surface;
355
356     /* Init Cairo buffer */
357     stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, width);
358     image_buffer = malloc(height*stride);
359     image_buffer_row = image_buffer;
360
361     /* Copy image data from image data to buffer */
362     for (y = 0; y<height; y++) {
363
364         unsigned int*  image_buffer_current;
365         
366         /* Get current buffer row, advance to next */
367         image_buffer_current  = (unsigned int*) image_buffer_row;
368         image_buffer_row     += stride;
369
370         for (x = 0; x<width;) {
371
372             /* Get byte from image data */
373             unsigned int v = *(data++);
374
375             /* Read bits, write pixels */
376             for (i = 0; i<8 && x<width; i++, x++) {
377
378                 /* Output RGB */
379                 if (v & 0x80)
380                     *(image_buffer_current++) = 0xFF000000;
381                 else
382                     *(image_buffer_current++) = 0x00000000;
383
384                 /* Next bit */
385                 v <<= 1;
386
387             }
388
389         }
390     }
391
392     surface = cairo_image_surface_create_for_data(image_buffer, CAIRO_FORMAT_ARGB32, width, height, stride);
393     guac_send_png(io, GUAC_COMP_SRC, glyph, 0, 0, surface);
394
395     /* Free surface */
396     cairo_surface_destroy(surface);
397     free(image_buffer);
398
399
400     return (RD_HGLYPH) glyph;
401
402 }
403
404 void guac_rdp_ui_destroy_glyph(rdpInst* inst, RD_HGLYPH glyph) {
405
406     /* Free buffer */
407     guac_client* client = (guac_client*) inst->param1;
408     guac_client_free_buffer(client, (guac_layer*) glyph);
409
410 }
411
412 int guac_rdp_ui_select(rdpInst* inst, int rdp_socket) {
413     return 1;
414 }
415
416 void guac_rdp_ui_set_clip(rdpInst* inst, int x, int y, int cx, int cy) {
417
418     guac_client* client = (guac_client*) inst->param1;
419     GUACIO* io = client->io;
420
421     guac_send_clip(io, GUAC_DEFAULT_LAYER, x, y, cx, cy);
422
423 }
424
425 void guac_rdp_ui_reset_clip(rdpInst* inst) {
426
427     guac_client* client = (guac_client*) inst->param1;
428     GUACIO* io = client->io;
429
430     guac_send_clip(io, GUAC_DEFAULT_LAYER, 0, 0, inst->settings->width, inst->settings->height);
431
432 }
433
434 void guac_rdp_ui_resize_window(rdpInst* inst) {
435     guac_log_info("guac_rdp_ui_resize_window: %ix%i\n", inst->settings->width, inst->settings->height);
436 }
437
438 void guac_rdp_ui_set_cursor(rdpInst* inst, RD_HCURSOR cursor) {
439     guac_log_info("guac_rdp_ui_set_cursor: STUB\n");
440 }
441
442 void guac_rdp_ui_destroy_cursor(rdpInst* inst, RD_HCURSOR cursor) {
443     guac_log_info("guac_rdp_ui_destroy_cursor: STUB\n");
444 }
445
446 RD_HCURSOR guac_rdp_ui_create_cursor(rdpInst* inst, unsigned int x, unsigned int y, int width, int height, uint8* andmask, uint8* xormask, int bpp) {
447     
448     guac_client* client = (guac_client*) inst->param1;
449     guac_log_info("guac_rdp_ui_create_cursor: STUB\n");
450     return guac_client_alloc_buffer(client);
451
452 }
453
454 void guac_rdp_ui_set_null_cursor(rdpInst* inst) {
455     guac_log_info("guac_rdp_ui_set_null_cursor: STUB\n");
456 }
457
458 void guac_rdp_ui_set_default_cursor(rdpInst* inst) {
459     guac_log_info("guac_rdp_ui_set_default_cursor: STUB\n");
460 }
461
462 RD_HPALETTE guac_rdp_ui_create_colormap(rdpInst* inst, RD_PALETTE* colors) {
463     guac_log_info("guac_rdp_ui_create_colormap: STUB\n");
464     return NULL;
465 }
466
467 void guac_rdp_ui_move_pointer(rdpInst* inst, int x, int y) {
468     guac_log_info("guac_rdp_ui_move_pointer: STUB\n");
469 }
470
471 void guac_rdp_ui_set_colormap(rdpInst* inst, RD_HPALETTE map) {
472     guac_log_info("guac_rdp_ui_set_colormap: STUB\n");
473 }
474
475 RD_HBITMAP guac_rdp_ui_create_surface(rdpInst* inst, int width, int height, RD_HBITMAP old) {
476
477     /* Allocate and return buffer */
478     guac_client* client = (guac_client*) inst->param1;
479     return (RD_HBITMAP) guac_client_alloc_buffer(client);
480
481 }
482
483 void guac_rdp_ui_set_surface(rdpInst* inst, RD_HBITMAP surface) {
484
485     guac_client* client = (guac_client*) inst->param1;
486     GUACIO* io = client->io;
487
488     /* Init desktop */
489     if (surface == NULL) {
490
491         guac_send_name(io, inst->settings->server);
492         guac_send_size(io, inst->settings->width, inst->settings->height);
493         guac_flush(io);
494
495     }
496     else
497         guac_log_info("guac_rdp_ui_set_surface: STUB (surface=%p) ... %ix%i\n", surface, inst->settings->width, inst->settings->height);
498
499 }
500
501 void guac_rdp_ui_destroy_surface(rdpInst* inst, RD_HBITMAP surface) {
502
503     /* Free buffer */
504     guac_client* client = (guac_client*) inst->param1;
505     guac_client_free_buffer(client, (guac_layer*) surface);
506
507 }
508
509 void guac_rdp_ui_channel_data(rdpInst* inst, int chan_id, char* data, int data_size, int flags, int total_size) {
510     guac_log_info("guac_rdp_ui_channel_data: STUB\n");
511 }
512
513