mirror of https://github.com/FreeRDP/FreeRDP
Add hotplug support for MacOS
This commit is contained in:
parent
a20950faa0
commit
2e1e6a4ff0
|
@ -2,6 +2,8 @@
|
|||
# FreeRDP cmake build script
|
||||
#
|
||||
# Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
|
||||
# Copyright 2016 Inuvika Inc.
|
||||
# Copyright 2016 David PHAM-VAN <d.phamvan@inuvika.com>
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
|
@ -32,6 +34,10 @@ add_channel_client_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} FALSE
|
|||
|
||||
|
||||
target_link_libraries(${MODULE_NAME} winpr freerdp)
|
||||
if(APPLE AND (NOT IOS))
|
||||
find_library(CORESERVICES_LIBRARY CoreServices)
|
||||
target_link_libraries(${MODULE_NAME} ${CORESERVICES_LIBRARY})
|
||||
endif()
|
||||
|
||||
|
||||
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "Channels/${CHANNEL_NAME}/Client")
|
||||
|
|
|
@ -46,6 +46,15 @@
|
|||
#include <fcntl.h>
|
||||
#endif
|
||||
|
||||
#ifdef __MACOSX__
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
#include <stdio.h>
|
||||
#include <dirent.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_UNISTD_H
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
@ -103,6 +112,34 @@ static UINT rdpdr_send_device_list_remove_request(rdpdrPlugin* rdpdr, UINT32 cou
|
|||
|
||||
#ifdef _WIN32
|
||||
|
||||
void first_hotplug(rdpdrPlugin *rdpdr)
|
||||
{
|
||||
int i;
|
||||
char drive_path[5] = { 'c', ':', '\\', '\0' };
|
||||
|
||||
DWORD unitmask = GetLogicalDrives();
|
||||
|
||||
for (i = 0; i < 26; i++)
|
||||
{
|
||||
if (unitmask & 0x01)
|
||||
{
|
||||
RDPDR_DRIVE* drive;
|
||||
|
||||
drive_path[0] = 'A' + i;
|
||||
drive_path[1] = ':';
|
||||
|
||||
drive = (RDPDR_DRIVE*)malloc(sizeof(RDPDR_DRIVE));
|
||||
ZeroMemory(drive, sizeof(RDPDR_DRIVE));
|
||||
drive->Type = RDPDR_DTYP_FILESYSTEM;
|
||||
drive->Path = _strdup(drive_path);
|
||||
drive_path[1] = '\0';
|
||||
drive->Name = _strdup(drive_path);
|
||||
devman_load_device_service(rdpdr->devman, (RDPDR_DEVICE *)drive, rdpdr->rdpcontext);
|
||||
}
|
||||
unitmask = unitmask >> 1;
|
||||
}
|
||||
}
|
||||
|
||||
LRESULT CALLBACK hotplug_proc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
rdpdrPlugin *rdpdr;
|
||||
|
@ -131,6 +168,7 @@ LRESULT CALLBACK hotplug_proc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
|
|||
RDPDR_DRIVE* drive;
|
||||
|
||||
drive_path[0] = 'A' + i;
|
||||
drive_path[1] = ':';
|
||||
|
||||
drive = (RDPDR_DRIVE*) malloc(sizeof(RDPDR_DRIVE));
|
||||
ZeroMemory(drive, sizeof(RDPDR_DRIVE));
|
||||
|
@ -212,6 +250,7 @@ static void* drive_hotplug_thread_func(void* arg)
|
|||
HDEVNOTIFY hDevNotify;
|
||||
|
||||
rdpdr = (rdpdrPlugin *)arg;
|
||||
first_hotplug(rdpdr);
|
||||
|
||||
/* init windows class */
|
||||
wnd_cls.cbSize = sizeof(WNDCLASSEX);
|
||||
|
@ -275,6 +314,239 @@ static UINT drive_hotplug_thread_terminate(rdpdrPlugin* rdpdr)
|
|||
return error;
|
||||
}
|
||||
|
||||
#elif __MACOSX__
|
||||
|
||||
#define MAX_USB_DEVICES 100
|
||||
|
||||
typedef struct _hotplug_dev
|
||||
{
|
||||
char* path;
|
||||
BOOL to_add;
|
||||
} hotplug_dev;
|
||||
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT handle_hotplug(rdpdrPlugin* rdpdr)
|
||||
{
|
||||
struct dirent *pDirent;
|
||||
DIR *pDir;
|
||||
char fullpath[PATH_MAX];
|
||||
char* szdir = (char*)"/Volumes";
|
||||
struct stat buf;
|
||||
hotplug_dev dev_array[MAX_USB_DEVICES];
|
||||
int count;
|
||||
DEVICE_DRIVE_EXT *device_ext;
|
||||
ULONG_PTR *keys;
|
||||
int i, j;
|
||||
int size = 0;
|
||||
UINT error;
|
||||
UINT32 ids[1];
|
||||
|
||||
pDir = opendir (szdir);
|
||||
if (pDir == NULL)
|
||||
{
|
||||
printf ("Cannot open directory\n");
|
||||
return ERROR_OPEN_FAILED;
|
||||
}
|
||||
|
||||
while ((pDirent = readdir(pDir)) != NULL)
|
||||
{
|
||||
if (pDirent->d_name[0] != '.')
|
||||
{
|
||||
sprintf(fullpath, "%s/%s", szdir, pDirent->d_name);
|
||||
lstat(fullpath, &buf);
|
||||
if(S_ISDIR(buf.st_mode))
|
||||
{
|
||||
dev_array[size].path = _strdup(fullpath);
|
||||
if (!dev_array[size].path)
|
||||
{
|
||||
closedir (pDir);
|
||||
error = CHANNEL_RC_NO_MEMORY;
|
||||
goto cleanup;
|
||||
}
|
||||
dev_array[size++].to_add = TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
closedir (pDir);
|
||||
|
||||
/* delete removed devices */
|
||||
count = ListDictionary_GetKeys(rdpdr->devman->devices, &keys);
|
||||
|
||||
for (j = 0; j < count; j++)
|
||||
{
|
||||
BOOL dev_found = FALSE;
|
||||
|
||||
device_ext = (DEVICE_DRIVE_EXT *)ListDictionary_GetItemValue(rdpdr->devman->devices, (void *)keys[j]);
|
||||
if (!device_ext)
|
||||
continue;
|
||||
|
||||
if (device_ext->path == NULL)
|
||||
continue;
|
||||
|
||||
/* not plugable device */
|
||||
if (strstr(device_ext->path, "/Volumes/") == NULL)
|
||||
continue;
|
||||
|
||||
for (i = 0; i < size; i++)
|
||||
{
|
||||
if (strstr(device_ext->path, dev_array[i].path) != NULL)
|
||||
{
|
||||
dev_found = TRUE;
|
||||
dev_array[i].to_add = FALSE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!dev_found)
|
||||
{
|
||||
devman_unregister_device(rdpdr->devman, (void *)keys[j]);
|
||||
ids[0] = keys[j];
|
||||
if ((error = rdpdr_send_device_list_remove_request(rdpdr, 1, ids)))
|
||||
{
|
||||
WLog_ERR(TAG, "rdpdr_send_device_list_remove_request failed with error %lu!", error);
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* add new devices */
|
||||
for (i = 0; i < size; i++)
|
||||
{
|
||||
RDPDR_DRIVE* drive;
|
||||
|
||||
if (dev_array[i].to_add)
|
||||
{
|
||||
char* name;
|
||||
|
||||
drive = (RDPDR_DRIVE*) calloc(1, sizeof(RDPDR_DRIVE));
|
||||
if (!drive)
|
||||
{
|
||||
WLog_ERR(TAG, "calloc failed!");
|
||||
error = CHANNEL_RC_NO_MEMORY;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
drive->Type = RDPDR_DTYP_FILESYSTEM;
|
||||
|
||||
drive->Path = dev_array[i].path;
|
||||
dev_array[i].path = NULL;
|
||||
|
||||
name = strrchr(drive->Path, '/') + 1;
|
||||
drive->Name = _strdup(name);
|
||||
if (!drive->Name)
|
||||
{
|
||||
WLog_ERR(TAG, "_strdup failed!");
|
||||
free(drive->Path);
|
||||
free(drive);
|
||||
error = CHANNEL_RC_NO_MEMORY;
|
||||
goto cleanup;
|
||||
}
|
||||
if ((error = devman_load_device_service(rdpdr->devman, (RDPDR_DEVICE *)drive, rdpdr->rdpcontext)))
|
||||
{
|
||||
WLog_ERR(TAG, "devman_load_device_service failed!");
|
||||
free(drive->Path);
|
||||
free(drive->Name);
|
||||
free(drive);
|
||||
error = CHANNEL_RC_NO_MEMORY;
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cleanup:
|
||||
for (i = 0; i < size; i++)
|
||||
free (dev_array[i].path);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
|
||||
static void drive_hotplug_fsevent_callback(ConstFSEventStreamRef streamRef, void *clientCallBackInfo,
|
||||
size_t numEvents, void *eventPaths, const FSEventStreamEventFlags eventFlags[],
|
||||
const FSEventStreamEventId eventIds[])
|
||||
{
|
||||
rdpdrPlugin* rdpdr;
|
||||
int i;
|
||||
UINT error;
|
||||
char **paths = (char**)eventPaths;
|
||||
|
||||
rdpdr = (rdpdrPlugin*) clientCallBackInfo;
|
||||
|
||||
for (i=0; i<numEvents; i++)
|
||||
{
|
||||
if (strcmp(paths[i], "/Volumes/") == 0)
|
||||
{
|
||||
if ((error = handle_hotplug(rdpdr)))
|
||||
{
|
||||
WLog_ERR(TAG, "handle_hotplug failed with error %lu!", error);
|
||||
}
|
||||
else
|
||||
rdpdr_send_device_list_announce_request(rdpdr, TRUE);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void* drive_hotplug_thread_func(void* arg)
|
||||
{
|
||||
rdpdrPlugin* rdpdr;
|
||||
FSEventStreamRef fsev;
|
||||
UINT error;
|
||||
|
||||
rdpdr = (rdpdrPlugin*) arg;
|
||||
CFStringRef path = CFSTR("/Volumes/");
|
||||
CFArrayRef pathsToWatch = CFArrayCreate(kCFAllocatorMalloc, (const void**)&path, 1, NULL);
|
||||
FSEventStreamContext ctx;
|
||||
ZeroMemory(&ctx, sizeof(ctx));
|
||||
ctx.info = arg;
|
||||
fsev = FSEventStreamCreate(kCFAllocatorMalloc, drive_hotplug_fsevent_callback, &ctx, pathsToWatch, kFSEventStreamEventIdSinceNow, 1, kFSEventStreamCreateFlagNone);
|
||||
|
||||
if ((error = handle_hotplug(rdpdr)))
|
||||
{
|
||||
WLog_ERR(TAG, "handle_hotplug failed with error %lu!", error);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
rdpdr->runLoop = CFRunLoopGetCurrent();
|
||||
FSEventStreamScheduleWithRunLoop(fsev, rdpdr->runLoop, kCFRunLoopDefaultMode);
|
||||
FSEventStreamStart(fsev);
|
||||
CFRunLoopRun();
|
||||
FSEventStreamStop(fsev);
|
||||
FSEventStreamRelease(fsev);
|
||||
|
||||
ExitThread(CHANNEL_RC_OK);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT drive_hotplug_thread_terminate(rdpdrPlugin* rdpdr)
|
||||
{
|
||||
UINT error;
|
||||
if (rdpdr->hotplugThread)
|
||||
{
|
||||
CFRunLoopStop(rdpdr->runLoop);
|
||||
|
||||
if (WaitForSingleObject(rdpdr->hotplugThread, INFINITE) == WAIT_FAILED)
|
||||
{
|
||||
error = GetLastError();
|
||||
WLog_ERR(TAG, "WaitForSingleObject failed with error %lu!", error);
|
||||
return error;
|
||||
}
|
||||
rdpdr->hotplugThread = NULL;
|
||||
}
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#define MAX_USB_DEVICES 100
|
||||
|
@ -506,7 +778,7 @@ cleanup:
|
|||
for (i = 0; i < size; i++)
|
||||
free (dev_array[i].path);
|
||||
|
||||
return error ? error : rdpdr_send_device_list_announce_request(rdpdr, TRUE);
|
||||
return error;
|
||||
}
|
||||
|
||||
static void* drive_hotplug_thread_func(void* arg)
|
||||
|
@ -568,6 +840,8 @@ static void* drive_hotplug_thread_func(void* arg)
|
|||
WLog_ERR(TAG, "handle_hotplug failed with error %lu!", error);
|
||||
goto out;
|
||||
}
|
||||
else
|
||||
rdpdr_send_device_list_announce_request(rdpdr, TRUE);
|
||||
}
|
||||
|
||||
FD_ZERO(&rfds);
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
* Copyright 2010-2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
|
||||
* Copyright 2015 Thincast Technologies GmbH
|
||||
* Copyright 2015 DI (FH) Martin Haimberger <martin.haimberger@thincast.com>
|
||||
* Copyright 2016 Inuvika Inc.
|
||||
* Copyright 2016 David PHAM-VAN <d.phamvan@inuvika.com>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -36,6 +38,10 @@
|
|||
#include <freerdp/channels/rdpdr.h>
|
||||
#include <freerdp/channels/log.h>
|
||||
|
||||
#ifdef __MACOSX__
|
||||
#include <CoreServices/CoreServices.h>
|
||||
#endif
|
||||
|
||||
#define TAG CHANNELS_TAG("rdpdr.client")
|
||||
|
||||
typedef struct rdpdr_plugin rdpdrPlugin;
|
||||
|
@ -64,6 +70,8 @@ struct rdpdr_plugin
|
|||
HANDLE hotplugThread;
|
||||
#ifdef _WIN32
|
||||
HWND hotplug_wnd;
|
||||
#elif __MACOSX__
|
||||
CFRunLoopRef runLoop;
|
||||
#else
|
||||
HANDLE stopEvent;
|
||||
#endif
|
||||
|
|
Loading…
Reference in New Issue