FreeRDP
Loading...
Searching...
No Matches
ios_freerdp.m
1/*
2 RDP run-loop
3
4 Copyright 2013 Thincast Technologies GmbH, Authors: Martin Fleisz, Dorian Johnson
5
6 This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
7 If a copy of the MPL was not distributed with this file, You can obtain one at
8 http://mozilla.org/MPL/2.0/.
9 */
10
11#include <winpr/assert.h>
12#import <winpr/clipboard.h>
13
14#import <freerdp/gdi/gdi.h>
15#import <freerdp/codec/color.h>
16#import <freerdp/channels/channels.h>
17#import <freerdp/client/channels.h>
18#import <freerdp/client/cmdline.h>
19#import <freerdp/freerdp.h>
20#import <freerdp/gdi/gfx.h>
21#import <freerdp/client/cliprdr.h>
22
23#import "ios_freerdp.h"
24#import "ios_freerdp_ui.h"
25#import "ios_freerdp_events.h"
26#import "ios_cliprdr.h"
27
28#import "RDPSession.h"
29#import "RDPCursor.h"
30#import "Utils.h"
31
32#include <errno.h>
33
34#define TAG FREERDP_TAG("iOS")
35
36typedef struct
37{
38 rdpPointer pointer;
39 RDPCursor *cursor;
40} iosPointer;
41
42#pragma mark Connection helpers
43
44static void ios_OnChannelConnectedEventHandler(void *context, const ChannelConnectedEventArgs *e)
45{
46 WLog_INFO(TAG, "ios_OnChannelConnectedEventHandler, channel %s", e->name);
47 rdpSettings *settings;
48 mfContext *afc;
49
50 if (!context || !e)
51 {
52 WLog_FATAL(TAG, "(context=%p, EventArgs=%p", context, (void *)e);
53 return;
54 }
55
56 afc = (mfContext *)context;
57 settings = afc->_p.settings;
58
59 if (strcmp(e->name, RDPGFX_DVC_CHANNEL_NAME) == 0)
60 {
61 if (freerdp_settings_get_bool(settings, FreeRDP_SoftwareGdi))
62 {
63 gdi_graphics_pipeline_init(afc->_p.gdi, (RdpgfxClientContext *)e->pInterface);
64 }
65 else
66 {
67 WLog_WARN(TAG, "GFX without software GDI requested. "
68 " This is not supported, add /gdi:sw");
69 }
70 }
71 else if (strcmp(e->name, CLIPRDR_SVC_CHANNEL_NAME) == 0)
72 {
73 ios_cliprdr_init(afc, (CliprdrClientContext *)e->pInterface);
74 }
75}
76
77static void ios_OnChannelDisconnectedEventHandler(void *context,
78 const ChannelDisconnectedEventArgs *e)
79{
80 WLog_INFO(TAG, "ios_OnChannelConnectedEventHandler, channel %s", e->name);
81 rdpSettings *settings;
82 mfContext *afc;
83
84 if (!context || !e)
85 {
86 WLog_FATAL(TAG, "(context=%p, EventArgs=%p", context, (void *)e);
87 return;
88 }
89
90 afc = (mfContext *)context;
91 settings = afc->_p.settings;
92
93 if (strcmp(e->name, RDPGFX_DVC_CHANNEL_NAME) == 0)
94 {
95 if (freerdp_settings_get_bool(settings, FreeRDP_SoftwareGdi))
96 {
97 gdi_graphics_pipeline_uninit(afc->_p.gdi, (RdpgfxClientContext *)e->pInterface);
98 }
99 else
100 {
101 WLog_WARN(TAG, "GFX without software GDI requested. "
102 " This is not supported, add /gdi:sw");
103 }
104 }
105 else if (strcmp(e->name, CLIPRDR_SVC_CHANNEL_NAME) == 0)
106 {
107 ios_cliprdr_uninit(afc, (CliprdrClientContext *)e->pInterface);
108 }
109}
110
111static BOOL ios_pre_connect(freerdp *instance)
112{
113 int rc;
114 rdpSettings *settings;
115
116 if (!instance || !instance->context)
117 return FALSE;
118
119 settings = instance->context->settings;
120 WINPR_ASSERT(settings);
121
122 const char *Password = freerdp_settings_get_string(settings, FreeRDP_Password);
123 if (!freerdp_settings_set_bool(settings, FreeRDP_AutoLogonEnabled,
124 Password && (Password && (strlen(Password) > 0))))
125 return FALSE;
126
127 if (!freerdp_settings_set_bool(settings, FreeRDP_GrabMouse, TRUE))
128 return FALSE;
129
130 // Verify screen width/height are sane
131 if ((freerdp_settings_get_uint32(settings, FreeRDP_DesktopWidth) < 64) ||
132 (freerdp_settings_get_uint32(settings, FreeRDP_DesktopHeight) < 64) ||
133 (freerdp_settings_get_uint32(settings, FreeRDP_DesktopWidth) > 4096) ||
134 (freerdp_settings_get_uint32(settings, FreeRDP_DesktopHeight) > 4096))
135 {
136 NSLog(@"%s: invalid dimensions %d %d", __func__,
137 freerdp_settings_get_uint32(settings, FreeRDP_DesktopWidth),
138 freerdp_settings_get_uint32(settings, FreeRDP_DesktopHeight));
139 return FALSE;
140 }
141
142 rc = PubSub_SubscribeChannelConnected(instance->context->pubSub,
143 ios_OnChannelConnectedEventHandler);
144
145 if (rc != CHANNEL_RC_OK)
146 {
147 WLog_ERR(TAG, "Could not subscribe to connect event handler [0x%08X]", (unsigned)rc);
148 return FALSE;
149 }
150
151 rc = PubSub_SubscribeChannelDisconnected(instance->context->pubSub,
152 ios_OnChannelDisconnectedEventHandler);
153
154 if (rc != CHANNEL_RC_OK)
155 {
156 WLog_ERR(TAG, "Could not subscribe to disconnect event handler [0x%08X]", (unsigned)rc);
157 return FALSE;
158 }
159
160 if (!freerdp_client_load_addins(instance->context->channels, settings))
161 {
162 WLog_ERR(TAG, "Failed to load addins [0x%08X]", (unsigned)GetLastError());
163 return FALSE;
164 }
165
166 return TRUE;
167}
168
169static BOOL ios_Pointer_New(rdpContext *context, rdpPointer *pointer)
170{
171 if (!context || !pointer || !context->gdi)
172 return FALSE;
173
174 // codes from android client code.
175 iosPointer *ptr = (iosPointer *)pointer;
176 const size_t size = 4 * pointer->width * pointer->height; // BGRA32
177 BYTE *data = winpr_aligned_malloc(size, 16);
178 if (!data)
179 return FALSE;
180
181 // get RDP server cursor
182 if (!freerdp_image_copy_from_pointer_data(
183 data, PIXEL_FORMAT_RGBA32, 0, 0, 0, pointer->width, pointer->height,
184 pointer->xorMaskData, pointer->lengthXorMask, pointer->andMaskData,
185 pointer->lengthAndMask, pointer->xorBpp, &context->gdi->palette))
186 {
187 winpr_aligned_free(data);
188 return FALSE;
189 }
190
191 ptr->cursor = [[RDPCursor alloc] initWithRGBABytes:data
192 width:pointer->width
193 height:pointer->height
194 hotspot:CGPointMake(pointer->xPos, pointer->yPos)];
195 winpr_aligned_free(data);
196 if (!ptr->cursor)
197 return FALSE;
198
199 return TRUE;
200}
201
202static void ios_Pointer_Free(rdpContext *context, rdpPointer *pointer)
203{
204 if (!context || !pointer)
205 return;
206
207 iosPointer *ptr = (iosPointer *)pointer;
208 [ptr->cursor release];
209 ptr->cursor = nil;
210}
211
212static BOOL ios_Pointer_Set(rdpContext *context, rdpPointer *pointer)
213{
214 if (!context || !context->instance || !pointer)
215 return FALSE;
216
217 mfInfo *mfi = MFI_FROM_INSTANCE(context->instance);
218 iosPointer *ptr = (iosPointer *)pointer;
219 if (!mfi || !mfi->session || !ptr->cursor)
220 return FALSE;
221
222 [mfi->session performSelectorOnMainThread:@selector(setRemoteCursor:)
223 withObject:ptr->cursor
224 waitUntilDone:YES];
225
226 return TRUE;
227}
228
229static BOOL ios_Pointer_SetPosition(rdpContext *context, UINT32 x, UINT32 y)
230{
231 if (!context || !context->instance)
232 return FALSE;
233
234 mfInfo *mfi = MFI_FROM_INSTANCE(context->instance);
235 if (!mfi || !mfi->session)
236 return FALSE;
237
238 NSValue *position = [NSValue valueWithCGPoint:CGPointMake(x, y)];
239 [mfi->session performSelectorOnMainThread:@selector(setRemoteCursorPositionValue:)
240 withObject:position
241 waitUntilDone:NO];
242
243 return TRUE;
244}
245
246static BOOL ios_Pointer_SetNull(rdpContext *context)
247{
248 if (!context || !context->instance)
249 return FALSE;
250
251 mfInfo *mfi = MFI_FROM_INSTANCE(context->instance);
252 if (!mfi || !mfi->session)
253 return FALSE;
254
255 [mfi->session performSelectorOnMainThread:@selector(hideRemoteCursor)
256 withObject:nil
257 waitUntilDone:NO];
258
259 return TRUE;
260}
261
262static BOOL ios_Pointer_SetDefault(rdpContext *context)
263{
264 if (!context || !context->instance)
265 return FALSE;
266
267 mfInfo *mfi = MFI_FROM_INSTANCE(context->instance);
268 if (!mfi || !mfi->session)
269 return FALSE;
270
271 [mfi->session performSelectorOnMainThread:@selector(setDefaultRemoteCursor)
272 withObject:nil
273 waitUntilDone:NO];
274
275 return TRUE;
276}
277
278static BOOL ios_register_pointer(rdpGraphics *graphics)
279{
280 rdpPointer pointer = WINPR_C_ARRAY_INIT;
281
282 if (!graphics)
283 return FALSE;
284
285 pointer.size = sizeof(iosPointer);
286 pointer.New = ios_Pointer_New;
287 pointer.Free = ios_Pointer_Free;
288 pointer.Set = ios_Pointer_Set;
289 pointer.SetNull = ios_Pointer_SetNull;
290 pointer.SetDefault = ios_Pointer_SetDefault;
291 pointer.SetPosition = ios_Pointer_SetPosition;
292 graphics_register_pointer(graphics, &pointer);
293 return TRUE;
294}
295
296static BOOL ios_post_connect(freerdp *instance)
297{
298 mfInfo *mfi;
299
300 if (!instance)
301 return FALSE;
302
303 mfi = MFI_FROM_INSTANCE(instance);
304
305 if (!mfi)
306 return FALSE;
307
308 if (!gdi_init(instance, PIXEL_FORMAT_BGRA32))
309 return FALSE;
310
311 if (!ios_register_pointer(instance->context->graphics))
312 return FALSE;
313
314 ios_allocate_display_buffer(mfi);
315 instance->context->update->BeginPaint = ios_ui_begin_paint;
316 instance->context->update->EndPaint = ios_ui_end_paint;
317 instance->context->update->DesktopResize = ios_ui_resize_window;
318 [mfi->session performSelectorOnMainThread:@selector(sessionDidConnect)
319 withObject:nil
320 waitUntilDone:YES];
321 return TRUE;
322}
323
324static void ios_post_disconnect(freerdp *instance)
325{
326 gdi_free(instance);
327}
328
329#pragma mark -
330#pragma mark Running the connection
331
332int ios_run_freerdp(freerdp *instance)
333{
334 mfContext *context = (mfContext *)instance->context;
335 mfInfo *mfi = context->mfi;
336 rdpChannels *channels = instance->context->channels;
337 mfi->connection_state = TSXConnectionConnecting;
338
339 if (!freerdp_connect(instance))
340 {
341 NSLog(@"%s: inst->rdp_connect failed", __func__);
342 return mfi->unwanted ? MF_EXIT_CONN_CANCELED : MF_EXIT_CONN_FAILED;
343 }
344
345 if (mfi->unwanted)
346 return MF_EXIT_CONN_CANCELED;
347
348 mfi->connection_state = TSXConnectionConnected;
349 // Connection main loop
350 NSAutoreleasePool *pool;
351
352 while (!freerdp_shall_disconnect_context(instance->context))
353 {
354 DWORD status;
355 DWORD nCount = 0;
356 HANDLE handles[MAXIMUM_WAIT_OBJECTS] = WINPR_C_ARRAY_INIT;
357 pool = [[NSAutoreleasePool alloc] init];
358
359 nCount = freerdp_get_event_handles(instance->context, handles, ARRAYSIZE(handles));
360 if (nCount == 0)
361 {
362 NSLog(@"%s: freerdp_get_event_handles failed", __func__);
363 break;
364 }
365
366 handles[nCount++] = ios_events_get_handle(mfi);
367
368 status = WaitForMultipleObjects(nCount, handles, FALSE, INFINITE);
369
370 if (WAIT_FAILED == status)
371 {
372 NSLog(@"%s: WaitForMultipleObjects failed!", __func__);
373 break;
374 }
375
376 // Check the libfreerdp fds
377 if (!freerdp_check_event_handles(instance->context))
378 {
379 NSLog(@"%s: freerdp_check_event_handles failed.", __func__);
380 break;
381 }
382
383 // Check input event fds
384 if (ios_events_check_handle(mfi) != TRUE)
385 {
386 // This event will fail when the app asks for a disconnect.
387 // NSLog(@"%s: ios_events_check_fds failed: terminating connection.", __func__);
388 break;
389 }
390
391 [pool release];
392 pool = nil;
393 }
394
395 CGContextRelease(mfi->bitmap_context);
396 mfi->bitmap_context = nullptr;
397 mfi->connection_state = TSXConnectionDisconnected;
398 // Cleanup
399 freerdp_disconnect(instance);
400 gdi_free(instance);
401 [pool release];
402 pool = nil;
403 return MF_EXIT_SUCCESS;
404}
405
406#pragma mark -
407#pragma mark Context callbacks
408
409static BOOL ios_client_new(freerdp *instance, rdpContext *context)
410{
411 mfContext *ctx = (mfContext *)context;
412
413 if (!instance || !context)
414 return FALSE;
415
416 if ((ctx->mfi = calloc(1, sizeof(mfInfo))) == nullptr)
417 return FALSE;
418
419 ctx->mfi->context = (mfContext *)context;
420 ctx->mfi->_context = context;
421 ctx->mfi->instance = instance;
422
423 if (!ios_events_create_pipe(ctx->mfi))
424 return FALSE;
425
426 instance->PreConnect = ios_pre_connect;
427 instance->PostConnect = ios_post_connect;
428 instance->PostDisconnect = ios_post_disconnect;
429 instance->AuthenticateEx = ios_ui_authenticate_ex;
430 instance->VerifyCertificateEx = ios_ui_verify_certificate_ex;
431 instance->VerifyChangedCertificateEx = ios_ui_verify_changed_certificate_ex;
432 instance->LogonErrorInfo = nullptr;
433 return TRUE;
434}
435
436static void ios_client_free(freerdp *instance, rdpContext *context)
437{
438 mfInfo *mfi;
439
440 if (!context)
441 return;
442
443 mfi = ((mfContext *)context)->mfi;
444 ios_events_free_pipe(mfi);
445 free(mfi);
446}
447
448static int RdpClientEntry(RDP_CLIENT_ENTRY_POINTS *pEntryPoints)
449{
450 WINPR_ASSERT(pEntryPoints);
451
452 ZeroMemory(pEntryPoints, sizeof(RDP_CLIENT_ENTRY_POINTS));
453 pEntryPoints->Version = RDP_CLIENT_INTERFACE_VERSION;
454 pEntryPoints->Size = sizeof(RDP_CLIENT_ENTRY_POINTS_V1);
455 pEntryPoints->GlobalInit = nullptr;
456 pEntryPoints->GlobalUninit = nullptr;
457 pEntryPoints->ContextSize = sizeof(mfContext);
458 pEntryPoints->ClientNew = ios_client_new;
459 pEntryPoints->ClientFree = ios_client_free;
460 pEntryPoints->ClientStart = nullptr;
461 pEntryPoints->ClientStop = nullptr;
462 return 0;
463}
464
465#pragma mark -
466#pragma mark Initialization and cleanup
467
468freerdp *ios_freerdp_new()
469{
470 rdpContext *context;
471 RDP_CLIENT_ENTRY_POINTS clientEntryPoints;
472 RdpClientEntry(&clientEntryPoints);
473 context = freerdp_client_context_new(&clientEntryPoints);
474
475 if (!context)
476 return nullptr;
477
478 return context->instance;
479}
480
481void ios_freerdp_free(freerdp *instance)
482{
483 if (!instance || !instance->context)
484 return;
485
486 freerdp_client_context_free(instance->context);
487}
488
489void ios_init_freerdp()
490{
491 signal(SIGPIPE, SIG_IGN);
492}
493
494void ios_uninit_freerdp()
495{
496}
497
498/* compatibility functions */
499size_t fwrite$UNIX2003(const void *ptr, size_t size, size_t nmemb, FILE *stream)
500{
501 return fwrite(ptr, size, nmemb, stream);
502}
503
504void ios_send_clipboard_data(void *context, const void *data, UINT32 size)
505{
506 mfContext *afc = (mfContext *)context;
507 ClipboardLock(afc->clipboard);
508 UINT32 formatId = ClipboardRegisterFormat(afc->clipboard, "UTF8_STRING");
509 if (size)
510 {
511 if (!ClipboardSetData(afc->clipboard, formatId, data, size))
512 {
513 ClipboardUnlock(afc->clipboard);
514 WLog_ERR(TAG, "ClipboardSetData failed");
515 return;
516 }
517 }
518 else
519 ClipboardEmpty(afc->clipboard);
520 ClipboardUnlock(afc->clipboard);
521 ios_cliprdr_send_client_format_list(afc->cliprdr);
522}
WINPR_ATTR_NODISCARD FREERDP_API const char * freerdp_settings_get_string(const rdpSettings *settings, FreeRDP_Settings_Keys_String id)
Returns a immutable string settings value.
WINPR_ATTR_NODISCARD FREERDP_API BOOL freerdp_settings_set_bool(rdpSettings *settings, FreeRDP_Settings_Keys_Bool id, BOOL val)
Sets a BOOL settings value.
WINPR_ATTR_NODISCARD FREERDP_API UINT32 freerdp_settings_get_uint32(const rdpSettings *settings, FreeRDP_Settings_Keys_UInt32 id)
Returns a UINT32 settings value.
WINPR_ATTR_NODISCARD FREERDP_API BOOL freerdp_settings_get_bool(const rdpSettings *settings, FreeRDP_Settings_Keys_Bool id)
Returns a boolean settings value.