[channels,rdpdr] expose device add/remove/hotplug

* Add RdpdrClientContext to OnChannelConnectedEventHandler
* Expose device register/unregister in RdpdrClientContext
* Expose device hotplug poll function in RdpdrClientContext
This commit is contained in:
Armin Novak 2025-05-07 15:56:24 +02:00 committed by akallabeth
parent 2cc81d1c44
commit f5924a6556
No known key found for this signature in database
GPG Key ID: A49454A3FC909FD5
4 changed files with 287 additions and 95 deletions

View File

@ -1029,7 +1029,7 @@ static UINT drive_register_drive_path(PDEVICE_SERVICE_ENTRY_POINTS pEntryPoints,
WINPR_ASSERT(obj); WINPR_ASSERT(obj);
obj->fnObjectFree = drive_message_free; obj->fnObjectFree = drive_message_free;
if ((error = pEntryPoints->RegisterDevice(pEntryPoints->devman, (DEVICE*)drive))) if ((error = pEntryPoints->RegisterDevice(pEntryPoints->devman, &drive->device)))
{ {
WLog_ERR(TAG, "RegisterDevice failed with error %" PRIu32 "!", error); WLog_ERR(TAG, "RegisterDevice failed with error %" PRIu32 "!", error);
goto out_error; goto out_error;

View File

@ -218,7 +218,8 @@ static BOOL rdpdr_load_drive(rdpdrPlugin* rdpdr, const char* name, const char* p
if (!drive.device) if (!drive.device)
goto fail; goto fail;
rc = devman_load_device_service(rdpdr->devman, drive.device, rdpdr->rdpcontext); WINPR_ASSERT(rdpdr->context.RdpdrRegisterDevice);
rc = rdpdr->context.RdpdrRegisterDevice(&rdpdr->context, drive.device, &drive.device->Id);
if (rc != CHANNEL_RC_OK) if (rc != CHANNEL_RC_OK)
goto fail; goto fail;
@ -232,7 +233,8 @@ fail:
* *
* @return 0 on success, otherwise a Win32 error code * @return 0 on success, otherwise a Win32 error code
*/ */
static UINT rdpdr_send_device_list_remove_request(rdpdrPlugin* rdpdr, UINT32 count, UINT32 ids[]) static UINT rdpdr_send_device_list_remove_request(rdpdrPlugin* rdpdr, UINT32 count,
const UINT32 ids[])
{ {
wStream* s = NULL; wStream* s = NULL;
@ -266,22 +268,34 @@ static UINT rdpdr_send_device_list_remove_request(rdpdrPlugin* rdpdr, UINT32 cou
#if defined(_UWP) || defined(__IOS__) #if defined(_UWP) || defined(__IOS__)
static void first_hotplug(rdpdrPlugin* rdpdr) static UINT handle_hotplug(WINPR_ATTR_UNUSED RdpdrClientContext* context,
WINPR_ATTR_UNUSED RdpdrHotplugEventType type)
{
return ERROR_CALL_NOT_IMPLEMENTED;
}
static void first_hotplug(WINPR_ATTR_UNUSED rdpdrPlugin* rdpdr)
{ {
} }
static DWORD WINAPI drive_hotplug_thread_func(LPVOID arg) static DWORD WINAPI drive_hotplug_thread_func(WINPR_ATTR_UNUSED LPVOID arg)
{ {
return CHANNEL_RC_OK; return CHANNEL_RC_OK;
} }
static UINT drive_hotplug_thread_terminate(rdpdrPlugin* rdpdr) static UINT drive_hotplug_thread_terminate(WINPR_ATTR_UNUSED rdpdrPlugin* rdpdr)
{ {
return CHANNEL_RC_OK; return CHANNEL_RC_OK;
} }
#elif defined(_WIN32) #elif defined(_WIN32)
static UINT handle_hotplug(WINPR_ATTR_UNUSED RdpdrClientContext* context,
WINPR_ATTR_UNUSED RdpdrHotplugEventType type)
{
return CHANNEL_RC_OK;
}
static BOOL check_path(const char* path) static BOOL check_path(const char* path)
{ {
UINT type = GetDriveTypeA(path); UINT type = GetDriveTypeA(path);
@ -346,7 +360,6 @@ static LRESULT CALLBACK hotplug_proc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM
if (check_path(drive_path)) if (check_path(drive_path))
{ {
rdpdr_load_drive(rdpdr, drive_name, drive_path, TRUE); rdpdr_load_drive(rdpdr, drive_name, drive_path, TRUE);
rdpdr_try_send_device_list_announce_request(rdpdr);
} }
} }
@ -388,12 +401,11 @@ static LRESULT CALLBACK hotplug_proc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM
{ {
if (device_ext->automount) if (device_ext->automount)
{ {
ULONG_PTR key = keys[j]; const uint32_t ids[] = { keys[j] };
devman_unregister_device(rdpdr->devman, (void*)key); WINPR_ASSERT(rdpdr->context.RdpdrUnregisterDevice);
ids[0] = WINPR_ASSERTING_INT_CAST(uint32_t, key); error = rdpdr->context.RdpdrUnregisterDevice(
&rdpdr->context, ARRAYSIZE(ids), ids);
if ((error = rdpdr_send_device_list_remove_request( if (error)
rdpdr, 1, ids)))
{ {
// don't end on error, just report ? // don't end on error, just report ?
WLog_Print( WLog_Print(
@ -514,8 +526,12 @@ typedef struct
* *
* @return 0 on success, otherwise a Win32 error code * @return 0 on success, otherwise a Win32 error code
*/ */
static UINT handle_hotplug(rdpdrPlugin* rdpdr) static UINT handle_hotplug(WINPR_ATTR_UNUSED RdpdrClientContext* context,
WINPR_ATTR_UNUSED RdpdrHotplugEventType type)
{ {
WINPR_ASSERT(context);
rdpdrPlugin* rdpdr = context->handle;
struct dirent* pDirent = NULL; struct dirent* pDirent = NULL;
char fullpath[PATH_MAX] = { 0 }; char fullpath[PATH_MAX] = { 0 };
char* szdir = (char*)"/Volumes"; char* szdir = (char*)"/Volumes";
@ -605,10 +621,10 @@ static UINT handle_hotplug(rdpdrPlugin* rdpdr)
if (!dev_found) if (!dev_found)
{ {
devman_unregister_device(rdpdr->devman, (void*)keys[j]); const uint32_t ids[] = { keys[j] };
ids[0] = keys[j]; WINPR_ASSERT(rdpdr->context.RdpdrUnregisterDevice);
error = rdpdr->context.RdpdrUnregisterDevice(&rdpdr->context, ARRAYSIZE(ids), ids);
if ((error = rdpdr_send_device_list_remove_request(rdpdr, 1, ids))) if (error)
{ {
WLog_Print(rdpdr->log, WLOG_ERROR, WLog_Print(rdpdr->log, WLOG_ERROR,
"rdpdr_send_device_list_remove_request failed with error %" PRIu32 "!", "rdpdr_send_device_list_remove_request failed with error %" PRIu32 "!",
@ -656,26 +672,43 @@ static void drive_hotplug_fsevent_callback(ConstFSEventStreamRef streamRef,
{ {
if (strcmp(paths[i], "/Volumes/") == 0) if (strcmp(paths[i], "/Volumes/") == 0)
{ {
if ((error = handle_hotplug(rdpdr))) UINT error = ERROR_CALL_NOT_IMPLEMENTED;
if (rdpdr->context.RdpdrHotplugDevice)
error = rdpdr->context.RdpdrHotplugDevice(&rdpdr->context,
RDPDR_HOTPLUG_CHECK_FOR_CHANGES);
switch (error)
{ {
WLog_Print(rdpdr->log, WLOG_ERROR, "handle_hotplug failed with error %" PRIu32 "!", case ERROR_DISK_CHANGE:
error); case CHANNEL_RC_OK:
break;
case ERROR_CALL_NOT_IMPLEMENTED:
break;
default:
WLog_Print(rdpdr->log, WLOG_ERROR,
"handle_hotplug failed with error %" PRIu32 "!", error);
break;
} }
else
rdpdr_try_send_device_list_announce_request(rdpdr);
return;
} }
} }
} }
static void first_hotplug(rdpdrPlugin* rdpdr) static void first_hotplug(rdpdrPlugin* rdpdr)
{ {
UINT error; WINPR_ASSERT(rdpdr);
UINT error = ERROR_CALL_NOT_IMPLEMENTED;
if (rdpdr->context.RdpdrHotplugDevice)
error = rdpdr->context.RdpdrHotplugDevice(&rdpdr->context, RDPDR_HOTPLUG_FIRST_CHECK);
if ((error = handle_hotplug(rdpdr))) switch (error)
{ {
WLog_Print(rdpdr->log, WLOG_ERROR, "handle_hotplug failed with error %" PRIu32 "!", error); case ERROR_DISK_CHANGE:
case CHANNEL_RC_OK:
case ERROR_CALL_NOT_IMPLEMENTED:
break;
default:
WLog_Print(rdpdr->log, WLOG_ERROR, "handle_hotplug failed with error %" PRIu32 "!",
error);
break;
} }
} }
@ -964,14 +997,11 @@ static BOOL hotplug_delete_foreach(ULONG_PTR key, void* element, void* data)
if (!dev_found) if (!dev_found)
{ {
UINT error = 0; const UINT32 ids[1] = { (UINT32)key };
UINT32 ids[1] = { (UINT32)key }; WINPR_ASSERT(arg->rdpdr->context.RdpdrUnregisterDevice);
const UINT error =
arg->rdpdr->context.RdpdrUnregisterDevice(&arg->rdpdr->context, ARRAYSIZE(ids), ids);
WINPR_ASSERT(arg->rdpdr->devman);
devman_unregister_device(arg->rdpdr->devman, (void*)key);
WINPR_ASSERT(key <= UINT32_MAX);
error = rdpdr_send_device_list_remove_request(arg->rdpdr, 1, ids);
if (error) if (error)
{ {
WLog_Print(arg->rdpdr->log, WLOG_ERROR, WLog_Print(arg->rdpdr->log, WLOG_ERROR,
@ -984,8 +1014,11 @@ static BOOL hotplug_delete_foreach(ULONG_PTR key, void* element, void* data)
return TRUE; return TRUE;
} }
static UINT handle_hotplug(rdpdrPlugin* rdpdr) static UINT handle_hotplug(RdpdrClientContext* context, RdpdrHotplugEventType type)
{ {
WINPR_ASSERT(context);
rdpdrPlugin* rdpdr = context->handle;
hotplug_dev dev_array[MAX_USB_DEVICES] = { 0 }; hotplug_dev dev_array[MAX_USB_DEVICES] = { 0 };
size_t size = 0; size_t size = 0;
UINT error = ERROR_SUCCESS; UINT error = ERROR_SUCCESS;
@ -1021,42 +1054,42 @@ static UINT handle_hotplug(rdpdrPlugin* rdpdr)
static void first_hotplug(rdpdrPlugin* rdpdr) static void first_hotplug(rdpdrPlugin* rdpdr)
{ {
UINT error = 0; UINT error = ERROR_CALL_NOT_IMPLEMENTED;
WINPR_ASSERT(rdpdr); WINPR_ASSERT(rdpdr);
if ((error = handle_hotplug(rdpdr))) if (rdpdr->context.RdpdrHotplugDevice)
error = rdpdr->context.RdpdrHotplugDevice(&rdpdr->context, RDPDR_HOTPLUG_FIRST_CHECK);
switch (error)
{ {
switch (error) case ERROR_DISK_CHANGE:
{ case CHANNEL_RC_OK:
case ERROR_DISK_CHANGE: case ERROR_OPEN_FAILED:
case CHANNEL_RC_OK: case ERROR_CALL_NOT_IMPLEMENTED:
case ERROR_OPEN_FAILED: break;
case ERROR_CALL_NOT_IMPLEMENTED: default:
break; WLog_Print(rdpdr->log, WLOG_ERROR, "handle_hotplug failed with error %" PRIu32 "!",
default: error);
WLog_Print(rdpdr->log, WLOG_ERROR, "handle_hotplug failed with error %" PRIu32 "!", break;
error);
break;
}
} }
} }
static DWORD WINAPI drive_hotplug_thread_func(LPVOID arg) static DWORD WINAPI drive_hotplug_thread_func(LPVOID arg)
{ {
rdpdrPlugin* rdpdr = NULL; rdpdrPlugin* rdpdr = (rdpdrPlugin*)arg;
UINT error = 0;
rdpdr = (rdpdrPlugin*)arg;
WINPR_ASSERT(rdpdr); WINPR_ASSERT(rdpdr);
WINPR_ASSERT(rdpdr->stopEvent); WINPR_ASSERT(rdpdr->stopEvent);
while (WaitForSingleObject(rdpdr->stopEvent, 1000) == WAIT_TIMEOUT) while (WaitForSingleObject(rdpdr->stopEvent, 1000) == WAIT_TIMEOUT)
{ {
error = handle_hotplug(rdpdr); UINT error = ERROR_CALL_NOT_IMPLEMENTED;
if (rdpdr->context.RdpdrHotplugDevice)
error =
rdpdr->context.RdpdrHotplugDevice(&rdpdr->context, RDPDR_HOTPLUG_CHECK_FOR_CHANGES);
switch (error) switch (error)
{ {
case ERROR_DISK_CHANGE: case ERROR_DISK_CHANGE:
rdpdr_try_send_device_list_announce_request(rdpdr);
break; break;
case CHANNEL_RC_OK: case CHANNEL_RC_OK:
case ERROR_OPEN_FAILED: case ERROR_OPEN_FAILED:
@ -1070,13 +1103,15 @@ static DWORD WINAPI drive_hotplug_thread_func(LPVOID arg)
} }
out: out:
error = GetLastError(); {
const UINT error = GetLastError();
if (error && rdpdr->rdpcontext) if (error && rdpdr->rdpcontext)
setChannelError(rdpdr->rdpcontext, error, "reported an error"); setChannelError(rdpdr->rdpcontext, error, "reported an error");
ExitThread(error); ExitThread(error);
return error; return error;
} }
}
#endif #endif
@ -1116,44 +1151,14 @@ static UINT drive_hotplug_thread_terminate(rdpdrPlugin* rdpdr)
#endif #endif
/** static UINT rdpdr_add_devices(rdpdrPlugin* rdpdr)
* Function description
*
* @return 0 on success, otherwise a Win32 error code
*/
static UINT rdpdr_process_connect(rdpdrPlugin* rdpdr)
{ {
UINT error = CHANNEL_RC_OK;
WINPR_ASSERT(rdpdr); WINPR_ASSERT(rdpdr);
rdpdr->devman = devman_new(rdpdr);
if (!rdpdr->devman)
{
WLog_Print(rdpdr->log, WLOG_ERROR, "devman_new failed!");
return CHANNEL_RC_NO_MEMORY;
}
WINPR_ASSERT(rdpdr->rdpcontext); WINPR_ASSERT(rdpdr->rdpcontext);
rdpSettings* settings = rdpdr->rdpcontext->settings; rdpSettings* settings = rdpdr->rdpcontext->settings;
WINPR_ASSERT(settings); WINPR_ASSERT(settings);
rdpdr->ignoreInvalidDevices = freerdp_settings_get_bool(settings, FreeRDP_IgnoreInvalidDevices);
const char* name = freerdp_settings_get_string(settings, FreeRDP_ClientHostname);
if (!name)
name = freerdp_settings_get_string(settings, FreeRDP_ComputerName);
if (!name)
{
DWORD size = ARRAYSIZE(rdpdr->computerName);
if (!GetComputerNameExA(ComputerNameNetBIOS, rdpdr->computerName, &size))
return ERROR_INTERNAL_ERROR;
}
else
strncpy(rdpdr->computerName, name, strnlen(name, sizeof(rdpdr->computerName)));
for (UINT32 index = 0; index < freerdp_settings_get_uint32(settings, FreeRDP_DeviceCount); for (UINT32 index = 0; index < freerdp_settings_get_uint32(settings, FreeRDP_DeviceCount);
index++) index++)
{ {
@ -1202,15 +1207,54 @@ static UINT rdpdr_process_connect(rdpdrPlugin* rdpdr)
} }
} }
if ((error = devman_load_device_service(rdpdr->devman, device, rdpdr->rdpcontext))) const UINT error = devman_load_device_service(rdpdr->devman, device, rdpdr->rdpcontext);
if (error)
{ {
WLog_Print(rdpdr->log, WLOG_ERROR, WLog_Print(rdpdr->log, WLOG_ERROR,
"devman_load_device_service failed with error %" PRIu32 "!", error); "devman_load_device_service failed with error %" PRIu32 "!", error);
return error; return error;
} }
} }
return CHANNEL_RC_OK;
}
return error; /**
* Function description
*
* @return 0 on success, otherwise a Win32 error code
*/
static UINT rdpdr_process_connect(rdpdrPlugin* rdpdr)
{
WINPR_ASSERT(rdpdr);
rdpdr->devman = devman_new(rdpdr);
if (!rdpdr->devman)
{
WLog_Print(rdpdr->log, WLOG_ERROR, "devman_new failed!");
return CHANNEL_RC_NO_MEMORY;
}
WINPR_ASSERT(rdpdr->rdpcontext);
rdpSettings* settings = rdpdr->rdpcontext->settings;
WINPR_ASSERT(settings);
rdpdr->ignoreInvalidDevices = freerdp_settings_get_bool(settings, FreeRDP_IgnoreInvalidDevices);
const char* name = freerdp_settings_get_string(settings, FreeRDP_ClientHostname);
if (!name)
name = freerdp_settings_get_string(settings, FreeRDP_ComputerName);
if (!name)
{
DWORD size = ARRAYSIZE(rdpdr->computerName);
if (!GetComputerNameExA(ComputerNameNetBIOS, rdpdr->computerName, &size))
return ERROR_INTERNAL_ERROR;
}
else
strncpy(rdpdr->computerName, name, strnlen(name, sizeof(rdpdr->computerName)));
return rdpdr_add_devices(rdpdr);
} }
static UINT rdpdr_process_server_announce_request(rdpdrPlugin* rdpdr, wStream* s) static UINT rdpdr_process_server_announce_request(rdpdrPlugin* rdpdr, wStream* s)
@ -2265,6 +2309,59 @@ static void rdpdr_virtual_channel_event_terminated(rdpdrPlugin* rdpdr)
free(rdpdr); free(rdpdr);
} }
static UINT rdpdr_register_device(RdpdrClientContext* context, const RDPDR_DEVICE* device,
uint32_t* pid)
{
WINPR_ASSERT(context);
WINPR_ASSERT(device);
WINPR_ASSERT(pid);
rdpdrPlugin* rdpdr = context->handle;
WINPR_ASSERT(rdpdr);
RDPDR_DEVICE* copy = freerdp_device_clone(device);
if (!copy)
return ERROR_INVALID_DATA;
UINT rc = devman_load_device_service(rdpdr->devman, copy, rdpdr->rdpcontext);
*pid = copy->Id;
if (rc == CHANNEL_RC_OK)
rc = rdpdr_try_send_device_list_announce_request(rdpdr);
return rc;
}
static UINT rdpdr_unregister_device(RdpdrClientContext* context, size_t count, const uint32_t ids[])
{
WINPR_ASSERT(context);
rdpdrPlugin* rdpdr = context->handle;
WINPR_ASSERT(rdpdr);
for (size_t x = 0; x < count; x++)
{
const uintptr_t id = ids[x];
devman_unregister_device(rdpdr->devman, (void*)id);
}
return rdpdr_send_device_list_remove_request(rdpdr, count, ids);
}
static UINT rdpdr_virtual_channel_event_initialized(rdpdrPlugin* rdpdr,
WINPR_ATTR_UNUSED LPVOID pData,
WINPR_ATTR_UNUSED UINT32 dataLength)
{
WINPR_ASSERT(rdpdr);
#if !defined(_WIN32)
WINPR_ASSERT(!rdpdr->stopEvent);
rdpdr->stopEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
WINPR_ASSERT(rdpdr->stopEvent);
#endif
rdpdr->context.handle = rdpdr;
rdpdr->context.RdpdrHotplugDevice = handle_hotplug;
rdpdr->context.RdpdrRegisterDevice = rdpdr_register_device;
rdpdr->context.RdpdrUnregisterDevice = rdpdr_unregister_device;
return CHANNEL_RC_OK;
}
static VOID VCAPITYPE rdpdr_virtual_channel_init_event_ex(LPVOID lpUserParam, LPVOID pInitHandle, static VOID VCAPITYPE rdpdr_virtual_channel_init_event_ex(LPVOID lpUserParam, LPVOID pInitHandle,
UINT event, LPVOID pData, UINT dataLength) UINT event, LPVOID pData, UINT dataLength)
{ {
@ -2282,11 +2379,7 @@ static VOID VCAPITYPE rdpdr_virtual_channel_init_event_ex(LPVOID lpUserParam, LP
switch (event) switch (event)
{ {
case CHANNEL_EVENT_INITIALIZED: case CHANNEL_EVENT_INITIALIZED:
#if !defined(_WIN32) error = rdpdr_virtual_channel_event_initialized(rdpdr, pData, dataLength);
WINPR_ASSERT(!rdpdr->stopEvent);
rdpdr->stopEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
WINPR_ASSERT(rdpdr->stopEvent);
#endif
break; break;
case CHANNEL_EVENT_CONNECTED: case CHANNEL_EVENT_CONNECTED:
@ -2383,7 +2476,7 @@ FREERDP_ENTRY_POINT(BOOL VCAPITYPE VirtualChannelEntryEx(PCHANNEL_ENTRY_POINTS p
CopyMemory(&(rdpdr->channelEntryPoints), pEntryPoints, sizeof(CHANNEL_ENTRY_POINTS_FREERDP_EX)); CopyMemory(&(rdpdr->channelEntryPoints), pEntryPoints, sizeof(CHANNEL_ENTRY_POINTS_FREERDP_EX));
rdpdr->InitHandle = pInitHandle; rdpdr->InitHandle = pInitHandle;
rc = rdpdr->channelEntryPoints.pVirtualChannelInitEx( rc = rdpdr->channelEntryPoints.pVirtualChannelInitEx(
rdpdr, NULL, pInitHandle, &rdpdr->channelDef, 1, VIRTUAL_CHANNEL_VERSION_WIN2000, rdpdr, &rdpdr->context, pInitHandle, &rdpdr->channelDef, 1, VIRTUAL_CHANNEL_VERSION_WIN2000,
rdpdr_virtual_channel_init_event_ex); rdpdr_virtual_channel_init_event_ex);
if (CHANNEL_RC_OK != rc) if (CHANNEL_RC_OK != rc)

View File

@ -36,6 +36,7 @@
#include <freerdp/addin.h> #include <freerdp/addin.h>
#include <freerdp/channels/rdpdr.h> #include <freerdp/channels/rdpdr.h>
#include <freerdp/client/rdpdr.h>
#include <freerdp/channels/log.h> #include <freerdp/channels/log.h>
#ifdef __MACOSX__ #ifdef __MACOSX__
@ -113,6 +114,8 @@ typedef struct
BOOL capabilities[6]; BOOL capabilities[6];
BOOL haveClientId; BOOL haveClientId;
BOOL haveServerCaps; BOOL haveServerCaps;
RdpdrClientContext context;
} rdpdrPlugin; } rdpdrPlugin;
BOOL rdpdr_state_advance(rdpdrPlugin* rdpdr, enum RDPDR_CHANNEL_STATE next); BOOL rdpdr_state_advance(rdpdrPlugin* rdpdr, enum RDPDR_CHANNEL_STATE next);

View File

@ -0,0 +1,96 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* Device Redirection Virtual Channel Extension
*
* Copyright 2025 Armin Novak <anovak@thincast.com>
* Copyright 2025 Thincast Technologies GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef FREERDP_CHANNEL_RDPDR_CLIENT_H
#define FREERDP_CHANNEL_RDPDR_CLIENT_H
#include <freerdp/channels/rdpdr.h>
#ifdef __cplusplus
extern "C"
{
#endif
/** @enum Hotplug event types.
*
* @since version 3.16.0
*/
typedef enum
{
RDPDR_HOTPLUG_FIRST_CHECK,
RDPDR_HOTPLUG_CHECK_FOR_CHANGES
} RdpdrHotplugEventType;
typedef struct s_rdpdr_client_context RdpdrClientContext;
/** @brief register a new device and announce it to remote
*
* @param context The \ref RdpdrClientContext to operate on.
* @param device A pointer to a \ref RDPDR_DEVICE struct to register. Contents is copied.
* @param pid A pointer to a variable that will be set to a unique identifier for that device.
*
* @return \ref CHANNEL_RC_OK for success or an appropriate error code otherwise.
*
* @since version 3.16.0
*/
typedef UINT (*pcRdpdrRegisterDevice)(RdpdrClientContext* context, const RDPDR_DEVICE* device,
uint32_t* pid);
/** @brief unregister a new device and announce it to remote
*
* @param context The \ref RdpdrClientContext to operate on.
* @param count The number of uintptr_t id unique identifiers for a device (see \ref
* pcRdpdrRegisterDevice) following
*
* @return \ref CHANNEL_RC_OK for success or an appropriate error code otherwise.
* @since version 3.16.0
*/
typedef UINT (*pcRdpdrUnregisterDevice)(RdpdrClientContext* context, size_t count,
const uint32_t ids[]);
/** @brief Check for device changes and announce it to remote
*
* @param context The \ref RdpdrClientContext to operate on.
* @param type The event type.
*
* @return \ref CHANNEL_RC_OK for success or an appropriate error code otherwise.
* @since version 3.16.0
*/
typedef UINT (*pcRdpdrHotplugDevice)(RdpdrClientContext* context, RdpdrHotplugEventType type);
/** @struct rdpdr channel client context
*
* @since version 3.16.0
*/
struct s_rdpdr_client_context
{
void* handle;
void* custom;
pcRdpdrRegisterDevice RdpdrRegisterDevice;
pcRdpdrUnregisterDevice RdpdrUnregisterDevice;
pcRdpdrHotplugDevice RdpdrHotplugDevice;
};
#ifdef __cplusplus
}
#endif
#endif /* FREERDP_CHANNEL_RDPDR_CLIENT_H */