1/**
2 * FreeRDP: A Remote Desktop Protocol Implementation
3 * MacFreeRDP
4 *
5 * Copyright 2012 Thomas Goddard
6 *
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
10 *
11 *     http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 */
19
20#include <winpr/windows.h>
21
22#include "mf_client.h"
23#import "mfreerdp.h"
24#import "MRDPView.h"
25#import "MRDPCursor.h"
26#import "Clipboard.h"
27#import "PasswordDialog.h"
28#import "CertificateDialog.h"
29
30#include <winpr/crt.h>
31#include <winpr/input.h>
32#include <winpr/synch.h>
33#include <winpr/sysinfo.h>
34
35#include <freerdp/constants.h>
36
37#import "freerdp/freerdp.h"
38#import "freerdp/types.h"
39#import "freerdp/channels/channels.h"
40#import "freerdp/gdi/gdi.h"
41#import "freerdp/gdi/dc.h"
42#import "freerdp/gdi/region.h"
43#import "freerdp/graphics.h"
44#import "freerdp/client/file.h"
45#import "freerdp/client/cmdline.h"
46#import "freerdp/log.h"
47
48#import <CoreGraphics/CoreGraphics.h>
49
50#define TAG CLIENT_TAG("mac")
51
52static BOOL mf_Pointer_New(rdpContext *context, rdpPointer *pointer);
53static void mf_Pointer_Free(rdpContext *context, rdpPointer *pointer);
54static BOOL mf_Pointer_Set(rdpContext *context, const rdpPointer *pointer);
55static BOOL mf_Pointer_SetNull(rdpContext *context);
56static BOOL mf_Pointer_SetDefault(rdpContext *context);
57static BOOL mf_Pointer_SetPosition(rdpContext *context, UINT32 x, UINT32 y);
58
59static BOOL mac_begin_paint(rdpContext *context);
60static BOOL mac_end_paint(rdpContext *context);
61static BOOL mac_desktop_resize(rdpContext *context);
62
63static void input_activity_cb(freerdp *instance);
64
65static DWORD WINAPI mac_client_thread(void *param);
66
67@implementation MRDPView
68
69@synthesize is_connected;
70
71- (int)rdpStart:(rdpContext *)rdp_context
72{
73	rdpSettings *settings;
74	EmbedWindowEventArgs e;
75	[self initializeView];
76	context = rdp_context;
77	mfc = (mfContext *)rdp_context;
78	instance = context->instance;
79	settings = context->settings;
80	EventArgsInit(&e, "mfreerdp");
81	e.embed = TRUE;
82	e.handle = (void *)self;
83	PubSub_OnEmbedWindow(context->pubSub, context, &e);
84	NSScreen *screen = [[NSScreen screens] objectAtIndex:0];
85	NSRect screenFrame = [screen frame];
86
87	if (instance->settings->Fullscreen)
88	{
89		instance->settings->DesktopWidth = screenFrame.size.width;
90		instance->settings->DesktopHeight = screenFrame.size.height;
91		[self enterFullScreenMode:[NSScreen mainScreen] withOptions:nil];
92	}
93	else
94	{
95		[self exitFullScreenModeWithOptions:nil];
96	}
97
98	mfc->client_height = instance->settings->DesktopHeight;
99	mfc->client_width = instance->settings->DesktopWidth;
100
101	if (!(mfc->thread =
102	          CreateThread(NULL, 0, mac_client_thread, (void *)context, 0, &mfc->mainThreadId)))
103	{
104		WLog_ERR(TAG, "failed to create client thread");
105		return -1;
106	}
107
108	return 0;
109}
110
111static DWORD WINAPI mac_client_input_thread(LPVOID param)
112{
113	int status;
114	wMessage message;
115	wMessageQueue *queue;
116	rdpContext *context = (rdpContext *)param;
117	status = 1;
118	queue = freerdp_get_message_queue(context->instance, FREERDP_INPUT_MESSAGE_QUEUE);
119
120	while (MessageQueue_Wait(queue))
121	{
122		while (MessageQueue_Peek(queue, &message, TRUE))
123		{
124			status = freerdp_message_queue_process_message(context->instance,
125			                                               FREERDP_INPUT_MESSAGE_QUEUE, &message);
126
127			if (!status)
128				break;
129		}
130
131		if (!status)
132			break;
133	}
134
135	ExitThread(0);
136	return 0;
137}
138
139DWORD WINAPI mac_client_thread(void *param)
140{
141	@autoreleasepool
142	{
143		int status;
144		DWORD rc;
145		HANDLE events[16];
146		HANDLE inputEvent;
147		HANDLE inputThread = NULL;
148		DWORD nCount;
149		DWORD nCountTmp;
150		DWORD nCountBase;
151		rdpContext *context = (rdpContext *)param;
152		mfContext *mfc = (mfContext *)context;
153		freerdp *instance = context->instance;
154		MRDPView *view = mfc->view;
155		rdpSettings *settings = context->settings;
156		status = freerdp_connect(context->instance);
157
158		if (!status)
159		{
160			[view setIs_connected:0];
161			return 0;
162		}
163
164		[view setIs_connected:1];
165		nCount = 0;
166		events[nCount++] = mfc->stopEvent;
167
168		if (settings->AsyncInput)
169		{
170			if (!(inputThread = CreateThread(NULL, 0, mac_client_input_thread, context, 0, NULL)))
171			{
172				WLog_ERR(TAG, "failed to create async input thread");
173				goto disconnect;
174			}
175		}
176		else
177		{
178			if (!(inputEvent = freerdp_get_message_queue_event_handle(instance,
179			                                                          FREERDP_INPUT_MESSAGE_QUEUE)))
180			{
181				WLog_ERR(TAG, "failed to get input event handle");
182				goto disconnect;
183			}
184
185			events[nCount++] = inputEvent;
186		}
187
188		nCountBase = nCount;
189
190		while (!freerdp_shall_disconnect(instance))
191		{
192			nCount = nCountBase;
193			{
194				if (!(nCountTmp = freerdp_get_event_handles(context, &events[nCount], 16 - nCount)))
195				{
196					WLog_ERR(TAG, "freerdp_get_event_handles failed");
197					break;
198				}
199
200				nCount += nCountTmp;
201			}
202			rc = WaitForMultipleObjects(nCount, events, FALSE, INFINITE);
203
204			if (rc >= (WAIT_OBJECT_0 + nCount))
205			{
206				WLog_ERR(TAG, "WaitForMultipleObjects failed (0x%08X)", rc);
207				break;
208			}
209
210			if (rc == WAIT_OBJECT_0)
211			{
212				/* stop event triggered */
213				break;
214			}
215
216			if (!settings->AsyncInput)
217			{
218				if (WaitForSingleObject(inputEvent, 0) == WAIT_OBJECT_0)
219				{
220					input_activity_cb(instance);
221				}
222			}
223
224			{
225				if (!freerdp_check_event_handles(context))
226				{
227					WLog_ERR(TAG, "freerdp_check_event_handles failed");
228					break;
229				}
230			}
231		}
232
233	disconnect:
234		[view setIs_connected:0];
235		freerdp_disconnect(instance);
236
237		if (settings->AsyncInput && inputThread)
238		{
239			wMessageQueue *inputQueue =
240			    freerdp_get_message_queue(instance, FREERDP_INPUT_MESSAGE_QUEUE);
241
242			if (inputQueue)
243			{
244				MessageQueue_PostQuit(inputQueue, 0);
245				WaitForSingleObject(inputThread, INFINITE);
246			}
247
248			CloseHandle(inputThread);
249		}
250
251		ExitThread(0);
252		return 0;
253	}
254}
255
256- (id)initWithFrame:(NSRect)frame
257{
258	self = [super initWithFrame:frame];
259
260	if (self)
261	{
262		// Initialization code here.
263	}
264
265	return self;
266}
267
268- (void)viewDidLoad
269{
270	[self initializeView];
271}
272
273- (void)initializeView
274{
275	if (!initialized)
276	{
277		cursors = [[NSMutableArray alloc] initWithCapacity:10];
278		// setup a mouse tracking area
279		NSTrackingArea *trackingArea = [[NSTrackingArea alloc]
280		    initWithRect:[self visibleRect]
281		         options:NSTrackingMouseEnteredAndExited | NSTrackingMouseMoved |
282		                 NSTrackingCursorUpdate | NSTrackingEnabledDuringMouseDrag |
283		                 NSTrackingActiveWhenFirstResponder
284		           owner:self
285		        userInfo:nil];
286		[self addTrackingArea:trackingArea];
287		// Set the default cursor
288		currentCursor = [NSCursor arrowCursor];
289		initialized = YES;
290	}
291}
292
293- (void)setCursor:(NSCursor *)cursor
294{
295	self->currentCursor = cursor;
296	dispatch_async(dispatch_get_main_queue(), ^{
297		[[self window] invalidateCursorRectsForView:self];
298	});
299}
300
301- (void)resetCursorRects
302{
303	[self addCursorRect:[self visibleRect] cursor:currentCursor];
304}
305
306- (BOOL)acceptsFirstResponder
307{
308	return YES;
309}
310
311- (void)mouseMoved:(NSEvent *)event
312{
313	[super mouseMoved:event];
314
315	if (!self.is_connected)
316		return;
317
318	NSPoint loc = [event locationInWindow];
319	int x = (int)loc.x;
320	int y = (int)loc.y;
321	mf_scale_mouse_event(context, instance->input, PTR_FLAGS_MOVE, x, y);
322}
323
324- (void)mouseDown:(NSEvent *)event
325{
326	[super mouseDown:event];
327
328	if (!self.is_connected)
329		return;
330
331	NSPoint loc = [event locationInWindow];
332	int x = (int)loc.x;
333	int y = (int)loc.y;
334	mf_press_mouse_button(context, instance->input, 0, x, y, TRUE);
335}
336
337- (void)mouseUp:(NSEvent *)event
338{
339	[super mouseUp:event];
340
341	if (!self.is_connected)
342		return;
343
344	NSPoint loc = [event locationInWindow];
345	int x = (int)loc.x;
346	int y = (int)loc.y;
347	mf_press_mouse_button(context, instance->input, 0, x, y, FALSE);
348}
349
350- (void)rightMouseDown:(NSEvent *)event
351{
352	[super rightMouseDown:event];
353
354	if (!self.is_connected)
355		return;
356
357	NSPoint loc = [event locationInWindow];
358	int x = (int)loc.x;
359	int y = (int)loc.y;
360	mf_press_mouse_button(context, instance->input, 1, x, y, TRUE);
361}
362
363- (void)rightMouseUp:(NSEvent *)event
364{
365	[super rightMouseUp:event];
366
367	if (!self.is_connected)
368		return;
369
370	NSPoint loc = [event locationInWindow];
371	int x = (int)loc.x;
372	int y = (int)loc.y;
373	mf_press_mouse_button(context, instance->input, 1, x, y, FALSE);
374}
375
376- (void)otherMouseDown:(NSEvent *)event
377{
378	[super otherMouseDown:event];
379
380	if (!self.is_connected)
381		return;
382
383	NSPoint loc = [event locationInWindow];
384	int x = (int)loc.x;
385	int y = (int)loc.y;
386	int pressed = [event buttonNumber];
387	mf_press_mouse_button(context, instance->input, pressed, x, y, TRUE);
388}
389
390- (void)otherMouseUp:(NSEvent *)event
391{
392	[super otherMouseUp:event];
393
394	if (!self.is_connected)
395		return;
396
397	NSPoint loc = [event locationInWindow];
398	int x = (int)loc.x;
399	int y = (int)loc.y;
400	int pressed = [event buttonNumber];
401	mf_press_mouse_button(context, instance->input, pressed, x, y, FALSE);
402}
403
404- (void)scrollWheel:(NSEvent *)event
405{
406	UINT16 flags;
407	[super scrollWheel:event];
408
409	if (!self.is_connected)
410		return;
411
412	float dx = [event deltaX];
413	float dy = [event deltaY];
414	/* 1 event = 120 units */
415	UINT16 units = 0;
416
417	if (fabsf(dy) > FLT_EPSILON)
418	{
419		flags = PTR_FLAGS_WHEEL;
420		units = fabsf(dy) * 120;
421
422		if (dy < 0)
423			flags |= PTR_FLAGS_WHEEL_NEGATIVE;
424	}
425	else if (fabsf(dx) > FLT_EPSILON)
426	{
427		flags = PTR_FLAGS_HWHEEL;
428		units = fabsf(dx) * 120;
429
430		if (dx > 0)
431			flags |= PTR_FLAGS_WHEEL_NEGATIVE;
432	}
433	else
434		return;
435
436	/* Wheel rotation steps:
437	 *
438	 * positive: 0 ... 0xFF  -> slow ... fast
439	 * negative: 0 ... 0xFF  -> fast ... slow
440	 */
441	UINT16 step = units;
442	if (step > 0xFF)
443		step = 0xFF;
444
445	/* Negative rotation, so count down steps from top
446	 * 9bit twos complement */
447	if (flags & PTR_FLAGS_WHEEL_NEGATIVE)
448		step = 0x100 - step;
449
450	mf_scale_mouse_event(context, instance->input, flags | step, 0, 0);
451}
452
453- (void)mouseDragged:(NSEvent *)event
454{
455	[super mouseDragged:event];
456
457	if (!self.is_connected)
458		return;
459
460	NSPoint loc = [event locationInWindow];
461	int x = (int)loc.x;
462	int y = (int)loc.y;
463	// send mouse motion event to RDP server
464	mf_scale_mouse_event(context, instance->input, PTR_FLAGS_MOVE, x, y);
465}
466
467DWORD fixKeyCode(DWORD keyCode, unichar keyChar, enum APPLE_KEYBOARD_TYPE type)
468{
469	/**
470	 * In 99% of cases, the given key code is truly keyboard independent.
471	 * This function handles the remaining 1% of edge cases.
472	 *
473	 * Hungarian Keyboard: This is 'QWERTZ' and not 'QWERTY'.
474	 * The '0' key is on the left of the '1' key, where '~' is on a US keyboard.
475	 * A special 'i' letter key with acute is found on the right of the left shift key.
476	 * On the hungarian keyboard, the 'i' key is at the left of the 'Y' key
477	 * Some international keyboards have a corresponding key which would be at
478	 * the left of the 'Z' key when using a QWERTY layout.
479	 *
480	 * The Apple Hungarian keyboard sends inverted key codes for the '0' and 'i' keys.
481	 * When using the US keyboard layout, key codes are left as-is (inverted).
482	 * When using the Hungarian keyboard layout, key codes are swapped (non-inverted).
483	 * This means that when using the Hungarian keyboard layout with a US keyboard,
484	 * the keys corresponding to '0' and 'i' will effectively be inverted.
485	 *
486	 * To fix the '0' and 'i' key inversion, we use the corresponding output character
487	 * provided by OS X and check for a character to key code mismatch: for instance,
488	 * when the output character is '0' for the key code corresponding to the 'i' key.
489	 */
490#if 0
491	switch (keyChar)
492	{
493		case '0':
494		case 0x00A7: /* section sign */
495			if (keyCode == APPLE_VK_ISO_Section)
496				keyCode = APPLE_VK_ANSI_Grave;
497
498			break;
499
500		case 0x00ED: /* latin small letter i with acute */
501		case 0x00CD: /* latin capital letter i with acute */
502			if (keyCode == APPLE_VK_ANSI_Grave)
503				keyCode = APPLE_VK_ISO_Section;
504
505			break;
506	}
507
508#endif
509
510	/* Perform keycode correction for all ISO keyboards */
511
512	if (type == APPLE_KEYBOARD_TYPE_ISO)
513	{
514		if (keyCode == APPLE_VK_ANSI_Grave)
515			keyCode = APPLE_VK_ISO_Section;
516		else if (keyCode == APPLE_VK_ISO_Section)
517			keyCode = APPLE_VK_ANSI_Grave;
518	}
519
520	return keyCode;
521}
522
523- (void)keyDown:(NSEvent *)event
524{
525	DWORD keyCode;
526	DWORD keyFlags;
527	DWORD vkcode;
528	DWORD scancode;
529	unichar keyChar;
530	NSString *characters;
531
532	if (!is_connected)
533		return;
534
535	keyFlags = KBD_FLAGS_DOWN;
536	keyCode = [event keyCode];
537	characters = [event charactersIgnoringModifiers];
538
539	if ([characters length] > 0)
540	{
541		keyChar = [characters characterAtIndex:0];
542		keyCode = fixKeyCode(keyCode, keyChar, mfc->appleKeyboardType);
543	}
544
545	vkcode = GetVirtualKeyCodeFromKeycode(keyCode + 8, KEYCODE_TYPE_APPLE);
546	scancode = GetVirtualScanCodeFromVirtualKeyCode(vkcode, 4);
547	keyFlags |= (scancode & KBDEXT) ? KBDEXT : 0;
548	scancode &= 0xFF;
549	vkcode &= 0xFF;
550#if 0
551	WLog_ERR(TAG,
552	         "keyDown: keyCode: 0x%04X scancode: 0x%04X vkcode: 0x%04X keyFlags: %d name: %s",
553	         keyCode, scancode, vkcode, keyFlags, GetVirtualKeyName(vkcode));
554#endif
555	sync_keyboard_state(instance);
556	freerdp_input_send_keyboard_event(instance->input, keyFlags, scancode);
557}
558
559- (void)keyUp:(NSEvent *)event
560{
561	DWORD keyCode;
562	DWORD keyFlags;
563	DWORD vkcode;
564	DWORD scancode;
565	unichar keyChar;
566	NSString *characters;
567
568	if (!is_connected)
569		return;
570
571	keyFlags = KBD_FLAGS_RELEASE;
572	keyCode = [event keyCode];
573	characters = [event charactersIgnoringModifiers];
574
575	if ([characters length] > 0)
576	{
577		keyChar = [characters characterAtIndex:0];
578		keyCode = fixKeyCode(keyCode, keyChar, mfc->appleKeyboardType);
579	}
580
581	vkcode = GetVirtualKeyCodeFromKeycode(keyCode + 8, KEYCODE_TYPE_APPLE);
582	scancode = GetVirtualScanCodeFromVirtualKeyCode(vkcode, 4);
583	keyFlags |= (scancode & KBDEXT) ? KBDEXT : 0;
584	scancode &= 0xFF;
585	vkcode &= 0xFF;
586#if 0
587	WLog_DBG(TAG,
588	         "keyUp: key: 0x%04X scancode: 0x%04X vkcode: 0x%04X keyFlags: %d name: %s",
589	         keyCode, scancode, vkcode, keyFlags, GetVirtualKeyName(vkcode));
590#endif
591	freerdp_input_send_keyboard_event(instance->input, keyFlags, scancode);
592}
593
594- (void)flagsChanged:(NSEvent *)event
595{
596	int key;
597	DWORD keyFlags;
598	DWORD vkcode;
599	DWORD scancode;
600	DWORD modFlags;
601
602	if (!is_connected)
603		return;
604
605	keyFlags = 0;
606	key = [event keyCode] + 8;
607	modFlags = [event modifierFlags] & NSDeviceIndependentModifierFlagsMask;
608	vkcode = GetVirtualKeyCodeFromKeycode(key, KEYCODE_TYPE_APPLE);
609	scancode = GetVirtualScanCodeFromVirtualKeyCode(vkcode, 4);
610	keyFlags |= (scancode & KBDEXT) ? KBDEXT : 0;
611	scancode &= 0xFF;
612	vkcode &= 0xFF;
613#if 0
614	WLog_DBG(TAG,
615	         "flagsChanged: key: 0x%04X scancode: 0x%04X vkcode: 0x%04X extended: %d name: %s modFlags: 0x%04X",
616	         key - 8, scancode, vkcode, keyFlags, GetVirtualKeyName(vkcode), modFlags);
617
618	if (modFlags & NSAlphaShiftKeyMask)
619		WLog_DBG(TAG,  "NSAlphaShiftKeyMask");
620
621	if (modFlags & NSShiftKeyMask)
622		WLog_DBG(TAG,  "NSShiftKeyMask");
623
624	if (modFlags & NSControlKeyMask)
625		WLog_DBG(TAG,  "NSControlKeyMask");
626
627	if (modFlags & NSAlternateKeyMask)
628		WLog_DBG(TAG,  "NSAlternateKeyMask");
629
630	if (modFlags & NSCommandKeyMask)
631		WLog_DBG(TAG,  "NSCommandKeyMask");
632
633	if (modFlags & NSNumericPadKeyMask)
634		WLog_DBG(TAG,  "NSNumericPadKeyMask");
635
636	if (modFlags & NSHelpKeyMask)
637		WLog_DBG(TAG,  "NSHelpKeyMask");
638
639#endif
640
641	if ((modFlags & NSAlphaShiftKeyMask) && !(kbdModFlags & NSAlphaShiftKeyMask))
642		freerdp_input_send_keyboard_event(instance->input, keyFlags | KBD_FLAGS_DOWN, scancode);
643	else if (!(modFlags & NSAlphaShiftKeyMask) && (kbdModFlags & NSAlphaShiftKeyMask))
644		freerdp_input_send_keyboard_event(instance->input, keyFlags | KBD_FLAGS_RELEASE, scancode);
645
646	if ((modFlags & NSShiftKeyMask) && !(kbdModFlags & NSShiftKeyMask))
647		freerdp_input_send_keyboard_event(instance->input, keyFlags | KBD_FLAGS_DOWN, scancode);
648	else if (!(modFlags & NSShiftKeyMask) && (kbdModFlags & NSShiftKeyMask))
649		freerdp_input_send_keyboard_event(instance->input, keyFlags | KBD_FLAGS_RELEASE, scancode);
650
651	if ((modFlags & NSControlKeyMask) && !(kbdModFlags & NSControlKeyMask))
652		freerdp_input_send_keyboard_event(instance->input, keyFlags | KBD_FLAGS_DOWN, scancode);
653	else if (!(modFlags & NSControlKeyMask) && (kbdModFlags & NSControlKeyMask))
654		freerdp_input_send_keyboard_event(instance->input, keyFlags | KBD_FLAGS_RELEASE, scancode);
655
656	if ((modFlags & NSAlternateKeyMask) && !(kbdModFlags & NSAlternateKeyMask))
657		freerdp_input_send_keyboard_event(instance->input, keyFlags | KBD_FLAGS_DOWN, scancode);
658	else if (!(modFlags & NSAlternateKeyMask) && (kbdModFlags & NSAlternateKeyMask))
659		freerdp_input_send_keyboard_event(instance->input, keyFlags | KBD_FLAGS_RELEASE, scancode);
660
661	if ((modFlags & NSCommandKeyMask) && !(kbdModFlags & NSCommandKeyMask))
662		freerdp_input_send_keyboard_event(instance->input, keyFlags | KBD_FLAGS_DOWN, scancode);
663	else if (!(modFlags & NSCommandKeyMask) && (kbdModFlags & NSCommandKeyMask))
664		freerdp_input_send_keyboard_event(instance->input, keyFlags | KBD_FLAGS_RELEASE, scancode);
665
666	if ((modFlags & NSNumericPadKeyMask) && !(kbdModFlags & NSNumericPadKeyMask))
667		freerdp_input_send_keyboard_event(instance->input, keyFlags | KBD_FLAGS_DOWN, scancode);
668	else if (!(modFlags & NSNumericPadKeyMask) && (kbdModFlags & NSNumericPadKeyMask))
669		freerdp_input_send_keyboard_event(instance->input, keyFlags | KBD_FLAGS_RELEASE, scancode);
670
671	if ((modFlags & NSHelpKeyMask) && !(kbdModFlags & NSHelpKeyMask))
672		freerdp_input_send_keyboard_event(instance->input, keyFlags | KBD_FLAGS_DOWN, scancode);
673	else if (!(modFlags & NSHelpKeyMask) && (kbdModFlags & NSHelpKeyMask))
674		freerdp_input_send_keyboard_event(instance->input, keyFlags | KBD_FLAGS_RELEASE, scancode);
675
676	kbdModFlags = modFlags;
677}
678
679- (void)releaseResources
680{
681	int i;
682
683	for (i = 0; i < argc; i++)
684		free(argv[i]);
685
686	if (!is_connected)
687		return;
688
689	free(pixel_data);
690}
691
692- (void)drawRect:(NSRect)rect
693{
694	if (!context)
695		return;
696
697	if (self->bitmap_context)
698	{
699		CGContextRef cgContext = [[NSGraphicsContext currentContext] graphicsPort];
700		CGImageRef cgImage = CGBitmapContextCreateImage(self->bitmap_context);
701		CGContextSaveGState(cgContext);
702		CGContextClipToRect(
703		    cgContext, CGRectMake(rect.origin.x, rect.origin.y, rect.size.width, rect.size.height));
704		CGContextDrawImage(cgContext,
705		                   CGRectMake(0, 0, [self bounds].size.width, [self bounds].size.height),
706		                   cgImage);
707		CGContextRestoreGState(cgContext);
708		CGImageRelease(cgImage);
709	}
710	else
711	{
712		/* Fill the screen with black */
713		[[NSColor blackColor] set];
714		NSRectFill([self bounds]);
715	}
716}
717
718- (void)onPasteboardTimerFired:(NSTimer *)timer
719{
720	const BYTE *data;
721	UINT32 size;
722	UINT32 formatId;
723	BOOL formatMatch;
724	int changeCount;
725	NSData *formatData;
726	const char *formatType;
727	NSPasteboardItem *item;
728	changeCount = (int)[pasteboard_rd changeCount];
729
730	if (changeCount == pasteboard_changecount)
731		return;
732
733	pasteboard_changecount = changeCount;
734	NSArray *items = [pasteboard_rd pasteboardItems];
735
736	if ([items count] < 1)
737		return;
738
739	item = [items objectAtIndex:0];
740	/**
741	 * System-Declared Uniform Type Identifiers:
742	 * https://developer.apple.com/library/ios/documentation/Miscellaneous/Reference/UTIRef/Articles/System-DeclaredUniformTypeIdentifiers.html
743	 */
744	formatMatch = FALSE;
745
746	for (NSString *type in [item types])
747	{
748		formatType = [type UTF8String];
749
750		if (strcmp(formatType, "public.utf8-plain-text") == 0)
751		{
752			formatData = [item dataForType:type];
753			formatId = ClipboardRegisterFormat(mfc->clipboard, "UTF8_STRING");
754			size = (UINT32)[formatData length];
755			data = [formatData bytes];
756			/* size is the string length without the terminating NULL terminator */
757			ClipboardSetData(mfc->clipboard, formatId, data, size + 1);
758			formatMatch = TRUE;
759			break;
760		}
761	}
762
763	if (!formatMatch)
764		ClipboardEmpty(mfc->clipboard);
765
766	if (mfc->clipboardSync)
767		mac_cliprdr_send_client_format_list(mfc->cliprdr);
768}
769
770- (void)pause
771{
772	dispatch_async(dispatch_get_main_queue(), ^{
773		[self->pasteboard_timer invalidate];
774	});
775	NSArray *trackingAreas = self.trackingAreas;
776
777	for (NSTrackingArea *ta in trackingAreas)
778	{
779		[self removeTrackingArea:ta];
780	}
781}
782
783- (void)resume
784{
785	if (!self.is_connected)
786		return;
787
788	dispatch_async(dispatch_get_main_queue(), ^{
789		self->pasteboard_timer =
790		    [NSTimer scheduledTimerWithTimeInterval:0.5
791		                                     target:self
792		                                   selector:@selector(onPasteboardTimerFired:)
793		                                   userInfo:nil
794		                                    repeats:YES];
795
796		NSTrackingArea *trackingArea = [[NSTrackingArea alloc]
797		    initWithRect:[self visibleRect]
798		         options:NSTrackingMouseEnteredAndExited | NSTrackingMouseMoved |
799		                 NSTrackingCursorUpdate | NSTrackingEnabledDuringMouseDrag |
800		                 NSTrackingActiveWhenFirstResponder
801		           owner:self
802		        userInfo:nil];
803		[self addTrackingArea:trackingArea];
804		[trackingArea release];
805	});
806}
807
808- (void)setScrollOffset:(int)xOffset y:(int)yOffset w:(int)width h:(int)height
809{
810	mfc->yCurrentScroll = yOffset;
811	mfc->xCurrentScroll = xOffset;
812	mfc->client_height = height;
813	mfc->client_width = width;
814}
815
816void mac_OnChannelConnectedEventHandler(void *context, ChannelConnectedEventArgs *e)
817{
818	mfContext *mfc = (mfContext *)context;
819	rdpSettings *settings = mfc->context.settings;
820
821	if (strcmp(e->name, RDPEI_DVC_CHANNEL_NAME) == 0)
822	{
823	}
824	else if (strcmp(e->name, RDPGFX_DVC_CHANNEL_NAME) == 0)
825	{
826		if (settings->SoftwareGdi)
827			gdi_graphics_pipeline_init(mfc->context.gdi, (RdpgfxClientContext *)e->pInterface);
828	}
829	else if (strcmp(e->name, CLIPRDR_SVC_CHANNEL_NAME) == 0)
830	{
831		mac_cliprdr_init(mfc, (CliprdrClientContext *)e->pInterface);
832	}
833	else if (strcmp(e->name, ENCOMSP_SVC_CHANNEL_NAME) == 0)
834	{
835	}
836}
837
838void mac_OnChannelDisconnectedEventHandler(void *context, ChannelDisconnectedEventArgs *e)
839{
840	mfContext *mfc = (mfContext *)context;
841	rdpSettings *settings = mfc->context.settings;
842
843	if (strcmp(e->name, RDPEI_DVC_CHANNEL_NAME) == 0)
844	{
845	}
846	else if (strcmp(e->name, RDPGFX_DVC_CHANNEL_NAME) == 0)
847	{
848		if (settings->SoftwareGdi)
849			gdi_graphics_pipeline_uninit(mfc->context.gdi, (RdpgfxClientContext *)e->pInterface);
850	}
851	else if (strcmp(e->name, CLIPRDR_SVC_CHANNEL_NAME) == 0)
852	{
853		mac_cliprdr_uninit(mfc, (CliprdrClientContext *)e->pInterface);
854	}
855	else if (strcmp(e->name, ENCOMSP_SVC_CHANNEL_NAME) == 0)
856	{
857	}
858}
859
860BOOL mac_pre_connect(freerdp *instance)
861{
862	rdpSettings *settings;
863	instance->update->BeginPaint = mac_begin_paint;
864	instance->update->EndPaint = mac_end_paint;
865	instance->update->DesktopResize = mac_desktop_resize;
866	settings = instance->settings;
867
868	if (!settings->ServerHostname)
869	{
870		WLog_ERR(TAG, "error: server hostname was not specified with /v:<server>[:port]");
871		return FALSE;
872	}
873
874	settings->OsMajorType = OSMAJORTYPE_MACINTOSH;
875	settings->OsMinorType = OSMINORTYPE_MACINTOSH;
876	PubSub_SubscribeChannelConnected(instance->context->pubSub, mac_OnChannelConnectedEventHandler);
877	PubSub_SubscribeChannelDisconnected(instance->context->pubSub,
878	                                    mac_OnChannelDisconnectedEventHandler);
879
880	if (!freerdp_client_load_addins(instance->context->channels, instance->settings))
881		return FALSE;
882
883	return TRUE;
884}
885
886BOOL mac_post_connect(freerdp *instance)
887{
888	rdpGdi *gdi;
889	rdpSettings *settings;
890	rdpPointer rdp_pointer;
891	mfContext *mfc = (mfContext *)instance->context;
892	MRDPView *view = (MRDPView *)mfc->view;
893	ZeroMemory(&rdp_pointer, sizeof(rdpPointer));
894	rdp_pointer.size = sizeof(rdpPointer);
895	rdp_pointer.New = mf_Pointer_New;
896	rdp_pointer.Free = mf_Pointer_Free;
897	rdp_pointer.Set = mf_Pointer_Set;
898	rdp_pointer.SetNull = mf_Pointer_SetNull;
899	rdp_pointer.SetDefault = mf_Pointer_SetDefault;
900	rdp_pointer.SetPosition = mf_Pointer_SetPosition;
901	settings = instance->settings;
902
903	if (!gdi_init(instance, PIXEL_FORMAT_BGRX32))
904		return FALSE;
905
906	gdi = instance->context->gdi;
907	view->bitmap_context = mac_create_bitmap_context(instance->context);
908	graphics_register_pointer(instance->context->graphics, &rdp_pointer);
909	/* setup pasteboard (aka clipboard) for copy operations (write only) */
910	view->pasteboard_wr = [NSPasteboard generalPasteboard];
911	/* setup pasteboard for read operations */
912	dispatch_async(dispatch_get_main_queue(), ^{
913		view->pasteboard_rd = [NSPasteboard generalPasteboard];
914		view->pasteboard_changecount = -1;
915	});
916	[view resume];
917	mfc->appleKeyboardType = mac_detect_keyboard_type();
918	return TRUE;
919}
920
921void mac_post_disconnect(freerdp *instance)
922{
923	mfContext *mfc;
924	MRDPView *view;
925	if (!instance || !instance->context)
926		return;
927
928	mfc = (mfContext *)instance->context;
929	view = (MRDPView *)mfc->view;
930
931	[view pause];
932
933	PubSub_UnsubscribeChannelConnected(instance->context->pubSub,
934	                                   mac_OnChannelConnectedEventHandler);
935	PubSub_UnsubscribeChannelDisconnected(instance->context->pubSub,
936	                                      mac_OnChannelDisconnectedEventHandler);
937	gdi_free(instance);
938}
939
940static BOOL mac_authenticate_int(NSString *title, freerdp *instance, char **username,
941                                 char **password, char **domain)
942{
943	mfContext *mfc = (mfContext *)instance->context;
944	MRDPView *view = (MRDPView *)mfc->view;
945	PasswordDialog *dialog = [PasswordDialog new];
946	dialog.serverHostname = title;
947
948	if (*username)
949		dialog.username = [NSString stringWithCString:*username encoding:NSUTF8StringEncoding];
950
951	if (*password)
952		dialog.password = [NSString stringWithCString:*password encoding:NSUTF8StringEncoding];
953
954	if (*domain)
955		dialog.domain = [NSString stringWithCString:*domain encoding:NSUTF8StringEncoding];
956
957	dispatch_sync(dispatch_get_main_queue(), ^{
958		[dialog performSelectorOnMainThread:@selector(runModal:)
959		                         withObject:[view window]
960		                      waitUntilDone:TRUE];
961	});
962	BOOL ok = dialog.modalCode;
963
964	if (ok)
965	{
966		size_t ulen, plen, dlen;
967		const char *submittedUsername = [dialog.username cStringUsingEncoding:NSUTF8StringEncoding];
968		ulen = (strlen(submittedUsername) + 1) * sizeof(char);
969		*username = malloc(ulen);
970
971		if (!(*username))
972			return FALSE;
973
974		sprintf_s(*username, ulen, "%s", submittedUsername);
975		const char *submittedPassword = [dialog.password cStringUsingEncoding:NSUTF8StringEncoding];
976		plen = (strlen(submittedPassword) + 1) * sizeof(char);
977		*password = malloc(plen);
978
979		if (!(*password))
980			return FALSE;
981
982		sprintf_s(*password, plen, "%s", submittedPassword);
983		const char *submittedDomain = [dialog.domain cStringUsingEncoding:NSUTF8StringEncoding];
984		dlen = (strlen(submittedDomain) + 1) * sizeof(char);
985		*domain = malloc(dlen);
986
987		if (!(*domain))
988			return FALSE;
989
990		sprintf_s(*domain, dlen, "%s", submittedDomain);
991	}
992
993	return ok;
994}
995
996BOOL mac_authenticate(freerdp *instance, char **username, char **password, char **domain)
997{
998	NSString *title =
999	    [NSString stringWithFormat:@"%@:%u",
1000	                               [NSString stringWithCString:instance->settings->ServerHostname
1001	                                                  encoding:NSUTF8StringEncoding],
1002	                               instance -> settings -> ServerPort];
1003	return mac_authenticate_int(title, instance, username, password, domain);
1004}
1005
1006BOOL mac_gw_authenticate(freerdp *instance, char **username, char **password, char **domain)
1007{
1008	NSString *title =
1009	    [NSString stringWithFormat:@"%@:%u",
1010	                               [NSString stringWithCString:instance->settings->GatewayHostname
1011	                                                  encoding:NSUTF8StringEncoding],
1012	                               instance -> settings -> GatewayPort];
1013	return mac_authenticate_int(title, instance, username, password, domain);
1014}
1015
1016DWORD mac_verify_certificate_ex(freerdp *instance, const char *host, UINT16 port,
1017                                const char *common_name, const char *subject, const char *issuer,
1018                                const char *fingerprint, DWORD flags)
1019{
1020	mfContext *mfc = (mfContext *)instance->context;
1021	MRDPView *view = (MRDPView *)mfc->view;
1022	CertificateDialog *dialog = [CertificateDialog new];
1023	const char *type = "RDP-Server";
1024	char hostname[8192];
1025
1026	if (flags & VERIFY_CERT_FLAG_GATEWAY)
1027		type = "RDP-Gateway";
1028
1029	if (flags & VERIFY_CERT_FLAG_REDIRECT)
1030		type = "RDP-Redirect";
1031
1032	sprintf_s(hostname, sizeof(hostname), "%s %s:%" PRIu16, type, host, port);
1033	dialog.serverHostname = [NSString stringWithCString:hostname];
1034	dialog.commonName = [NSString stringWithCString:common_name encoding:NSUTF8StringEncoding];
1035	dialog.subject = [NSString stringWithCString:subject encoding:NSUTF8StringEncoding];
1036	dialog.issuer = [NSString stringWithCString:issuer encoding:NSUTF8StringEncoding];
1037	dialog.fingerprint = [NSString stringWithCString:fingerprint encoding:NSUTF8StringEncoding];
1038
1039	if (flags & VERIFY_CERT_FLAG_MISMATCH)
1040		dialog.hostMismatch = TRUE;
1041
1042	if (flags & VERIFY_CERT_FLAG_CHANGED)
1043		dialog.changed = TRUE;
1044
1045	[dialog performSelectorOnMainThread:@selector(runModal:)
1046	                         withObject:[view window]
1047	                      waitUntilDone:TRUE];
1048	return dialog.result;
1049}
1050
1051DWORD mac_verify_changed_certificate_ex(freerdp *instance, const char *host, UINT16 port,
1052                                        const char *common_name, const char *subject,
1053                                        const char *issuer, const char *fingerprint,
1054                                        const char *old_subject, const char *old_issuer,
1055                                        const char *old_fingerprint, DWORD flags)
1056{
1057	mfContext *mfc = (mfContext *)instance->context;
1058	MRDPView *view = (MRDPView *)mfc->view;
1059	CertificateDialog *dialog = [CertificateDialog new];
1060	const char *type = "RDP-Server";
1061	char hostname[8192];
1062
1063	if (flags & VERIFY_CERT_FLAG_GATEWAY)
1064		type = "RDP-Gateway";
1065
1066	if (flags & VERIFY_CERT_FLAG_REDIRECT)
1067		type = "RDP-Redirect";
1068
1069	sprintf_s(hostname, sizeof(hostname), "%s %s:%" PRIu16, type, host, port);
1070	dialog.serverHostname = [NSString stringWithCString:hostname];
1071	dialog.commonName = [NSString stringWithCString:common_name encoding:NSUTF8StringEncoding];
1072	dialog.subject = [NSString stringWithCString:subject encoding:NSUTF8StringEncoding];
1073	dialog.issuer = [NSString stringWithCString:issuer encoding:NSUTF8StringEncoding];
1074	dialog.fingerprint = [NSString stringWithCString:fingerprint encoding:NSUTF8StringEncoding];
1075
1076	if (flags & VERIFY_CERT_FLAG_MISMATCH)
1077		dialog.hostMismatch = TRUE;
1078
1079	if (flags & VERIFY_CERT_FLAG_CHANGED)
1080		dialog.changed = TRUE;
1081
1082	[dialog performSelectorOnMainThread:@selector(runModal:)
1083	                         withObject:[view window]
1084	                      waitUntilDone:TRUE];
1085	return dialog.result;
1086}
1087
1088int mac_logon_error_info(freerdp *instance, UINT32 data, UINT32 type)
1089{
1090	const char *str_data = freerdp_get_logon_error_info_data(data);
1091	const char *str_type = freerdp_get_logon_error_info_type(type);
1092	// TODO: Error message dialog
1093	WLog_INFO(TAG, "Logon Error Info %s [%s]", str_data, str_type);
1094	return 1;
1095}
1096
1097BOOL mf_Pointer_New(rdpContext *context, rdpPointer *pointer)
1098{
1099	rdpGdi *gdi;
1100	NSRect rect;
1101	NSImage *image;
1102	NSPoint hotSpot;
1103	NSCursor *cursor;
1104	BYTE *cursor_data;
1105	NSMutableArray *ma;
1106	NSBitmapImageRep *bmiRep;
1107	MRDPCursor *mrdpCursor = [[MRDPCursor alloc] init];
1108	mfContext *mfc = (mfContext *)context;
1109	MRDPView *view;
1110	UINT32 format;
1111
1112	if (!mfc || !context || !pointer)
1113		return FALSE;
1114
1115	view = (MRDPView *)mfc->view;
1116	gdi = context->gdi;
1117
1118	if (!gdi || !view)
1119		return FALSE;
1120
1121	rect.size.width = pointer->width;
1122	rect.size.height = pointer->height;
1123	rect.origin.x = pointer->xPos;
1124	rect.origin.y = pointer->yPos;
1125	cursor_data = (BYTE *)malloc(rect.size.width * rect.size.height * 4);
1126
1127	if (!cursor_data)
1128		return FALSE;
1129
1130	mrdpCursor->cursor_data = cursor_data;
1131	format = PIXEL_FORMAT_RGBA32;
1132
1133	if (!freerdp_image_copy_from_pointer_data(cursor_data, format, 0, 0, 0, pointer->width,
1134	                                          pointer->height, pointer->xorMaskData,
1135	                                          pointer->lengthXorMask, pointer->andMaskData,
1136	                                          pointer->lengthAndMask, pointer->xorBpp, NULL))
1137	{
1138		free(cursor_data);
1139		mrdpCursor->cursor_data = NULL;
1140		return FALSE;
1141	}
1142
1143	/* store cursor bitmap image in representation - required by NSImage */
1144	bmiRep = [[NSBitmapImageRep alloc]
1145	    initWithBitmapDataPlanes:(unsigned char **)&cursor_data
1146	                  pixelsWide:rect.size.width
1147	                  pixelsHigh:rect.size.height
1148	               bitsPerSample:8
1149	             samplesPerPixel:4
1150	                    hasAlpha:YES
1151	                    isPlanar:NO
1152	              colorSpaceName:NSDeviceRGBColorSpace
1153	                bitmapFormat:0
1154	                 bytesPerRow:rect.size.width * GetBytesPerPixel(format)
1155	                bitsPerPixel:0];
1156	mrdpCursor->bmiRep = bmiRep;
1157	/* create an image using above representation */
1158	image = [[NSImage alloc] initWithSize:[bmiRep size]];
1159	[image addRepresentation:bmiRep];
1160	[image setFlipped:NO];
1161	mrdpCursor->nsImage = image;
1162	/* need hotspot to create cursor */
1163	hotSpot.x = pointer->xPos;
1164	hotSpot.y = pointer->yPos;
1165	cursor = [[NSCursor alloc] initWithImage:image hotSpot:hotSpot];
1166	mrdpCursor->nsCursor = cursor;
1167	mrdpCursor->pointer = pointer;
1168	/* save cursor for later use in mf_Pointer_Set() */
1169	ma = view->cursors;
1170	[ma addObject:mrdpCursor];
1171	return TRUE;
1172}
1173
1174void mf_Pointer_Free(rdpContext *context, rdpPointer *pointer)
1175{
1176	mfContext *mfc = (mfContext *)context;
1177	MRDPView *view = (MRDPView *)mfc->view;
1178	NSMutableArray *ma = view->cursors;
1179
1180	for (MRDPCursor *cursor in ma)
1181	{
1182		if (cursor->pointer == pointer)
1183		{
1184			cursor->nsImage = nil;
1185			cursor->nsCursor = nil;
1186			cursor->bmiRep = nil;
1187			free(cursor->cursor_data);
1188			[ma removeObject:cursor];
1189			return;
1190		}
1191	}
1192}
1193
1194BOOL mf_Pointer_Set(rdpContext *context, const rdpPointer *pointer)
1195{
1196	mfContext *mfc = (mfContext *)context;
1197	MRDPView *view = (MRDPView *)mfc->view;
1198	NSMutableArray *ma = view->cursors;
1199
1200	for (MRDPCursor *cursor in ma)
1201	{
1202		if (cursor->pointer == pointer)
1203		{
1204			[view setCursor:cursor->nsCursor];
1205			return TRUE;
1206		}
1207	}
1208
1209	NSLog(@"Cursor not found");
1210	return TRUE;
1211}
1212
1213BOOL mf_Pointer_SetNull(rdpContext *context)
1214{
1215	return TRUE;
1216}
1217
1218BOOL mf_Pointer_SetDefault(rdpContext *context)
1219{
1220	mfContext *mfc = (mfContext *)context;
1221	MRDPView *view = (MRDPView *)mfc->view;
1222	[view setCursor:[NSCursor arrowCursor]];
1223	return TRUE;
1224}
1225
1226static BOOL mf_Pointer_SetPosition(rdpContext *context, UINT32 x, UINT32 y)
1227{
1228	mfContext *mfc = (mfContext *)context;
1229
1230	if (!mfc)
1231		return FALSE;
1232
1233	/* TODO: Set pointer position */
1234	return TRUE;
1235}
1236
1237CGContextRef mac_create_bitmap_context(rdpContext *context)
1238{
1239	CGContextRef bitmap_context;
1240	rdpGdi *gdi = context->gdi;
1241	UINT32 bpp = GetBytesPerPixel(gdi->dstFormat);
1242	CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
1243
1244	if (bpp == 2)
1245	{
1246		bitmap_context = CGBitmapContextCreate(
1247		    gdi->primary_buffer, gdi->width, gdi->height, 5, gdi->stride, colorSpace,
1248		    kCGBitmapByteOrder16Little | kCGImageAlphaNoneSkipFirst);
1249	}
1250	else
1251	{
1252		bitmap_context = CGBitmapContextCreate(
1253		    gdi->primary_buffer, gdi->width, gdi->height, 8, gdi->stride, colorSpace,
1254		    kCGBitmapByteOrder32Little | kCGImageAlphaNoneSkipFirst);
1255	}
1256
1257	CGColorSpaceRelease(colorSpace);
1258	return bitmap_context;
1259}
1260
1261BOOL mac_begin_paint(rdpContext *context)
1262{
1263	rdpGdi *gdi = context->gdi;
1264
1265	if (!gdi)
1266		return FALSE;
1267
1268	gdi->primary->hdc->hwnd->invalid->null = TRUE;
1269	return TRUE;
1270}
1271
1272BOOL mac_end_paint(rdpContext *context)
1273{
1274	rdpGdi *gdi;
1275	HGDI_RGN invalid;
1276	NSRect newDrawRect;
1277	int ww, wh, dw, dh;
1278	mfContext *mfc = (mfContext *)context;
1279	MRDPView *view = (MRDPView *)mfc->view;
1280	gdi = context->gdi;
1281
1282	if (!gdi)
1283		return FALSE;
1284
1285	ww = mfc->client_width;
1286	wh = mfc->client_height;
1287	dw = mfc->context.settings->DesktopWidth;
1288	dh = mfc->context.settings->DesktopHeight;
1289
1290	if ((!context) || (!context->gdi))
1291		return FALSE;
1292
1293	if (context->gdi->primary->hdc->hwnd->invalid->null)
1294		return TRUE;
1295
1296	invalid = gdi->primary->hdc->hwnd->invalid;
1297	newDrawRect.origin.x = invalid->x;
1298	newDrawRect.origin.y = invalid->y;
1299	newDrawRect.size.width = invalid->w;
1300	newDrawRect.size.height = invalid->h;
1301
1302	if (mfc->context.settings->SmartSizing && (ww != dw || wh != dh))
1303	{
1304		newDrawRect.origin.y = newDrawRect.origin.y * wh / dh - 1;
1305		newDrawRect.size.height = newDrawRect.size.height * wh / dh + 1;
1306		newDrawRect.origin.x = newDrawRect.origin.x * ww / dw - 1;
1307		newDrawRect.size.width = newDrawRect.size.width * ww / dw + 1;
1308	}
1309	else
1310	{
1311		newDrawRect.origin.y = newDrawRect.origin.y - 1;
1312		newDrawRect.size.height = newDrawRect.size.height + 1;
1313		newDrawRect.origin.x = newDrawRect.origin.x - 1;
1314		newDrawRect.size.width = newDrawRect.size.width + 1;
1315	}
1316
1317	windows_to_apple_cords(mfc->view, &newDrawRect);
1318	dispatch_sync(dispatch_get_main_queue(), ^{
1319		[view setNeedsDisplayInRect:newDrawRect];
1320	});
1321	gdi->primary->hdc->hwnd->ninvalid = 0;
1322	return TRUE;
1323}
1324
1325BOOL mac_desktop_resize(rdpContext *context)
1326{
1327	ResizeWindowEventArgs e;
1328	mfContext *mfc = (mfContext *)context;
1329	MRDPView *view = (MRDPView *)mfc->view;
1330	rdpSettings *settings = context->settings;
1331
1332	if (!context->gdi)
1333		return TRUE;
1334
1335	/**
1336	 * TODO: Fix resizing race condition. We should probably implement a message to be
1337	 * put on the update message queue to be able to properly flush pending updates,
1338	 * resize, and then continue with post-resizing graphical updates.
1339	 */
1340	CGContextRef old_context = view->bitmap_context;
1341	view->bitmap_context = NULL;
1342	CGContextRelease(old_context);
1343	mfc->width = settings->DesktopWidth;
1344	mfc->height = settings->DesktopHeight;
1345
1346	if (!gdi_resize(context->gdi, mfc->width, mfc->height))
1347		return FALSE;
1348
1349	view->bitmap_context = mac_create_bitmap_context(context);
1350
1351	if (!view->bitmap_context)
1352		return FALSE;
1353
1354	mfc->client_width = mfc->width;
1355	mfc->client_height = mfc->height;
1356	[view setFrameSize:NSMakeSize(mfc->width, mfc->height)];
1357	EventArgsInit(&e, "mfreerdp");
1358	e.width = settings->DesktopWidth;
1359	e.height = settings->DesktopHeight;
1360	PubSub_OnResizeWindow(context->pubSub, context, &e);
1361	return TRUE;
1362}
1363
1364void input_activity_cb(freerdp *instance)
1365{
1366	int status;
1367	wMessage message;
1368	wMessageQueue *queue;
1369	status = 1;
1370	queue = freerdp_get_message_queue(instance, FREERDP_INPUT_MESSAGE_QUEUE);
1371
1372	if (queue)
1373	{
1374		while (MessageQueue_Peek(queue, &message, TRUE))
1375		{
1376			status = freerdp_message_queue_process_message(instance, FREERDP_INPUT_MESSAGE_QUEUE,
1377			                                               &message);
1378
1379			if (!status)
1380				break;
1381		}
1382	}
1383	else
1384	{
1385		WLog_ERR(TAG, "input_activity_cb: No queue!");
1386	}
1387}
1388
1389/**
1390 * given a rect with 0,0 at the top left (windows cords)
1391 * convert it to a rect with 0,0 at the bottom left (apple cords)
1392 *
1393 * Note: the formula works for conversions in both directions.
1394 *
1395 */
1396
1397void windows_to_apple_cords(MRDPView *view, NSRect *r)
1398{
1399	dispatch_sync(dispatch_get_main_queue(), ^{
1400		r->origin.y = [view frame].size.height - (r->origin.y + r->size.height);
1401	});
1402}
1403
1404void sync_keyboard_state(freerdp *instance)
1405{
1406	mfContext *context = (mfContext *)instance->context;
1407	UINT32 flags = 0;
1408	CGEventFlags currentFlags = CGEventSourceFlagsState(kCGEventSourceStateHIDSystemState);
1409
1410	if (context->kbdFlags != currentFlags)
1411	{
1412		if (currentFlags & kCGEventFlagMaskAlphaShift)
1413			flags |= KBD_SYNC_CAPS_LOCK;
1414
1415		if (currentFlags & kCGEventFlagMaskNumericPad)
1416			flags |= KBD_SYNC_NUM_LOCK;
1417
1418		freerdp_input_send_synchronize_event(instance->input, flags);
1419		context->kbdFlags = currentFlags;
1420	}
1421}
1422
1423@end
1424