Initial commit - from Precise source
[freerdp-ubuntu-pcb-backport.git] / server / X11 / xf_peer.c
1 /**
2  * FreeRDP: A Remote Desktop Protocol Client
3  * X11 Peer
4  *
5  * Copyright 2011 Marc-Andre Moreau <marcandre.moreau@gmail.com>
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  *     http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  */
19
20 #include <errno.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <unistd.h>
25 #include <sys/ipc.h>
26 #include <sys/shm.h>
27 #include <X11/Xlib.h>
28 #include <X11/Xutil.h>
29 #include <sys/select.h>
30 #include <freerdp/kbd/kbd.h>
31 #include <freerdp/codec/color.h>
32 #include <freerdp/utils/file.h>
33 #include <freerdp/utils/sleep.h>
34 #include <freerdp/utils/memory.h>
35 #include <freerdp/utils/thread.h>
36
37 extern char* xf_pcap_file;
38 extern boolean xf_pcap_dump_realtime;
39
40 #include "xf_event.h"
41 #include "xf_input.h"
42 #include "xf_encode.h"
43
44 #include "xf_peer.h"
45
46 #ifdef WITH_XDAMAGE
47
48 void xf_xdamage_init(xfInfo* xfi)
49 {
50         Bool pixmaps;
51         int damage_event;
52         int damage_error;
53         int major, minor;
54         XGCValues values;
55
56         if (XShmQueryExtension(xfi->display) != False)
57         {
58                 XShmQueryVersion(xfi->display, &major, &minor, &pixmaps);
59
60                 if (pixmaps != True)
61                 {
62                         printf("XShmQueryVersion failed\n");
63                         return;
64                 }
65         }
66         else
67         {
68                 printf("XShmQueryExtension failed\n");
69                 return;
70         }
71
72         if (XDamageQueryExtension(xfi->display, &damage_event, &damage_error) == 0)
73         {
74                 printf("XDamageQueryExtension failed\n");
75                 return;
76         }
77
78         XDamageQueryVersion(xfi->display, &major, &minor);
79
80         if (XDamageQueryVersion(xfi->display, &major, &minor) == 0)
81         {
82                 printf("XDamageQueryVersion failed\n");
83                 return;
84         }
85         else if (major < 1)
86         {
87                 printf("XDamageQueryVersion failed: major:%d minor:%d\n", major, minor);
88                 return;
89         }
90
91         xfi->xdamage_notify_event = damage_event + XDamageNotify;
92         xfi->xdamage = XDamageCreate(xfi->display, xfi->root_window, XDamageReportDeltaRectangles);
93
94         if (xfi->xdamage == None)
95         {
96                 printf("XDamageCreate failed\n");
97                 return;
98         }
99
100 #ifdef WITH_XFIXES
101         xfi->xdamage_region = XFixesCreateRegion(xfi->display, NULL, 0);
102
103         if (xfi->xdamage_region == None)
104         {
105                 printf("XFixesCreateRegion failed\n");
106                 XDamageDestroy(xfi->display, xfi->xdamage);
107                 xfi->xdamage = None;
108                 return;
109         }
110 #endif
111
112         values.subwindow_mode = IncludeInferiors;
113         xfi->xdamage_gc = XCreateGC(xfi->display, xfi->root_window, GCSubwindowMode, &values);
114         XSetFunction(xfi->display, xfi->xdamage_gc, GXcopy);
115 }
116
117 #endif
118
119 void xf_xshm_init(xfInfo* xfi)
120 {
121         xfi->fb_shm_info.shmid = -1;
122         xfi->fb_shm_info.shmaddr = (char*) -1;
123
124         xfi->fb_image = XShmCreateImage(xfi->display, xfi->visual, xfi->depth,
125                         ZPixmap, NULL, &(xfi->fb_shm_info), xfi->width, xfi->height);
126
127         if (xfi->fb_image == NULL)
128         {
129                 printf("XShmCreateImage failed\n");
130                 return;
131         }
132
133         xfi->fb_shm_info.shmid = shmget(IPC_PRIVATE,
134                         xfi->fb_image->bytes_per_line * xfi->fb_image->height, IPC_CREAT | 0600);
135
136         if (xfi->fb_shm_info.shmid == -1)
137         {
138                 printf("shmget failed\n");
139                 return;
140         }
141
142         xfi->fb_shm_info.readOnly = False;
143         xfi->fb_shm_info.shmaddr = shmat(xfi->fb_shm_info.shmid, 0, 0);
144         xfi->fb_image->data = xfi->fb_shm_info.shmaddr;
145
146         if (xfi->fb_shm_info.shmaddr == ((char*) -1))
147         {
148                 printf("shmat failed\n");
149                 return;
150         }
151
152         XShmAttach(xfi->display, &(xfi->fb_shm_info));
153         XSync(xfi->display, False);
154
155         shmctl(xfi->fb_shm_info.shmid, IPC_RMID, 0);
156
157         xfi->fb_pixmap = XShmCreatePixmap(xfi->display,
158                         xfi->root_window, xfi->fb_image->data, &(xfi->fb_shm_info),
159                         xfi->fb_image->width, xfi->fb_image->height, xfi->fb_image->depth);
160 }
161
162 xfInfo* xf_info_init()
163 {
164         int i;
165         xfInfo* xfi;
166         int pf_count;
167         int vi_count;
168         XVisualInfo* vi;
169         XVisualInfo* vis;
170         XVisualInfo template;
171         XPixmapFormatValues* pf;
172         XPixmapFormatValues* pfs;
173
174         xfi = xnew(xfInfo);
175
176         //xfi->use_xshm = true;
177         xfi->display = XOpenDisplay(NULL);
178
179         XInitThreads();
180
181         if (xfi->display == NULL)
182         {
183                 printf("failed to open display: %s\n", XDisplayName(NULL));
184                 exit(1);
185         }
186
187         xfi->xfds = ConnectionNumber(xfi->display);
188         xfi->number = DefaultScreen(xfi->display);
189         xfi->screen = ScreenOfDisplay(xfi->display, xfi->number);
190         xfi->depth = DefaultDepthOfScreen(xfi->screen);
191         xfi->width = WidthOfScreen(xfi->screen);
192         xfi->height = HeightOfScreen(xfi->screen);
193         xfi->root_window = DefaultRootWindow(xfi->display);
194
195         pfs = XListPixmapFormats(xfi->display, &pf_count);
196
197         if (pfs == NULL)
198         {
199                 printf("XListPixmapFormats failed\n");
200                 exit(1);
201         }
202
203         for (i = 0; i < pf_count; i++)
204         {
205                 pf = pfs + i;
206
207                 if (pf->depth == xfi->depth)
208                 {
209                         xfi->bpp = pf->bits_per_pixel;
210                         xfi->scanline_pad = pf->scanline_pad;
211                         break;
212                 }
213         }
214         XFree(pfs);
215
216         memset(&template, 0, sizeof(template));
217         template.class = TrueColor;
218         template.screen = xfi->number;
219
220         vis = XGetVisualInfo(xfi->display, VisualClassMask | VisualScreenMask, &template, &vi_count);
221
222         if (vis == NULL)
223         {
224                 printf("XGetVisualInfo failed\n");
225                 exit(1);
226         }
227
228         for (i = 0; i < vi_count; i++)
229         {
230                 vi = vis + i;
231
232                 if (vi->depth == xfi->depth)
233                 {
234                         xfi->visual = vi->visual;
235                         break;
236                 }
237         }
238         XFree(vis);
239
240         xfi->clrconv = freerdp_clrconv_new(CLRCONV_ALPHA | CLRCONV_INVERT);
241
242         XSelectInput(xfi->display, xfi->root_window, SubstructureNotifyMask);
243
244 #ifdef WITH_XDAMAGE
245         xf_xdamage_init(xfi);
246 #endif
247
248         xf_xshm_init(xfi);
249
250         xfi->bytesPerPixel = 4;
251
252         freerdp_kbd_init(xfi->display, 0);
253
254         return xfi;
255 }
256
257 void xf_peer_context_new(freerdp_peer* client, xfPeerContext* context)
258 {
259         context->info = xf_info_init();
260         context->rfx_context = rfx_context_new();
261         context->rfx_context->mode = RLGR3;
262         context->rfx_context->width = context->info->width;
263         context->rfx_context->height = context->info->height;
264
265         rfx_context_set_pixel_format(context->rfx_context, RFX_PIXEL_FORMAT_BGRA);
266
267         context->s = stream_new(65536);
268 }
269
270 void xf_peer_context_free(freerdp_peer* client, xfPeerContext* context)
271 {
272         if (context)
273         {
274                 stream_free(context->s);
275                 rfx_context_free(context->rfx_context);
276                 xfree(context);
277         }
278 }
279
280 void xf_peer_init(freerdp_peer* client)
281 {
282         xfInfo* xfi;
283         xfPeerContext* xfp;
284
285         client->context_size = sizeof(xfPeerContext);
286         client->ContextNew = (psPeerContextNew) xf_peer_context_new;
287         client->ContextFree = (psPeerContextFree) xf_peer_context_free;
288         freerdp_peer_context_new(client);
289
290         xfp = (xfPeerContext*) client->context;
291
292         xfp->fps = 24;
293         xfp->thread = 0;
294         xfp->activations = 0;
295         xfp->event_queue = xf_event_queue_new();
296
297         xfi = xfp->info;
298         xfp->hdc = gdi_CreateDC(xfi->clrconv, xfi->bpp);
299
300         pthread_mutex_init(&(xfp->mutex), NULL);
301 }
302
303 STREAM* xf_peer_stream_init(xfPeerContext* context)
304 {
305         stream_clear(context->s);
306         stream_set_pos(context->s, 0);
307         return context->s;
308 }
309
310 void xf_peer_live_rfx(freerdp_peer* client)
311 {
312         xfPeerContext* xfp = (xfPeerContext*) client->context;
313
314         if (xfp->activations == 1)
315                 pthread_create(&(xfp->thread), 0, xf_monitor_updates, (void*) client);
316 }
317
318 static boolean xf_peer_sleep_tsdiff(uint32 *old_sec, uint32 *old_usec, uint32 new_sec, uint32 new_usec)
319 {
320         sint32 sec, usec;
321
322         if (*old_sec == 0 && *old_usec == 0)
323         {
324                 *old_sec = new_sec;
325                 *old_usec = new_usec;
326                 return true;
327         }
328
329         sec = new_sec - *old_sec;
330         usec = new_usec - *old_usec;
331
332         if (sec < 0 || (sec == 0 && usec < 0))
333         {
334                 printf("Invalid time stamp detected.\n");
335                 return false;
336         }
337
338         *old_sec = new_sec;
339         *old_usec = new_usec;
340
341         while (usec < 0)
342         {
343                 usec += 1000000;
344                 sec--;
345         }
346
347         if (sec > 0)
348                 freerdp_sleep(sec);
349
350         if (usec > 0)
351                 freerdp_usleep(usec);
352
353         return true;
354 }
355
356 void xf_peer_dump_rfx(freerdp_peer* client)
357 {
358         STREAM* s;
359         uint32 prev_seconds;
360         uint32 prev_useconds;
361         rdpUpdate* update;
362         rdpPcap* pcap_rfx;
363         pcap_record record;
364
365         s = stream_new(512);
366         update = client->update;
367         client->update->pcap_rfx = pcap_open(xf_pcap_file, false);
368         pcap_rfx = client->update->pcap_rfx;
369
370         if (pcap_rfx == NULL)
371                 return;
372
373         prev_seconds = prev_useconds = 0;
374
375         while (pcap_has_next_record(pcap_rfx))
376         {
377                 pcap_get_next_record_header(pcap_rfx, &record);
378
379                 s->data = xrealloc(s->data, record.length);
380                 record.data = s->data;
381                 s->size = record.length;
382
383                 pcap_get_next_record_content(pcap_rfx, &record);
384                 s->p = s->data + s->size;
385
386                 if (xf_pcap_dump_realtime && xf_peer_sleep_tsdiff(&prev_seconds, &prev_useconds, record.header.ts_sec, record.header.ts_usec) == false)
387                         break;
388
389                 update->SurfaceCommand(update->context, s);
390         }
391 }
392
393 void xf_peer_rfx_update(freerdp_peer* client, int x, int y, int width, int height)
394 {
395         STREAM* s;
396         uint8* data;
397         xfInfo* xfi;
398         RFX_RECT rect;
399         XImage* image;
400         rdpUpdate* update;
401         xfPeerContext* xfp;
402         SURFACE_BITS_COMMAND* cmd;
403
404         update = client->update;
405         xfp = (xfPeerContext*) client->context;
406         cmd = &update->surface_bits_command;
407         xfi = xfp->info;
408
409         if (width * height <= 0)
410                 return;
411
412         s = xf_peer_stream_init(xfp);
413
414         if (xfi->use_xshm)
415         {
416                 width = x + width;
417                 height = y + height;
418                 x = 0;
419                 y = 0;
420
421                 rect.x = x;
422                 rect.y = y;
423                 rect.width = width;
424                 rect.height = height;
425
426                 image = xf_snapshot(xfp, x, y, width, height);
427
428                 data = (uint8*) image->data;
429                 data = &data[(y * image->bytes_per_line) + (x * image->bits_per_pixel)];
430
431                 rfx_compose_message(xfp->rfx_context, s, &rect, 1, data,
432                                 width, height, image->bytes_per_line);
433
434                 cmd->destLeft = x;
435                 cmd->destTop = y;
436                 cmd->destRight = x + width;
437                 cmd->destBottom = y + height;
438         }
439         else
440         {
441                 rect.x = 0;
442                 rect.y = 0;
443                 rect.width = width;
444                 rect.height = height;
445
446                 image = xf_snapshot(xfp, x, y, width, height);
447
448                 rfx_compose_message(xfp->rfx_context, s, &rect, 1,
449                                 (uint8*) image->data, width, height, width * xfi->bytesPerPixel);
450
451                 cmd->destLeft = x;
452                 cmd->destTop = y;
453                 cmd->destRight = x + width;
454                 cmd->destBottom = y + height;
455
456                 XDestroyImage(image);
457         }
458
459         cmd->bpp = 32;
460         cmd->codecID = client->settings->rfx_codec_id;
461         cmd->width = width;
462         cmd->height = height;
463         cmd->bitmapDataLength = stream_get_length(s);
464         cmd->bitmapData = stream_get_head(s);
465
466         update->SurfaceBits(update->context, cmd);
467 }
468
469 boolean xf_peer_get_fds(freerdp_peer* client, void** rfds, int* rcount)
470 {
471         xfPeerContext* xfp = (xfPeerContext*) client->context;
472
473         if (xfp->event_queue->pipe_fd[0] == -1)
474                 return true;
475
476         rfds[*rcount] = (void *)(long) xfp->event_queue->pipe_fd[0];
477         (*rcount)++;
478
479         return true;
480 }
481
482 boolean xf_peer_check_fds(freerdp_peer* client)
483 {
484         xfInfo* xfi;
485         xfEvent* event;
486         xfPeerContext* xfp;
487         HGDI_RGN invalid_region;
488
489         xfp = (xfPeerContext*) client->context;
490         xfi = xfp->info;
491
492         if (xfp->activated == false)
493                 return true;
494
495         event = xf_event_peek(xfp->event_queue);
496
497         if (event != NULL)
498         {
499                 if (event->type == XF_EVENT_TYPE_REGION)
500                 {
501                         xfEventRegion* region = (xfEventRegion*) xf_event_pop(xfp->event_queue);
502                         gdi_InvalidateRegion(xfp->hdc, region->x, region->y, region->width, region->height);
503                         xf_event_region_free(region);
504                 }
505                 else if (event->type == XF_EVENT_TYPE_FRAME_TICK)
506                 {
507                         event = xf_event_pop(xfp->event_queue);
508                         invalid_region = xfp->hdc->hwnd->invalid;
509
510                         if (invalid_region->null == false)
511                         {
512                                 xf_peer_rfx_update(client, invalid_region->x, invalid_region->y,
513                                         invalid_region->w, invalid_region->h);
514                         }
515
516                         invalid_region->null = 1;
517                         xfp->hdc->hwnd->ninvalid = 0;
518
519                         xf_event_free(event);
520                 }
521         }
522
523         return true;
524 }
525
526 boolean xf_peer_capabilities(freerdp_peer* client)
527 {
528         return true;
529 }
530
531 boolean xf_peer_post_connect(freerdp_peer* client)
532 {
533         xfInfo* xfi;
534         xfPeerContext* xfp;
535
536         xfp = (xfPeerContext*) client->context;
537         xfi = (xfInfo*) xfp->info;
538
539         /**
540          * This callback is called when the entire connection sequence is done, i.e. we've received the
541          * Font List PDU from the client and sent out the Font Map PDU.
542          * The server may start sending graphics output and receiving keyboard/mouse input after this
543          * callback returns.
544          */
545         printf("Client %s is activated", client->hostname);
546         if (client->settings->autologon)
547         {
548                 printf(" and wants to login automatically as %s\\%s",
549                         client->settings->domain ? client->settings->domain : "",
550                         client->settings->username);
551
552                 /* A real server may perform OS login here if NLA is not executed previously. */
553         }
554         printf("\n");
555
556         printf("Client requested desktop: %dx%dx%d\n",
557                 client->settings->width, client->settings->height, client->settings->color_depth);
558
559         /* A real server should tag the peer as activated here and start sending updates in mainloop. */
560
561         client->settings->width = xfi->width;
562         client->settings->height = xfi->height;
563
564         client->update->DesktopResize(client->update->context);
565         xfp->activated = false;
566
567         /* Return false here would stop the execution of the peer mainloop. */
568         return true;
569 }
570
571 boolean xf_peer_activate(freerdp_peer* client)
572 {
573         xfPeerContext* xfp = (xfPeerContext*) client->context;
574
575         rfx_context_reset(xfp->rfx_context);
576         xfp->activated = true;
577
578         if (xf_pcap_file != NULL)
579         {
580                 client->update->dump_rfx = true;
581                 xf_peer_dump_rfx(client);
582         }
583         else
584         {
585                 xf_peer_live_rfx(client);
586                 xfp->activations++;
587         }
588
589         return true;
590 }
591
592 void* xf_peer_main_loop(void* arg)
593 {
594         int i;
595         int fds;
596         int max_fds;
597         int rcount;
598         void* rfds[32];
599         fd_set rfds_set;
600         rdpSettings* settings;
601         char* server_file_path;
602         freerdp_peer* client = (freerdp_peer*) arg;
603
604         memset(rfds, 0, sizeof(rfds));
605
606         printf("We've got a client %s\n", client->hostname);
607
608         xf_peer_init(client);
609         settings = client->settings;
610
611         /* Initialize the real server settings here */
612
613         if (settings->development_mode)
614         {
615                 server_file_path = freerdp_construct_path(settings->development_path, "server/X11");
616         }
617         else
618         {
619                 server_file_path = freerdp_construct_path(settings->config_path, "server");
620
621                 if (!freerdp_check_file_exists(server_file_path))
622                         freerdp_mkdir(server_file_path);
623         }
624
625         settings->cert_file = freerdp_construct_path(server_file_path, "server.crt");
626         settings->privatekey_file = freerdp_construct_path(server_file_path, "server.key");
627
628         settings->nla_security = false;
629         settings->rfx_codec = true;
630
631         client->Capabilities = xf_peer_capabilities;
632         client->PostConnect = xf_peer_post_connect;
633         client->Activate = xf_peer_activate;
634
635         xf_input_register_callbacks(client->input);
636
637         client->Initialize(client);
638
639         while (1)
640         {
641                 rcount = 0;
642
643                 if (client->GetFileDescriptor(client, rfds, &rcount) != true)
644                 {
645                         printf("Failed to get FreeRDP file descriptor\n");
646                         break;
647                 }
648                 if (xf_peer_get_fds(client, rfds, &rcount) != true)
649                 {
650                         printf("Failed to get xfreerdp file descriptor\n");
651                         break;
652                 }
653
654                 max_fds = 0;
655                 FD_ZERO(&rfds_set);
656
657                 for (i = 0; i < rcount; i++)
658                 {
659                         fds = (int)(long)(rfds[i]);
660
661                         if (fds > max_fds)
662                                 max_fds = fds;
663
664                         FD_SET(fds, &rfds_set);
665                 }
666
667                 if (max_fds == 0)
668                         break;
669
670                 if (select(max_fds + 1, &rfds_set, NULL, NULL, NULL) == -1)
671                 {
672                         /* these are not really errors */
673                         if (!((errno == EAGAIN) ||
674                                 (errno == EWOULDBLOCK) ||
675                                 (errno == EINPROGRESS) ||
676                                 (errno == EINTR))) /* signal occurred */
677                         {
678                                 printf("select failed\n");
679                                 break;
680                         }
681                 }
682
683                 if (client->CheckFileDescriptor(client) != true)
684                 {
685                         printf("Failed to check freerdp file descriptor\n");
686                         break;
687                 }
688                 if ((xf_peer_check_fds(client)) != true)
689                 {
690                         printf("Failed to check xfreerdp file descriptor\n");
691                         break;
692                 }
693         }
694
695         printf("Client %s disconnected.\n", client->hostname);
696
697         client->Disconnect(client);
698         freerdp_peer_context_free(client);
699         freerdp_peer_free(client);
700
701         return NULL;
702 }
703
704 void xf_peer_accepted(freerdp_listener* instance, freerdp_peer* client)
705 {
706         pthread_t th;
707
708         pthread_create(&th, 0, xf_peer_main_loop, client);
709         pthread_detach(th);
710 }