FreeRDP
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Modules Pages
mac_shadow.c
1
19#include <winpr/crt.h>
20#include <winpr/synch.h>
21#include <winpr/input.h>
22#include <winpr/sysinfo.h>
23
24#include <freerdp/server/server-common.h>
25#include <freerdp/codec/color.h>
26#include <freerdp/codec/region.h>
27#include <freerdp/log.h>
28
29#include "mac_shadow.h"
30
31#define TAG SERVER_TAG("shadow.mac")
32
33static macShadowSubsystem* g_Subsystem = NULL;
34
35static BOOL mac_shadow_input_synchronize_event(rdpShadowSubsystem* subsystem,
36 rdpShadowClient* client, UINT32 flags)
37{
38 if (!subsystem || !client)
39 return FALSE;
40
41 return TRUE;
42}
43
44static BOOL mac_shadow_input_keyboard_event(rdpShadowSubsystem* subsystem, rdpShadowClient* client,
45 UINT16 flags, UINT8 code)
46{
47 DWORD vkcode;
48 DWORD keycode;
49 BOOL extended;
50 CGEventRef kbdEvent;
51 CGEventSourceRef source;
52 extended = (flags & KBD_FLAGS_EXTENDED) ? TRUE : FALSE;
53
54 if (!subsystem || !client)
55 return FALSE;
56
57 if (extended)
58 code |= KBDEXT;
59
60 vkcode = GetVirtualKeyCodeFromVirtualScanCode(code, 4);
61
62 if (extended)
63 vkcode |= KBDEXT;
64
65 keycode = GetKeycodeFromVirtualKeyCode(vkcode, WINPR_KEYCODE_TYPE_APPLE);
66
67 source = CGEventSourceCreate(kCGEventSourceStateHIDSystemState);
68
69 if (flags & KBD_FLAGS_DOWN)
70 {
71 kbdEvent = CGEventCreateKeyboardEvent(source, (CGKeyCode)keycode, TRUE);
72 CGEventPost(kCGHIDEventTap, kbdEvent);
73 CFRelease(kbdEvent);
74 }
75 else if (flags & KBD_FLAGS_RELEASE)
76 {
77 kbdEvent = CGEventCreateKeyboardEvent(source, (CGKeyCode)keycode, FALSE);
78 CGEventPost(kCGHIDEventTap, kbdEvent);
79 CFRelease(kbdEvent);
80 }
81
82 CFRelease(source);
83 return TRUE;
84}
85
86static BOOL mac_shadow_input_unicode_keyboard_event(rdpShadowSubsystem* subsystem,
87 rdpShadowClient* client, UINT16 flags,
88 UINT16 code)
89{
90 if (!subsystem || !client)
91 return FALSE;
92
93 return TRUE;
94}
95
96static BOOL mac_shadow_input_mouse_event(rdpShadowSubsystem* subsystem, rdpShadowClient* client,
97 UINT16 flags, UINT16 x, UINT16 y)
98{
99 macShadowSubsystem* mac = (macShadowSubsystem*)subsystem;
100 UINT32 scrollX = 0;
101 UINT32 scrollY = 0;
102 CGWheelCount wheelCount = 2;
103
104 if (!subsystem || !client)
105 return FALSE;
106
107 if (flags & PTR_FLAGS_WHEEL)
108 {
109 scrollY = flags & WheelRotationMask;
110
111 if (flags & PTR_FLAGS_WHEEL_NEGATIVE)
112 {
113 scrollY = -(flags & WheelRotationMask) / 392;
114 }
115 else
116 {
117 scrollY = (flags & WheelRotationMask) / 120;
118 }
119
120 CGEventSourceRef source = CGEventSourceCreate(kCGEventSourceStateHIDSystemState);
121 CGEventRef scroll = CGEventCreateScrollWheelEvent(source, kCGScrollEventUnitLine,
122 wheelCount, scrollY, scrollX);
123 CGEventPost(kCGHIDEventTap, scroll);
124 CFRelease(scroll);
125 CFRelease(source);
126 }
127 else
128 {
129 CGEventSourceRef source = CGEventSourceCreate(kCGEventSourceStateHIDSystemState);
130 CGEventType mouseType = kCGEventNull;
131 CGMouseButton mouseButton = kCGMouseButtonLeft;
132
133 if (flags & PTR_FLAGS_MOVE)
134 {
135 if (mac->mouseDownLeft)
136 mouseType = kCGEventLeftMouseDragged;
137 else if (mac->mouseDownRight)
138 mouseType = kCGEventRightMouseDragged;
139 else if (mac->mouseDownOther)
140 mouseType = kCGEventOtherMouseDragged;
141 else
142 mouseType = kCGEventMouseMoved;
143
144 CGEventRef move =
145 CGEventCreateMouseEvent(source, mouseType, CGPointMake(x, y), mouseButton);
146 CGEventPost(kCGHIDEventTap, move);
147 CFRelease(move);
148 }
149
150 if (flags & PTR_FLAGS_BUTTON1)
151 {
152 mouseButton = kCGMouseButtonLeft;
153
154 if (flags & PTR_FLAGS_DOWN)
155 {
156 mouseType = kCGEventLeftMouseDown;
157 mac->mouseDownLeft = TRUE;
158 }
159 else
160 {
161 mouseType = kCGEventLeftMouseUp;
162 mac->mouseDownLeft = FALSE;
163 }
164 }
165 else if (flags & PTR_FLAGS_BUTTON2)
166 {
167 mouseButton = kCGMouseButtonRight;
168
169 if (flags & PTR_FLAGS_DOWN)
170 {
171 mouseType = kCGEventRightMouseDown;
172 mac->mouseDownRight = TRUE;
173 }
174 else
175 {
176 mouseType = kCGEventRightMouseUp;
177 mac->mouseDownRight = FALSE;
178 }
179 }
180 else if (flags & PTR_FLAGS_BUTTON3)
181 {
182 mouseButton = kCGMouseButtonCenter;
183
184 if (flags & PTR_FLAGS_DOWN)
185 {
186 mouseType = kCGEventOtherMouseDown;
187 mac->mouseDownOther = TRUE;
188 }
189 else
190 {
191 mouseType = kCGEventOtherMouseUp;
192 mac->mouseDownOther = FALSE;
193 }
194 }
195
196 CGEventRef mouseEvent =
197 CGEventCreateMouseEvent(source, mouseType, CGPointMake(x, y), mouseButton);
198 CGEventPost(kCGHIDEventTap, mouseEvent);
199 CFRelease(mouseEvent);
200 CFRelease(source);
201 }
202
203 return TRUE;
204}
205
206static BOOL mac_shadow_input_extended_mouse_event(rdpShadowSubsystem* subsystem,
207 rdpShadowClient* client, UINT16 flags, UINT16 x,
208 UINT16 y)
209{
210 if (!subsystem || !client)
211 return FALSE;
212
213 return TRUE;
214}
215
216static int mac_shadow_detect_monitors(macShadowSubsystem* subsystem)
217{
218 size_t wide, high;
219 MONITOR_DEF* monitor;
220 CGDirectDisplayID displayId;
221 displayId = CGMainDisplayID();
222 CGDisplayModeRef mode = CGDisplayCopyDisplayMode(displayId);
223 subsystem->pixelWidth = CGDisplayModeGetPixelWidth(mode);
224 subsystem->pixelHeight = CGDisplayModeGetPixelHeight(mode);
225 wide = CGDisplayPixelsWide(displayId);
226 high = CGDisplayPixelsHigh(displayId);
227 CGDisplayModeRelease(mode);
228 subsystem->retina = ((subsystem->pixelWidth / wide) == 2) ? TRUE : FALSE;
229
230 if (subsystem->retina)
231 {
232 subsystem->width = wide;
233 subsystem->height = high;
234 }
235 else
236 {
237 subsystem->width = subsystem->pixelWidth;
238 subsystem->height = subsystem->pixelHeight;
239 }
240
241 subsystem->common.numMonitors = 1;
242 monitor = &(subsystem->common.monitors[0]);
243 monitor->left = 0;
244 monitor->top = 0;
245 monitor->right = subsystem->width;
246 monitor->bottom = subsystem->height;
247 monitor->flags = 1;
248 return 1;
249}
250
251static int mac_shadow_capture_start(macShadowSubsystem* subsystem)
252{
253 CGError err;
254 err = CGDisplayStreamStart(subsystem->stream);
255
256 if (err != kCGErrorSuccess)
257 return -1;
258
259 return 1;
260}
261
262static int mac_shadow_capture_stop(macShadowSubsystem* subsystem)
263{
264 CGError err;
265 err = CGDisplayStreamStop(subsystem->stream);
266
267 if (err != kCGErrorSuccess)
268 return -1;
269
270 return 1;
271}
272
273static int mac_shadow_capture_get_dirty_region(macShadowSubsystem* subsystem)
274{
275 size_t numRects;
276 const CGRect* rects;
277 RECTANGLE_16 invalidRect;
278 rdpShadowSurface* surface = subsystem->common.server->surface;
279 rects = CGDisplayStreamUpdateGetRects(subsystem->lastUpdate, kCGDisplayStreamUpdateDirtyRects,
280 &numRects);
281
282 if (!numRects)
283 return -1;
284
285 for (size_t index = 0; index < numRects; index++)
286 {
287 invalidRect.left = (UINT16)rects[index].origin.x;
288 invalidRect.top = (UINT16)rects[index].origin.y;
289 invalidRect.right = invalidRect.left + (UINT16)rects[index].size.width;
290 invalidRect.bottom = invalidRect.top + (UINT16)rects[index].size.height;
291
292 if (subsystem->retina)
293 {
294 /* scale invalid rect */
295 invalidRect.left /= 2;
296 invalidRect.top /= 2;
297 invalidRect.right /= 2;
298 invalidRect.bottom /= 2;
299 }
300
301 region16_union_rect(&(surface->invalidRegion), &(surface->invalidRegion), &invalidRect);
302 }
303
304 return 0;
305}
306
307static int freerdp_image_copy_from_retina(BYTE* pDstData, DWORD DstFormat, int nDstStep, int nXDst,
308 int nYDst, int nWidth, int nHeight, BYTE* pSrcData,
309 int nSrcStep, int nXSrc, int nYSrc)
310{
311 BYTE* pSrcPixel;
312 BYTE* pDstPixel;
313 int nSrcPad;
314 int nDstPad;
315 int srcBitsPerPixel;
316 int srcBytesPerPixel;
317 int dstBitsPerPixel;
318 int dstBytesPerPixel;
319 srcBitsPerPixel = 24;
320 srcBytesPerPixel = 8;
321
322 if (nSrcStep < 0)
323 nSrcStep = srcBytesPerPixel * nWidth;
324
325 dstBitsPerPixel = FreeRDPGetBitsPerPixel(DstFormat);
326 dstBytesPerPixel = FreeRDPGetBytesPerPixel(DstFormat);
327
328 if (nDstStep < 0)
329 nDstStep = dstBytesPerPixel * nWidth;
330
331 nSrcPad = (nSrcStep - (nWidth * srcBytesPerPixel));
332 nDstPad = (nDstStep - (nWidth * dstBytesPerPixel));
333 pSrcPixel = &pSrcData[(nYSrc * nSrcStep) + (nXSrc * 4)];
334 pDstPixel = &pDstData[(nYDst * nDstStep) + (nXDst * 4)];
335
336 for (int y = 0; y < nHeight; y++)
337 {
338 for (int x = 0; x < nWidth; x++)
339 {
340 UINT32 R, G, B;
341 UINT32 color;
342 /* simple box filter scaling, could be improved with better algorithm */
343 B = pSrcPixel[0] + pSrcPixel[4] + pSrcPixel[nSrcStep + 0] + pSrcPixel[nSrcStep + 4];
344 G = pSrcPixel[1] + pSrcPixel[5] + pSrcPixel[nSrcStep + 1] + pSrcPixel[nSrcStep + 5];
345 R = pSrcPixel[2] + pSrcPixel[6] + pSrcPixel[nSrcStep + 2] + pSrcPixel[nSrcStep + 6];
346 pSrcPixel += 8;
347 color = FreeRDPGetColor(DstFormat, R >> 2, G >> 2, B >> 2, 0xFF);
348 FreeRDPWriteColor(pDstPixel, DstFormat, color);
349 pDstPixel += dstBytesPerPixel;
350 }
351
352 pSrcPixel = &pSrcPixel[nSrcPad + nSrcStep];
353 pDstPixel = &pDstPixel[nDstPad];
354 }
355
356 return 1;
357}
358
359static void (^mac_capture_stream_handler)(
360 CGDisplayStreamFrameStatus, uint64_t, IOSurfaceRef,
361 CGDisplayStreamUpdateRef) = ^(CGDisplayStreamFrameStatus status, uint64_t displayTime,
362 IOSurfaceRef frameSurface, CGDisplayStreamUpdateRef updateRef) {
363 int x, y;
364 int count;
365 int width;
366 int height;
367 int nSrcStep;
368 BOOL empty;
369 BYTE* pSrcData;
370 RECTANGLE_16 surfaceRect;
371 const RECTANGLE_16* extents;
372 macShadowSubsystem* subsystem = g_Subsystem;
373 rdpShadowServer* server = subsystem->common.server;
374 rdpShadowSurface* surface = server->surface;
375 count = ArrayList_Count(server->clients);
376
377 if (count < 1)
378 return;
379
380 EnterCriticalSection(&(surface->lock));
381 mac_shadow_capture_get_dirty_region(subsystem);
382 surfaceRect.left = 0;
383 surfaceRect.top = 0;
384 surfaceRect.right = surface->width;
385 surfaceRect.bottom = surface->height;
386 region16_intersect_rect(&(surface->invalidRegion), &(surface->invalidRegion), &surfaceRect);
387 empty = region16_is_empty(&(surface->invalidRegion));
388 LeaveCriticalSection(&(surface->lock));
389
390 if (!empty)
391 {
392 extents = region16_extents(&(surface->invalidRegion));
393 x = extents->left;
394 y = extents->top;
395 width = extents->right - extents->left;
396 height = extents->bottom - extents->top;
397 IOSurfaceLock(frameSurface, kIOSurfaceLockReadOnly, NULL);
398 pSrcData = (BYTE*)IOSurfaceGetBaseAddress(frameSurface);
399 nSrcStep = (int)IOSurfaceGetBytesPerRow(frameSurface);
400
401 if (subsystem->retina)
402 {
403 freerdp_image_copy_from_retina(surface->data, surface->format, surface->scanline, x, y,
404 width, height, pSrcData, nSrcStep, x, y);
405 }
406 else
407 {
408 freerdp_image_copy_no_overlap(surface->data, surface->format, surface->scanline, x, y,
409 width, height, pSrcData, PIXEL_FORMAT_BGRX32, nSrcStep, x,
410 y, NULL, FREERDP_FLIP_NONE);
411 }
412 LeaveCriticalSection(&(surface->lock));
413
414 IOSurfaceUnlock(frameSurface, kIOSurfaceLockReadOnly, NULL);
415 ArrayList_Lock(server->clients);
416 count = ArrayList_Count(server->clients);
417 shadow_subsystem_frame_update(&subsystem->common);
418
419 if (count == 1)
420 {
421 rdpShadowClient* client;
422 client = (rdpShadowClient*)ArrayList_GetItem(server->clients, 0);
423
424 if (client)
425 {
426 subsystem->common.captureFrameRate = shadow_encoder_preferred_fps(client->encoder);
427 }
428 }
429
430 ArrayList_Unlock(server->clients);
431 EnterCriticalSection(&(surface->lock));
432 region16_clear(&(surface->invalidRegion));
433 LeaveCriticalSection(&(surface->lock));
434 }
435
436 if (status != kCGDisplayStreamFrameStatusFrameComplete)
437 {
438 switch (status)
439 {
440 case kCGDisplayStreamFrameStatusFrameIdle:
441 break;
442
443 case kCGDisplayStreamFrameStatusStopped:
444 break;
445
446 case kCGDisplayStreamFrameStatusFrameBlank:
447 break;
448
449 default:
450 break;
451 }
452 }
453 else if (!subsystem->lastUpdate)
454 {
455 CFRetain(updateRef);
456 subsystem->lastUpdate = updateRef;
457 }
458 else
459 {
460 CGDisplayStreamUpdateRef tmpRef = subsystem->lastUpdate;
461 subsystem->lastUpdate = CGDisplayStreamUpdateCreateMergedUpdate(tmpRef, updateRef);
462 CFRelease(tmpRef);
463 }
464};
465
466static int mac_shadow_capture_init(macShadowSubsystem* subsystem)
467{
468 void* keys[2];
469 void* values[2];
470 CFDictionaryRef opts;
471 CGDirectDisplayID displayId;
472 displayId = CGMainDisplayID();
473 subsystem->captureQueue = dispatch_queue_create("mac.shadow.capture", NULL);
474 keys[0] = (void*)kCGDisplayStreamShowCursor;
475 values[0] = (void*)kCFBooleanFalse;
476 opts = CFDictionaryCreate(kCFAllocatorDefault, (const void**)keys, (const void**)values, 1,
477 NULL, NULL);
478 subsystem->stream = CGDisplayStreamCreateWithDispatchQueue(
479 displayId, subsystem->pixelWidth, subsystem->pixelHeight, 'BGRA', opts,
480 subsystem->captureQueue, mac_capture_stream_handler);
481 CFRelease(opts);
482 return 1;
483}
484
485static int mac_shadow_screen_grab(macShadowSubsystem* subsystem)
486{
487 return 1;
488}
489
490static int mac_shadow_subsystem_process_message(macShadowSubsystem* subsystem, wMessage* message)
491{
492 rdpShadowServer* server = subsystem->common.server;
493 rdpShadowSurface* surface = server->surface;
494
495 switch (message->id)
496 {
497 case SHADOW_MSG_IN_REFRESH_REQUEST_ID:
498 EnterCriticalSection(&(surface->lock));
499 shadow_subsystem_frame_update((rdpShadowSubsystem*)subsystem);
500 LeaveCriticalSection(&(surface->lock));
501 break;
502
503 default:
504 WLog_ERR(TAG, "Unknown message id: %" PRIu32 "", message->id);
505 break;
506 }
507
508 if (message->Free)
509 message->Free(message);
510
511 return 1;
512}
513
514static DWORD WINAPI mac_shadow_subsystem_thread(LPVOID arg)
515{
516 macShadowSubsystem* subsystem = (macShadowSubsystem*)arg;
517 DWORD status;
518 DWORD nCount;
519 UINT64 cTime;
520 DWORD dwTimeout;
521 DWORD dwInterval;
522 UINT64 frameTime;
523 HANDLE events[32];
524 wMessage message;
525 wMessagePipe* MsgPipe;
526 MsgPipe = subsystem->common.MsgPipe;
527 nCount = 0;
528 events[nCount++] = MessageQueue_Event(MsgPipe->In);
529 subsystem->common.captureFrameRate = 16;
530 dwInterval = 1000 / subsystem->common.captureFrameRate;
531 frameTime = GetTickCount64() + dwInterval;
532
533 while (1)
534 {
535 cTime = GetTickCount64();
536 dwTimeout = (cTime > frameTime) ? 0 : frameTime - cTime;
537 status = WaitForMultipleObjects(nCount, events, FALSE, dwTimeout);
538
539 if (WaitForSingleObject(MessageQueue_Event(MsgPipe->In), 0) == WAIT_OBJECT_0)
540 {
541 if (MessageQueue_Peek(MsgPipe->In, &message, TRUE))
542 {
543 if (message.id == WMQ_QUIT)
544 break;
545
546 mac_shadow_subsystem_process_message(subsystem, &message);
547 }
548 }
549
550 if ((status == WAIT_TIMEOUT) || (GetTickCount64() > frameTime))
551 {
552 mac_shadow_screen_grab(subsystem);
553 dwInterval = 1000 / subsystem->common.captureFrameRate;
554 frameTime += dwInterval;
555 }
556 }
557
558 ExitThread(0);
559 return 0;
560}
561
562static UINT32 mac_shadow_enum_monitors(MONITOR_DEF* monitors, UINT32 maxMonitors)
563{
564 int index;
565 size_t wide, high;
566 UINT32 numMonitors = 0;
567 MONITOR_DEF* monitor;
568 CGDirectDisplayID displayId;
569 displayId = CGMainDisplayID();
570 CGDisplayModeRef mode = CGDisplayCopyDisplayMode(displayId);
571 wide = CGDisplayPixelsWide(displayId);
572 high = CGDisplayPixelsHigh(displayId);
573 CGDisplayModeRelease(mode);
574 index = 0;
575 numMonitors = 1;
576 monitor = &monitors[index];
577 monitor->left = 0;
578 monitor->top = 0;
579 monitor->right = (int)wide;
580 monitor->bottom = (int)high;
581 monitor->flags = 1;
582 return numMonitors;
583}
584
585static int mac_shadow_subsystem_init(rdpShadowSubsystem* rdpsubsystem)
586{
587 macShadowSubsystem* subsystem = (macShadowSubsystem*)rdpsubsystem;
588 g_Subsystem = subsystem;
589
590 mac_shadow_detect_monitors(subsystem);
591 mac_shadow_capture_init(subsystem);
592 return 1;
593}
594
595static int mac_shadow_subsystem_uninit(rdpShadowSubsystem* rdpsubsystem)
596{
597 macShadowSubsystem* subsystem = (macShadowSubsystem*)rdpsubsystem;
598 if (!subsystem)
599 return -1;
600
601 if (subsystem->lastUpdate)
602 {
603 CFRelease(subsystem->lastUpdate);
604 subsystem->lastUpdate = NULL;
605 }
606
607 return 1;
608}
609
610static int mac_shadow_subsystem_start(rdpShadowSubsystem* rdpsubsystem)
611{
612 macShadowSubsystem* subsystem = (macShadowSubsystem*)rdpsubsystem;
613 HANDLE thread;
614
615 if (!subsystem)
616 return -1;
617
618 mac_shadow_capture_start(subsystem);
619
620 if (!(thread = CreateThread(NULL, 0, mac_shadow_subsystem_thread, (void*)subsystem, 0, NULL)))
621 {
622 WLog_ERR(TAG, "Failed to create thread");
623 return -1;
624 }
625
626 return 1;
627}
628
629static int mac_shadow_subsystem_stop(rdpShadowSubsystem* subsystem)
630{
631 if (!subsystem)
632 return -1;
633
634 return 1;
635}
636
637static void mac_shadow_subsystem_free(rdpShadowSubsystem* subsystem)
638{
639 if (!subsystem)
640 return;
641
642 mac_shadow_subsystem_uninit(subsystem);
643 free(subsystem);
644}
645
646static rdpShadowSubsystem* mac_shadow_subsystem_new(void)
647{
648 macShadowSubsystem* subsystem = calloc(1, sizeof(macShadowSubsystem));
649
650 if (!subsystem)
651 return NULL;
652
653 subsystem->common.SynchronizeEvent = mac_shadow_input_synchronize_event;
654 subsystem->common.KeyboardEvent = mac_shadow_input_keyboard_event;
655 subsystem->common.UnicodeKeyboardEvent = mac_shadow_input_unicode_keyboard_event;
656 subsystem->common.MouseEvent = mac_shadow_input_mouse_event;
657 subsystem->common.ExtendedMouseEvent = mac_shadow_input_extended_mouse_event;
658 return &subsystem->common;
659}
660
661FREERDP_API const char* ShadowSubsystemName(void)
662{
663 return "Mac";
664}
665
666FREERDP_API int ShadowSubsystemEntry(RDP_SHADOW_ENTRY_POINTS* pEntryPoints)
667{
668 char name[] = "mac shadow subsystem";
669 char* arg[] = { name };
670
671 freerdp_server_warn_unmaintained(ARRAYSIZE(arg), arg);
672 pEntryPoints->New = mac_shadow_subsystem_new;
673 pEntryPoints->Free = mac_shadow_subsystem_free;
674 pEntryPoints->Init = mac_shadow_subsystem_init;
675 pEntryPoints->Uninit = mac_shadow_subsystem_uninit;
676 pEntryPoints->Start = mac_shadow_subsystem_start;
677 pEntryPoints->Stop = mac_shadow_subsystem_stop;
678 pEntryPoints->EnumMonitors = mac_shadow_enum_monitors;
679 return 1;
680}