FreeRDP/server/shadow/Mac/mac_shadow.c
2014-09-19 19:58:49 -04:00

677 lines
16 KiB
C

/**
* 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/codec/color.h>
#include <freerdp/codec/region.h>
#include "../shadow_screen.h"
#include "../shadow_client.h"
#include "../shadow_surface.h"
#include "../shadow_capture.h"
#include "../shadow_encoder.h"
#include "../shadow_subsystem.h"
#include "mac_shadow.h"
static macShadowSubsystem* g_Subsystem = NULL;
void mac_shadow_input_synchronize_event(macShadowSubsystem* subsystem, UINT32 flags)
{
}
void mac_shadow_input_keyboard_event(macShadowSubsystem* subsystem, UINT16 flags, UINT16 code)
{
DWORD vkcode;
DWORD keycode;
BOOL extended;
CGEventRef kbdEvent;
CGEventSourceRef source;
extended = (flags & KBD_FLAGS_EXTENDED) ? TRUE : FALSE;
if (extended)
code |= KBDEXT;
vkcode = GetVirtualKeyCodeFromVirtualScanCode(code, 4);
if (extended)
vkcode |= KBDEXT;
keycode = GetKeycodeFromVirtualKeyCode(vkcode, KEYCODE_TYPE_APPLE);
if (keycode < 8)
return;
keycode -= 8;
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);
}
void mac_shadow_input_unicode_keyboard_event(macShadowSubsystem* subsystem, UINT16 flags, UINT16 code)
{
}
void mac_shadow_input_mouse_event(macShadowSubsystem* subsystem, UINT16 flags, UINT16 x, UINT16 y)
{
UINT32 scrollX = 0;
UINT32 scrollY = 0;
CGWheelCount wheelCount = 2;
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 (subsystem->mouseDownLeft)
mouseType = kCGEventLeftMouseDragged;
else if (subsystem->mouseDownRight)
mouseType = kCGEventRightMouseDragged;
else if (subsystem->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;
subsystem->mouseDownLeft = TRUE;
}
else
{
mouseType = kCGEventLeftMouseUp;
subsystem->mouseDownLeft = FALSE;
}
}
else if (flags & PTR_FLAGS_BUTTON2)
{
mouseButton = kCGMouseButtonRight;
if (flags & PTR_FLAGS_DOWN)
{
mouseType = kCGEventRightMouseDown;
subsystem->mouseDownRight = TRUE;
}
else
{
mouseType = kCGEventRightMouseUp;
subsystem->mouseDownRight = FALSE;
}
}
else if (flags & PTR_FLAGS_BUTTON3)
{
mouseButton = kCGMouseButtonCenter;
if (flags & PTR_FLAGS_DOWN)
{
mouseType = kCGEventOtherMouseDown;
subsystem->mouseDownOther = TRUE;
}
else
{
mouseType = kCGEventOtherMouseUp;
subsystem->mouseDownOther = FALSE;
}
}
CGEventRef mouseEvent = CGEventCreateMouseEvent(source, mouseType, CGPointMake(x, y), mouseButton);
CGEventPost(kCGHIDEventTap, mouseEvent);
CFRelease(mouseEvent);
CFRelease(source);
}
}
void mac_shadow_input_extended_mouse_event(macShadowSubsystem* subsystem, UINT16 flags, UINT16 x, UINT16 y)
{
}
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->numMonitors = 1;
monitor = &(subsystem->monitors[0]);
monitor->left = 0;
monitor->top = 0;
monitor->right = subsystem->width;
monitor->bottom = subsystem->height;
monitor->flags = 1;
return 1;
}
int mac_shadow_capture_start(macShadowSubsystem* subsystem)
{
CGError err;
err = CGDisplayStreamStart(subsystem->stream);
if (err != kCGErrorSuccess)
return -1;
return 1;
}
int mac_shadow_capture_stop(macShadowSubsystem* subsystem)
{
CGError err;
err = CGDisplayStreamStop(subsystem->stream);
if (err != kCGErrorSuccess)
return -1;
return 1;
}
int mac_shadow_capture_get_dirty_region(macShadowSubsystem* subsystem)
{
size_t index;
size_t numRects;
const CGRect* rects;
RECTANGLE_16 invalidRect;
rects = CGDisplayStreamUpdateGetRects(subsystem->lastUpdate, kCGDisplayStreamUpdateDirtyRects, &numRects);
if (!numRects)
return -1;
for (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(&(subsystem->invalidRegion), &(subsystem->invalidRegion), &invalidRect);
}
return 0;
}
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;
BYTE* pSrcData;
RECTANGLE_16 surfaceRect;
const RECTANGLE_16* extents;
macShadowSubsystem* subsystem = g_Subsystem;
rdpShadowServer* server = subsystem->server;
rdpShadowSurface* surface = server->surface;
count = ArrayList_Count(server->clients);
if (count < 1)
return;
if ((count == 1) && subsystem->suppressOutput)
return;
mac_shadow_capture_get_dirty_region(subsystem);
surfaceRect.left = 0;
surfaceRect.top = 0;
surfaceRect.right = surface->width;
surfaceRect.bottom = surface->height;
region16_intersect_rect(&(subsystem->invalidRegion), &(subsystem->invalidRegion), &surfaceRect);
if (!region16_is_empty(&(subsystem->invalidRegion)))
{
extents = region16_extents(&(subsystem->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, PIXEL_FORMAT_XRGB32, surface->scanline,
x, y, width, height, pSrcData, nSrcStep, x, y);
}
else
{
freerdp_image_copy(surface->data, PIXEL_FORMAT_XRGB32, surface->scanline,
x, y, width, height, pSrcData, PIXEL_FORMAT_XRGB32, nSrcStep, x, y, NULL);
}
IOSurfaceUnlock(frameSurface, kIOSurfaceLockReadOnly, NULL);
ArrayList_Lock(server->clients);
count = ArrayList_Count(server->clients);
InitializeSynchronizationBarrier(&(subsystem->barrier), count + 1, -1);
SetEvent(subsystem->updateEvent);
EnterSynchronizationBarrier(&(subsystem->barrier), 0);
DeleteSynchronizationBarrier(&(subsystem->barrier));
if (count == 1)
{
rdpShadowClient* client;
client = (rdpShadowClient*) ArrayList_GetItem(server->clients, 0);
if (client)
{
subsystem->captureFrameRate = client->encoder->fps;
}
}
ResetEvent(subsystem->updateEvent);
ArrayList_Unlock(server->clients);
region16_clear(&(subsystem->invalidRegion));
}
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);
}
};
int mac_shadow_capture_init(macShadowSubsystem* subsystem)
{
void* keys[2];
void* values[2];
CFDictionaryRef opts;
CGDirectDisplayID displayId;
displayId = CGMainDisplayID();
subsystem->updateBuffer = (BYTE*) malloc(subsystem->pixelWidth * subsystem->pixelHeight * 4);
if (!subsystem->updateBuffer)
return -1;
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;
}
int mac_shadow_screen_grab(macShadowSubsystem* subsystem)
{
return 1;
}
int mac_shadow_subsystem_process_message(macShadowSubsystem* subsystem, wMessage* message)
{
if (message->id == SHADOW_MSG_IN_REFRESH_OUTPUT_ID)
{
UINT32 index;
SHADOW_MSG_IN_REFRESH_OUTPUT* msg = (SHADOW_MSG_IN_REFRESH_OUTPUT*) message->wParam;
if (msg->numRects)
{
for (index = 0; index < msg->numRects; index++)
{
region16_union_rect(&(subsystem->invalidRegion),
&(subsystem->invalidRegion), &msg->rects[index]);
}
}
else
{
RECTANGLE_16 refreshRect;
refreshRect.left = 0;
refreshRect.top = 0;
refreshRect.right = subsystem->width;
refreshRect.bottom = subsystem->height;
region16_union_rect(&(subsystem->invalidRegion),
&(subsystem->invalidRegion), &refreshRect);
}
}
else if (message->id == SHADOW_MSG_IN_SUPPRESS_OUTPUT_ID)
{
SHADOW_MSG_IN_SUPPRESS_OUTPUT* msg = (SHADOW_MSG_IN_SUPPRESS_OUTPUT*) message->wParam;
subsystem->suppressOutput = (msg->allow) ? FALSE : TRUE;
if (msg->allow)
{
region16_union_rect(&(subsystem->invalidRegion),
&(subsystem->invalidRegion), &(msg->rect));
}
}
if (message->Free)
message->Free(message);
return 1;
}
void* mac_shadow_subsystem_thread(macShadowSubsystem* subsystem)
{
DWORD status;
DWORD nCount;
UINT64 cTime;
DWORD dwTimeout;
DWORD dwInterval;
UINT64 frameTime;
HANDLE events[32];
wMessage message;
wMessagePipe* MsgPipe;
MsgPipe = subsystem->MsgPipe;
nCount = 0;
events[nCount++] = MessageQueue_Event(MsgPipe->In);
subsystem->captureFrameRate = 16;
dwInterval = 1000 / subsystem->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->captureFrameRate;
frameTime += dwInterval;
}
}
ExitThread(0);
return NULL;
}
int mac_shadow_enum_monitors(MONITOR_DEF* monitors, int maxMonitors)
{
int index;
size_t wide, high;
int 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;
}
int mac_shadow_subsystem_init(macShadowSubsystem* subsystem)
{
g_Subsystem = subsystem;
mac_shadow_detect_monitors(subsystem);
mac_shadow_capture_init(subsystem);
return 1;
}
int mac_shadow_subsystem_uninit(macShadowSubsystem* subsystem)
{
if (!subsystem)
return -1;
if (subsystem->lastUpdate)
{
CFRelease(subsystem->lastUpdate);
subsystem->lastUpdate = NULL;
}
return 1;
}
int mac_shadow_subsystem_start(macShadowSubsystem* subsystem)
{
HANDLE thread;
if (!subsystem)
return -1;
mac_shadow_capture_start(subsystem);
thread = CreateThread(NULL, 0,
(LPTHREAD_START_ROUTINE) mac_shadow_subsystem_thread,
(void*) subsystem, 0, NULL);
return 1;
}
int mac_shadow_subsystem_stop(macShadowSubsystem* subsystem)
{
if (!subsystem)
return -1;
return 1;
}
void mac_shadow_subsystem_free(macShadowSubsystem* subsystem)
{
if (!subsystem)
return;
mac_shadow_subsystem_uninit(subsystem);
free(subsystem);
}
macShadowSubsystem* mac_shadow_subsystem_new()
{
macShadowSubsystem* subsystem;
subsystem = (macShadowSubsystem*) calloc(1, sizeof(macShadowSubsystem));
if (!subsystem)
return NULL;
subsystem->SynchronizeEvent = (pfnShadowSynchronizeEvent) mac_shadow_input_synchronize_event;
subsystem->KeyboardEvent = (pfnShadowKeyboardEvent) mac_shadow_input_keyboard_event;
subsystem->UnicodeKeyboardEvent = (pfnShadowUnicodeKeyboardEvent) mac_shadow_input_unicode_keyboard_event;
subsystem->MouseEvent = (pfnShadowMouseEvent) mac_shadow_input_mouse_event;
subsystem->ExtendedMouseEvent = (pfnShadowExtendedMouseEvent) mac_shadow_input_extended_mouse_event;
return subsystem;
}
int Mac_ShadowSubsystemEntry(RDP_SHADOW_ENTRY_POINTS* pEntryPoints)
{
pEntryPoints->New = (pfnShadowSubsystemNew) mac_shadow_subsystem_new;
pEntryPoints->Free = (pfnShadowSubsystemFree) mac_shadow_subsystem_free;
pEntryPoints->Init = (pfnShadowSubsystemInit) mac_shadow_subsystem_init;
pEntryPoints->Uninit = (pfnShadowSubsystemInit) mac_shadow_subsystem_uninit;
pEntryPoints->Start = (pfnShadowSubsystemStart) mac_shadow_subsystem_start;
pEntryPoints->Stop = (pfnShadowSubsystemStop) mac_shadow_subsystem_stop;
pEntryPoints->EnumMonitors = (pfnShadowEnumMonitors) mac_shadow_enum_monitors;
return 1;
}