FreeRDP
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 
33 static macShadowSubsystem* g_Subsystem = NULL;
34 
35 static 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 
44 static 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 
86 static 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 
96 static 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 
206 static 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 
216 static 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 
251 static 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 
262 static 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 
273 static 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 
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)
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 
359 static 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 
466 static 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 
485 static int mac_shadow_screen_grab(macShadowSubsystem* subsystem)
486 {
487  return 1;
488 }
489 
490 static 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 
514 static 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 
562 static 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 
585 static 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 
595 static 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 
610 static 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 
629 static int mac_shadow_subsystem_stop(rdpShadowSubsystem* subsystem)
630 {
631  if (!subsystem)
632  return -1;
633 
634  return 1;
635 }
636 
637 static 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 
646 static 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 
661 FREERDP_API const char* ShadowSubsystemName(void)
662 {
663  return "Mac";
664 }
665 
666 FREERDP_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 }