2 * FreeRDP: A Remote Desktop Protocol Client
5 * Copyright 2011 Marc-Andre Moreau <marcandre.moreau@gmail.com>
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
11 * http://www.apache.org/licenses/LICENSE-2.0
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.
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>
37 extern char* xf_pcap_file;
38 extern boolean xf_pcap_dump_realtime;
42 #include "xf_encode.h"
48 void xf_xdamage_init(xfInfo* xfi)
56 if (XShmQueryExtension(xfi->display) != False)
58 XShmQueryVersion(xfi->display, &major, &minor, &pixmaps);
62 printf("XShmQueryVersion failed\n");
68 printf("XShmQueryExtension failed\n");
72 if (XDamageQueryExtension(xfi->display, &damage_event, &damage_error) == 0)
74 printf("XDamageQueryExtension failed\n");
78 XDamageQueryVersion(xfi->display, &major, &minor);
80 if (XDamageQueryVersion(xfi->display, &major, &minor) == 0)
82 printf("XDamageQueryVersion failed\n");
87 printf("XDamageQueryVersion failed: major:%d minor:%d\n", major, minor);
91 xfi->xdamage_notify_event = damage_event + XDamageNotify;
92 xfi->xdamage = XDamageCreate(xfi->display, xfi->root_window, XDamageReportDeltaRectangles);
94 if (xfi->xdamage == None)
96 printf("XDamageCreate failed\n");
101 xfi->xdamage_region = XFixesCreateRegion(xfi->display, NULL, 0);
103 if (xfi->xdamage_region == None)
105 printf("XFixesCreateRegion failed\n");
106 XDamageDestroy(xfi->display, xfi->xdamage);
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);
119 void xf_xshm_init(xfInfo* xfi)
121 xfi->fb_shm_info.shmid = -1;
122 xfi->fb_shm_info.shmaddr = (char*) -1;
124 xfi->fb_image = XShmCreateImage(xfi->display, xfi->visual, xfi->depth,
125 ZPixmap, NULL, &(xfi->fb_shm_info), xfi->width, xfi->height);
127 if (xfi->fb_image == NULL)
129 printf("XShmCreateImage failed\n");
133 xfi->fb_shm_info.shmid = shmget(IPC_PRIVATE,
134 xfi->fb_image->bytes_per_line * xfi->fb_image->height, IPC_CREAT | 0600);
136 if (xfi->fb_shm_info.shmid == -1)
138 printf("shmget failed\n");
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;
146 if (xfi->fb_shm_info.shmaddr == ((char*) -1))
148 printf("shmat failed\n");
152 XShmAttach(xfi->display, &(xfi->fb_shm_info));
153 XSync(xfi->display, False);
155 shmctl(xfi->fb_shm_info.shmid, IPC_RMID, 0);
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);
162 xfInfo* xf_info_init()
170 XVisualInfo template;
171 XPixmapFormatValues* pf;
172 XPixmapFormatValues* pfs;
176 //xfi->use_xshm = true;
177 xfi->display = XOpenDisplay(NULL);
181 if (xfi->display == NULL)
183 printf("failed to open display: %s\n", XDisplayName(NULL));
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);
195 pfs = XListPixmapFormats(xfi->display, &pf_count);
199 printf("XListPixmapFormats failed\n");
203 for (i = 0; i < pf_count; i++)
207 if (pf->depth == xfi->depth)
209 xfi->bpp = pf->bits_per_pixel;
210 xfi->scanline_pad = pf->scanline_pad;
216 memset(&template, 0, sizeof(template));
217 template.class = TrueColor;
218 template.screen = xfi->number;
220 vis = XGetVisualInfo(xfi->display, VisualClassMask | VisualScreenMask, &template, &vi_count);
224 printf("XGetVisualInfo failed\n");
228 for (i = 0; i < vi_count; i++)
232 if (vi->depth == xfi->depth)
234 xfi->visual = vi->visual;
240 xfi->clrconv = freerdp_clrconv_new(CLRCONV_ALPHA | CLRCONV_INVERT);
242 XSelectInput(xfi->display, xfi->root_window, SubstructureNotifyMask);
245 xf_xdamage_init(xfi);
250 xfi->bytesPerPixel = 4;
252 freerdp_kbd_init(xfi->display, 0);
257 void xf_peer_context_new(freerdp_peer* client, xfPeerContext* context)
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;
265 rfx_context_set_pixel_format(context->rfx_context, RFX_PIXEL_FORMAT_BGRA);
267 context->s = stream_new(65536);
270 void xf_peer_context_free(freerdp_peer* client, xfPeerContext* context)
274 stream_free(context->s);
275 rfx_context_free(context->rfx_context);
280 void xf_peer_init(freerdp_peer* client)
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);
290 xfp = (xfPeerContext*) client->context;
294 xfp->activations = 0;
295 xfp->event_queue = xf_event_queue_new();
298 xfp->hdc = gdi_CreateDC(xfi->clrconv, xfi->bpp);
300 pthread_mutex_init(&(xfp->mutex), NULL);
303 STREAM* xf_peer_stream_init(xfPeerContext* context)
305 stream_clear(context->s);
306 stream_set_pos(context->s, 0);
310 void xf_peer_live_rfx(freerdp_peer* client)
312 xfPeerContext* xfp = (xfPeerContext*) client->context;
314 if (xfp->activations == 1)
315 pthread_create(&(xfp->thread), 0, xf_monitor_updates, (void*) client);
318 static boolean xf_peer_sleep_tsdiff(uint32 *old_sec, uint32 *old_usec, uint32 new_sec, uint32 new_usec)
322 if (*old_sec == 0 && *old_usec == 0)
325 *old_usec = new_usec;
329 sec = new_sec - *old_sec;
330 usec = new_usec - *old_usec;
332 if (sec < 0 || (sec == 0 && usec < 0))
334 printf("Invalid time stamp detected.\n");
339 *old_usec = new_usec;
351 freerdp_usleep(usec);
356 void xf_peer_dump_rfx(freerdp_peer* client)
360 uint32 prev_useconds;
366 update = client->update;
367 client->update->pcap_rfx = pcap_open(xf_pcap_file, false);
368 pcap_rfx = client->update->pcap_rfx;
370 if (pcap_rfx == NULL)
373 prev_seconds = prev_useconds = 0;
375 while (pcap_has_next_record(pcap_rfx))
377 pcap_get_next_record_header(pcap_rfx, &record);
379 s->data = xrealloc(s->data, record.length);
380 record.data = s->data;
381 s->size = record.length;
383 pcap_get_next_record_content(pcap_rfx, &record);
384 s->p = s->data + s->size;
386 if (xf_pcap_dump_realtime && xf_peer_sleep_tsdiff(&prev_seconds, &prev_useconds, record.header.ts_sec, record.header.ts_usec) == false)
389 update->SurfaceCommand(update->context, s);
393 void xf_peer_rfx_update(freerdp_peer* client, int x, int y, int width, int height)
402 SURFACE_BITS_COMMAND* cmd;
404 update = client->update;
405 xfp = (xfPeerContext*) client->context;
406 cmd = &update->surface_bits_command;
409 if (width * height <= 0)
412 s = xf_peer_stream_init(xfp);
424 rect.height = height;
426 image = xf_snapshot(xfp, x, y, width, height);
428 data = (uint8*) image->data;
429 data = &data[(y * image->bytes_per_line) + (x * image->bits_per_pixel)];
431 rfx_compose_message(xfp->rfx_context, s, &rect, 1, data,
432 width, height, image->bytes_per_line);
436 cmd->destRight = x + width;
437 cmd->destBottom = y + height;
444 rect.height = height;
446 image = xf_snapshot(xfp, x, y, width, height);
448 rfx_compose_message(xfp->rfx_context, s, &rect, 1,
449 (uint8*) image->data, width, height, width * xfi->bytesPerPixel);
453 cmd->destRight = x + width;
454 cmd->destBottom = y + height;
456 XDestroyImage(image);
460 cmd->codecID = client->settings->rfx_codec_id;
462 cmd->height = height;
463 cmd->bitmapDataLength = stream_get_length(s);
464 cmd->bitmapData = stream_get_head(s);
466 update->SurfaceBits(update->context, cmd);
469 boolean xf_peer_get_fds(freerdp_peer* client, void** rfds, int* rcount)
471 xfPeerContext* xfp = (xfPeerContext*) client->context;
473 if (xfp->event_queue->pipe_fd[0] == -1)
476 rfds[*rcount] = (void *)(long) xfp->event_queue->pipe_fd[0];
482 boolean xf_peer_check_fds(freerdp_peer* client)
487 HGDI_RGN invalid_region;
489 xfp = (xfPeerContext*) client->context;
492 if (xfp->activated == false)
495 event = xf_event_peek(xfp->event_queue);
499 if (event->type == XF_EVENT_TYPE_REGION)
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);
505 else if (event->type == XF_EVENT_TYPE_FRAME_TICK)
507 event = xf_event_pop(xfp->event_queue);
508 invalid_region = xfp->hdc->hwnd->invalid;
510 if (invalid_region->null == false)
512 xf_peer_rfx_update(client, invalid_region->x, invalid_region->y,
513 invalid_region->w, invalid_region->h);
516 invalid_region->null = 1;
517 xfp->hdc->hwnd->ninvalid = 0;
519 xf_event_free(event);
526 boolean xf_peer_capabilities(freerdp_peer* client)
531 boolean xf_peer_post_connect(freerdp_peer* client)
536 xfp = (xfPeerContext*) client->context;
537 xfi = (xfInfo*) xfp->info;
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
545 printf("Client %s is activated", client->hostname);
546 if (client->settings->autologon)
548 printf(" and wants to login automatically as %s\\%s",
549 client->settings->domain ? client->settings->domain : "",
550 client->settings->username);
552 /* A real server may perform OS login here if NLA is not executed previously. */
556 printf("Client requested desktop: %dx%dx%d\n",
557 client->settings->width, client->settings->height, client->settings->color_depth);
559 /* A real server should tag the peer as activated here and start sending updates in mainloop. */
561 client->settings->width = xfi->width;
562 client->settings->height = xfi->height;
564 client->update->DesktopResize(client->update->context);
565 xfp->activated = false;
567 /* Return false here would stop the execution of the peer mainloop. */
571 boolean xf_peer_activate(freerdp_peer* client)
573 xfPeerContext* xfp = (xfPeerContext*) client->context;
575 rfx_context_reset(xfp->rfx_context);
576 xfp->activated = true;
578 if (xf_pcap_file != NULL)
580 client->update->dump_rfx = true;
581 xf_peer_dump_rfx(client);
585 xf_peer_live_rfx(client);
592 void* xf_peer_main_loop(void* arg)
600 rdpSettings* settings;
601 char* server_file_path;
602 freerdp_peer* client = (freerdp_peer*) arg;
604 memset(rfds, 0, sizeof(rfds));
606 printf("We've got a client %s\n", client->hostname);
608 xf_peer_init(client);
609 settings = client->settings;
611 /* Initialize the real server settings here */
613 if (settings->development_mode)
615 server_file_path = freerdp_construct_path(settings->development_path, "server/X11");
619 server_file_path = freerdp_construct_path(settings->config_path, "server");
621 if (!freerdp_check_file_exists(server_file_path))
622 freerdp_mkdir(server_file_path);
625 settings->cert_file = freerdp_construct_path(server_file_path, "server.crt");
626 settings->privatekey_file = freerdp_construct_path(server_file_path, "server.key");
628 settings->nla_security = false;
629 settings->rfx_codec = true;
631 client->Capabilities = xf_peer_capabilities;
632 client->PostConnect = xf_peer_post_connect;
633 client->Activate = xf_peer_activate;
635 xf_input_register_callbacks(client->input);
637 client->Initialize(client);
643 if (client->GetFileDescriptor(client, rfds, &rcount) != true)
645 printf("Failed to get FreeRDP file descriptor\n");
648 if (xf_peer_get_fds(client, rfds, &rcount) != true)
650 printf("Failed to get xfreerdp file descriptor\n");
657 for (i = 0; i < rcount; i++)
659 fds = (int)(long)(rfds[i]);
664 FD_SET(fds, &rfds_set);
670 if (select(max_fds + 1, &rfds_set, NULL, NULL, NULL) == -1)
672 /* these are not really errors */
673 if (!((errno == EAGAIN) ||
674 (errno == EWOULDBLOCK) ||
675 (errno == EINPROGRESS) ||
676 (errno == EINTR))) /* signal occurred */
678 printf("select failed\n");
683 if (client->CheckFileDescriptor(client) != true)
685 printf("Failed to check freerdp file descriptor\n");
688 if ((xf_peer_check_fds(client)) != true)
690 printf("Failed to check xfreerdp file descriptor\n");
695 printf("Client %s disconnected.\n", client->hostname);
697 client->Disconnect(client);
698 freerdp_peer_context_free(client);
699 freerdp_peer_free(client);
704 void xf_peer_accepted(freerdp_listener* instance, freerdp_peer* client)
708 pthread_create(&th, 0, xf_peer_main_loop, client);