FreeRDP
Loading...
Searching...
No Matches
wf_rail.c
1
19#include <freerdp/config.h>
20
21#include <winpr/crt.h>
22#include <freerdp/log.h>
23#include <freerdp/client/rail.h>
24#include <winpr/tchar.h>
25#include <winpr/print.h>
26
27#include "wf_rail.h"
28
29#define TAG CLIENT_TAG("windows")
30
31#define GET_X_LPARAM(lParam) ((UINT16)(lParam & 0xFFFF))
32#define GET_Y_LPARAM(lParam) ((UINT16)((lParam >> 16) & 0xFFFF))
33
34struct wf_rail_window
35{
36 wfContext* wfc;
37
38 HWND hWnd;
39
40 DWORD dwStyle;
41 DWORD dwExStyle;
42
43 int x;
44 int y;
45 int width;
46 int height;
47 char* title;
48};
49
50/* RemoteApp Core Protocol Extension */
51
52typedef struct
53{
54 UINT32 style;
55 const char* name;
56 BOOL multi;
57} WINDOW_STYLE;
58
59static const WINDOW_STYLE WINDOW_STYLES[] = { { WS_BORDER, "WS_BORDER", FALSE },
60 { WS_CAPTION, "WS_CAPTION", FALSE },
61 { WS_CHILD, "WS_CHILD", FALSE },
62 { WS_CLIPCHILDREN, "WS_CLIPCHILDREN", FALSE },
63 { WS_CLIPSIBLINGS, "WS_CLIPSIBLINGS", FALSE },
64 { WS_DISABLED, "WS_DISABLED", FALSE },
65 { WS_DLGFRAME, "WS_DLGFRAME", FALSE },
66 { WS_GROUP, "WS_GROUP", FALSE },
67 { WS_HSCROLL, "WS_HSCROLL", FALSE },
68 { WS_ICONIC, "WS_ICONIC", FALSE },
69 { WS_MAXIMIZE, "WS_MAXIMIZE", FALSE },
70 { WS_MAXIMIZEBOX, "WS_MAXIMIZEBOX", FALSE },
71 { WS_MINIMIZE, "WS_MINIMIZE", FALSE },
72 { WS_MINIMIZEBOX, "WS_MINIMIZEBOX", FALSE },
73 { WS_OVERLAPPED, "WS_OVERLAPPED", FALSE },
74 { WS_OVERLAPPEDWINDOW, "WS_OVERLAPPEDWINDOW", TRUE },
75 { WS_POPUP, "WS_POPUP", FALSE },
76 { WS_POPUPWINDOW, "WS_POPUPWINDOW", TRUE },
77 { WS_SIZEBOX, "WS_SIZEBOX", FALSE },
78 { WS_SYSMENU, "WS_SYSMENU", FALSE },
79 { WS_TABSTOP, "WS_TABSTOP", FALSE },
80 { WS_THICKFRAME, "WS_THICKFRAME", FALSE },
81 { WS_VISIBLE, "WS_VISIBLE", FALSE } };
82
83static const WINDOW_STYLE EXTENDED_WINDOW_STYLES[] = {
84 { WS_EX_ACCEPTFILES, "WS_EX_ACCEPTFILES", FALSE },
85 { WS_EX_APPWINDOW, "WS_EX_APPWINDOW", FALSE },
86 { WS_EX_CLIENTEDGE, "WS_EX_CLIENTEDGE", FALSE },
87 { WS_EX_COMPOSITED, "WS_EX_COMPOSITED", FALSE },
88 { WS_EX_CONTEXTHELP, "WS_EX_CONTEXTHELP", FALSE },
89 { WS_EX_CONTROLPARENT, "WS_EX_CONTROLPARENT", FALSE },
90 { WS_EX_DLGMODALFRAME, "WS_EX_DLGMODALFRAME", FALSE },
91 { WS_EX_LAYERED, "WS_EX_LAYERED", FALSE },
92 { WS_EX_LAYOUTRTL, "WS_EX_LAYOUTRTL", FALSE },
93 { WS_EX_LEFT, "WS_EX_LEFT", FALSE },
94 { WS_EX_LEFTSCROLLBAR, "WS_EX_LEFTSCROLLBAR", FALSE },
95 { WS_EX_LTRREADING, "WS_EX_LTRREADING", FALSE },
96 { WS_EX_MDICHILD, "WS_EX_MDICHILD", FALSE },
97 { WS_EX_NOACTIVATE, "WS_EX_NOACTIVATE", FALSE },
98 { WS_EX_NOINHERITLAYOUT, "WS_EX_NOINHERITLAYOUT", FALSE },
99 { WS_EX_NOPARENTNOTIFY, "WS_EX_NOPARENTNOTIFY", FALSE },
100 { WS_EX_OVERLAPPEDWINDOW, "WS_EX_OVERLAPPEDWINDOW", TRUE },
101 { WS_EX_PALETTEWINDOW, "WS_EX_PALETTEWINDOW", TRUE },
102 { WS_EX_RIGHT, "WS_EX_RIGHT", FALSE },
103 { WS_EX_RIGHTSCROLLBAR, "WS_EX_RIGHTSCROLLBAR", FALSE },
104 { WS_EX_RTLREADING, "WS_EX_RTLREADING", FALSE },
105 { WS_EX_STATICEDGE, "WS_EX_STATICEDGE", FALSE },
106 { WS_EX_TOOLWINDOW, "WS_EX_TOOLWINDOW", FALSE },
107 { WS_EX_TOPMOST, "WS_EX_TOPMOST", FALSE },
108 { WS_EX_TRANSPARENT, "WS_EX_TRANSPARENT", FALSE },
109 { WS_EX_WINDOWEDGE, "WS_EX_WINDOWEDGE", FALSE }
110};
111
112static void PrintWindowStyles(UINT32 style)
113{
114 WLog_INFO(TAG, "\tWindow Styles:\t{");
115
116 for (size_t i = 0; i < ARRAYSIZE(WINDOW_STYLES); i++)
117 {
118 if (style & WINDOW_STYLES[i].style)
119 {
120 if (WINDOW_STYLES[i].multi)
121 {
122 if ((style & WINDOW_STYLES[i].style) != WINDOW_STYLES[i].style)
123 continue;
124 }
125
126 WLog_INFO(TAG, "\t\t%s", WINDOW_STYLES[i].name);
127 }
128 }
129}
130
131static void PrintExtendedWindowStyles(UINT32 style)
132{
133 WLog_INFO(TAG, "\tExtended Window Styles:\t{");
134
135 for (size_t i = 0; i < ARRAYSIZE(EXTENDED_WINDOW_STYLES); i++)
136 {
137 if (style & EXTENDED_WINDOW_STYLES[i].style)
138 {
139 if (EXTENDED_WINDOW_STYLES[i].multi)
140 {
141 if ((style & EXTENDED_WINDOW_STYLES[i].style) != EXTENDED_WINDOW_STYLES[i].style)
142 continue;
143 }
144
145 WLog_INFO(TAG, "\t\t%s", EXTENDED_WINDOW_STYLES[i].name);
146 }
147 }
148}
149
150static void PrintRailWindowState(const WINDOW_ORDER_INFO* orderInfo,
151 const WINDOW_STATE_ORDER* windowState)
152{
153 if (orderInfo->fieldFlags & WINDOW_ORDER_STATE_NEW)
154 WLog_INFO(TAG, "WindowCreate: WindowId: 0x%08X", orderInfo->windowId);
155 else
156 WLog_INFO(TAG, "WindowUpdate: WindowId: 0x%08X", orderInfo->windowId);
157
158 WLog_INFO(TAG, "{");
159
160 if (orderInfo->fieldFlags & WINDOW_ORDER_FIELD_OWNER)
161 {
162 WLog_INFO(TAG, "\tOwnerWindowId: 0x%08X", windowState->ownerWindowId);
163 }
164
165 if (orderInfo->fieldFlags & WINDOW_ORDER_FIELD_STYLE)
166 {
167 WLog_INFO(TAG, "\tStyle: 0x%08X ExtendedStyle: 0x%08X", windowState->style,
168 windowState->extendedStyle);
169 PrintWindowStyles(windowState->style);
170 PrintExtendedWindowStyles(windowState->extendedStyle);
171 }
172
173 if (orderInfo->fieldFlags & WINDOW_ORDER_FIELD_SHOW)
174 {
175 WLog_INFO(TAG, "\tShowState: %u", windowState->showState);
176 }
177
178 if (orderInfo->fieldFlags & WINDOW_ORDER_FIELD_TITLE)
179 {
180 const WCHAR* str = (const WCHAR*)windowState->titleInfo.string;
181 char* title =
182 ConvertWCharNToUtf8Alloc(str, windowState->titleInfo.length / sizeof(WCHAR), nullptr);
183 WLog_INFO(TAG, "\tTitleInfo: %s (length = %hu)", title, windowState->titleInfo.length);
184 free(title);
185 }
186
187 if (orderInfo->fieldFlags & WINDOW_ORDER_FIELD_CLIENT_AREA_OFFSET)
188 {
189 WLog_INFO(TAG, "\tClientOffsetX: %d ClientOffsetY: %d", windowState->clientOffsetX,
190 windowState->clientOffsetY);
191 }
192
193 if (orderInfo->fieldFlags & WINDOW_ORDER_FIELD_CLIENT_AREA_SIZE)
194 {
195 WLog_INFO(TAG, "\tClientAreaWidth: %u ClientAreaHeight: %u", windowState->clientAreaWidth,
196 windowState->clientAreaHeight);
197 }
198
199 if (orderInfo->fieldFlags & WINDOW_ORDER_FIELD_RP_CONTENT)
200 {
201 WLog_INFO(TAG, "\tRPContent: %u", windowState->RPContent);
202 }
203
204 if (orderInfo->fieldFlags & WINDOW_ORDER_FIELD_ROOT_PARENT)
205 {
206 WLog_INFO(TAG, "\tRootParentHandle: 0x%08X", windowState->rootParentHandle);
207 }
208
209 if (orderInfo->fieldFlags & WINDOW_ORDER_FIELD_WND_OFFSET)
210 {
211 WLog_INFO(TAG, "\tWindowOffsetX: %d WindowOffsetY: %d", windowState->windowOffsetX,
212 windowState->windowOffsetY);
213 }
214
215 if (orderInfo->fieldFlags & WINDOW_ORDER_FIELD_WND_CLIENT_DELTA)
216 {
217 WLog_INFO(TAG, "\tWindowClientDeltaX: %d WindowClientDeltaY: %d",
218 windowState->windowClientDeltaX, windowState->windowClientDeltaY);
219 }
220
221 if (orderInfo->fieldFlags & WINDOW_ORDER_FIELD_WND_SIZE)
222 {
223 WLog_INFO(TAG, "\tWindowWidth: %u WindowHeight: %u", windowState->windowWidth,
224 windowState->windowHeight);
225 }
226
227 if (orderInfo->fieldFlags & WINDOW_ORDER_FIELD_WND_RECTS)
228 {
229 RECTANGLE_16* rect;
230 WLog_INFO(TAG, "\tnumWindowRects: %u", windowState->numWindowRects);
231
232 for (UINT32 index = 0; index < windowState->numWindowRects; index++)
233 {
234 rect = &windowState->windowRects[index];
235 WLog_INFO(TAG, "\twindowRect[%u]: left: %hu top: %hu right: %hu bottom: %hu", index,
236 rect->left, rect->top, rect->right, rect->bottom);
237 }
238 }
239
240 if (orderInfo->fieldFlags & WINDOW_ORDER_FIELD_VIS_OFFSET)
241 {
242 WLog_INFO(TAG, "\tvisibileOffsetX: %d visibleOffsetY: %d", windowState->visibleOffsetX,
243 windowState->visibleOffsetY);
244 }
245
246 if (orderInfo->fieldFlags & WINDOW_ORDER_FIELD_VISIBILITY)
247 {
248 RECTANGLE_16* rect;
249 WLog_INFO(TAG, "\tnumVisibilityRects: %u", windowState->numVisibilityRects);
250
251 for (UINT32 index = 0; index < windowState->numVisibilityRects; index++)
252 {
253 rect = &windowState->visibilityRects[index];
254 WLog_INFO(TAG, "\tvisibilityRect[%u]: left: %hu top: %hu right: %hu bottom: %hu", index,
255 rect->left, rect->top, rect->right, rect->bottom);
256 }
257 }
258
259 WLog_INFO(TAG, "}");
260}
261
262static void PrintRailIconInfo(const WINDOW_ORDER_INFO* orderInfo, const ICON_INFO* iconInfo)
263{
264 WLog_INFO(TAG, "ICON_INFO");
265 WLog_INFO(TAG, "{");
266 WLog_INFO(TAG, "\tbigIcon: %s",
267 (orderInfo->fieldFlags & WINDOW_ORDER_FIELD_ICON_BIG) ? "true" : "false");
268 WLog_INFO(TAG, "\tcacheEntry; 0x%08X", iconInfo->cacheEntry);
269 WLog_INFO(TAG, "\tcacheId: 0x%08X", iconInfo->cacheId);
270 WLog_INFO(TAG, "\tbpp: %u", iconInfo->bpp);
271 WLog_INFO(TAG, "\twidth: %u", iconInfo->width);
272 WLog_INFO(TAG, "\theight: %u", iconInfo->height);
273 WLog_INFO(TAG, "\tcbColorTable: %u", iconInfo->cbColorTable);
274 WLog_INFO(TAG, "\tcbBitsMask: %u", iconInfo->cbBitsMask);
275 WLog_INFO(TAG, "\tcbBitsColor: %u", iconInfo->cbBitsColor);
276 WLog_INFO(TAG, "\tcolorTable: %p", (void*)iconInfo->colorTable);
277 WLog_INFO(TAG, "\tbitsMask: %p", (void*)iconInfo->bitsMask);
278 WLog_INFO(TAG, "\tbitsColor: %p", (void*)iconInfo->bitsColor);
279 WLog_INFO(TAG, "}");
280}
281
282LRESULT CALLBACK wf_RailWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
283{
284 HDC hDC;
285 int x, y;
286 int width;
287 int height;
288 UINT32 xPos;
289 UINT32 yPos;
290 PAINTSTRUCT ps;
291 UINT32 inputFlags;
292 wfContext* wfc = nullptr;
293 rdpInput* input = nullptr;
294 rdpContext* context = nullptr;
295 wfRailWindow* railWindow;
296 railWindow = (wfRailWindow*)GetWindowLongPtr(hWnd, GWLP_USERDATA);
297
298 if (railWindow)
299 wfc = railWindow->wfc;
300
301 if (wfc)
302 context = (rdpContext*)wfc;
303
304 if (context)
305 input = context->input;
306
307 switch (msg)
308 {
309 case WM_PAINT:
310 {
311 if (!wfc)
312 return 0;
313
314 hDC = BeginPaint(hWnd, &ps);
315 x = ps.rcPaint.left;
316 y = ps.rcPaint.top;
317 width = ps.rcPaint.right - ps.rcPaint.left + 1;
318 height = ps.rcPaint.bottom - ps.rcPaint.top + 1;
319 BitBlt(hDC, x, y, width, height, wfc->primary->hdc, railWindow->x + x,
320 railWindow->y + y, SRCCOPY);
321 EndPaint(hWnd, &ps);
322 }
323 break;
324
325 case WM_LBUTTONDOWN:
326 {
327 if (!railWindow || !input)
328 return 0;
329
330 xPos = GET_X_LPARAM(lParam) + railWindow->x;
331 yPos = GET_Y_LPARAM(lParam) + railWindow->y;
332 inputFlags = PTR_FLAGS_DOWN | PTR_FLAGS_BUTTON1;
333
334 if (input)
335 input->MouseEvent(input, inputFlags, xPos, yPos);
336 }
337 break;
338
339 case WM_LBUTTONUP:
340 {
341 if (!railWindow || !input)
342 return 0;
343
344 xPos = GET_X_LPARAM(lParam) + railWindow->x;
345 yPos = GET_Y_LPARAM(lParam) + railWindow->y;
346 inputFlags = PTR_FLAGS_BUTTON1;
347
348 if (input)
349 input->MouseEvent(input, inputFlags, xPos, yPos);
350 }
351 break;
352
353 case WM_RBUTTONDOWN:
354 {
355 if (!railWindow || !input)
356 return 0;
357
358 xPos = GET_X_LPARAM(lParam) + railWindow->x;
359 yPos = GET_Y_LPARAM(lParam) + railWindow->y;
360 inputFlags = PTR_FLAGS_DOWN | PTR_FLAGS_BUTTON2;
361
362 if (input)
363 input->MouseEvent(input, inputFlags, xPos, yPos);
364 }
365 break;
366
367 case WM_RBUTTONUP:
368 {
369 if (!railWindow || !input)
370 return 0;
371
372 xPos = GET_X_LPARAM(lParam) + railWindow->x;
373 yPos = GET_Y_LPARAM(lParam) + railWindow->y;
374 inputFlags = PTR_FLAGS_BUTTON2;
375
376 if (input)
377 input->MouseEvent(input, inputFlags, xPos, yPos);
378 }
379 break;
380
381 case WM_MOUSEMOVE:
382 {
383 if (!railWindow || !input)
384 return 0;
385
386 xPos = GET_X_LPARAM(lParam) + railWindow->x;
387 yPos = GET_Y_LPARAM(lParam) + railWindow->y;
388 inputFlags = PTR_FLAGS_MOVE;
389
390 if (input)
391 input->MouseEvent(input, inputFlags, xPos, yPos);
392 }
393 break;
394
395 case WM_MOUSEWHEEL:
396 break;
397
398 case WM_CLOSE:
399 DestroyWindow(hWnd);
400 break;
401
402 case WM_DESTROY:
403 /* PostQuitMessage(0) - REMOVED: closing RemoteApp window should not quit the session */
404 break;
405
406 default:
407 return DefWindowProc(hWnd, msg, wParam, lParam);
408 }
409
410 return 0;
411}
412
413#define RAIL_DISABLED_WINDOW_STYLES \
414 (WS_BORDER | WS_THICKFRAME | WS_DLGFRAME | WS_CAPTION | WS_OVERLAPPED | WS_VSCROLL | \
415 WS_HSCROLL | WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZEBOX)
416#define RAIL_DISABLED_EXTENDED_WINDOW_STYLES \
417 (WS_EX_DLGMODALFRAME | WS_EX_CLIENTEDGE | WS_EX_STATICEDGE | WS_EX_WINDOWEDGE)
418
419static BOOL wf_rail_window_common(rdpContext* context, const WINDOW_ORDER_INFO* orderInfo,
420 const WINDOW_STATE_ORDER* windowState)
421{
422 wfRailWindow* railWindow = nullptr;
423 wfContext* wfc = (wfContext*)context;
424 RailClientContext* rail = wfc->rail;
425 UINT32 fieldFlags = orderInfo->fieldFlags;
426 PrintRailWindowState(orderInfo, windowState);
427
428 if (fieldFlags & WINDOW_ORDER_STATE_NEW)
429 {
430 BOOL rc;
431 HANDLE hInstance;
432 WCHAR* titleW = nullptr;
433 WNDCLASSEX wndClassEx = WINPR_C_ARRAY_INIT;
434 railWindow = (wfRailWindow*)calloc(1, sizeof(wfRailWindow));
435
436 if (!railWindow)
437 return FALSE;
438
439 railWindow->wfc = wfc;
440 railWindow->dwStyle = windowState->style;
441 railWindow->dwStyle &= ~RAIL_DISABLED_WINDOW_STYLES;
442 railWindow->dwExStyle = windowState->extendedStyle;
443 railWindow->dwExStyle &= ~RAIL_DISABLED_EXTENDED_WINDOW_STYLES;
444 railWindow->x = windowState->windowOffsetX;
445 railWindow->y = windowState->windowOffsetY;
446 railWindow->width = windowState->windowWidth;
447 railWindow->height = windowState->windowHeight;
448
449 if (fieldFlags & WINDOW_ORDER_FIELD_TITLE)
450 {
451 const WCHAR* str = (const WCHAR*)windowState->titleInfo.string;
452 char* title = nullptr;
453
454 if (windowState->titleInfo.length == 0)
455 {
456 if (!(title = _strdup("")))
457 {
458 WLog_ERR(TAG, "failed to duplicate empty window title string");
459 /* error handled below */
460 }
461 }
462 else if (!(title = ConvertWCharNToUtf8Alloc(
463 str, windowState->titleInfo.length / sizeof(WCHAR), nullptr)))
464 {
465 WLog_ERR(TAG, "failed to convert window title");
466 /* error handled below */
467 }
468
469 railWindow->title = title;
470 }
471 else
472 {
473 if (!(railWindow->title = _strdup("RdpRailWindow")))
474 WLog_ERR(TAG, "failed to duplicate default window title string");
475 }
476
477 if (!railWindow->title)
478 {
479 free(railWindow);
480 return FALSE;
481 }
482
483 titleW = ConvertUtf8ToWCharAlloc(railWindow->title, nullptr);
484 hInstance = GetModuleHandle(nullptr);
485
486 wndClassEx.cbSize = sizeof(WNDCLASSEX);
487 wndClassEx.style = 0;
488 wndClassEx.lpfnWndProc = wf_RailWndProc;
489 wndClassEx.cbClsExtra = 0;
490 wndClassEx.cbWndExtra = 0;
491 wndClassEx.hIcon = nullptr;
492 wndClassEx.hCursor = nullptr;
493 wndClassEx.hbrBackground = nullptr;
494 wndClassEx.lpszMenuName = nullptr;
495 wndClassEx.lpszClassName = _T("RdpRailWindow");
496 wndClassEx.hInstance = hInstance;
497 wndClassEx.hIconSm = nullptr;
498 RegisterClassEx(&wndClassEx);
499 railWindow->hWnd = CreateWindowExW(railWindow->dwExStyle, /* dwExStyle */
500 _T("RdpRailWindow"), /* lpClassName */
501 titleW, /* lpWindowName */
502 railWindow->dwStyle, /* dwStyle */
503 railWindow->x, /* x */
504 railWindow->y, /* y */
505 railWindow->width, /* nWidth */
506 railWindow->height, /* nHeight */
507 nullptr, /* hWndParent */
508 nullptr, /* hMenu */
509 hInstance, /* hInstance */
510 nullptr /* lpParam */
511 );
512
513 if (!railWindow->hWnd)
514 {
515 free(titleW);
516 free(railWindow->title);
517 free(railWindow);
518 WLog_ERR(TAG, "CreateWindowExW failed with error %" PRIu32 "", GetLastError());
519 return FALSE;
520 }
521
522 SetWindowLongPtr(railWindow->hWnd, GWLP_USERDATA, (LONG_PTR)railWindow);
523 rc = HashTable_Insert(wfc->railWindows, (void*)(UINT_PTR)orderInfo->windowId,
524 (void*)railWindow);
525 free(titleW);
526 UpdateWindow(railWindow->hWnd);
527 return rc;
528 }
529 else
530 {
531 railWindow = (wfRailWindow*)HashTable_GetItemValue(wfc->railWindows,
532 (void*)(UINT_PTR)orderInfo->windowId);
533 }
534
535 if (!railWindow)
536 return TRUE;
537
538 if ((fieldFlags & WINDOW_ORDER_FIELD_WND_OFFSET) || (fieldFlags & WINDOW_ORDER_FIELD_WND_SIZE))
539 {
540 if (fieldFlags & WINDOW_ORDER_FIELD_WND_OFFSET)
541 {
542 railWindow->x = windowState->windowOffsetX;
543 railWindow->y = windowState->windowOffsetY;
544 }
545
546 if (fieldFlags & WINDOW_ORDER_FIELD_WND_SIZE)
547 {
548 railWindow->width = windowState->windowWidth;
549 railWindow->height = windowState->windowHeight;
550 }
551
552 SetWindowPos(railWindow->hWnd, nullptr, railWindow->x, railWindow->y, railWindow->width,
553 railWindow->height, 0);
554 }
555
556 if (fieldFlags & WINDOW_ORDER_FIELD_OWNER)
557 {
558 }
559
560 if (fieldFlags & WINDOW_ORDER_FIELD_STYLE)
561 {
562 railWindow->dwStyle = windowState->style;
563 railWindow->dwStyle &= ~RAIL_DISABLED_WINDOW_STYLES;
564 railWindow->dwExStyle = windowState->extendedStyle;
565 railWindow->dwExStyle &= ~RAIL_DISABLED_EXTENDED_WINDOW_STYLES;
566 SetWindowLongPtr(railWindow->hWnd, GWL_STYLE, (LONG)railWindow->dwStyle);
567 SetWindowLongPtr(railWindow->hWnd, GWL_EXSTYLE, (LONG)railWindow->dwExStyle);
568 }
569
570 if (fieldFlags & WINDOW_ORDER_FIELD_SHOW)
571 {
572 ShowWindow(railWindow->hWnd, windowState->showState);
573 }
574
575 if (fieldFlags & WINDOW_ORDER_FIELD_TITLE)
576 {
577 const WCHAR* raw = (const WCHAR*)windowState->titleInfo.string;
578 char* title = nullptr;
579 size_t utflen = 0;
580
581 if (windowState->titleInfo.length == 0)
582 {
583 if (!(title = _strdup("")))
584 {
585 WLog_ERR(TAG, "failed to duplicate empty window title string");
586 return FALSE;
587 }
588 }
589 else if (!(title = ConvertWCharNToUtf8Alloc(
590 raw, windowState->titleInfo.length / sizeof(WCHAR), &utflen)))
591 {
592 WLog_ERR(TAG, "failed to convert window title");
593 return FALSE;
594 }
595
596 free(railWindow->title);
597 railWindow->title = title;
598
599 WCHAR* str = ConvertUtf8NToWCharAlloc(title, utflen, nullptr);
600 if (str)
601 SetWindowTextW(railWindow->hWnd, str);
602 free(str);
603 }
604
605 if (fieldFlags & WINDOW_ORDER_FIELD_CLIENT_AREA_OFFSET)
606 {
607 }
608
609 if (fieldFlags & WINDOW_ORDER_FIELD_CLIENT_AREA_SIZE)
610 {
611 }
612
613 if (fieldFlags & WINDOW_ORDER_FIELD_WND_CLIENT_DELTA)
614 {
615 }
616
617 if (fieldFlags & WINDOW_ORDER_FIELD_RP_CONTENT)
618 {
619 }
620
621 if (fieldFlags & WINDOW_ORDER_FIELD_ROOT_PARENT)
622 {
623 }
624
625 if (fieldFlags & WINDOW_ORDER_FIELD_WND_RECTS)
626 {
627 HRGN hWndRect;
628 HRGN hWndRects;
629 RECTANGLE_16* rect;
630
631 if (windowState->numWindowRects > 0)
632 {
633 rect = &(windowState->windowRects[0]);
634 hWndRects = CreateRectRgn(rect->left, rect->top, rect->right, rect->bottom);
635
636 for (UINT32 index = 1; index < windowState->numWindowRects; index++)
637 {
638 rect = &(windowState->windowRects[index]);
639 hWndRect = CreateRectRgn(rect->left, rect->top, rect->right, rect->bottom);
640 CombineRgn(hWndRects, hWndRects, hWndRect, RGN_OR);
641 DeleteObject(hWndRect);
642 }
643
644 SetWindowRgn(railWindow->hWnd, hWndRects, TRUE);
645 DeleteObject(hWndRects);
646 }
647 }
648
649 if (fieldFlags & WINDOW_ORDER_FIELD_VIS_OFFSET)
650 {
651 }
652
653 if (fieldFlags & WINDOW_ORDER_FIELD_VISIBILITY)
654 {
655 }
656
657 UpdateWindow(railWindow->hWnd);
658 return TRUE;
659}
660
661static BOOL wf_rail_window_delete(rdpContext* context, const WINDOW_ORDER_INFO* orderInfo)
662{
663 wfRailWindow* railWindow = nullptr;
664 wfContext* wfc = (wfContext*)context;
665 RailClientContext* rail = wfc->rail;
666 WLog_DBG(TAG, "RailWindowDelete");
667 railWindow = (wfRailWindow*)HashTable_GetItemValue(wfc->railWindows,
668 (void*)(UINT_PTR)orderInfo->windowId);
669
670 if (!railWindow)
671 return TRUE;
672
673 HashTable_Remove(wfc->railWindows, (void*)(UINT_PTR)orderInfo->windowId);
674 DestroyWindow(railWindow->hWnd);
675 free(railWindow);
676 return TRUE;
677}
678
679static BOOL wf_rail_window_icon(rdpContext* context, const WINDOW_ORDER_INFO* orderInfo,
680 const WINDOW_ICON_ORDER* windowIcon)
681{
682 HDC hDC;
683 int bpp;
684 int width;
685 int height;
686 HICON hIcon;
687 BOOL bigIcon;
688 ICONINFO iconInfo = WINPR_C_ARRAY_INIT;
689 BITMAPINFO bitmapInfo = WINPR_C_ARRAY_INIT;
690 wfRailWindow* railWindow;
691 BITMAPINFOHEADER* bitmapInfoHeader;
692 wfContext* wfc = (wfContext*)context;
693 RailClientContext* rail = wfc->rail;
694 WLog_DBG(TAG, "RailWindowIcon");
695 PrintRailIconInfo(orderInfo, windowIcon->iconInfo);
696 railWindow = (wfRailWindow*)HashTable_GetItemValue(wfc->railWindows,
697 (void*)(UINT_PTR)orderInfo->windowId);
698
699 if (!railWindow)
700 return TRUE;
701
702 bigIcon = (orderInfo->fieldFlags & WINDOW_ORDER_FIELD_ICON_BIG) ? TRUE : FALSE;
703 hDC = GetDC(railWindow->hWnd);
704 iconInfo.fIcon = TRUE;
705 iconInfo.xHotspot = 0;
706 iconInfo.yHotspot = 0;
707
708 bitmapInfoHeader = &(bitmapInfo.bmiHeader);
709 bpp = windowIcon->iconInfo->bpp;
710 width = windowIcon->iconInfo->width;
711 height = windowIcon->iconInfo->height;
712 bitmapInfoHeader->biSize = sizeof(BITMAPINFOHEADER);
713 bitmapInfoHeader->biWidth = width;
714 bitmapInfoHeader->biHeight = height;
715 bitmapInfoHeader->biPlanes = 1;
716 bitmapInfoHeader->biBitCount = bpp;
717 bitmapInfoHeader->biCompression = 0;
718 bitmapInfoHeader->biSizeImage = height * width * ((bpp + 7) / 8);
719 bitmapInfoHeader->biXPelsPerMeter = width;
720 bitmapInfoHeader->biYPelsPerMeter = height;
721 bitmapInfoHeader->biClrUsed = 0;
722 bitmapInfoHeader->biClrImportant = 0;
723 iconInfo.hbmMask = CreateDIBitmap(hDC, bitmapInfoHeader, CBM_INIT,
724 windowIcon->iconInfo->bitsMask, &bitmapInfo, DIB_RGB_COLORS);
725 iconInfo.hbmColor =
726 CreateDIBitmap(hDC, bitmapInfoHeader, CBM_INIT, windowIcon->iconInfo->bitsColor,
727 &bitmapInfo, DIB_RGB_COLORS);
728 hIcon = CreateIconIndirect(&iconInfo);
729
730 if (hIcon)
731 {
732 WPARAM wParam;
733 LPARAM lParam;
734 wParam = (WPARAM)bigIcon ? ICON_BIG : ICON_SMALL;
735 lParam = (LPARAM)hIcon;
736 SendMessage(railWindow->hWnd, WM_SETICON, wParam, lParam);
737 }
738
739 ReleaseDC(nullptr, hDC);
740
741 if (windowIcon->iconInfo->cacheEntry != 0xFFFF)
742 {
743 /* icon should be cached */
744 }
745
746 return TRUE;
747}
748
749static BOOL wf_rail_window_cached_icon(rdpContext* context, const WINDOW_ORDER_INFO* orderInfo,
750 const WINDOW_CACHED_ICON_ORDER* windowCachedIcon)
751{
752 WLog_DBG(TAG, "RailWindowCachedIcon");
753 return TRUE;
754}
755
756static void wf_rail_notify_icon_common(rdpContext* context, const WINDOW_ORDER_INFO* orderInfo,
757 const NOTIFY_ICON_STATE_ORDER* notifyIconState)
758{
759 if (orderInfo->fieldFlags & WINDOW_ORDER_FIELD_NOTIFY_VERSION)
760 {
761 }
762
763 if (orderInfo->fieldFlags & WINDOW_ORDER_FIELD_NOTIFY_TIP)
764 {
765 }
766
767 if (orderInfo->fieldFlags & WINDOW_ORDER_FIELD_NOTIFY_INFO_TIP)
768 {
769 }
770
771 if (orderInfo->fieldFlags & WINDOW_ORDER_FIELD_NOTIFY_STATE)
772 {
773 }
774
775 if (orderInfo->fieldFlags & WINDOW_ORDER_ICON)
776 {
777 const ICON_INFO* iconInfo = &(notifyIconState->icon);
778 PrintRailIconInfo(orderInfo, iconInfo);
779 }
780
781 if (orderInfo->fieldFlags & WINDOW_ORDER_CACHED_ICON)
782 {
783 }
784}
785
786static BOOL wf_rail_notify_icon_create(rdpContext* context, const WINDOW_ORDER_INFO* orderInfo,
787 const NOTIFY_ICON_STATE_ORDER* notifyIconState)
788{
789 wfContext* wfc = (wfContext*)context;
790 RailClientContext* rail = wfc->rail;
791 WLog_DBG(TAG, "RailNotifyIconCreate");
792 wf_rail_notify_icon_common(context, orderInfo, notifyIconState);
793 return TRUE;
794}
795
796static BOOL wf_rail_notify_icon_update(rdpContext* context, const WINDOW_ORDER_INFO* orderInfo,
797 const NOTIFY_ICON_STATE_ORDER* notifyIconState)
798{
799 wfContext* wfc = (wfContext*)context;
800 RailClientContext* rail = wfc->rail;
801 WLog_DBG(TAG, "RailNotifyIconUpdate");
802 wf_rail_notify_icon_common(context, orderInfo, notifyIconState);
803 return TRUE;
804}
805
806static BOOL wf_rail_notify_icon_delete(rdpContext* context, const WINDOW_ORDER_INFO* orderInfo)
807{
808 wfContext* wfc = (wfContext*)context;
809 RailClientContext* rail = wfc->rail;
810 WLog_DBG(TAG, "RailNotifyIconDelete");
811 return TRUE;
812}
813
814static BOOL wf_rail_monitored_desktop(rdpContext* context, const WINDOW_ORDER_INFO* orderInfo,
815 const MONITORED_DESKTOP_ORDER* monitoredDesktop)
816{
817 wfContext* wfc = (wfContext*)context;
818 RailClientContext* rail = wfc->rail;
819 WLog_DBG(TAG, "RailMonitorDesktop");
820 return TRUE;
821}
822
823static BOOL wf_rail_non_monitored_desktop(rdpContext* context, const WINDOW_ORDER_INFO* orderInfo)
824{
825 wfContext* wfc = (wfContext*)context;
826 RailClientContext* rail = wfc->rail;
827 WLog_DBG(TAG, "RailNonMonitorDesktop");
828 return TRUE;
829}
830
831void wf_rail_register_update_callbacks(rdpUpdate* update)
832{
833 rdpWindowUpdate* window = update->window;
834 window->WindowCreate = wf_rail_window_common;
835 window->WindowUpdate = wf_rail_window_common;
836 window->WindowDelete = wf_rail_window_delete;
837 window->WindowIcon = wf_rail_window_icon;
838 window->WindowCachedIcon = wf_rail_window_cached_icon;
839 window->NotifyIconCreate = wf_rail_notify_icon_create;
840 window->NotifyIconUpdate = wf_rail_notify_icon_update;
841 window->NotifyIconDelete = wf_rail_notify_icon_delete;
842 window->MonitoredDesktop = wf_rail_monitored_desktop;
843 window->NonMonitoredDesktop = wf_rail_non_monitored_desktop;
844}
845
846/* RemoteApp Virtual Channel Extension */
847
853static UINT wf_rail_server_execute_result(RailClientContext* context,
854 const RAIL_EXEC_RESULT_ORDER* execResult)
855{
856 WLog_DBG(TAG, "RailServerExecuteResult: 0x%08X", execResult->rawResult);
857 return CHANNEL_RC_OK;
858}
859
865static UINT wf_rail_server_system_param(RailClientContext* context,
866 const RAIL_SYSPARAM_ORDER* sysparam)
867{
868 return CHANNEL_RC_OK;
869}
870
876static UINT wf_rail_server_handshake(RailClientContext* context,
877 const RAIL_HANDSHAKE_ORDER* handshake)
878{
879 return client_rail_server_start_cmd(context);
880}
881
887static UINT wf_rail_server_handshake_ex(RailClientContext* context,
888 const RAIL_HANDSHAKE_EX_ORDER* handshakeEx)
889{
890 return client_rail_server_start_cmd(context);
891}
892
898static UINT wf_rail_server_local_move_size(RailClientContext* context,
899 const RAIL_LOCALMOVESIZE_ORDER* localMoveSize)
900{
901 return CHANNEL_RC_OK;
902}
903
909static UINT wf_rail_server_min_max_info(RailClientContext* context,
910 const RAIL_MINMAXINFO_ORDER* minMaxInfo)
911{
912 return CHANNEL_RC_OK;
913}
914
920static UINT wf_rail_server_language_bar_info(RailClientContext* context,
921 const RAIL_LANGBAR_INFO_ORDER* langBarInfo)
922{
923 return CHANNEL_RC_OK;
924}
925
931static UINT wf_rail_server_get_appid_response(RailClientContext* context,
932 const RAIL_GET_APPID_RESP_ORDER* getAppIdResp)
933{
934 return CHANNEL_RC_OK;
935}
936
937void wf_rail_invalidate_region(wfContext* wfc, REGION16* invalidRegion)
938{
939 RECT updateRect;
940 RECTANGLE_16 windowRect;
941 ULONG_PTR* pKeys = nullptr;
942 wfRailWindow* railWindow;
943 const RECTANGLE_16* extents;
944 REGION16 windowInvalidRegion;
945 region16_init(&windowInvalidRegion);
946 size_t count = HashTable_GetKeys(wfc->railWindows, &pKeys);
947
948 for (size_t index = 0; index < count; index++)
949 {
950 railWindow = (wfRailWindow*)HashTable_GetItemValue(wfc->railWindows, (void*)pKeys[index]);
951
952 if (railWindow)
953 {
954 windowRect.left = railWindow->x;
955 windowRect.top = railWindow->y;
956 windowRect.right = railWindow->x + railWindow->width;
957 windowRect.bottom = railWindow->y + railWindow->height;
958 region16_clear(&windowInvalidRegion);
959 region16_intersect_rect(&windowInvalidRegion, invalidRegion, &windowRect);
960
961 if (!region16_is_empty(&windowInvalidRegion))
962 {
963 extents = region16_extents(&windowInvalidRegion);
964 updateRect.left = extents->left - railWindow->x;
965 updateRect.top = extents->top - railWindow->y;
966 updateRect.right = extents->right - railWindow->x;
967 updateRect.bottom = extents->bottom - railWindow->y;
968 InvalidateRect(railWindow->hWnd, &updateRect, FALSE);
969 }
970 }
971 }
972
973 region16_uninit(&windowInvalidRegion);
974}
975
976BOOL wf_rail_init(wfContext* wfc, RailClientContext* rail)
977{
978 rdpContext* context = (rdpContext*)wfc;
979 wfc->rail = rail;
980 rail->custom = (void*)wfc;
981 rail->ServerExecuteResult = wf_rail_server_execute_result;
982 rail->ServerSystemParam = wf_rail_server_system_param;
983 rail->ServerHandshake = wf_rail_server_handshake;
984 rail->ServerHandshakeEx = wf_rail_server_handshake_ex;
985 rail->ServerLocalMoveSize = wf_rail_server_local_move_size;
986 rail->ServerMinMaxInfo = wf_rail_server_min_max_info;
987 rail->ServerLanguageBarInfo = wf_rail_server_language_bar_info;
988 rail->ServerGetAppIdResponse = wf_rail_server_get_appid_response;
989 wf_rail_register_update_callbacks(context->update);
990 wfc->railWindows = HashTable_New(TRUE);
991 return (wfc->railWindows != nullptr);
992}
993
994void wf_rail_uninit(wfContext* wfc, RailClientContext* rail)
995{
996 wfc->rail = nullptr;
997 rail->custom = nullptr;
998 HashTable_Free(wfc->railWindows);
999}