19 #include <winpr/crt.h>
20 #include <winpr/synch.h>
21 #include <winpr/input.h>
22 #include <winpr/sysinfo.h>
24 #include <freerdp/server/server-common.h>
25 #include <freerdp/codec/color.h>
26 #include <freerdp/codec/region.h>
27 #include <freerdp/log.h>
29 #include "mac_shadow.h"
31 #define TAG SERVER_TAG("shadow.mac")
33 static macShadowSubsystem* g_Subsystem = NULL;
35 static BOOL mac_shadow_input_synchronize_event(rdpShadowSubsystem* subsystem,
36 rdpShadowClient* client, UINT32 flags)
38 if (!subsystem || !client)
44 static BOOL mac_shadow_input_keyboard_event(rdpShadowSubsystem* subsystem, rdpShadowClient* client,
45 UINT16 flags, UINT8 code)
51 CGEventSourceRef source;
52 extended = (flags & KBD_FLAGS_EXTENDED) ? TRUE : FALSE;
54 if (!subsystem || !client)
60 vkcode = GetVirtualKeyCodeFromVirtualScanCode(code, 4);
65 keycode = GetKeycodeFromVirtualKeyCode(vkcode, WINPR_KEYCODE_TYPE_APPLE);
67 source = CGEventSourceCreate(kCGEventSourceStateHIDSystemState);
69 if (flags & KBD_FLAGS_DOWN)
71 kbdEvent = CGEventCreateKeyboardEvent(source, (CGKeyCode)keycode, TRUE);
72 CGEventPost(kCGHIDEventTap, kbdEvent);
75 else if (flags & KBD_FLAGS_RELEASE)
77 kbdEvent = CGEventCreateKeyboardEvent(source, (CGKeyCode)keycode, FALSE);
78 CGEventPost(kCGHIDEventTap, kbdEvent);
86 static BOOL mac_shadow_input_unicode_keyboard_event(rdpShadowSubsystem* subsystem,
87 rdpShadowClient* client, UINT16 flags,
90 if (!subsystem || !client)
96 static BOOL mac_shadow_input_mouse_event(rdpShadowSubsystem* subsystem, rdpShadowClient* client,
97 UINT16 flags, UINT16 x, UINT16 y)
99 macShadowSubsystem* mac = (macShadowSubsystem*)subsystem;
102 CGWheelCount wheelCount = 2;
104 if (!subsystem || !client)
107 if (flags & PTR_FLAGS_WHEEL)
109 scrollY = flags & WheelRotationMask;
111 if (flags & PTR_FLAGS_WHEEL_NEGATIVE)
113 scrollY = -(flags & WheelRotationMask) / 392;
117 scrollY = (flags & WheelRotationMask) / 120;
120 CGEventSourceRef source = CGEventSourceCreate(kCGEventSourceStateHIDSystemState);
121 CGEventRef scroll = CGEventCreateScrollWheelEvent(source, kCGScrollEventUnitLine,
122 wheelCount, scrollY, scrollX);
123 CGEventPost(kCGHIDEventTap, scroll);
129 CGEventSourceRef source = CGEventSourceCreate(kCGEventSourceStateHIDSystemState);
130 CGEventType mouseType = kCGEventNull;
131 CGMouseButton mouseButton = kCGMouseButtonLeft;
133 if (flags & PTR_FLAGS_MOVE)
135 if (mac->mouseDownLeft)
136 mouseType = kCGEventLeftMouseDragged;
137 else if (mac->mouseDownRight)
138 mouseType = kCGEventRightMouseDragged;
139 else if (mac->mouseDownOther)
140 mouseType = kCGEventOtherMouseDragged;
142 mouseType = kCGEventMouseMoved;
145 CGEventCreateMouseEvent(source, mouseType, CGPointMake(x, y), mouseButton);
146 CGEventPost(kCGHIDEventTap, move);
150 if (flags & PTR_FLAGS_BUTTON1)
152 mouseButton = kCGMouseButtonLeft;
154 if (flags & PTR_FLAGS_DOWN)
156 mouseType = kCGEventLeftMouseDown;
157 mac->mouseDownLeft = TRUE;
161 mouseType = kCGEventLeftMouseUp;
162 mac->mouseDownLeft = FALSE;
165 else if (flags & PTR_FLAGS_BUTTON2)
167 mouseButton = kCGMouseButtonRight;
169 if (flags & PTR_FLAGS_DOWN)
171 mouseType = kCGEventRightMouseDown;
172 mac->mouseDownRight = TRUE;
176 mouseType = kCGEventRightMouseUp;
177 mac->mouseDownRight = FALSE;
180 else if (flags & PTR_FLAGS_BUTTON3)
182 mouseButton = kCGMouseButtonCenter;
184 if (flags & PTR_FLAGS_DOWN)
186 mouseType = kCGEventOtherMouseDown;
187 mac->mouseDownOther = TRUE;
191 mouseType = kCGEventOtherMouseUp;
192 mac->mouseDownOther = FALSE;
196 CGEventRef mouseEvent =
197 CGEventCreateMouseEvent(source, mouseType, CGPointMake(x, y), mouseButton);
198 CGEventPost(kCGHIDEventTap, mouseEvent);
199 CFRelease(mouseEvent);
206 static BOOL mac_shadow_input_extended_mouse_event(rdpShadowSubsystem* subsystem,
207 rdpShadowClient* client, UINT16 flags, UINT16 x,
210 if (!subsystem || !client)
216 static int mac_shadow_detect_monitors(macShadowSubsystem* subsystem)
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;
230 if (subsystem->retina)
232 subsystem->width = wide;
233 subsystem->height = high;
237 subsystem->width = subsystem->pixelWidth;
238 subsystem->height = subsystem->pixelHeight;
241 subsystem->common.numMonitors = 1;
242 monitor = &(subsystem->common.monitors[0]);
245 monitor->right = subsystem->width;
246 monitor->bottom = subsystem->height;
251 static int mac_shadow_capture_start(macShadowSubsystem* subsystem)
254 err = CGDisplayStreamStart(subsystem->stream);
256 if (err != kCGErrorSuccess)
262 static int mac_shadow_capture_stop(macShadowSubsystem* subsystem)
265 err = CGDisplayStreamStop(subsystem->stream);
267 if (err != kCGErrorSuccess)
273 static int mac_shadow_capture_get_dirty_region(macShadowSubsystem* subsystem)
278 rdpShadowSurface* surface = subsystem->common.server->surface;
279 rects = CGDisplayStreamUpdateGetRects(subsystem->lastUpdate, kCGDisplayStreamUpdateDirtyRects,
285 for (
size_t index = 0; index < numRects; index++)
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;
292 if (subsystem->retina)
295 invalidRect.left /= 2;
296 invalidRect.top /= 2;
297 invalidRect.right /= 2;
298 invalidRect.bottom /= 2;
301 region16_union_rect(&(surface->invalidRegion), &(surface->invalidRegion), &invalidRect);
307 static 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)
316 int srcBytesPerPixel;
318 int dstBytesPerPixel;
319 srcBitsPerPixel = 24;
320 srcBytesPerPixel = 8;
323 nSrcStep = srcBytesPerPixel * nWidth;
325 dstBitsPerPixel = FreeRDPGetBitsPerPixel(DstFormat);
326 dstBytesPerPixel = FreeRDPGetBytesPerPixel(DstFormat);
329 nDstStep = dstBytesPerPixel * nWidth;
331 nSrcPad = (nSrcStep - (nWidth * srcBytesPerPixel));
332 nDstPad = (nDstStep - (nWidth * dstBytesPerPixel));
333 pSrcPixel = &pSrcData[(nYSrc * nSrcStep) + (nXSrc * 4)];
334 pDstPixel = &pDstData[(nYDst * nDstStep) + (nXDst * 4)];
336 for (
int y = 0; y < nHeight; y++)
338 for (
int x = 0; x < nWidth; x++)
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];
347 color = FreeRDPGetColor(DstFormat, R >> 2, G >> 2, B >> 2, 0xFF);
348 FreeRDPWriteColor(pDstPixel, DstFormat, color);
349 pDstPixel += dstBytesPerPixel;
352 pSrcPixel = &pSrcPixel[nSrcPad + nSrcStep];
353 pDstPixel = &pDstPixel[nDstPad];
359 static void (^mac_capture_stream_handler)(
360 CGDisplayStreamFrameStatus, uint64_t, IOSurfaceRef,
361 CGDisplayStreamUpdateRef) = ^(CGDisplayStreamFrameStatus status, uint64_t displayTime,
362 IOSurfaceRef frameSurface, CGDisplayStreamUpdateRef updateRef) {
372 macShadowSubsystem* subsystem = g_Subsystem;
373 rdpShadowServer* server = subsystem->common.server;
374 rdpShadowSurface* surface = server->surface;
375 count = ArrayList_Count(server->clients);
380 EnterCriticalSection(&(surface->lock));
381 mac_shadow_capture_get_dirty_region(subsystem);
382 surfaceRect.left = 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));
392 extents = region16_extents(&(surface->invalidRegion));
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);
401 if (subsystem->retina)
403 freerdp_image_copy_from_retina(surface->data, surface->format, surface->scanline, x, y,
404 width, height, pSrcData, nSrcStep, x, y);
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);
412 LeaveCriticalSection(&(surface->lock));
414 IOSurfaceUnlock(frameSurface, kIOSurfaceLockReadOnly, NULL);
415 ArrayList_Lock(server->clients);
416 count = ArrayList_Count(server->clients);
417 shadow_subsystem_frame_update(&subsystem->common);
421 rdpShadowClient* client;
422 client = (rdpShadowClient*)ArrayList_GetItem(server->clients, 0);
426 subsystem->common.captureFrameRate = shadow_encoder_preferred_fps(client->encoder);
430 ArrayList_Unlock(server->clients);
431 EnterCriticalSection(&(surface->lock));
432 region16_clear(&(surface->invalidRegion));
433 LeaveCriticalSection(&(surface->lock));
436 if (status != kCGDisplayStreamFrameStatusFrameComplete)
440 case kCGDisplayStreamFrameStatusFrameIdle:
443 case kCGDisplayStreamFrameStatusStopped:
446 case kCGDisplayStreamFrameStatusFrameBlank:
453 else if (!subsystem->lastUpdate)
456 subsystem->lastUpdate = updateRef;
460 CGDisplayStreamUpdateRef tmpRef = subsystem->lastUpdate;
461 subsystem->lastUpdate = CGDisplayStreamUpdateCreateMergedUpdate(tmpRef, updateRef);
466 static int mac_shadow_capture_init(macShadowSubsystem* subsystem)
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,
478 subsystem->stream = CGDisplayStreamCreateWithDispatchQueue(
479 displayId, subsystem->pixelWidth, subsystem->pixelHeight,
'BGRA', opts,
480 subsystem->captureQueue, mac_capture_stream_handler);
485 static int mac_shadow_screen_grab(macShadowSubsystem* subsystem)
490 static int mac_shadow_subsystem_process_message(macShadowSubsystem* subsystem, wMessage* message)
492 rdpShadowServer* server = subsystem->common.server;
493 rdpShadowSurface* surface = server->surface;
497 case SHADOW_MSG_IN_REFRESH_REQUEST_ID:
498 EnterCriticalSection(&(surface->lock));
499 shadow_subsystem_frame_update((rdpShadowSubsystem*)subsystem);
500 LeaveCriticalSection(&(surface->lock));
504 WLog_ERR(TAG,
"Unknown message id: %" PRIu32
"", message->id);
509 message->Free(message);
514 static DWORD WINAPI mac_shadow_subsystem_thread(LPVOID arg)
516 macShadowSubsystem* subsystem = (macShadowSubsystem*)arg;
526 MsgPipe = subsystem->common.MsgPipe;
528 events[nCount++] = MessageQueue_Event(MsgPipe->In);
529 subsystem->common.captureFrameRate = 16;
530 dwInterval = 1000 / subsystem->common.captureFrameRate;
531 frameTime = GetTickCount64() + dwInterval;
535 cTime = GetTickCount64();
536 dwTimeout = (cTime > frameTime) ? 0 : frameTime - cTime;
537 status = WaitForMultipleObjects(nCount, events, FALSE, dwTimeout);
539 if (WaitForSingleObject(MessageQueue_Event(MsgPipe->In), 0) == WAIT_OBJECT_0)
541 if (MessageQueue_Peek(MsgPipe->In, &message, TRUE))
543 if (message.id == WMQ_QUIT)
546 mac_shadow_subsystem_process_message(subsystem, &message);
550 if ((status == WAIT_TIMEOUT) || (GetTickCount64() > frameTime))
552 mac_shadow_screen_grab(subsystem);
553 dwInterval = 1000 / subsystem->common.captureFrameRate;
554 frameTime += dwInterval;
562 static UINT32 mac_shadow_enum_monitors(
MONITOR_DEF* monitors, UINT32 maxMonitors)
566 UINT32 numMonitors = 0;
568 CGDirectDisplayID displayId;
569 displayId = CGMainDisplayID();
570 CGDisplayModeRef mode = CGDisplayCopyDisplayMode(displayId);
571 wide = CGDisplayPixelsWide(displayId);
572 high = CGDisplayPixelsHigh(displayId);
573 CGDisplayModeRelease(mode);
576 monitor = &monitors[index];
579 monitor->right = (int)wide;
580 monitor->bottom = (int)high;
585 static int mac_shadow_subsystem_init(rdpShadowSubsystem* rdpsubsystem)
587 macShadowSubsystem* subsystem = (macShadowSubsystem*)rdpsubsystem;
588 g_Subsystem = subsystem;
590 mac_shadow_detect_monitors(subsystem);
591 mac_shadow_capture_init(subsystem);
595 static int mac_shadow_subsystem_uninit(rdpShadowSubsystem* rdpsubsystem)
597 macShadowSubsystem* subsystem = (macShadowSubsystem*)rdpsubsystem;
601 if (subsystem->lastUpdate)
603 CFRelease(subsystem->lastUpdate);
604 subsystem->lastUpdate = NULL;
610 static int mac_shadow_subsystem_start(rdpShadowSubsystem* rdpsubsystem)
612 macShadowSubsystem* subsystem = (macShadowSubsystem*)rdpsubsystem;
618 mac_shadow_capture_start(subsystem);
620 if (!(thread = CreateThread(NULL, 0, mac_shadow_subsystem_thread, (
void*)subsystem, 0, NULL)))
622 WLog_ERR(TAG,
"Failed to create thread");
629 static int mac_shadow_subsystem_stop(rdpShadowSubsystem* subsystem)
637 static void mac_shadow_subsystem_free(rdpShadowSubsystem* subsystem)
642 mac_shadow_subsystem_uninit(subsystem);
646 static rdpShadowSubsystem* mac_shadow_subsystem_new(
void)
648 macShadowSubsystem* subsystem = calloc(1,
sizeof(macShadowSubsystem));
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;
661 FREERDP_API
const char* ShadowSubsystemName(
void)
666 FREERDP_API
int ShadowSubsystemEntry(RDP_SHADOW_ENTRY_POINTS* pEntryPoints)
668 char name[] =
"mac shadow subsystem";
669 char* arg[] = { name };
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;