mirror of
https://github.com/morgan9e/FreeRDP
synced 2026-04-15 08:54:38 +09:00
@@ -42,6 +42,7 @@
|
||||
#include <freerdp/channels/log.h>
|
||||
#include <freerdp/codec/h264.h>
|
||||
#include <freerdp/codec/yuv.h>
|
||||
#include <freerdp/timer.h>
|
||||
|
||||
#define TAG CHANNELS_TAG("video")
|
||||
|
||||
@@ -58,6 +59,7 @@ typedef struct
|
||||
|
||||
VideoClientContext* context;
|
||||
BOOL initialized;
|
||||
rdpContext* rdpcontext;
|
||||
} VIDEO_PLUGIN;
|
||||
|
||||
#define XF_VIDEO_UNLIMITED_RATE 31
|
||||
@@ -105,6 +107,7 @@ struct s_VideoClientContextPriv
|
||||
UINT32 lastSentRate;
|
||||
UINT64 nextFeedbackTime;
|
||||
PresentationContext* currentPresentation;
|
||||
FreeRDP_TimerID timerID;
|
||||
};
|
||||
|
||||
static void PresentationContext_unref(PresentationContext** presentation);
|
||||
@@ -1087,6 +1090,21 @@ static UINT video_data_on_new_channel_connection(IWTSListenerCallback* pListener
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
static uint64_t timer_cb(WINPR_ATTR_UNUSED rdpContext* context, void* userdata,
|
||||
WINPR_ATTR_UNUSED FreeRDP_TimerID timerID, uint64_t timestamp,
|
||||
uint64_t interval)
|
||||
{
|
||||
VideoClientContext* video = userdata;
|
||||
if (!video)
|
||||
return 0;
|
||||
if (!video->timer)
|
||||
return 0;
|
||||
|
||||
video->timer(video, timestamp);
|
||||
|
||||
return interval;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
@@ -1140,7 +1158,12 @@ static UINT video_plugin_initialize(IWTSPlugin* plugin, IWTSVirtualChannelManage
|
||||
if (status == CHANNEL_RC_OK)
|
||||
video->dataListener->pInterface = video->wtsPlugin.pInterface;
|
||||
|
||||
video->initialized = status == CHANNEL_RC_OK;
|
||||
if (status == CHANNEL_RC_OK)
|
||||
video->context->priv->timerID =
|
||||
freerdp_timer_add(video->rdpcontext, 20000, timer_cb, video->context, true);
|
||||
video->initialized = video->context->priv->timerID != 0;
|
||||
if (!video->initialized)
|
||||
status = ERROR_INTERNAL_ERROR;
|
||||
return status;
|
||||
}
|
||||
|
||||
@@ -1153,6 +1176,7 @@ static UINT video_plugin_terminated(IWTSPlugin* pPlugin)
|
||||
{
|
||||
VIDEO_PLUGIN* video = (VIDEO_PLUGIN*)pPlugin;
|
||||
|
||||
freerdp_timer_remove(video->rdpcontext, video->context->priv->timerID);
|
||||
if (video->control_callback)
|
||||
{
|
||||
IWTSVirtualChannelManager* mgr = video->control_callback->channel_mgr;
|
||||
@@ -1186,7 +1210,7 @@ static UINT video_plugin_terminated(IWTSPlugin* pPlugin)
|
||||
*/
|
||||
FREERDP_ENTRY_POINT(UINT VCAPITYPE video_DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints))
|
||||
{
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
UINT error = ERROR_INTERNAL_ERROR;
|
||||
VIDEO_PLUGIN* videoPlugin = NULL;
|
||||
VideoClientContext* videoContext = NULL;
|
||||
VideoClientContextPriv* priv = NULL;
|
||||
@@ -1230,8 +1254,9 @@ FREERDP_ENTRY_POINT(UINT VCAPITYPE video_DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* p
|
||||
|
||||
videoPlugin->wtsPlugin.pInterface = (void*)videoContext;
|
||||
videoPlugin->context = videoContext;
|
||||
|
||||
error = pEntryPoints->RegisterPlugin(pEntryPoints, "video", &videoPlugin->wtsPlugin);
|
||||
videoPlugin->rdpcontext = pEntryPoints->GetRdpContext(pEntryPoints);
|
||||
if (videoPlugin->rdpcontext)
|
||||
error = pEntryPoints->RegisterPlugin(pEntryPoints, "video", &videoPlugin->wtsPlugin);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -21,11 +21,13 @@
|
||||
#include <winpr/sysinfo.h>
|
||||
#include <winpr/cast.h>
|
||||
|
||||
#include <freerdp/timer.h>
|
||||
|
||||
#include "wlf_disp.h"
|
||||
|
||||
#define TAG CLIENT_TAG("wayland.disp")
|
||||
|
||||
#define RESIZE_MIN_DELAY 200 /* minimum delay in ms between two resizes */
|
||||
#define RESIZE_MIN_DELAY_NS 200000UL /* minimum delay in ns between two resizes */
|
||||
|
||||
struct s_wlfDispContext
|
||||
{
|
||||
@@ -42,8 +44,12 @@ struct s_wlfDispContext
|
||||
UINT16 lastSentDesktopOrientation;
|
||||
UINT32 lastSentDesktopScaleFactor;
|
||||
UINT32 lastSentDeviceScaleFactor;
|
||||
FreeRDP_TimerID timerID;
|
||||
};
|
||||
|
||||
static BOOL wlf_disp_sendResize(wlfDispContext* wlfDisp, BOOL fromTimer);
|
||||
static BOOL wlf_disp_check_context(void* context, wlfContext** ppwlc, wlfDispContext** ppwlfDisp,
|
||||
rdpSettings** ppSettings);
|
||||
static UINT wlf_disp_sendLayout(DispClientContext* disp, const rdpMonitor* monitors,
|
||||
size_t nmonitors);
|
||||
|
||||
@@ -91,6 +97,7 @@ static BOOL wlf_update_last_sent(wlfDispContext* wlfDisp)
|
||||
settings = wlfDisp->wlc->common.context.settings;
|
||||
WINPR_ASSERT(settings);
|
||||
|
||||
wlfDisp->lastSentDate = winpr_GetTickCount64NS();
|
||||
wlfDisp->lastSentWidth = wlfDisp->targetWidth;
|
||||
wlfDisp->lastSentHeight = wlfDisp->targetHeight;
|
||||
wlfDisp->lastSentDesktopOrientation =
|
||||
@@ -103,7 +110,39 @@ static BOOL wlf_update_last_sent(wlfDispContext* wlfDisp)
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static BOOL wlf_disp_sendResize(wlfDispContext* wlfDisp)
|
||||
static uint64_t wlf_disp_OnTimer(rdpContext* context, WINPR_ATTR_UNUSED void* userdata,
|
||||
WINPR_ATTR_UNUSED FreeRDP_TimerID timerID,
|
||||
WINPR_ATTR_UNUSED uint64_t timestamp, uint64_t interval)
|
||||
{
|
||||
wlfContext* wlc = NULL;
|
||||
wlfDispContext* wlfDisp = NULL;
|
||||
rdpSettings* settings = NULL;
|
||||
|
||||
if (!wlf_disp_check_context(context, &wlc, &wlfDisp, &settings))
|
||||
return interval;
|
||||
|
||||
if (!wlfDisp->activated || freerdp_settings_get_bool(settings, FreeRDP_Fullscreen))
|
||||
return interval;
|
||||
|
||||
wlf_disp_sendResize(wlfDisp, TRUE);
|
||||
wlfDisp->timerID = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static BOOL update_timer(wlfDispContext* wlfDisp, uint64_t intervalNS)
|
||||
{
|
||||
WINPR_ASSERT(wlfDisp);
|
||||
|
||||
if (wlfDisp->timerID == 0)
|
||||
{
|
||||
rdpContext* context = &wlfDisp->wlc->common.context;
|
||||
|
||||
wlfDisp->timerID = freerdp_timer_add(context, intervalNS, wlf_disp_OnTimer, NULL, true);
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL wlf_disp_sendResize(wlfDispContext* wlfDisp, BOOL fromTimer)
|
||||
{
|
||||
DISPLAY_CONTROL_MONITOR_LAYOUT layout;
|
||||
wlfContext* wlc = NULL;
|
||||
@@ -119,12 +158,15 @@ static BOOL wlf_disp_sendResize(wlfDispContext* wlfDisp)
|
||||
return FALSE;
|
||||
|
||||
if (!wlfDisp->activated || !wlfDisp->disp)
|
||||
return TRUE;
|
||||
return update_timer(wlfDisp, RESIZE_MIN_DELAY_NS);
|
||||
|
||||
if (GetTickCount64() - wlfDisp->lastSentDate < RESIZE_MIN_DELAY)
|
||||
return TRUE;
|
||||
const uint64_t now = winpr_GetTickCount64NS();
|
||||
const uint64_t diff = now - wlfDisp->lastSentDate;
|
||||
if (diff < RESIZE_MIN_DELAY_NS)
|
||||
return update_timer(wlfDisp, RESIZE_MIN_DELAY_NS);
|
||||
|
||||
wlfDisp->lastSentDate = GetTickCount64();
|
||||
if (!fromTimer && (wlfDisp->timerID != 0))
|
||||
return TRUE;
|
||||
|
||||
if (!wlf_disp_settings_changed(wlfDisp))
|
||||
return TRUE;
|
||||
@@ -164,8 +206,8 @@ static BOOL wlf_disp_set_window_resizable(WINPR_ATTR_UNUSED wlfDispContext* wlfD
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static BOOL wlf_disp_check_context(void* context, wlfContext** ppwlc, wlfDispContext** ppwlfDisp,
|
||||
rdpSettings** ppSettings)
|
||||
BOOL wlf_disp_check_context(void* context, wlfContext** ppwlc, wlfDispContext** ppwlfDisp,
|
||||
rdpSettings** ppSettings)
|
||||
{
|
||||
wlfContext* wlc = NULL;
|
||||
|
||||
@@ -204,7 +246,7 @@ static void wlf_disp_OnActivated(void* context, const ActivatedEventArgs* e)
|
||||
if (e->firstActivation)
|
||||
return;
|
||||
|
||||
wlf_disp_sendResize(wlfDisp);
|
||||
wlf_disp_sendResize(wlfDisp, FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -223,26 +265,10 @@ static void wlf_disp_OnGraphicsReset(void* context, const GraphicsResetEventArgs
|
||||
if (wlfDisp->activated && !freerdp_settings_get_bool(settings, FreeRDP_Fullscreen))
|
||||
{
|
||||
wlf_disp_set_window_resizable(wlfDisp);
|
||||
wlf_disp_sendResize(wlfDisp);
|
||||
wlf_disp_sendResize(wlfDisp, FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
static void wlf_disp_OnTimer(void* context, const TimerEventArgs* e)
|
||||
{
|
||||
wlfContext* wlc = NULL;
|
||||
wlfDispContext* wlfDisp = NULL;
|
||||
rdpSettings* settings = NULL;
|
||||
|
||||
WINPR_UNUSED(e);
|
||||
if (!wlf_disp_check_context(context, &wlc, &wlfDisp, &settings))
|
||||
return;
|
||||
|
||||
if (!wlfDisp->activated || freerdp_settings_get_bool(settings, FreeRDP_Fullscreen))
|
||||
return;
|
||||
|
||||
wlf_disp_sendResize(wlfDisp);
|
||||
}
|
||||
|
||||
wlfDispContext* wlf_disp_new(wlfContext* wlc)
|
||||
{
|
||||
wlfDispContext* ret = NULL;
|
||||
@@ -266,7 +292,6 @@ wlfDispContext* wlf_disp_new(wlfContext* wlc)
|
||||
WINPR_ASSERTING_INT_CAST(int, freerdp_settings_get_uint32(settings, FreeRDP_DesktopHeight));
|
||||
PubSub_SubscribeActivated(pubSub, wlf_disp_OnActivated);
|
||||
PubSub_SubscribeGraphicsReset(pubSub, wlf_disp_OnGraphicsReset);
|
||||
PubSub_SubscribeTimer(pubSub, wlf_disp_OnTimer);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -280,7 +305,6 @@ void wlf_disp_free(wlfDispContext* disp)
|
||||
wPubSub* pubSub = disp->wlc->common.context.pubSub;
|
||||
PubSub_UnsubscribeActivated(pubSub, wlf_disp_OnActivated);
|
||||
PubSub_UnsubscribeGraphicsReset(pubSub, wlf_disp_OnGraphicsReset);
|
||||
PubSub_UnsubscribeTimer(pubSub, wlf_disp_OnTimer);
|
||||
}
|
||||
|
||||
free(disp);
|
||||
@@ -369,7 +393,7 @@ BOOL wlf_disp_handle_configure(wlfDispContext* disp, int32_t width, int32_t heig
|
||||
|
||||
disp->targetWidth = width;
|
||||
disp->targetHeight = height;
|
||||
return wlf_disp_sendResize(disp);
|
||||
return wlf_disp_sendResize(disp, FALSE);
|
||||
}
|
||||
|
||||
static UINT wlf_DisplayControlCaps(DispClientContext* disp, UINT32 maxNumMonitors,
|
||||
|
||||
@@ -476,11 +476,6 @@ static int wlfreerdp_run(freerdp* instance)
|
||||
wlfContext* context = NULL;
|
||||
HANDLE handles[MAXIMUM_WAIT_OBJECTS] = { 0 };
|
||||
DWORD status = WAIT_ABANDONED;
|
||||
HANDLE timer = NULL;
|
||||
LARGE_INTEGER due = { 0 };
|
||||
|
||||
TimerEventArgs timerEvent;
|
||||
EventArgsInit(&timerEvent, "xfreerdp");
|
||||
|
||||
if (!instance)
|
||||
return -1;
|
||||
@@ -496,25 +491,9 @@ static int wlfreerdp_run(freerdp* instance)
|
||||
return -1;
|
||||
}
|
||||
|
||||
timer = CreateWaitableTimerA(NULL, FALSE, "mainloop-periodic-timer");
|
||||
|
||||
if (!timer)
|
||||
{
|
||||
WLog_ERR(TAG, "failed to create timer");
|
||||
goto disconnect;
|
||||
}
|
||||
|
||||
due.QuadPart = 0;
|
||||
|
||||
if (!SetWaitableTimer(timer, &due, 20, NULL, NULL, FALSE))
|
||||
{
|
||||
goto disconnect;
|
||||
}
|
||||
|
||||
while (!freerdp_shall_disconnect_context(instance->context))
|
||||
{
|
||||
DWORD count = 0;
|
||||
handles[count++] = timer;
|
||||
handles[count++] = context->displayHandle;
|
||||
count += freerdp_get_event_handles(instance->context, &handles[count],
|
||||
ARRAYSIZE(handles) - count);
|
||||
@@ -564,17 +543,8 @@ static int wlfreerdp_run(freerdp* instance)
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if ((status != WAIT_TIMEOUT) && (status == WAIT_OBJECT_0))
|
||||
{
|
||||
timerEvent.now = GetTickCount64();
|
||||
PubSub_OnTimer(context->common.context.pubSub, context, &timerEvent);
|
||||
}
|
||||
}
|
||||
|
||||
disconnect:
|
||||
if (timer)
|
||||
(void)CloseHandle(timer);
|
||||
freerdp_disconnect(instance);
|
||||
return WINPR_ASSERTING_INT_CAST(int, status);
|
||||
}
|
||||
|
||||
@@ -1553,11 +1553,7 @@ static DWORD WINAPI xf_client_thread(LPVOID param)
|
||||
DWORD exit_code = 0;
|
||||
DWORD waitStatus = 0;
|
||||
HANDLE inputEvent = NULL;
|
||||
HANDLE timer = NULL;
|
||||
LARGE_INTEGER due = { 0 };
|
||||
TimerEventArgs timerEvent = { 0 };
|
||||
|
||||
EventArgsInit(&timerEvent, "xfreerdp");
|
||||
freerdp* instance = (freerdp*)param;
|
||||
WINPR_ASSERT(instance);
|
||||
|
||||
@@ -1601,27 +1597,12 @@ static DWORD WINAPI xf_client_thread(LPVOID param)
|
||||
goto disconnect;
|
||||
}
|
||||
|
||||
timer = CreateWaitableTimerA(NULL, FALSE, "mainloop-periodic-timer");
|
||||
|
||||
if (!timer)
|
||||
{
|
||||
WLog_ERR(TAG, "failed to create timer");
|
||||
goto disconnect;
|
||||
}
|
||||
|
||||
due.QuadPart = 0;
|
||||
|
||||
if (!SetWaitableTimer(timer, &due, 20, NULL, NULL, FALSE))
|
||||
{
|
||||
goto disconnect;
|
||||
}
|
||||
inputEvent = xfc->x11event;
|
||||
|
||||
while (!freerdp_shall_disconnect_context(instance->context))
|
||||
{
|
||||
HANDLE handles[MAXIMUM_WAIT_OBJECTS] = { 0 };
|
||||
DWORD nCount = 0;
|
||||
handles[nCount++] = timer;
|
||||
handles[nCount++] = inputEvent;
|
||||
|
||||
/*
|
||||
@@ -1682,12 +1663,6 @@ static DWORD WINAPI xf_client_thread(LPVOID param)
|
||||
|
||||
if (!handle_window_events(instance))
|
||||
break;
|
||||
|
||||
if ((waitStatus != WAIT_TIMEOUT) && (waitStatus == WAIT_OBJECT_0))
|
||||
{
|
||||
timerEvent.now = GetTickCount64();
|
||||
PubSub_OnTimer(context->pubSub, context, &timerEvent);
|
||||
}
|
||||
}
|
||||
|
||||
if (!exit_code)
|
||||
@@ -1706,9 +1681,6 @@ static DWORD WINAPI xf_client_thread(LPVOID param)
|
||||
|
||||
disconnect:
|
||||
|
||||
if (timer)
|
||||
(void)CloseHandle(timer);
|
||||
|
||||
freerdp_disconnect(instance);
|
||||
end:
|
||||
ExitThread(exit_code);
|
||||
|
||||
@@ -20,6 +20,9 @@
|
||||
#include <math.h>
|
||||
#include <winpr/assert.h>
|
||||
#include <winpr/sysinfo.h>
|
||||
|
||||
#include <freerdp/timer.h>
|
||||
|
||||
#include <X11/Xutil.h>
|
||||
|
||||
#ifdef WITH_XRANDR
|
||||
@@ -37,7 +40,7 @@
|
||||
|
||||
#include <freerdp/log.h>
|
||||
#define TAG CLIENT_TAG("x11disp")
|
||||
#define RESIZE_MIN_DELAY 200 /* minimum delay in ms between two resizes */
|
||||
#define RESIZE_MIN_DELAY_NS 200000UL /* minimum delay in ms between two resizes */
|
||||
|
||||
struct s_xfDispContext
|
||||
{
|
||||
@@ -59,8 +62,12 @@ struct s_xfDispContext
|
||||
UINT32 lastSentDesktopScaleFactor;
|
||||
UINT32 lastSentDeviceScaleFactor;
|
||||
BYTE reserved3[4];
|
||||
FreeRDP_TimerID timerID;
|
||||
};
|
||||
|
||||
static BOOL xf_disp_check_context(void* context, xfContext** ppXfc, xfDispContext** ppXfDisp,
|
||||
rdpSettings** ppSettings);
|
||||
static BOOL xf_disp_sendResize(xfDispContext* xfDisp, BOOL fromTimer);
|
||||
static UINT xf_disp_sendLayout(DispClientContext* disp, const rdpMonitor* monitors,
|
||||
UINT32 nmonitors);
|
||||
|
||||
@@ -120,31 +127,71 @@ static BOOL xf_update_last_sent(xfDispContext* xfDisp)
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static BOOL xf_disp_sendResize(xfDispContext* xfDisp)
|
||||
static uint64_t xf_disp_OnTimer(rdpContext* context, WINPR_ATTR_UNUSED void* userdata,
|
||||
WINPR_ATTR_UNUSED FreeRDP_TimerID timerID,
|
||||
WINPR_ATTR_UNUSED uint64_t timestamp,
|
||||
WINPR_ATTR_UNUSED uint64_t interval)
|
||||
|
||||
{
|
||||
xfContext* xfc = NULL;
|
||||
xfDispContext* xfDisp = NULL;
|
||||
rdpSettings* settings = NULL;
|
||||
|
||||
if (!xf_disp_check_context(context, &xfc, &xfDisp, &settings))
|
||||
return interval;
|
||||
|
||||
if (!xfDisp->activated)
|
||||
return interval;
|
||||
|
||||
xf_disp_sendResize(xfDisp, TRUE);
|
||||
xfDisp->timerID = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static BOOL update_timer(xfDispContext* xfDisp, uint64_t intervalNS)
|
||||
{
|
||||
WINPR_ASSERT(xfDisp);
|
||||
|
||||
if (xfDisp->timerID == 0)
|
||||
{
|
||||
rdpContext* context = &xfDisp->xfc->common.context;
|
||||
|
||||
xfDisp->timerID = freerdp_timer_add(context, intervalNS, xf_disp_OnTimer, NULL, true);
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL xf_disp_sendResize(xfDispContext* xfDisp, BOOL fromTimer)
|
||||
{
|
||||
DISPLAY_CONTROL_MONITOR_LAYOUT layout = { 0 };
|
||||
xfContext* xfc = NULL;
|
||||
rdpSettings* settings = NULL;
|
||||
|
||||
if (!xfDisp || !xfDisp->xfc)
|
||||
return FALSE;
|
||||
|
||||
xfc = xfDisp->xfc;
|
||||
settings = xfc->common.context.settings;
|
||||
/* If there is already a timer running skip the update and wait for the timer to expire. */
|
||||
if ((xfDisp->timerID != 0) && !fromTimer)
|
||||
return TRUE;
|
||||
|
||||
xfContext* xfc = xfDisp->xfc;
|
||||
rdpSettings* settings = xfc->common.context.settings;
|
||||
|
||||
if (!settings)
|
||||
return FALSE;
|
||||
|
||||
if (!xfDisp->activated || !xfDisp->disp)
|
||||
return TRUE;
|
||||
return update_timer(xfDisp, RESIZE_MIN_DELAY_NS);
|
||||
|
||||
if (GetTickCount64() - xfDisp->lastSentDate < RESIZE_MIN_DELAY)
|
||||
return TRUE;
|
||||
const uint64_t diff = winpr_GetTickCount64NS() - xfDisp->lastSentDate;
|
||||
if (diff < RESIZE_MIN_DELAY_NS)
|
||||
{
|
||||
const uint64_t interval = RESIZE_MIN_DELAY_NS - diff;
|
||||
return update_timer(xfDisp, interval);
|
||||
}
|
||||
|
||||
if (!xf_disp_settings_changed(xfDisp))
|
||||
return TRUE;
|
||||
|
||||
xfDisp->lastSentDate = GetTickCount64();
|
||||
xfDisp->lastSentDate = winpr_GetTickCount64NS();
|
||||
|
||||
const UINT32 mcount = freerdp_settings_get_uint32(settings, FreeRDP_MonitorCount);
|
||||
if (mcount > 1)
|
||||
@@ -184,8 +231,7 @@ static BOOL xf_disp_queueResize(xfDispContext* xfDisp, UINT32 width, UINT32 heig
|
||||
return TRUE;
|
||||
xfDisp->targetWidth = width;
|
||||
xfDisp->targetHeight = height;
|
||||
xfDisp->lastSentDate = GetTickCount64();
|
||||
return xf_disp_sendResize(xfDisp);
|
||||
return xf_disp_sendResize(xfDisp, FALSE);
|
||||
}
|
||||
|
||||
static BOOL xf_disp_set_window_resizable(xfDispContext* xfDisp)
|
||||
@@ -207,8 +253,8 @@ static BOOL xf_disp_set_window_resizable(xfDispContext* xfDisp)
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static BOOL xf_disp_check_context(void* context, xfContext** ppXfc, xfDispContext** ppXfDisp,
|
||||
rdpSettings** ppSettings)
|
||||
BOOL xf_disp_check_context(void* context, xfContext** ppXfc, xfDispContext** ppXfDisp,
|
||||
rdpSettings** ppSettings)
|
||||
{
|
||||
xfContext* xfc = NULL;
|
||||
|
||||
@@ -245,7 +291,7 @@ static void xf_disp_OnActivated(void* context, const ActivatedEventArgs* e)
|
||||
if (e->firstActivation)
|
||||
return;
|
||||
|
||||
xf_disp_sendResize(xfDisp);
|
||||
xf_disp_sendResize(xfDisp, FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -263,27 +309,10 @@ static void xf_disp_OnGraphicsReset(void* context, const GraphicsResetEventArgs*
|
||||
if (xfDisp->activated && !freerdp_settings_get_bool(settings, FreeRDP_Fullscreen))
|
||||
{
|
||||
xf_disp_set_window_resizable(xfDisp);
|
||||
xf_disp_sendResize(xfDisp);
|
||||
xf_disp_sendResize(xfDisp, FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
static void xf_disp_OnTimer(void* context, const TimerEventArgs* e)
|
||||
{
|
||||
xfContext* xfc = NULL;
|
||||
xfDispContext* xfDisp = NULL;
|
||||
rdpSettings* settings = NULL;
|
||||
|
||||
WINPR_UNUSED(e);
|
||||
|
||||
if (!xf_disp_check_context(context, &xfc, &xfDisp, &settings))
|
||||
return;
|
||||
|
||||
if (!xfDisp->activated)
|
||||
return;
|
||||
|
||||
xf_disp_sendResize(xfDisp);
|
||||
}
|
||||
|
||||
static void xf_disp_OnWindowStateChange(void* context, const WindowStateChangeEventArgs* e)
|
||||
{
|
||||
xfContext* xfc = NULL;
|
||||
@@ -298,7 +327,7 @@ static void xf_disp_OnWindowStateChange(void* context, const WindowStateChangeEv
|
||||
if (!xfDisp->activated || !xfc->fullscreen)
|
||||
return;
|
||||
|
||||
xf_disp_sendResize(xfDisp);
|
||||
xf_disp_sendResize(xfDisp, FALSE);
|
||||
}
|
||||
|
||||
xfDispContext* xf_disp_new(xfContext* xfc)
|
||||
@@ -335,7 +364,6 @@ xfDispContext* xf_disp_new(xfContext* xfc)
|
||||
freerdp_settings_get_uint32(settings, FreeRDP_DesktopHeight);
|
||||
PubSub_SubscribeActivated(pubSub, xf_disp_OnActivated);
|
||||
PubSub_SubscribeGraphicsReset(pubSub, xf_disp_OnGraphicsReset);
|
||||
PubSub_SubscribeTimer(pubSub, xf_disp_OnTimer);
|
||||
PubSub_SubscribeWindowStateChange(pubSub, xf_disp_OnWindowStateChange);
|
||||
return ret;
|
||||
}
|
||||
@@ -350,7 +378,6 @@ void xf_disp_free(xfDispContext* disp)
|
||||
wPubSub* pubSub = disp->xfc->common.context.pubSub;
|
||||
PubSub_UnsubscribeActivated(pubSub, xf_disp_OnActivated);
|
||||
PubSub_UnsubscribeGraphicsReset(pubSub, xf_disp_OnGraphicsReset);
|
||||
PubSub_UnsubscribeTimer(pubSub, xf_disp_OnTimer);
|
||||
PubSub_UnsubscribeWindowStateChange(pubSub, xf_disp_OnWindowStateChange);
|
||||
}
|
||||
|
||||
|
||||
101
include/freerdp/timer.h
Normal file
101
include/freerdp/timer.h
Normal file
@@ -0,0 +1,101 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* Timer implementation
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include <freerdp/api.h>
|
||||
#include <freerdp/types.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
/** Type definition for timer IDs
|
||||
* @since version 3.16.0
|
||||
*/
|
||||
typedef uint64_t FreeRDP_TimerID;
|
||||
|
||||
/** @brief Callback function pointer type definition.
|
||||
* An expired timer will be called, depending on \ref mainloop argument of \ref
|
||||
* freerdp_timer_add, background thread or mainloop. This also greatly influence jitter and
|
||||
* precision of the call. If called by \b mainloop, which might be blocked, delays for up to
|
||||
* 100ms are to be expected. If called from a background thread no locking is performed, so be
|
||||
* sure to lock your resources where necessary.
|
||||
*
|
||||
*
|
||||
* @param context The RDP context this timer belongs to
|
||||
* @param userdata Custom userdata provided by \ref freerdp_timer_add
|
||||
* @param timerID The timer ID that expired
|
||||
* @param timestamp The current timestamp for the call. The base is not specified, but the
|
||||
* resolution is in nanoseconds.
|
||||
* @param interval The last interval value
|
||||
*
|
||||
* @return A new interval (might differ from the last one set) or \b 0 to disable the timer
|
||||
*
|
||||
* @since version 3.16.0
|
||||
*/
|
||||
typedef uint64_t (*FreeRDP_TimerCallback)(rdpContext* context, void* userdata,
|
||||
FreeRDP_TimerID timerID, uint64_t timestamp,
|
||||
uint64_t interval);
|
||||
|
||||
/** @brief Add a new timer to the list of running timers
|
||||
*
|
||||
* @note While the API allows nano second precision the execution time might vary depending on
|
||||
* various circumstances.
|
||||
* \b mainloop executed callbacks will have a huge jitter and execution times are expected to be
|
||||
* delayed up to multiple 10s of milliseconds. Current implementation also does not guarantee
|
||||
* more than 10ms granularity even for background thread callbacks, but that might improve with
|
||||
* newer versions.
|
||||
*
|
||||
* @note Current implementation limits all timers to be executed by a single background thread.
|
||||
* So ensure your callbacks are not blocking for a long time as both, \b mainloop and background
|
||||
* thread executed callbacks will delay execution of other tasks.
|
||||
*
|
||||
* @param context The RDP context the timer belongs to
|
||||
* @param intervalNS The (first) timer expiration interval in nanoseconds
|
||||
* @param callback The function to be called when the timer expires. Must not be \b NULL
|
||||
* @param userdata Custom userdata passed to the callback. The pointer is only passed, it is up
|
||||
* to the user to ensure the data exists when the timer expires.
|
||||
* @param mainloop \b true run the callback in mainloop context or \b false from background
|
||||
* thread
|
||||
* @return A new timer ID or \b 0 in case of failure
|
||||
* @since version 3.16.0
|
||||
*/
|
||||
FREERDP_API FreeRDP_TimerID freerdp_timer_add(rdpContext* context, uint64_t intervalNS,
|
||||
FreeRDP_TimerCallback callback, void* userdata,
|
||||
bool mainloop);
|
||||
|
||||
/** @brief Remove a timer from the list of running timers
|
||||
*
|
||||
* @param context The RDP context the timer belongs to
|
||||
* @param id The timer ID to remove
|
||||
*
|
||||
* @return \b true if the timer was removed, \b false otherwise
|
||||
* @since version 3.16.0
|
||||
*/
|
||||
FREERDP_API bool freerdp_timer_remove(rdpContext* context, FreeRDP_TimerID id);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -149,6 +149,8 @@ set(${MODULE_PREFIX}_SRCS
|
||||
rdstls.h
|
||||
aad.c
|
||||
aad.h
|
||||
timer.c
|
||||
timer.h
|
||||
)
|
||||
|
||||
set(${MODULE_PREFIX}_SRCS ${${MODULE_PREFIX}_SRCS} ${${MODULE_PREFIX}_GATEWAY_SRCS})
|
||||
|
||||
@@ -390,16 +390,16 @@ DWORD freerdp_get_event_handles(rdpContext* context, HANDLE* events, DWORD count
|
||||
WINPR_ASSERT(context->rdp);
|
||||
WINPR_ASSERT(events || (count == 0));
|
||||
|
||||
nCount += transport_get_event_handles(context->rdp->transport, events, count);
|
||||
|
||||
if (nCount == 0)
|
||||
const size_t rrc = rdp_get_event_handles(context->rdp, &events[nCount], count - nCount);
|
||||
if (rrc == 0)
|
||||
return 0;
|
||||
|
||||
nCount += WINPR_ASSERTING_INT_CAST(uint32_t, rrc);
|
||||
|
||||
if (events && (nCount < count + 2))
|
||||
{
|
||||
events[nCount++] = freerdp_channels_get_event_handle(context->instance);
|
||||
events[nCount++] = getChannelErrorEventHandle(context);
|
||||
events[nCount++] = utils_get_abort_event(context->rdp);
|
||||
}
|
||||
else
|
||||
return 0;
|
||||
|
||||
@@ -2279,6 +2279,8 @@ int rdp_check_fds(rdpRdp* rdp)
|
||||
|
||||
if (status < 0)
|
||||
WLog_Print(rdp->log, WLOG_DEBUG, "transport_check_fds() - %i", status);
|
||||
else
|
||||
status = freerdp_timer_poll(rdp->timer);
|
||||
|
||||
return status;
|
||||
}
|
||||
@@ -2301,6 +2303,46 @@ BOOL freerdp_get_stats(rdpRdp* rdp, UINT64* inBytes, UINT64* outBytes, UINT64* i
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static bool rdp_new_common(rdpRdp* rdp)
|
||||
{
|
||||
WINPR_ASSERT(rdp);
|
||||
|
||||
bool rc = false;
|
||||
rdp->transport = transport_new(rdp->context);
|
||||
if (!rdp->transport)
|
||||
goto fail;
|
||||
|
||||
if (rdp->io)
|
||||
{
|
||||
if (!transport_set_io_callbacks(rdp->transport, rdp->io))
|
||||
goto fail;
|
||||
}
|
||||
|
||||
rdp->aad = aad_new(rdp->context, rdp->transport);
|
||||
if (!rdp->aad)
|
||||
goto fail;
|
||||
|
||||
rdp->nego = nego_new(rdp->transport);
|
||||
if (!rdp->nego)
|
||||
goto fail;
|
||||
|
||||
rdp->mcs = mcs_new(rdp->transport);
|
||||
if (!rdp->mcs)
|
||||
goto fail;
|
||||
|
||||
rdp->license = license_new(rdp);
|
||||
if (!rdp->license)
|
||||
goto fail;
|
||||
|
||||
rdp->fastpath = fastpath_new(rdp);
|
||||
if (!rdp->fastpath)
|
||||
goto fail;
|
||||
|
||||
rc = true;
|
||||
fail:
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiate new RDP module.
|
||||
* @return new RDP module
|
||||
@@ -2308,9 +2350,8 @@ BOOL freerdp_get_stats(rdpRdp* rdp, UINT64* inBytes, UINT64* outBytes, UINT64* i
|
||||
|
||||
rdpRdp* rdp_new(rdpContext* context)
|
||||
{
|
||||
rdpRdp* rdp = NULL;
|
||||
DWORD flags = 0;
|
||||
rdp = (rdpRdp*)calloc(1, sizeof(rdpRdp));
|
||||
rdpRdp* rdp = (rdpRdp*)calloc(1, sizeof(rdpRdp));
|
||||
|
||||
if (!rdp)
|
||||
return NULL;
|
||||
@@ -2356,9 +2397,7 @@ rdpRdp* rdp_new(rdpContext* context)
|
||||
#endif
|
||||
}
|
||||
|
||||
rdp->transport = transport_new(context);
|
||||
|
||||
if (!rdp->transport)
|
||||
if (!rdp_new_common(rdp))
|
||||
goto fail;
|
||||
|
||||
{
|
||||
@@ -2371,15 +2410,6 @@ rdpRdp* rdp_new(rdpContext* context)
|
||||
*rdp->io = *io;
|
||||
}
|
||||
|
||||
rdp->aad = aad_new(context, rdp->transport);
|
||||
if (!rdp->aad)
|
||||
goto fail;
|
||||
|
||||
rdp->license = license_new(rdp);
|
||||
|
||||
if (!rdp->license)
|
||||
goto fail;
|
||||
|
||||
rdp->input = input_new(rdp);
|
||||
|
||||
if (!rdp->input)
|
||||
@@ -2390,21 +2420,6 @@ rdpRdp* rdp_new(rdpContext* context)
|
||||
if (!rdp->update)
|
||||
goto fail;
|
||||
|
||||
rdp->fastpath = fastpath_new(rdp);
|
||||
|
||||
if (!rdp->fastpath)
|
||||
goto fail;
|
||||
|
||||
rdp->nego = nego_new(rdp->transport);
|
||||
|
||||
if (!rdp->nego)
|
||||
goto fail;
|
||||
|
||||
rdp->mcs = mcs_new(rdp->transport);
|
||||
|
||||
if (!rdp->mcs)
|
||||
goto fail;
|
||||
|
||||
rdp->redirection = redirection_new();
|
||||
|
||||
if (!rdp->redirection)
|
||||
@@ -2438,6 +2453,11 @@ rdpRdp* rdp_new(rdpContext* context)
|
||||
rdp->abortEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
|
||||
if (!rdp->abortEvent)
|
||||
goto fail;
|
||||
|
||||
rdp->timer = freerdp_timer_new(rdp);
|
||||
if (!rdp->timer)
|
||||
goto fail;
|
||||
|
||||
return rdp;
|
||||
|
||||
fail:
|
||||
@@ -2462,12 +2482,14 @@ static void rdp_reset_free(rdpRdp* rdp)
|
||||
rdp->fips_decrypt = NULL;
|
||||
(void)security_unlock(rdp);
|
||||
|
||||
aad_free(rdp->aad);
|
||||
mcs_free(rdp->mcs);
|
||||
nego_free(rdp->nego);
|
||||
license_free(rdp->license);
|
||||
transport_free(rdp->transport);
|
||||
fastpath_free(rdp->fastpath);
|
||||
|
||||
rdp->aad = NULL;
|
||||
rdp->mcs = NULL;
|
||||
rdp->nego = NULL;
|
||||
rdp->license = NULL;
|
||||
@@ -2478,15 +2500,10 @@ static void rdp_reset_free(rdpRdp* rdp)
|
||||
BOOL rdp_reset(rdpRdp* rdp)
|
||||
{
|
||||
BOOL rc = TRUE;
|
||||
rdpContext* context = NULL;
|
||||
rdpSettings* settings = NULL;
|
||||
|
||||
WINPR_ASSERT(rdp);
|
||||
|
||||
context = rdp->context;
|
||||
WINPR_ASSERT(context);
|
||||
|
||||
settings = rdp->settings;
|
||||
rdpSettings* settings = rdp->settings;
|
||||
WINPR_ASSERT(settings);
|
||||
|
||||
bulk_reset(rdp->bulk);
|
||||
@@ -2505,41 +2522,13 @@ BOOL rdp_reset(rdpRdp* rdp)
|
||||
if (!rc)
|
||||
goto fail;
|
||||
|
||||
rc = FALSE;
|
||||
rdp->transport = transport_new(context);
|
||||
if (!rdp->transport)
|
||||
goto fail;
|
||||
|
||||
if (rdp->io)
|
||||
{
|
||||
if (!transport_set_io_callbacks(rdp->transport, rdp->io))
|
||||
goto fail;
|
||||
}
|
||||
|
||||
aad_free(rdp->aad);
|
||||
rdp->aad = aad_new(context, rdp->transport);
|
||||
if (!rdp->aad)
|
||||
goto fail;
|
||||
|
||||
rdp->nego = nego_new(rdp->transport);
|
||||
if (!rdp->nego)
|
||||
goto fail;
|
||||
|
||||
rdp->mcs = mcs_new(rdp->transport);
|
||||
if (!rdp->mcs)
|
||||
rc = rdp_new_common(rdp);
|
||||
if (!rc)
|
||||
goto fail;
|
||||
|
||||
if (!transport_set_layer(rdp->transport, TRANSPORT_LAYER_TCP))
|
||||
goto fail;
|
||||
|
||||
rdp->license = license_new(rdp);
|
||||
if (!rdp->license)
|
||||
goto fail;
|
||||
|
||||
rdp->fastpath = fastpath_new(rdp);
|
||||
if (!rdp->fastpath)
|
||||
goto fail;
|
||||
|
||||
rdp->errorInfo = 0;
|
||||
rc = rdp_finalize_reset_flags(rdp, TRUE);
|
||||
|
||||
@@ -2556,6 +2545,7 @@ void rdp_free(rdpRdp* rdp)
|
||||
{
|
||||
if (rdp)
|
||||
{
|
||||
freerdp_timer_free(rdp->timer);
|
||||
rdp_reset_free(rdp);
|
||||
|
||||
freerdp_settings_free(rdp->settings);
|
||||
@@ -3147,3 +3137,18 @@ void rdp_log_build_warnings(rdpRdp* rdp)
|
||||
option_is_runtime_checks);
|
||||
log_build_warn_ssl(rdp);
|
||||
}
|
||||
|
||||
size_t rdp_get_event_handles(rdpRdp* rdp, HANDLE* handles, uint32_t count)
|
||||
{
|
||||
size_t nCount = transport_get_event_handles(rdp->transport, handles, count);
|
||||
|
||||
if (nCount == 0)
|
||||
return 0;
|
||||
|
||||
if (count < nCount + 2UL)
|
||||
return 0;
|
||||
|
||||
handles[nCount++] = utils_get_abort_event(rdp);
|
||||
handles[nCount++] = freerdp_timer_get_event(rdp->timer);
|
||||
return nCount;
|
||||
}
|
||||
|
||||
@@ -45,6 +45,7 @@
|
||||
#include "redirection.h"
|
||||
#include "capabilities.h"
|
||||
#include "channels.h"
|
||||
#include "timer.h"
|
||||
|
||||
#include <freerdp/freerdp.h>
|
||||
#include <freerdp/settings.h>
|
||||
@@ -207,6 +208,7 @@ struct rdp_rdp
|
||||
wLog* log;
|
||||
char log_context[64];
|
||||
WINPR_JSON* wellknown;
|
||||
FreeRDPTimer* timer;
|
||||
};
|
||||
|
||||
FREERDP_LOCAL BOOL rdp_read_security_header(rdpRdp* rdp, wStream* s, UINT16* flags, UINT16* length);
|
||||
@@ -304,4 +306,6 @@ BOOL rdp_reset_runtime_settings(rdpRdp* rdp);
|
||||
|
||||
void rdp_log_build_warnings(rdpRdp* rdp);
|
||||
|
||||
FREERDP_LOCAL size_t rdp_get_event_handles(rdpRdp* rdp, HANDLE* handles, uint32_t count);
|
||||
|
||||
#endif /* FREERDP_LIB_CORE_RDP_H */
|
||||
|
||||
321
libfreerdp/core/timer.c
Normal file
321
libfreerdp/core/timer.c
Normal file
@@ -0,0 +1,321 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* Timer implementation
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <winpr/thread.h>
|
||||
#include <winpr/collections.h>
|
||||
|
||||
#include <freerdp/timer.h>
|
||||
#include "rdp.h"
|
||||
#include "utils.h"
|
||||
#include "timer.h"
|
||||
|
||||
typedef ALIGN64 struct
|
||||
{
|
||||
FreeRDP_TimerID id;
|
||||
uint64_t intervallNS;
|
||||
uint64_t nextRunTimeNS;
|
||||
FreeRDP_TimerCallback cb;
|
||||
void* userdata;
|
||||
rdpContext* context;
|
||||
bool mainloop;
|
||||
} timer_entry_t;
|
||||
|
||||
struct ALIGN64 freerdp_timer_s
|
||||
{
|
||||
rdpRdp* rdp;
|
||||
wArrayList* entries;
|
||||
HANDLE thread;
|
||||
HANDLE event;
|
||||
HANDLE mainevent;
|
||||
size_t maxIdx;
|
||||
bool running;
|
||||
};
|
||||
|
||||
FreeRDP_TimerID freerdp_timer_add(rdpContext* context, uint64_t intervalNS,
|
||||
FreeRDP_TimerCallback callback, void* userdata, bool mainloop)
|
||||
{
|
||||
WINPR_ASSERT(context);
|
||||
WINPR_ASSERT(context->rdp);
|
||||
|
||||
FreeRDPTimer* timer = context->rdp->timer;
|
||||
WINPR_ASSERT(timer);
|
||||
|
||||
if ((intervalNS == 0) || !callback)
|
||||
return false;
|
||||
|
||||
const uint64_t cur = winpr_GetTickCount64NS();
|
||||
const timer_entry_t entry = { .id = timer->maxIdx++,
|
||||
.intervallNS = intervalNS,
|
||||
.nextRunTimeNS = cur + intervalNS,
|
||||
.cb = callback,
|
||||
.userdata = userdata,
|
||||
.context = context,
|
||||
.mainloop = mainloop };
|
||||
|
||||
if (!ArrayList_Append(timer->entries, &entry))
|
||||
return 0;
|
||||
(void)SetEvent(timer->event);
|
||||
return entry.id;
|
||||
}
|
||||
|
||||
static BOOL foreach_entry(void* data, WINPR_ATTR_UNUSED size_t index, va_list ap)
|
||||
{
|
||||
timer_entry_t* entry = data;
|
||||
WINPR_ASSERT(entry);
|
||||
|
||||
FreeRDP_TimerID id = va_arg(ap, FreeRDP_TimerID);
|
||||
|
||||
if (entry->id == id)
|
||||
{
|
||||
/* Mark the timer to be disabled.
|
||||
* It will be removed on next rescheduling event
|
||||
*/
|
||||
entry->intervallNS = 0;
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
bool freerdp_timer_remove(rdpContext* context, FreeRDP_TimerID id)
|
||||
{
|
||||
WINPR_ASSERT(context);
|
||||
WINPR_ASSERT(context->rdp);
|
||||
|
||||
FreeRDPTimer* timer = context->rdp->timer;
|
||||
WINPR_ASSERT(timer);
|
||||
|
||||
return !ArrayList_ForEach(timer->entries, foreach_entry, id);
|
||||
}
|
||||
|
||||
static BOOL runTimerEvent(timer_entry_t* entry, uint64_t* now)
|
||||
{
|
||||
WINPR_ASSERT(entry);
|
||||
|
||||
entry->intervallNS =
|
||||
entry->cb(entry->context, entry->userdata, entry->id, *now, entry->intervallNS);
|
||||
*now = winpr_GetTickCount64NS();
|
||||
entry->nextRunTimeNS = *now + entry->intervallNS;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static BOOL runExpiredTimer(void* data, WINPR_ATTR_UNUSED size_t index,
|
||||
WINPR_ATTR_UNUSED va_list ap)
|
||||
{
|
||||
timer_entry_t* entry = data;
|
||||
WINPR_ASSERT(entry);
|
||||
WINPR_ASSERT(entry->cb);
|
||||
|
||||
/* Skip all timers that have been deactivated. */
|
||||
if (entry->intervallNS == 0)
|
||||
return TRUE;
|
||||
|
||||
uint64_t* now = va_arg(ap, uint64_t*);
|
||||
WINPR_ASSERT(now);
|
||||
|
||||
bool* mainloop = va_arg(ap, bool*);
|
||||
WINPR_ASSERT(mainloop);
|
||||
|
||||
if (entry->nextRunTimeNS > *now)
|
||||
return TRUE;
|
||||
|
||||
if (entry->mainloop)
|
||||
*mainloop = true;
|
||||
else
|
||||
runTimerEvent(entry, now);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static uint64_t expire_and_reschedule(FreeRDPTimer* timer)
|
||||
{
|
||||
WINPR_ASSERT(timer);
|
||||
|
||||
bool mainloop = false;
|
||||
uint64_t next = UINT64_MAX;
|
||||
uint64_t now = winpr_GetTickCount64NS();
|
||||
|
||||
ArrayList_Lock(timer->entries);
|
||||
ArrayList_ForEach(timer->entries, runExpiredTimer, &now, &mainloop);
|
||||
if (mainloop)
|
||||
(void)SetEvent(timer->mainevent);
|
||||
|
||||
size_t pos = 0;
|
||||
while (pos < ArrayList_Count(timer->entries))
|
||||
{
|
||||
timer_entry_t* entry = ArrayList_GetItem(timer->entries, pos);
|
||||
WINPR_ASSERT(entry);
|
||||
if (entry->intervallNS == 0)
|
||||
{
|
||||
ArrayList_RemoveAt(timer->entries, pos);
|
||||
continue;
|
||||
}
|
||||
if (next > entry->nextRunTimeNS)
|
||||
next = entry->nextRunTimeNS;
|
||||
pos++;
|
||||
}
|
||||
ArrayList_Unlock(timer->entries);
|
||||
|
||||
if (next == UINT64_MAX)
|
||||
return 0;
|
||||
return next;
|
||||
}
|
||||
|
||||
static DWORD WINAPI timer_thread(LPVOID arg)
|
||||
{
|
||||
FreeRDPTimer* timer = arg;
|
||||
WINPR_ASSERT(timer);
|
||||
|
||||
// TODO: Currently we only support ms granularity, look for ways to improve
|
||||
DWORD timeout = INFINITE;
|
||||
HANDLE handles[2] = { utils_get_abort_event(timer->rdp), timer->event };
|
||||
|
||||
while (WaitForMultipleObjects(ARRAYSIZE(handles), handles, FALSE, timeout) != WAIT_OBJECT_0)
|
||||
{
|
||||
(void)ResetEvent(timer->event);
|
||||
const uint64_t next = expire_and_reschedule(timer);
|
||||
const uint64_t now = winpr_GetTickCount64NS();
|
||||
if (now >= next)
|
||||
{
|
||||
timeout = INFINITE;
|
||||
continue;
|
||||
}
|
||||
|
||||
const uint64_t diff = next - now;
|
||||
const uint64_t diffMS = diff / 1000;
|
||||
timeout = MIN(INFINITE, (uint32_t)diffMS);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void freerdp_timer_free(FreeRDPTimer* timer)
|
||||
{
|
||||
if (!timer)
|
||||
return;
|
||||
|
||||
if (timer->event)
|
||||
(void)SetEvent(timer->event);
|
||||
timer->running = false;
|
||||
if (timer->thread)
|
||||
{
|
||||
(void)WaitForSingleObject(timer->thread, INFINITE);
|
||||
CloseHandle(timer->thread);
|
||||
}
|
||||
if (timer->mainevent)
|
||||
CloseHandle(timer->mainevent);
|
||||
if (timer->event)
|
||||
CloseHandle(timer->event);
|
||||
ArrayList_Free(timer->entries);
|
||||
free(timer);
|
||||
}
|
||||
|
||||
static void* entry_new(const void* val)
|
||||
{
|
||||
const timer_entry_t* entry = val;
|
||||
if (!entry)
|
||||
return NULL;
|
||||
|
||||
timer_entry_t* copy = calloc(1, sizeof(timer_entry_t));
|
||||
if (!copy)
|
||||
return NULL;
|
||||
*copy = *entry;
|
||||
return copy;
|
||||
}
|
||||
|
||||
FreeRDPTimer* freerdp_timer_new(rdpRdp* rdp)
|
||||
{
|
||||
WINPR_ASSERT(rdp);
|
||||
FreeRDPTimer* timer = calloc(1, sizeof(FreeRDPTimer));
|
||||
if (!timer)
|
||||
return NULL;
|
||||
timer->rdp = rdp;
|
||||
|
||||
timer->entries = ArrayList_New(TRUE);
|
||||
if (!timer->entries)
|
||||
goto fail;
|
||||
wObject* obj = ArrayList_Object(timer->entries);
|
||||
WINPR_ASSERT(obj);
|
||||
obj->fnObjectNew = entry_new;
|
||||
obj->fnObjectFree = free;
|
||||
|
||||
timer->event = CreateEventA(NULL, TRUE, FALSE, NULL);
|
||||
if (!timer->event)
|
||||
goto fail;
|
||||
|
||||
timer->mainevent = CreateEventA(NULL, TRUE, FALSE, NULL);
|
||||
if (!timer->mainevent)
|
||||
goto fail;
|
||||
|
||||
timer->running = true;
|
||||
timer->thread = CreateThread(NULL, 0, timer_thread, timer, 0, NULL);
|
||||
if (!timer->thread)
|
||||
goto fail;
|
||||
return timer;
|
||||
|
||||
fail:
|
||||
freerdp_timer_free(timer);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static BOOL runExpiredTimerOnMainloop(void* data, WINPR_ATTR_UNUSED size_t index,
|
||||
WINPR_ATTR_UNUSED va_list ap)
|
||||
{
|
||||
timer_entry_t* entry = data;
|
||||
WINPR_ASSERT(entry);
|
||||
WINPR_ASSERT(entry->cb);
|
||||
|
||||
/* Skip events not on mainloop */
|
||||
if (!entry->mainloop)
|
||||
return TRUE;
|
||||
|
||||
/* Skip all timers that have been deactivated. */
|
||||
if (entry->intervallNS == 0)
|
||||
return TRUE;
|
||||
|
||||
uint64_t* now = va_arg(ap, uint64_t*);
|
||||
WINPR_ASSERT(now);
|
||||
|
||||
if (entry->nextRunTimeNS > *now)
|
||||
return TRUE;
|
||||
|
||||
runTimerEvent(entry, now);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
bool freerdp_timer_poll(FreeRDPTimer* timer)
|
||||
{
|
||||
WINPR_ASSERT(timer);
|
||||
|
||||
if (WaitForSingleObject(timer->mainevent, 0) != WAIT_OBJECT_0)
|
||||
return true;
|
||||
|
||||
ArrayList_Lock(timer->entries);
|
||||
(void)ResetEvent(timer->mainevent);
|
||||
uint64_t now = winpr_GetTickCount64NS();
|
||||
ArrayList_ForEach(timer->entries, runExpiredTimerOnMainloop, &now);
|
||||
(void)SetEvent(timer->event); // Trigger a wakeup of timer thread to reschedule
|
||||
ArrayList_Unlock(timer->entries);
|
||||
return true;
|
||||
}
|
||||
|
||||
HANDLE freerdp_timer_get_event(FreeRDPTimer* timer)
|
||||
{
|
||||
WINPR_ASSERT(timer);
|
||||
return timer->mainevent;
|
||||
}
|
||||
34
libfreerdp/core/timer.h
Normal file
34
libfreerdp/core/timer.h
Normal file
@@ -0,0 +1,34 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* Timer implementation
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <freerdp/api.h>
|
||||
#include <freerdp/types.h>
|
||||
|
||||
typedef struct freerdp_timer_s FreeRDPTimer;
|
||||
|
||||
FREERDP_LOCAL void freerdp_timer_free(FreeRDPTimer* timer);
|
||||
|
||||
WINPR_ATTR_MALLOC(freerdp_timer_free, 1)
|
||||
FREERDP_LOCAL FreeRDPTimer* freerdp_timer_new(rdpRdp* rdp);
|
||||
|
||||
FREERDP_LOCAL bool freerdp_timer_poll(FreeRDPTimer* timer);
|
||||
FREERDP_LOCAL HANDLE freerdp_timer_get_event(FreeRDPTimer* timer);
|
||||
@@ -145,32 +145,17 @@ void gdi_video_control_uninit(rdpGdi* gdi, WINPR_ATTR_UNUSED VideoClientContext*
|
||||
gdi->video = NULL;
|
||||
}
|
||||
|
||||
static void gdi_video_timer(void* context, const TimerEventArgs* timer)
|
||||
{
|
||||
rdpContext* ctx = (rdpContext*)context;
|
||||
rdpGdi* gdi = NULL;
|
||||
|
||||
WINPR_ASSERT(ctx);
|
||||
WINPR_ASSERT(timer);
|
||||
|
||||
gdi = ctx->gdi;
|
||||
|
||||
if (gdi && gdi->video)
|
||||
gdi->video->timer(gdi->video, timer->now);
|
||||
}
|
||||
|
||||
void gdi_video_data_init(rdpGdi* gdi, WINPR_ATTR_UNUSED VideoClientContext* video)
|
||||
void gdi_video_data_init(WINPR_ATTR_UNUSED rdpGdi* gdi, WINPR_ATTR_UNUSED VideoClientContext* video)
|
||||
{
|
||||
WINPR_ASSERT(gdi);
|
||||
WINPR_ASSERT(gdi->context);
|
||||
PubSub_SubscribeTimer(gdi->context->pubSub, gdi_video_timer);
|
||||
}
|
||||
|
||||
void gdi_video_data_uninit(rdpGdi* gdi, WINPR_ATTR_UNUSED VideoClientContext* context)
|
||||
void gdi_video_data_uninit(WINPR_ATTR_UNUSED rdpGdi* gdi,
|
||||
WINPR_ATTR_UNUSED VideoClientContext* context)
|
||||
{
|
||||
WINPR_ASSERT(gdi);
|
||||
WINPR_ASSERT(gdi->context);
|
||||
PubSub_UnsubscribeTimer(gdi->context->pubSub, gdi_video_timer);
|
||||
}
|
||||
|
||||
VideoSurface* VideoClient_CreateCommonContext(size_t size, UINT32 x, UINT32 y, UINT32 w, UINT32 h)
|
||||
|
||||
Reference in New Issue
Block a user