summaryrefslogtreecommitdiffstats
path: root/server/shadow/Mac
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 01:24:41 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 01:24:41 +0000
commita9bcc81f821d7c66f623779fa5147e728eb3c388 (patch)
tree98676963bcdd537ae5908a067a8eb110b93486a6 /server/shadow/Mac
parentInitial commit. (diff)
downloadfreerdp3-a9bcc81f821d7c66f623779fa5147e728eb3c388.tar.xz
freerdp3-a9bcc81f821d7c66f623779fa5147e728eb3c388.zip
Adding upstream version 3.3.0+dfsg1.upstream/3.3.0+dfsg1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'server/shadow/Mac')
-rw-r--r--server/shadow/Mac/CMakeLists.txt31
-rw-r--r--server/shadow/Mac/mac_shadow.c680
-rw-r--r--server/shadow/Mac/mac_shadow.h64
3 files changed, 775 insertions, 0 deletions
diff --git a/server/shadow/Mac/CMakeLists.txt b/server/shadow/Mac/CMakeLists.txt
new file mode 100644
index 0000000..20c9a7b
--- /dev/null
+++ b/server/shadow/Mac/CMakeLists.txt
@@ -0,0 +1,31 @@
+
+include (WarnUnmaintained)
+warn_unmaintained("mac shadow server subsystem")
+
+find_library(IOKIT IOKit REQUIRED)
+find_library(IOSURFACE IOSurface REQUIRED)
+find_library(CARBON Carbon REQUIRED)
+find_package(PAM)
+
+set(LIBS
+ ${IOKIT}
+ ${IOSURFACE}
+ ${CARBON}
+)
+
+if(PAM_FOUND)
+ add_definitions(-DWITH_PAM)
+ include_directories(${PAM_INCLUDE_DIR})
+ list(APPEND LIBS ${PAM_LIBRARY})
+else()
+ message("building without PAM authentication support")
+endif()
+
+add_definitions(-DWITH_SHADOW_MAC)
+add_library(freerdp-shadow-subsystem-impl STATIC
+ mac_shadow.h
+ mac_shadow.c
+)
+target_link_libraries(freerdp-shadow-subsystem-impl PRIVATE
+ ${LIBS}
+)
diff --git a/server/shadow/Mac/mac_shadow.c b/server/shadow/Mac/mac_shadow.c
new file mode 100644
index 0000000..ba6d2b0
--- /dev/null
+++ b/server/shadow/Mac/mac_shadow.c
@@ -0,0 +1,680 @@
+/**
+ * FreeRDP: A Remote Desktop Protocol Implementation
+ *
+ * Copyright 2011-2014 Marc-Andre Moreau <marcandre.moreau@gmail.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <winpr/crt.h>
+#include <winpr/synch.h>
+#include <winpr/input.h>
+#include <winpr/sysinfo.h>
+
+#include <freerdp/server/server-common.h>
+#include <freerdp/codec/color.h>
+#include <freerdp/codec/region.h>
+#include <freerdp/log.h>
+
+#include "mac_shadow.h"
+
+#define TAG SERVER_TAG("shadow.mac")
+
+static macShadowSubsystem* g_Subsystem = NULL;
+
+static BOOL mac_shadow_input_synchronize_event(rdpShadowSubsystem* subsystem,
+ rdpShadowClient* client, UINT32 flags)
+{
+ if (!subsystem || !client)
+ return FALSE;
+
+ return TRUE;
+}
+
+static BOOL mac_shadow_input_keyboard_event(rdpShadowSubsystem* subsystem, rdpShadowClient* client,
+ UINT16 flags, UINT8 code)
+{
+ DWORD vkcode;
+ DWORD keycode;
+ BOOL extended;
+ CGEventRef kbdEvent;
+ CGEventSourceRef source;
+ extended = (flags & KBD_FLAGS_EXTENDED) ? TRUE : FALSE;
+
+ if (!subsystem || !client)
+ return FALSE;
+
+ if (extended)
+ code |= KBDEXT;
+
+ vkcode = GetVirtualKeyCodeFromVirtualScanCode(code, 4);
+
+ if (extended)
+ vkcode |= KBDEXT;
+
+ keycode = GetKeycodeFromVirtualKeyCode(vkcode, WINPR_KEYCODE_TYPE_APPLE);
+
+ source = CGEventSourceCreate(kCGEventSourceStateHIDSystemState);
+
+ if (flags & KBD_FLAGS_DOWN)
+ {
+ kbdEvent = CGEventCreateKeyboardEvent(source, (CGKeyCode)keycode, TRUE);
+ CGEventPost(kCGHIDEventTap, kbdEvent);
+ CFRelease(kbdEvent);
+ }
+ else if (flags & KBD_FLAGS_RELEASE)
+ {
+ kbdEvent = CGEventCreateKeyboardEvent(source, (CGKeyCode)keycode, FALSE);
+ CGEventPost(kCGHIDEventTap, kbdEvent);
+ CFRelease(kbdEvent);
+ }
+
+ CFRelease(source);
+ return TRUE;
+}
+
+static BOOL mac_shadow_input_unicode_keyboard_event(rdpShadowSubsystem* subsystem,
+ rdpShadowClient* client, UINT16 flags,
+ UINT16 code)
+{
+ if (!subsystem || !client)
+ return FALSE;
+
+ return TRUE;
+}
+
+static BOOL mac_shadow_input_mouse_event(rdpShadowSubsystem* subsystem, rdpShadowClient* client,
+ UINT16 flags, UINT16 x, UINT16 y)
+{
+ macShadowSubsystem* mac = (macShadowSubsystem*)subsystem;
+ UINT32 scrollX = 0;
+ UINT32 scrollY = 0;
+ CGWheelCount wheelCount = 2;
+
+ if (!subsystem || !client)
+ return FALSE;
+
+ if (flags & PTR_FLAGS_WHEEL)
+ {
+ scrollY = flags & WheelRotationMask;
+
+ if (flags & PTR_FLAGS_WHEEL_NEGATIVE)
+ {
+ scrollY = -(flags & WheelRotationMask) / 392;
+ }
+ else
+ {
+ scrollY = (flags & WheelRotationMask) / 120;
+ }
+
+ CGEventSourceRef source = CGEventSourceCreate(kCGEventSourceStateHIDSystemState);
+ CGEventRef scroll = CGEventCreateScrollWheelEvent(source, kCGScrollEventUnitLine,
+ wheelCount, scrollY, scrollX);
+ CGEventPost(kCGHIDEventTap, scroll);
+ CFRelease(scroll);
+ CFRelease(source);
+ }
+ else
+ {
+ CGEventSourceRef source = CGEventSourceCreate(kCGEventSourceStateHIDSystemState);
+ CGEventType mouseType = kCGEventNull;
+ CGMouseButton mouseButton = kCGMouseButtonLeft;
+
+ if (flags & PTR_FLAGS_MOVE)
+ {
+ if (mac->mouseDownLeft)
+ mouseType = kCGEventLeftMouseDragged;
+ else if (mac->mouseDownRight)
+ mouseType = kCGEventRightMouseDragged;
+ else if (mac->mouseDownOther)
+ mouseType = kCGEventOtherMouseDragged;
+ else
+ mouseType = kCGEventMouseMoved;
+
+ CGEventRef move =
+ CGEventCreateMouseEvent(source, mouseType, CGPointMake(x, y), mouseButton);
+ CGEventPost(kCGHIDEventTap, move);
+ CFRelease(move);
+ }
+
+ if (flags & PTR_FLAGS_BUTTON1)
+ {
+ mouseButton = kCGMouseButtonLeft;
+
+ if (flags & PTR_FLAGS_DOWN)
+ {
+ mouseType = kCGEventLeftMouseDown;
+ mac->mouseDownLeft = TRUE;
+ }
+ else
+ {
+ mouseType = kCGEventLeftMouseUp;
+ mac->mouseDownLeft = FALSE;
+ }
+ }
+ else if (flags & PTR_FLAGS_BUTTON2)
+ {
+ mouseButton = kCGMouseButtonRight;
+
+ if (flags & PTR_FLAGS_DOWN)
+ {
+ mouseType = kCGEventRightMouseDown;
+ mac->mouseDownRight = TRUE;
+ }
+ else
+ {
+ mouseType = kCGEventRightMouseUp;
+ mac->mouseDownRight = FALSE;
+ }
+ }
+ else if (flags & PTR_FLAGS_BUTTON3)
+ {
+ mouseButton = kCGMouseButtonCenter;
+
+ if (flags & PTR_FLAGS_DOWN)
+ {
+ mouseType = kCGEventOtherMouseDown;
+ mac->mouseDownOther = TRUE;
+ }
+ else
+ {
+ mouseType = kCGEventOtherMouseUp;
+ mac->mouseDownOther = FALSE;
+ }
+ }
+
+ CGEventRef mouseEvent =
+ CGEventCreateMouseEvent(source, mouseType, CGPointMake(x, y), mouseButton);
+ CGEventPost(kCGHIDEventTap, mouseEvent);
+ CFRelease(mouseEvent);
+ CFRelease(source);
+ }
+
+ return TRUE;
+}
+
+static BOOL mac_shadow_input_extended_mouse_event(rdpShadowSubsystem* subsystem,
+ rdpShadowClient* client, UINT16 flags, UINT16 x,
+ UINT16 y)
+{
+ if (!subsystem || !client)
+ return FALSE;
+
+ return TRUE;
+}
+
+static int mac_shadow_detect_monitors(macShadowSubsystem* subsystem)
+{
+ size_t wide, high;
+ MONITOR_DEF* monitor;
+ CGDirectDisplayID displayId;
+ displayId = CGMainDisplayID();
+ CGDisplayModeRef mode = CGDisplayCopyDisplayMode(displayId);
+ subsystem->pixelWidth = CGDisplayModeGetPixelWidth(mode);
+ subsystem->pixelHeight = CGDisplayModeGetPixelHeight(mode);
+ wide = CGDisplayPixelsWide(displayId);
+ high = CGDisplayPixelsHigh(displayId);
+ CGDisplayModeRelease(mode);
+ subsystem->retina = ((subsystem->pixelWidth / wide) == 2) ? TRUE : FALSE;
+
+ if (subsystem->retina)
+ {
+ subsystem->width = wide;
+ subsystem->height = high;
+ }
+ else
+ {
+ subsystem->width = subsystem->pixelWidth;
+ subsystem->height = subsystem->pixelHeight;
+ }
+
+ subsystem->common.numMonitors = 1;
+ monitor = &(subsystem->common.monitors[0]);
+ monitor->left = 0;
+ monitor->top = 0;
+ monitor->right = subsystem->width;
+ monitor->bottom = subsystem->height;
+ monitor->flags = 1;
+ return 1;
+}
+
+static int mac_shadow_capture_start(macShadowSubsystem* subsystem)
+{
+ CGError err;
+ err = CGDisplayStreamStart(subsystem->stream);
+
+ if (err != kCGErrorSuccess)
+ return -1;
+
+ return 1;
+}
+
+static int mac_shadow_capture_stop(macShadowSubsystem* subsystem)
+{
+ CGError err;
+ err = CGDisplayStreamStop(subsystem->stream);
+
+ if (err != kCGErrorSuccess)
+ return -1;
+
+ return 1;
+}
+
+static int mac_shadow_capture_get_dirty_region(macShadowSubsystem* subsystem)
+{
+ size_t numRects;
+ const CGRect* rects;
+ RECTANGLE_16 invalidRect;
+ rdpShadowSurface* surface = subsystem->common.server->surface;
+ rects = CGDisplayStreamUpdateGetRects(subsystem->lastUpdate, kCGDisplayStreamUpdateDirtyRects,
+ &numRects);
+
+ if (!numRects)
+ return -1;
+
+ for (size_t index = 0; index < numRects; index++)
+ {
+ invalidRect.left = (UINT16)rects[index].origin.x;
+ invalidRect.top = (UINT16)rects[index].origin.y;
+ invalidRect.right = invalidRect.left + (UINT16)rects[index].size.width;
+ invalidRect.bottom = invalidRect.top + (UINT16)rects[index].size.height;
+
+ if (subsystem->retina)
+ {
+ /* scale invalid rect */
+ invalidRect.left /= 2;
+ invalidRect.top /= 2;
+ invalidRect.right /= 2;
+ invalidRect.bottom /= 2;
+ }
+
+ region16_union_rect(&(surface->invalidRegion), &(surface->invalidRegion), &invalidRect);
+ }
+
+ return 0;
+}
+
+static int freerdp_image_copy_from_retina(BYTE* pDstData, DWORD DstFormat, int nDstStep, int nXDst,
+ int nYDst, int nWidth, int nHeight, BYTE* pSrcData,
+ int nSrcStep, int nXSrc, int nYSrc)
+{
+ BYTE* pSrcPixel;
+ BYTE* pDstPixel;
+ int nSrcPad;
+ int nDstPad;
+ int srcBitsPerPixel;
+ int srcBytesPerPixel;
+ int dstBitsPerPixel;
+ int dstBytesPerPixel;
+ srcBitsPerPixel = 24;
+ srcBytesPerPixel = 8;
+
+ if (nSrcStep < 0)
+ nSrcStep = srcBytesPerPixel * nWidth;
+
+ dstBitsPerPixel = FreeRDPGetBitsPerPixel(DstFormat);
+ dstBytesPerPixel = FreeRDPGetBytesPerPixel(DstFormat);
+
+ if (nDstStep < 0)
+ nDstStep = dstBytesPerPixel * nWidth;
+
+ nSrcPad = (nSrcStep - (nWidth * srcBytesPerPixel));
+ nDstPad = (nDstStep - (nWidth * dstBytesPerPixel));
+ pSrcPixel = &pSrcData[(nYSrc * nSrcStep) + (nXSrc * 4)];
+ pDstPixel = &pDstData[(nYDst * nDstStep) + (nXDst * 4)];
+
+ for (int y = 0; y < nHeight; y++)
+ {
+ for (int x = 0; x < nWidth; x++)
+ {
+ UINT32 R, G, B;
+ UINT32 color;
+ /* simple box filter scaling, could be improved with better algorithm */
+ B = pSrcPixel[0] + pSrcPixel[4] + pSrcPixel[nSrcStep + 0] + pSrcPixel[nSrcStep + 4];
+ G = pSrcPixel[1] + pSrcPixel[5] + pSrcPixel[nSrcStep + 1] + pSrcPixel[nSrcStep + 5];
+ R = pSrcPixel[2] + pSrcPixel[6] + pSrcPixel[nSrcStep + 2] + pSrcPixel[nSrcStep + 6];
+ pSrcPixel += 8;
+ color = FreeRDPGetColor(DstFormat, R >> 2, G >> 2, B >> 2, 0xFF);
+ FreeRDPWriteColor(pDstPixel, DstFormat, color);
+ pDstPixel += dstBytesPerPixel;
+ }
+
+ pSrcPixel = &pSrcPixel[nSrcPad + nSrcStep];
+ pDstPixel = &pDstPixel[nDstPad];
+ }
+
+ return 1;
+}
+
+static void (^mac_capture_stream_handler)(
+ CGDisplayStreamFrameStatus, uint64_t, IOSurfaceRef,
+ CGDisplayStreamUpdateRef) = ^(CGDisplayStreamFrameStatus status, uint64_t displayTime,
+ IOSurfaceRef frameSurface, CGDisplayStreamUpdateRef updateRef) {
+ int x, y;
+ int count;
+ int width;
+ int height;
+ int nSrcStep;
+ BOOL empty;
+ BYTE* pSrcData;
+ RECTANGLE_16 surfaceRect;
+ const RECTANGLE_16* extents;
+ macShadowSubsystem* subsystem = g_Subsystem;
+ rdpShadowServer* server = subsystem->common.server;
+ rdpShadowSurface* surface = server->surface;
+ count = ArrayList_Count(server->clients);
+
+ if (count < 1)
+ return;
+
+ EnterCriticalSection(&(surface->lock));
+ mac_shadow_capture_get_dirty_region(subsystem);
+ surfaceRect.left = 0;
+ surfaceRect.top = 0;
+ surfaceRect.right = surface->width;
+ surfaceRect.bottom = surface->height;
+ region16_intersect_rect(&(surface->invalidRegion), &(surface->invalidRegion), &surfaceRect);
+ empty = region16_is_empty(&(surface->invalidRegion));
+ LeaveCriticalSection(&(surface->lock));
+
+ if (!empty)
+ {
+ extents = region16_extents(&(surface->invalidRegion));
+ x = extents->left;
+ y = extents->top;
+ width = extents->right - extents->left;
+ height = extents->bottom - extents->top;
+ IOSurfaceLock(frameSurface, kIOSurfaceLockReadOnly, NULL);
+ pSrcData = (BYTE*)IOSurfaceGetBaseAddress(frameSurface);
+ nSrcStep = (int)IOSurfaceGetBytesPerRow(frameSurface);
+
+ if (subsystem->retina)
+ {
+ freerdp_image_copy_from_retina(surface->data, surface->format, surface->scanline, x, y,
+ width, height, pSrcData, nSrcStep, x, y);
+ }
+ else
+ {
+ freerdp_image_copy(surface->data, surface->format, surface->scanline, x, y, width, height,
+ pSrcData, PIXEL_FORMAT_BGRX32, nSrcStep, x, y, NULL,
+ FREERDP_FLIP_NONE);
+ }
+ LeaveCriticalSection(&(surface->lock));
+
+ IOSurfaceUnlock(frameSurface, kIOSurfaceLockReadOnly, NULL);
+ ArrayList_Lock(server->clients);
+ count = ArrayList_Count(server->clients);
+ shadow_subsystem_frame_update(&subsystem->common);
+
+ if (count == 1)
+ {
+ rdpShadowClient* client;
+ client = (rdpShadowClient*)ArrayList_GetItem(server->clients, 0);
+
+ if (client)
+ {
+ subsystem->common.captureFrameRate = shadow_encoder_preferred_fps(client->encoder);
+ }
+ }
+
+ ArrayList_Unlock(server->clients);
+ EnterCriticalSection(&(surface->lock));
+ region16_clear(&(surface->invalidRegion));
+ LeaveCriticalSection(&(surface->lock));
+ }
+
+ if (status != kCGDisplayStreamFrameStatusFrameComplete)
+ {
+ switch (status)
+ {
+ case kCGDisplayStreamFrameStatusFrameIdle:
+ break;
+
+ case kCGDisplayStreamFrameStatusStopped:
+ break;
+
+ case kCGDisplayStreamFrameStatusFrameBlank:
+ break;
+
+ default:
+ break;
+ }
+ }
+ else if (!subsystem->lastUpdate)
+ {
+ CFRetain(updateRef);
+ subsystem->lastUpdate = updateRef;
+ }
+ else
+ {
+ CGDisplayStreamUpdateRef tmpRef = subsystem->lastUpdate;
+ subsystem->lastUpdate = CGDisplayStreamUpdateCreateMergedUpdate(tmpRef, updateRef);
+ CFRelease(tmpRef);
+ }
+};
+
+static int mac_shadow_capture_init(macShadowSubsystem* subsystem)
+{
+ void* keys[2];
+ void* values[2];
+ CFDictionaryRef opts;
+ CGDirectDisplayID displayId;
+ displayId = CGMainDisplayID();
+ subsystem->captureQueue = dispatch_queue_create("mac.shadow.capture", NULL);
+ keys[0] = (void*)kCGDisplayStreamShowCursor;
+ values[0] = (void*)kCFBooleanFalse;
+ opts = CFDictionaryCreate(kCFAllocatorDefault, (const void**)keys, (const void**)values, 1,
+ NULL, NULL);
+ subsystem->stream = CGDisplayStreamCreateWithDispatchQueue(
+ displayId, subsystem->pixelWidth, subsystem->pixelHeight, 'BGRA', opts,
+ subsystem->captureQueue, mac_capture_stream_handler);
+ CFRelease(opts);
+ return 1;
+}
+
+static int mac_shadow_screen_grab(macShadowSubsystem* subsystem)
+{
+ return 1;
+}
+
+static int mac_shadow_subsystem_process_message(macShadowSubsystem* subsystem, wMessage* message)
+{
+ rdpShadowServer* server = subsystem->common.server;
+ rdpShadowSurface* surface = server->surface;
+
+ switch (message->id)
+ {
+ case SHADOW_MSG_IN_REFRESH_REQUEST_ID:
+ EnterCriticalSection(&(surface->lock));
+ shadow_subsystem_frame_update((rdpShadowSubsystem*)subsystem);
+ LeaveCriticalSection(&(surface->lock));
+ break;
+
+ default:
+ WLog_ERR(TAG, "Unknown message id: %" PRIu32 "", message->id);
+ break;
+ }
+
+ if (message->Free)
+ message->Free(message);
+
+ return 1;
+}
+
+static DWORD WINAPI mac_shadow_subsystem_thread(LPVOID arg)
+{
+ macShadowSubsystem* subsystem = (macShadowSubsystem*)arg;
+ DWORD status;
+ DWORD nCount;
+ UINT64 cTime;
+ DWORD dwTimeout;
+ DWORD dwInterval;
+ UINT64 frameTime;
+ HANDLE events[32];
+ wMessage message;
+ wMessagePipe* MsgPipe;
+ MsgPipe = subsystem->common.MsgPipe;
+ nCount = 0;
+ events[nCount++] = MessageQueue_Event(MsgPipe->In);
+ subsystem->common.captureFrameRate = 16;
+ dwInterval = 1000 / subsystem->common.captureFrameRate;
+ frameTime = GetTickCount64() + dwInterval;
+
+ while (1)
+ {
+ cTime = GetTickCount64();
+ dwTimeout = (cTime > frameTime) ? 0 : frameTime - cTime;
+ status = WaitForMultipleObjects(nCount, events, FALSE, dwTimeout);
+
+ if (WaitForSingleObject(MessageQueue_Event(MsgPipe->In), 0) == WAIT_OBJECT_0)
+ {
+ if (MessageQueue_Peek(MsgPipe->In, &message, TRUE))
+ {
+ if (message.id == WMQ_QUIT)
+ break;
+
+ mac_shadow_subsystem_process_message(subsystem, &message);
+ }
+ }
+
+ if ((status == WAIT_TIMEOUT) || (GetTickCount64() > frameTime))
+ {
+ mac_shadow_screen_grab(subsystem);
+ dwInterval = 1000 / subsystem->common.captureFrameRate;
+ frameTime += dwInterval;
+ }
+ }
+
+ ExitThread(0);
+ return 0;
+}
+
+static UINT32 mac_shadow_enum_monitors(MONITOR_DEF* monitors, UINT32 maxMonitors)
+{
+ int index;
+ size_t wide, high;
+ UINT32 numMonitors = 0;
+ MONITOR_DEF* monitor;
+ CGDirectDisplayID displayId;
+ displayId = CGMainDisplayID();
+ CGDisplayModeRef mode = CGDisplayCopyDisplayMode(displayId);
+ wide = CGDisplayPixelsWide(displayId);
+ high = CGDisplayPixelsHigh(displayId);
+ CGDisplayModeRelease(mode);
+ index = 0;
+ numMonitors = 1;
+ monitor = &monitors[index];
+ monitor->left = 0;
+ monitor->top = 0;
+ monitor->right = (int)wide;
+ monitor->bottom = (int)high;
+ monitor->flags = 1;
+ return numMonitors;
+}
+
+static int mac_shadow_subsystem_init(rdpShadowSubsystem* rdpsubsystem)
+{
+ macShadowSubsystem* subsystem = (macShadowSubsystem*)rdpsubsystem;
+ g_Subsystem = subsystem;
+
+ mac_shadow_detect_monitors(subsystem);
+ mac_shadow_capture_init(subsystem);
+ return 1;
+}
+
+static int mac_shadow_subsystem_uninit(rdpShadowSubsystem* rdpsubsystem)
+{
+ macShadowSubsystem* subsystem = (macShadowSubsystem*)rdpsubsystem;
+ if (!subsystem)
+ return -1;
+
+ if (subsystem->lastUpdate)
+ {
+ CFRelease(subsystem->lastUpdate);
+ subsystem->lastUpdate = NULL;
+ }
+
+ return 1;
+}
+
+static int mac_shadow_subsystem_start(rdpShadowSubsystem* rdpsubsystem)
+{
+ macShadowSubsystem* subsystem = (macShadowSubsystem*)rdpsubsystem;
+ HANDLE thread;
+
+ if (!subsystem)
+ return -1;
+
+ mac_shadow_capture_start(subsystem);
+
+ if (!(thread = CreateThread(NULL, 0, mac_shadow_subsystem_thread, (void*)subsystem, 0, NULL)))
+ {
+ WLog_ERR(TAG, "Failed to create thread");
+ return -1;
+ }
+
+ return 1;
+}
+
+static int mac_shadow_subsystem_stop(rdpShadowSubsystem* subsystem)
+{
+ if (!subsystem)
+ return -1;
+
+ return 1;
+}
+
+static void mac_shadow_subsystem_free(rdpShadowSubsystem* subsystem)
+{
+ if (!subsystem)
+ return;
+
+ mac_shadow_subsystem_uninit(subsystem);
+ free(subsystem);
+}
+
+static rdpShadowSubsystem* mac_shadow_subsystem_new(void)
+{
+ macShadowSubsystem* subsystem = calloc(1, sizeof(macShadowSubsystem));
+
+ if (!subsystem)
+ return NULL;
+
+ subsystem->common.SynchronizeEvent = mac_shadow_input_synchronize_event;
+ subsystem->common.KeyboardEvent = mac_shadow_input_keyboard_event;
+ subsystem->common.UnicodeKeyboardEvent = mac_shadow_input_unicode_keyboard_event;
+ subsystem->common.MouseEvent = mac_shadow_input_mouse_event;
+ subsystem->common.ExtendedMouseEvent = mac_shadow_input_extended_mouse_event;
+ return &subsystem->common;
+}
+
+FREERDP_API const char* ShadowSubsystemName(void)
+{
+ return "Mac";
+}
+
+FREERDP_API int ShadowSubsystemEntry(RDP_SHADOW_ENTRY_POINTS* pEntryPoints)
+{
+ char name[] = "mac shadow subsystem";
+ char* arg[] = { name };
+
+ freerdp_server_warn_unmaintained(ARRAYSIZE(arg), arg);
+ pEntryPoints->New = mac_shadow_subsystem_new;
+ pEntryPoints->Free = mac_shadow_subsystem_free;
+ pEntryPoints->Init = mac_shadow_subsystem_init;
+ pEntryPoints->Uninit = mac_shadow_subsystem_uninit;
+ pEntryPoints->Start = mac_shadow_subsystem_start;
+ pEntryPoints->Stop = mac_shadow_subsystem_stop;
+ pEntryPoints->EnumMonitors = mac_shadow_enum_monitors;
+ return 1;
+}
diff --git a/server/shadow/Mac/mac_shadow.h b/server/shadow/Mac/mac_shadow.h
new file mode 100644
index 0000000..2c31572
--- /dev/null
+++ b/server/shadow/Mac/mac_shadow.h
@@ -0,0 +1,64 @@
+/**
+ * FreeRDP: A Remote Desktop Protocol Implementation
+ *
+ * Copyright 2011-2014 Marc-Andre Moreau <marcandre.moreau@gmail.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FREERDP_SERVER_SHADOW_MAC_SHADOW_H
+#define FREERDP_SERVER_SHADOW_MAC_SHADOW_H
+
+#include <freerdp/server/shadow.h>
+
+typedef struct mac_shadow_subsystem macShadowSubsystem;
+
+#include <winpr/crt.h>
+#include <winpr/synch.h>
+#include <winpr/thread.h>
+#include <winpr/stream.h>
+#include <winpr/collections.h>
+
+#include <dispatch/dispatch.h>
+#include <IOKit/IOKitLib.h>
+#include <IOSurface/IOSurface.h>
+#include <CoreVideo/CoreVideo.h>
+#include <CoreGraphics/CoreGraphics.h>
+
+struct mac_shadow_subsystem
+{
+ rdpShadowSubsystem common;
+
+ int width;
+ int height;
+ BOOL retina;
+ int pixelWidth;
+ int pixelHeight;
+ BOOL mouseDownLeft;
+ BOOL mouseDownRight;
+ BOOL mouseDownOther;
+ CGDisplayStreamRef stream;
+ dispatch_queue_t captureQueue;
+ CGDisplayStreamUpdateRef lastUpdate;
+};
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* FREERDP_SERVER_SHADOW_MAC_SHADOW_H */