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
332static BOOL ios_reconnect(freerdp *instance)
333{
334 mfInfo *mfi = MFI_FROM_INSTANCE(instance);
335
336 // keeps input from being queued for the dropped connection and lets cancel abort the attempts
337 mfi->connection_state = TSXConnectionConnecting;
338 if (!client_auto_reconnect_ex(instance, nullptr))
339 return FALSE;
340
341 mfi->connection_state = TSXConnectionConnected;
342 [mfi->session performSelectorOnMainThread:@selector(sessionDidReconnect)
343 withObject:nil
344 waitUntilDone:NO];
345 return TRUE;
346}
347
348int ios_run_freerdp(freerdp *instance)
349{
350 mfContext *context = (mfContext *)instance->context;
351 mfInfo *mfi = context->mfi;
352 rdpChannels *channels = instance->context->channels;
353 mfi->connection_state = TSXConnectionConnecting;
354
355 if (!freerdp_connect(instance))
356 {
357 NSLog(@"%s: inst->rdp_connect failed", __func__);
358 return mfi->unwanted ? MF_EXIT_CONN_CANCELED : MF_EXIT_CONN_FAILED;
359 }
360
361 if (mfi->unwanted)
362 return MF_EXIT_CONN_CANCELED;
363
364 mfi->connection_state = TSXConnectionConnected;
365 // Connection main loop
366 NSAutoreleasePool *pool;
367
368 while (!freerdp_shall_disconnect_context(instance->context))
369 {
370 DWORD status;
371 DWORD nCount = 0;
372 HANDLE handles[MAXIMUM_WAIT_OBJECTS] = WINPR_C_ARRAY_INIT;
373 pool = [[NSAutoreleasePool alloc] init];
374
375 nCount = freerdp_get_event_handles(instance->context, handles, ARRAYSIZE(handles));
376 if (nCount == 0)
377 {
378 NSLog(@"%s: freerdp_get_event_handles failed", __func__);
379 break;
380 }
381
382 handles[nCount++] = ios_events_get_handle(mfi);
383
384 status = WaitForMultipleObjects(nCount, handles, FALSE, INFINITE);
385
386 if (WAIT_FAILED == status)
387 {
388 NSLog(@"%s: WaitForMultipleObjects failed!", __func__);
389 break;
390 }
391
392 // Check the libfreerdp fds
393 if (!freerdp_check_event_handles(instance->context) && !ios_reconnect(instance))
394 {
395 NSLog(@"%s: freerdp_check_event_handles failed.", __func__);
396 break;
397 }
398
399 // Check input event fds
400 if (ios_events_check_handle(mfi) != TRUE)
401 {
402 // This event will fail when the app asks for a disconnect.
403 // NSLog(@"%s: ios_events_check_fds failed: terminating connection.", __func__);
404 break;
405 }
406
407 [pool release];
408 pool = nil;
409 }
410
411 CGContextRelease(mfi->bitmap_context);
412 mfi->bitmap_context = nullptr;
413 mfi->connection_state = TSXConnectionDisconnected;
414 // Cleanup
415 freerdp_disconnect(instance);
416 gdi_free(instance);
417 [pool release];
418 pool = nil;
419 return MF_EXIT_SUCCESS;
420}
421
422#pragma mark -
423#pragma mark Context callbacks
424
425static BOOL ios_client_new(freerdp *instance, rdpContext *context)
426{
427 mfContext *ctx = (mfContext *)context;
428
429 if (!instance || !context)
430 return FALSE;
431
432 if ((ctx->mfi = calloc(1, sizeof(mfInfo))) == nullptr)
433 return FALSE;
434
435 ctx->mfi->context = (mfContext *)context;
436 ctx->mfi->_context = context;
437 ctx->mfi->instance = instance;
438
439 if (!ios_events_create_pipe(ctx->mfi))
440 return FALSE;
441
442 instance->PreConnect = ios_pre_connect;
443 instance->PostConnect = ios_post_connect;
444 instance->PostDisconnect = ios_post_disconnect;
445 instance->AuthenticateEx = ios_ui_authenticate_ex;
446 instance->VerifyCertificateEx = ios_ui_verify_certificate_ex;
447 instance->VerifyChangedCertificateEx = ios_ui_verify_changed_certificate_ex;
448 instance->RetryDialog = ios_ui_retry_dialog;
449 instance->LogonErrorInfo = nullptr;
450 return TRUE;
451}
452
453static void ios_client_free(freerdp *instance, rdpContext *context)
454{
455 mfInfo *mfi;
456
457 if (!context)
458 return;
459
460 mfi = ((mfContext *)context)->mfi;
461 ios_events_free_pipe(mfi);
462 free(mfi);
463}
464
465static int RdpClientEntry(RDP_CLIENT_ENTRY_POINTS *pEntryPoints)
466{
467 WINPR_ASSERT(pEntryPoints);
468
469 ZeroMemory(pEntryPoints, sizeof(RDP_CLIENT_ENTRY_POINTS));
470 pEntryPoints->Version = RDP_CLIENT_INTERFACE_VERSION;
471 pEntryPoints->Size = sizeof(RDP_CLIENT_ENTRY_POINTS_V1);
472 pEntryPoints->GlobalInit = nullptr;
473 pEntryPoints->GlobalUninit = nullptr;
474 pEntryPoints->ContextSize = sizeof(mfContext);
475 pEntryPoints->ClientNew = ios_client_new;
476 pEntryPoints->ClientFree = ios_client_free;
477 pEntryPoints->ClientStart = nullptr;
478 pEntryPoints->ClientStop = nullptr;
479 return 0;
480}
481
482#pragma mark -
483#pragma mark Initialization and cleanup
484
485freerdp *ios_freerdp_new()
486{
487 rdpContext *context;
488 RDP_CLIENT_ENTRY_POINTS clientEntryPoints;
489 RdpClientEntry(&clientEntryPoints);
490 context = freerdp_client_context_new(&clientEntryPoints);
491
492 if (!context)
493 return nullptr;
494
495 return context->instance;
496}
497
498void ios_freerdp_free(freerdp *instance)
499{
500 if (!instance || !instance->context)
501 return;
502
503 freerdp_client_context_free(instance->context);
504}
505
506void ios_init_freerdp()
507{
508 signal(SIGPIPE, SIG_IGN);
509}
510
511void ios_uninit_freerdp()
512{
513}
514
515/* compatibility functions */
516size_t fwrite$UNIX2003(const void *ptr, size_t size, size_t nmemb, FILE *stream)
517{
518 return fwrite(ptr, size, nmemb, stream);
519}
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.