Merge pull request #10663 from akallabeth/client-channel-event-process-api

[core,client] add channel poll registration API
This commit is contained in:
akallabeth 2024-09-24 11:05:09 +02:00 committed by GitHub
commit fcdbc05979
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 164 additions and 17 deletions

View File

@ -33,6 +33,9 @@ extern "C"
{
#endif
/** @since version 3.9.0 */
typedef BOOL (*freerdp_channel_handle_fkt_t)(rdpContext* context, void* userdata);
FREERDP_API int freerdp_channels_client_load(rdpChannels* channels, rdpSettings* settings,
PVIRTUALCHANNELENTRY entry, void* data);
FREERDP_API int freerdp_channels_client_load_ex(rdpChannels* channels, rdpSettings* settings,
@ -50,6 +53,40 @@ extern "C"
FREERDP_API void* freerdp_channels_get_static_channel_interface(rdpChannels* channels,
const char* name);
/** @brief A channel may register an event handle and a callback function to be called by \b
* freerdp_check_event_handles
*
* If a channel can not or does not want to use a thread to process data asynchronously it can
* register a \b non-blocking function that does this processing. Notification is done with the
* \b event-handle which will trigger the RDP loop. So this function is triggered until the \b
* event-handle is reset.
* @note This function may be called even without the \b event-handle to be set, so it must be
* capable of handling these calls properly.
*
* @param channels A pointer to the channels instance to register with. Must not be \b NULL
* @param handle A \b event-handle to be used to notify the RDP main thread that the callback
* function should be called again. Must not be \b INVALID_HANDLE_PARAM
* @param fkt The callback function responsible to handle the channel specifics. Must not be \b
* NULL
* @param userdata A pointer to a channel specific context. Most likely the channel context.
* May be \b NULL if not required.
*
* @return \b TRUE if successful, \b FALSE if any error occurs.
* @since version 3.9.0 */
FREERDP_API BOOL freerdp_client_channel_register(rdpChannels* channels, HANDLE handle,
freerdp_channel_handle_fkt_t fkt,
void* userdata);
/** @brief Remove an existing registration for \b event-handle from the channels instance
*
* @param channels A pointer to the channels instance to register with. Must not be \b NULL
* @param handle A \b event-handle to be used to notify the RDP main thread that the callback
* function should be called again. Must not be \b INVALID_HANDLE_PARAM
*
* @return \b TRUE if successful, \b FALSE if any error occurs.
* @since version 3.9.0 */
FREERDP_API BOOL freerdp_client_channel_unregister(rdpChannels* channels, HANDLE handle);
FREERDP_API HANDLE freerdp_channels_get_event_handle(freerdp* instance);
FREERDP_API int freerdp_channels_process_pending_messages(freerdp* instance);

View File

@ -33,6 +33,12 @@
#define TAG FREERDP_TAG("core.client")
typedef struct
{
freerdp_channel_handle_fkt_t fkt;
void* userdata;
} ChannelEventEntry;
/* Use this instance to get access to channels in VirtualChannelInit. It is set during
* freerdp_connect so channels that use VirtualChannelInit must be initialized from the same thread
* as freerdp_connect was called */
@ -131,6 +137,19 @@ static BOOL CALLBACK init_channel_handles_table(PINIT_ONCE once, PVOID param, PV
return TRUE;
}
static void* channel_event_entry_clone(const void* data)
{
const ChannelEventEntry* entry = data;
if (!entry)
return NULL;
ChannelEventEntry* copy = calloc(1, sizeof(ChannelEventEntry));
if (!copy)
return NULL;
*copy = *entry;
return copy;
}
rdpChannels* freerdp_channels_new(freerdp* instance)
{
wObject* obj = NULL;
@ -156,6 +175,14 @@ rdpChannels* freerdp_channels_new(freerdp* instance)
obj = MessageQueue_Object(channels->queue);
obj->fnObjectFree = channel_queue_free;
channels->channelEvents = HashTable_New(FALSE);
if (!channels->channelEvents)
goto error;
obj = HashTable_ValueObject(channels->channelEvents);
WINPR_ASSERT(obj);
obj->fnObjectFree = free;
obj->fnObjectNew = channel_event_entry_clone;
return channels;
error:
WINPR_PRAGMA_DIAG_PUSH
@ -170,13 +197,10 @@ void freerdp_channels_free(rdpChannels* channels)
if (!channels)
return;
DeleteCriticalSection(&channels->channelsLock);
HashTable_Free(channels->channelEvents);
MessageQueue_Free(channels->queue);
if (channels->queue)
{
MessageQueue_Free(channels->queue);
channels->queue = NULL;
}
DeleteCriticalSection(&channels->channelsLock);
free(channels);
}
@ -706,24 +730,45 @@ void* freerdp_channels_get_static_channel_interface(rdpChannels* channels, const
HANDLE freerdp_channels_get_event_handle(freerdp* instance)
{
HANDLE event = NULL;
rdpChannels* channels = NULL;
channels = instance->context->channels;
event = MessageQueue_Event(channels->queue);
return event;
WINPR_ASSERT(instance);
WINPR_ASSERT(instance->context);
rdpChannels* channels = instance->context->channels;
WINPR_ASSERT(channels);
return MessageQueue_Event(channels->queue);
}
static BOOL channels_process(const void* key, void* value, void* arg)
{
ChannelEventEntry* entry = value;
rdpContext* context = arg;
WINPR_UNUSED(key);
if (!entry->fkt)
return FALSE;
return entry->fkt(context, entry->userdata);
}
int freerdp_channels_process_pending_messages(freerdp* instance)
{
rdpChannels* channels = NULL;
channels = instance->context->channels;
WINPR_ASSERT(instance);
WINPR_ASSERT(instance->context);
rdpChannels* channels = instance->context->channels;
WINPR_ASSERT(channels);
if (WaitForSingleObject(MessageQueue_Event(channels->queue), 0) == WAIT_OBJECT_0)
{
return freerdp_channels_process_sync(channels, instance);
if (!freerdp_channels_process_sync(channels, instance))
return -1;
}
return TRUE;
if (!HashTable_Foreach(channels->channelEvents, channels_process, instance->context))
return -1;
return 1;
}
/**
@ -743,6 +788,60 @@ BOOL freerdp_channels_check_fds(rdpChannels* channels, freerdp* instance)
return status;
}
BOOL freerdp_client_channel_register(rdpChannels* channels, HANDLE handle,
freerdp_channel_handle_fkt_t fkt, void* userdata)
{
if (!channels || (handle == INVALID_HANDLE_VALUE) || !fkt)
{
WLog_ERR(TAG, "Invalid function arguments (channels=%p, handle=%p, fkt=%p, userdata=%p",
channels, handle, fkt, userdata);
return FALSE;
}
ChannelEventEntry entry = { .fkt = fkt, .userdata = userdata };
return HashTable_Insert(channels->channelEvents, handle, &entry);
}
BOOL freerdp_client_channel_unregister(rdpChannels* channels, HANDLE handle)
{
if (!channels || (handle == INVALID_HANDLE_VALUE))
{
WLog_ERR(TAG, "Invalid function arguments (channels=%p, handle=%p", channels, handle);
return FALSE;
}
return HashTable_Remove(channels->channelEvents, handle);
}
SSIZE_T freerdp_client_channel_get_registered_event_handles(rdpChannels* channels, HANDLE* events,
DWORD count)
{
SSIZE_T rc = -1;
WINPR_ASSERT(channels);
WINPR_ASSERT(events || (count == 0));
HashTable_Lock(channels->channelEvents);
size_t len = HashTable_Count(channels->channelEvents);
if (len <= count)
{
ULONG_PTR* keys = NULL;
const size_t nrKeys = HashTable_GetKeys(channels->channelEvents, &keys);
if ((nrKeys <= SSIZE_MAX) && (nrKeys == len))
{
for (size_t x = 0; x < nrKeys; x++)
{
HANDLE cur = (HANDLE)keys[x];
events[x] = cur;
}
rc = (SSIZE_T)nrKeys;
}
free(keys);
}
HashTable_Unlock(channels->channelEvents);
return rc;
}
UINT freerdp_channels_disconnect(rdpChannels* channels, freerdp* instance)
{
UINT error = CHANNEL_RC_OK;
@ -759,7 +858,7 @@ UINT freerdp_channels_disconnect(rdpChannels* channels, freerdp* instance)
/* tell all libraries we are shutting down */
for (int index = 0; index < channels->clientDataCount; index++)
{
ChannelDisconnectedEventArgs e;
ChannelDisconnectedEventArgs e = { 0 };
pChannelClientData = &channels->clientDataList[index];
if (pChannelClientData->pChannelInitEventProc)

View File

@ -107,6 +107,8 @@ struct rdp_channels
DrdynvcClientContext* drdynvc;
CRITICAL_SECTION channelsLock;
wHashTable* channelEvents;
};
FREERDP_LOCAL void freerdp_channels_free(rdpChannels* channels);
@ -121,4 +123,9 @@ FREERDP_LOCAL void freerdp_channels_register_instance(rdpChannels* channels, fre
FREERDP_LOCAL UINT freerdp_channels_pre_connect(rdpChannels* channels, freerdp* instance);
FREERDP_LOCAL UINT freerdp_channels_post_connect(rdpChannels* channels, freerdp* instance);
/** @since version 3.9.0 */
FREERDP_LOCAL SSIZE_T freerdp_client_channel_get_registered_event_handles(rdpChannels* channels,
HANDLE* events,
DWORD count);
#endif /* FREERDP_LIB_CORE_CLIENT_H */

View File

@ -391,7 +391,11 @@ DWORD freerdp_get_event_handles(rdpContext* context, HANDLE* events, DWORD count
else
return 0;
return nCount;
const SSIZE_T rc = freerdp_client_channel_get_registered_event_handles(
context->channels, &events[nCount], count - nCount);
if (rc < 0)
return 0;
return nCount + (DWORD)rc;
}
/* Resend mouse cursor position to prevent session lock in prevent-session-lock mode */