2 * FreeRDP: A Remote Desktop Protocol Client
5 * Copyright 2009-2011 Jay Sorg
6 * Copyright 2010-2011 Vic Lee
7 * Copyright 2010-2011 Marc-Andre Moreau <marcandre.moreau@gmail.com>
9 * Licensed under the Apache License, Version 2.0 (the "License");
10 * you may not use this file except in compliance with the License.
11 * You may obtain a copy of the License at
13 * http://www.apache.org/licenses/LICENSE-2.0
15 * Unless required by applicable law or agreed to in writing, software
16 * distributed under the License is distributed on an "AS IS" BASIS,
17 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18 * See the License for the specific language governing permissions and
19 * limitations under the License.
26 #include <sys/types.h>
32 #include <freerdp/freerdp.h>
33 #include <freerdp/constants.h>
34 #include <freerdp/utils/args.h>
35 #include <freerdp/utils/event.h>
36 #include <freerdp/utils/memory.h>
37 #include <freerdp/channels/channels.h>
40 #include "wf_graphics.h"
48 typedef struct _thread_data thread_data;
51 HINSTANCE g_hInstance;
52 HCURSOR g_default_cursor;
53 volatile int g_thread_count = 0;
54 LPCTSTR g_wnd_class_name = L"wfreerdp";
56 void wf_context_new(freerdp* instance, rdpContext* context)
58 context->channels = freerdp_channels_new();
61 void wf_context_free(freerdp* instance, rdpContext* context)
66 int wf_create_console(void)
71 freopen("CONOUT$", "w", stdout);
72 printf("Debug console created.\n");
77 void wf_sw_begin_paint(rdpContext* context)
79 rdpGdi* gdi = context->gdi;
80 gdi->primary->hdc->hwnd->invalid->null = 1;
81 gdi->primary->hdc->hwnd->ninvalid = 0;
84 void wf_sw_end_paint(rdpContext* context)
96 wfi = ((wfContext*) context)->wfi;
98 if (gdi->primary->hdc->hwnd->ninvalid < 1)
101 ninvalid = gdi->primary->hdc->hwnd->ninvalid;
102 cinvalid = gdi->primary->hdc->hwnd->cinvalid;
104 for (i = 0; i < ninvalid; i++)
111 update_rect.left = x;
113 update_rect.right = x + w - 1;
114 update_rect.bottom = y + h - 1;
116 InvalidateRect(wfi->hwnd, &update_rect, FALSE);
120 void wf_hw_begin_paint(rdpContext* context)
122 wfInfo* wfi = ((wfContext*) context)->wfi;
123 wfi->hdc->hwnd->invalid->null = 1;
124 wfi->hdc->hwnd->ninvalid = 0;
127 void wf_hw_end_paint(rdpContext* context)
132 boolean wf_pre_connect(freerdp* instance)
137 rdpSettings* settings;
139 wfi = (wfInfo*) xzalloc(sizeof(wfInfo));
140 context = (wfContext*) instance->context;
141 wfi->instance = instance;
144 settings = instance->settings;
146 settings->os_major_type = OSMAJORTYPE_WINDOWS;
147 settings->os_minor_type = OSMINORTYPE_WINDOWS_NT;
148 settings->order_support[NEG_DSTBLT_INDEX] = true;
149 settings->order_support[NEG_PATBLT_INDEX] = true;
150 settings->order_support[NEG_SCRBLT_INDEX] = true;
151 settings->order_support[NEG_OPAQUE_RECT_INDEX] = true;
152 settings->order_support[NEG_DRAWNINEGRID_INDEX] = false;
153 settings->order_support[NEG_MULTIDSTBLT_INDEX] = false;
154 settings->order_support[NEG_MULTIPATBLT_INDEX] = false;
155 settings->order_support[NEG_MULTISCRBLT_INDEX] = false;
156 settings->order_support[NEG_MULTIOPAQUERECT_INDEX] = true;
157 settings->order_support[NEG_MULTI_DRAWNINEGRID_INDEX] = false;
158 settings->order_support[NEG_LINETO_INDEX] = true;
159 settings->order_support[NEG_POLYLINE_INDEX] = true;
160 settings->order_support[NEG_MEMBLT_INDEX] = true;
161 settings->order_support[NEG_MEM3BLT_INDEX] = false;
162 settings->order_support[NEG_SAVEBITMAP_INDEX] = false;
163 settings->order_support[NEG_GLYPH_INDEX_INDEX] = false;
164 settings->order_support[NEG_FAST_INDEX_INDEX] = false;
165 settings->order_support[NEG_FAST_GLYPH_INDEX] = false;
166 settings->order_support[NEG_POLYGON_SC_INDEX] = false;
167 settings->order_support[NEG_POLYGON_CB_INDEX] = false;
168 settings->order_support[NEG_ELLIPSE_SC_INDEX] = false;
169 settings->order_support[NEG_ELLIPSE_CB_INDEX] = false;
171 settings->glyph_cache = false;
173 wfi->cursor = g_default_cursor;
175 wfi->fullscreen = settings->fullscreen;
176 wfi->fs_toggle = wfi->fullscreen;
177 wfi->sw_gdi = settings->sw_gdi;
179 wfi->clrconv = (HCLRCONV) xzalloc(sizeof(CLRCONV));
180 wfi->clrconv->palette = NULL;
181 wfi->clrconv->alpha = 0;
183 instance->context->cache = cache_new(settings);
185 if (wfi->percentscreen > 0)
187 i1 = (GetSystemMetrics(SM_CXSCREEN) * wfi->percentscreen) / 100;
188 settings->width = i1;
190 i1 = (GetSystemMetrics(SM_CYSCREEN) * wfi->percentscreen) / 100;
191 settings->height = i1;
196 settings->width = GetSystemMetrics(SM_CXSCREEN);
197 settings->height = GetSystemMetrics(SM_CYSCREEN);
200 i1 = settings->width;
201 i1 = (i1 + 3) & (~3);
202 settings->width = i1;
204 if ((settings->width < 64) || (settings->height < 64) ||
205 (settings->width > 4096) || (settings->height > 4096))
207 printf("wf_pre_connect: invalid dimensions %d %d\n", settings->width, settings->height);
211 settings->kbd_layout = (int) GetKeyboardLayout(0) & 0x0000FFFF;
212 freerdp_channels_pre_connect(instance->context->channels, instance);
217 void cpuid(unsigned info, unsigned *eax, unsigned *ebx, unsigned *ecx, unsigned *edx)
219 #if defined(__GNUC__)
220 #if defined(__i386__) || defined(__x86_64__)
223 ("mov %%ebx, %%edi;" /* 32bit PIC: don't clobber ebx */
227 :"+a" (*eax), "=S" (*ebx), "=c" (*ecx), "=d" (*edx)
230 #elif defined(_MSC_VER)
240 uint32 wfi_detect_cpu()
243 unsigned int eax, ebx, ecx, edx = 0;
245 cpuid(1, &eax, &ebx, &ecx, &edx);
255 boolean wf_post_connect(freerdp* instance)
262 wchar_t win_title[64];
263 rdpSettings* settings;
265 settings = instance->settings;
266 context = (wfContext*) instance->context;
267 cache = instance->context->cache;
271 width = settings->width;
272 height = settings->height;
276 gdi_init(instance, CLRCONV_ALPHA | CLRCONV_INVERT | CLRBUF_32BPP, NULL);
277 gdi = instance->context->gdi;
278 wfi->hdc = gdi->primary->hdc;
279 wfi->primary = wf_image_new(wfi, width, height, wfi->dstBpp, gdi->primary_buffer);
281 rfx_context_set_cpu_opt(gdi->rfx_context, wfi_detect_cpu());
285 wf_gdi_register_update_callbacks(instance->update);
286 wfi->srcBpp = instance->settings->color_depth;
287 wfi->primary = wf_image_new(wfi, width, height, wfi->dstBpp, NULL);
289 wfi->hdc = gdi_GetDC();
290 wfi->hdc->bitsPerPixel = wfi->dstBpp;
291 wfi->hdc->bytesPerPixel = wfi->dstBpp / 8;
293 wfi->hdc->alpha = wfi->clrconv->alpha;
294 wfi->hdc->invert = wfi->clrconv->invert;
296 wfi->hdc->hwnd = (HGDI_WND) malloc(sizeof(GDI_WND));
297 wfi->hdc->hwnd->invalid = gdi_CreateRectRgn(0, 0, 0, 0);
298 wfi->hdc->hwnd->invalid->null = 1;
300 wfi->hdc->hwnd->count = 32;
301 wfi->hdc->hwnd->cinvalid = (HGDI_RGN) malloc(sizeof(GDI_RGN) * wfi->hdc->hwnd->count);
302 wfi->hdc->hwnd->ninvalid = 0;
304 wfi->image = wf_bitmap_new(wfi, 64, 64, 32, NULL);
305 wfi->image->_bitmap.data = NULL;
307 if (settings->rfx_codec)
309 wfi->tile = wf_bitmap_new(wfi, 64, 64, 32, NULL);
310 wfi->rfx_context = rfx_context_new();
311 rfx_context_set_cpu_opt(wfi->rfx_context, wfi_detect_cpu());
314 if (settings->ns_codec)
315 wfi->nsc_context = nsc_context_new();
318 if (settings->window_title != NULL)
319 _snwprintf(win_title, sizeof(win_title), L"%S", settings->window_title);
320 else if (settings->port == 3389)
321 _snwprintf(win_title, sizeof(win_title) / sizeof(win_title[0]), L"FreeRDP: %S", settings->hostname);
323 _snwprintf(win_title, sizeof(win_title) / sizeof(win_title[0]), L"FreeRDP: %S:%d", settings->hostname, settings->port);
327 wfi->hwnd = CreateWindowEx((DWORD) NULL, g_wnd_class_name, win_title,
328 0, 0, 0, 0, 0, NULL, NULL, g_hInstance, NULL);
330 SetWindowLongPtr(wfi->hwnd, GWLP_USERDATA, (LONG_PTR) wfi);
335 SetWindowLongPtr(wfi->hwnd, GWL_STYLE, WS_POPUP);
336 SetWindowPos(wfi->hwnd, HWND_TOP, 0, 0, width, height, SWP_FRAMECHANGED);
341 RECT rc_client, rc_wnd;
343 SetWindowLongPtr(wfi->hwnd, GWL_STYLE, WS_CAPTION | WS_OVERLAPPED | WS_SYSMENU | WS_MINIMIZEBOX);
344 /* Now resize to get full canvas size and room for caption and borders */
345 SetWindowPos(wfi->hwnd, HWND_TOP, 10, 10, width, height, SWP_FRAMECHANGED);
346 GetClientRect(wfi->hwnd, &rc_client);
347 GetWindowRect(wfi->hwnd, &rc_wnd);
348 diff.x = (rc_wnd.right - rc_wnd.left) - rc_client.right;
349 diff.y = (rc_wnd.bottom - rc_wnd.top) - rc_client.bottom;
350 SetWindowPos(wfi->hwnd, HWND_TOP, -1, -1, width + diff.x, height + diff.y, SWP_NOMOVE | SWP_FRAMECHANGED);
353 BitBlt(wfi->primary->hdc, 0, 0, width, height, NULL, 0, 0, BLACKNESS);
354 wfi->drawing = wfi->primary;
356 ShowWindow(wfi->hwnd, SW_SHOWNORMAL);
357 UpdateWindow(wfi->hwnd);
361 instance->update->BeginPaint = wf_sw_begin_paint;
362 instance->update->EndPaint = wf_sw_end_paint;
366 instance->update->BeginPaint = wf_hw_begin_paint;
367 instance->update->EndPaint = wf_hw_end_paint;
370 pointer_cache_register_callbacks(instance->update);
372 if (wfi->sw_gdi != true)
374 brush_cache_register_callbacks(instance->update);
375 bitmap_cache_register_callbacks(instance->update);
376 offscreen_cache_register_callbacks(instance->update);
379 wf_register_graphics(instance->context->graphics);
381 freerdp_channels_post_connect(instance->context->channels, instance);
386 boolean wf_verify_certificate(freerdp* instance, char* subject, char* issuer, char* fingerprint)
391 int wf_receive_channel_data(freerdp* instance, int channelId, uint8* data, int size, int flags, int total_size)
393 return freerdp_channels_data(instance, channelId, data, size, flags, total_size);
396 void wf_process_channel_event(rdpChannels* channels, freerdp* instance)
400 event = freerdp_channels_pop_event(channels);
403 freerdp_event_free(event);
406 boolean wf_get_fds(freerdp* instance, void** rfds, int* rcount, void** wfds, int* wcount)
411 boolean wf_check_fds(freerdp* instance)
416 int wf_process_plugin_args(rdpSettings* settings, const char* name, RDP_PLUGIN_DATA* plugin_data, void* user_data)
418 rdpChannels* channels = (rdpChannels*) user_data;
420 printf("loading plugin %s\n", name);
421 freerdp_channels_load_plugin(channels, settings, name, plugin_data);
426 int wf_process_client_args(rdpSettings* settings, const char* opt, const char* val, void* user_data)
431 int wfreerdp_run(freerdp* instance)
443 rdpChannels* channels;
445 memset(rfds, 0, sizeof(rfds));
446 memset(wfds, 0, sizeof(wfds));
448 if (freerdp_connect(instance) != true)
451 channels = instance->context->channels;
458 if (freerdp_get_fds(instance, rfds, &rcount, wfds, &wcount) != true)
460 printf("Failed to get FreeRDP file descriptor\n");
463 if (wf_get_fds(instance, rfds, &rcount, wfds, &wcount) != true)
465 printf("Failed to get wfreerdp file descriptor\n");
468 if (freerdp_channels_get_fds(channels, instance, rfds, &rcount, wfds, &wcount) != true)
470 printf("Failed to get channel manager file descriptor\n");
476 for (index = 0; index < rcount; index++)
478 fds[fds_count++] = rfds[index];
480 /* setup write fds */
481 for (index = 0; index < wcount; index++)
483 fds[fds_count++] = wfds[index];
485 /* exit if nothing to do */
488 printf("wfreerdp_run: fds_count is zero\n");
493 if (MsgWaitForMultipleObjects(fds_count, fds, FALSE, 1, QS_ALLINPUT) == WAIT_FAILED)
495 printf("wfreerdp_run: WaitForMultipleObjects failed: 0x%04X\n", GetLastError());
499 if (freerdp_check_fds(instance) != true)
501 printf("Failed to check FreeRDP file descriptor\n");
504 if (wf_check_fds(instance) != true)
506 printf("Failed to check wfreerdp file descriptor\n");
509 if (freerdp_channels_check_fds(channels, instance) != true)
511 printf("Failed to check channel manager file descriptor\n");
514 wf_process_channel_event(channels, instance);
517 while (PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE))
519 msg_ret = GetMessage(&msg, NULL, 0, 0);
521 if (msg_ret == 0 || msg_ret == -1)
527 TranslateMessage(&msg);
528 DispatchMessage(&msg);
536 freerdp_channels_free(channels);
537 freerdp_free(instance);
542 static DWORD WINAPI thread_func(LPVOID lpParam)
548 data = (thread_data*) lpParam;
549 instance = data->instance;
551 wfi = (wfInfo*) xzalloc(sizeof(wfInfo));
552 ((wfContext*) instance->context)->wfi = wfi;
553 wfi->instance = instance;
555 wfreerdp_run(instance);
559 if (g_thread_count < 1)
560 SetEvent(g_done_event);
565 static DWORD WINAPI kbd_thread_func(LPVOID lpParam)
571 hook_handle = SetWindowsHookEx(WH_KEYBOARD_LL, wf_ll_kbd_proc, g_hInstance, 0);
575 while ((status = GetMessage( &msg, NULL, 0, 0 )) != 0)
579 printf("keyboard thread error getting message\n");
584 TranslateMessage(&msg);
585 DispatchMessage(&msg);
588 UnhookWindowsHookEx(hook_handle);
591 printf("failed to install keyboard hook\n");
596 INT WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
603 if (NULL == getenv("HOME"))
605 char home[MAX_PATH * 2] = "HOME=";
606 strcat(home, getenv("HOMEDRIVE"));
607 strcat(home, getenv("HOMEPATH"));
611 if (WSAStartup(0x101, &wsa_data) != 0)
614 g_done_event = CreateEvent(0, 1, 0, 0);
616 #if defined(WITH_DEBUG) || defined(_DEBUG)
620 g_default_cursor = LoadCursor(NULL, IDC_ARROW);
622 wnd_cls.cbSize = sizeof(WNDCLASSEX);
623 wnd_cls.style = CS_HREDRAW | CS_VREDRAW;
624 wnd_cls.lpfnWndProc = wf_event_proc;
625 wnd_cls.cbClsExtra = 0;
626 wnd_cls.cbWndExtra = 0;
627 wnd_cls.hIcon = LoadIcon(NULL, IDI_APPLICATION);
628 wnd_cls.hCursor = g_default_cursor;
629 wnd_cls.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH);
630 wnd_cls.lpszMenuName = NULL;
631 wnd_cls.lpszClassName = g_wnd_class_name;
632 wnd_cls.hInstance = hInstance;
633 wnd_cls.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
634 RegisterClassEx(&wnd_cls);
636 g_hInstance = hInstance;
637 freerdp_channels_global_init();
639 instance = freerdp_new();
640 instance->PreConnect = wf_pre_connect;
641 instance->PostConnect = wf_post_connect;
642 instance->VerifyCertificate = wf_verify_certificate;
643 instance->ReceiveChannelData = wf_receive_channel_data;
645 instance->context_size = sizeof(wfContext);
646 instance->ContextNew = wf_context_new;
647 instance->ContextFree = wf_context_free;
648 freerdp_context_new(instance);
650 instance->context->argc = __argc;
651 instance->context->argv = __argv;
653 if (!CreateThread(NULL, 0, kbd_thread_func, NULL, 0, NULL))
654 printf("error creating keyboard handler thread");
658 data = (thread_data*) xzalloc(sizeof(thread_data));
659 data->instance = instance;
661 freerdp_parse_args(instance->settings, __argc, __argv,
662 wf_process_plugin_args, instance->context->channels, wf_process_client_args, NULL);
664 if (CreateThread(NULL, 0, thread_func, data, 0, NULL) != 0)
668 if (g_thread_count > 0)
669 WaitForSingleObject(g_done_event, INFINITE);
671 MessageBox(GetConsoleWindow(),
672 L"Failed to start wfreerdp.\n\nPlease check the debug output.",
673 L"FreeRDP Error", MB_ICONSTOP);