FreeRDP
Loading...
Searching...
No Matches
client/ainput_main.c
1
21#include <freerdp/config.h>
22
23#include <stdio.h>
24#include <stdlib.h>
25
26#include <winpr/crt.h>
27#include <winpr/assert.h>
28#include <winpr/stream.h>
29#include <winpr/sysinfo.h>
30
31#include "ainput_main.h"
32#include <freerdp/channels/log.h>
33#include <freerdp/client/channels.h>
34#include <freerdp/client/ainput.h>
35#include <freerdp/channels/ainput.h>
36
37#include "../common/ainput_common.h"
38
39#define TAG CHANNELS_TAG("ainput.client")
40
41typedef struct AINPUT_PLUGIN_ AINPUT_PLUGIN;
42struct AINPUT_PLUGIN_
43{
45 AInputClientContext* context;
46 UINT32 MajorVersion;
47 UINT32 MinorVersion;
49};
50
56static UINT ainput_on_data_received(IWTSVirtualChannelCallback* pChannelCallback, wStream* data)
57{
58 UINT16 type = 0;
59 AINPUT_PLUGIN* ainput = NULL;
60 GENERIC_CHANNEL_CALLBACK* callback = (GENERIC_CHANNEL_CALLBACK*)pChannelCallback;
61
62 WINPR_ASSERT(callback);
63 WINPR_ASSERT(data);
64
65 ainput = (AINPUT_PLUGIN*)callback->plugin;
66 WINPR_ASSERT(ainput);
67
68 if (!Stream_CheckAndLogRequiredLength(TAG, data, 2))
69 return ERROR_NO_DATA;
70 Stream_Read_UINT16(data, type);
71 switch (type)
72 {
73 case MSG_AINPUT_VERSION:
74 if (!Stream_CheckAndLogRequiredLength(TAG, data, 8))
75 return ERROR_NO_DATA;
76 Stream_Read_UINT32(data, ainput->MajorVersion);
77 Stream_Read_UINT32(data, ainput->MinorVersion);
78 break;
79 default:
80 WLog_WARN(TAG, "Received unsupported message type 0x%04" PRIx16, type);
81 break;
82 }
83
84 return CHANNEL_RC_OK;
85}
86
87static UINT ainput_send_input_event(AInputClientContext* context, UINT64 flags, INT32 x, INT32 y)
88{
89 BYTE buffer[32] = { 0 };
90 wStream sbuffer = { 0 };
91 wStream* s = Stream_StaticInit(&sbuffer, buffer, sizeof(buffer));
92
93 WINPR_ASSERT(s);
94 WINPR_ASSERT(context);
95
96 const UINT64 time = GetTickCount64();
97 AINPUT_PLUGIN* ainput = (AINPUT_PLUGIN*)context->handle;
98 WINPR_ASSERT(ainput);
99
100 if (ainput->MajorVersion != AINPUT_VERSION_MAJOR)
101 {
102 WLog_WARN(TAG, "Unsupported channel version %" PRIu32 ".%" PRIu32 ", aborting.",
103 ainput->MajorVersion, ainput->MinorVersion);
104 return CHANNEL_RC_UNSUPPORTED_VERSION;
105 }
106
107 {
108 char ebuffer[128] = { 0 };
109 WLog_VRB(TAG, "sending timestamp=0x%08" PRIx64 ", flags=%s, %" PRId32 "x%" PRId32, time,
110 ainput_flags_to_string(flags, ebuffer, sizeof(ebuffer)), x, y);
111 }
112
113 /* Message type */
114 Stream_Write_UINT16(s, MSG_AINPUT_MOUSE);
115
116 /* Event data */
117 Stream_Write_UINT64(s, time);
118 Stream_Write_UINT64(s, flags);
119 Stream_Write_INT32(s, x);
120 Stream_Write_INT32(s, y);
121 Stream_SealLength(s);
122
123 /* ainput back what we have received. AINPUT does not have any message IDs. */
124 EnterCriticalSection(&ainput->lock);
125 GENERIC_CHANNEL_CALLBACK* callback = ainput->base.listener_callback->channel_callback;
126 WINPR_ASSERT(callback);
127 WINPR_ASSERT(callback->channel);
128 WINPR_ASSERT(callback->channel->Write);
129 const UINT rc = callback->channel->Write(callback->channel, (ULONG)Stream_Length(s),
130 Stream_Buffer(s), NULL);
131 LeaveCriticalSection(&ainput->lock);
132 return rc;
133}
134
140static UINT ainput_on_close(IWTSVirtualChannelCallback* pChannelCallback)
141{
142 GENERIC_CHANNEL_CALLBACK* callback = (GENERIC_CHANNEL_CALLBACK*)pChannelCallback;
143
144 if (callback)
145 {
146 AINPUT_PLUGIN* ainput = (AINPUT_PLUGIN*)callback->plugin;
147 WINPR_ASSERT(ainput);
148
149 /* Lock here to ensure that no ainput_send_input_event is in progress. */
150 EnterCriticalSection(&ainput->lock);
151 free(callback);
152 LeaveCriticalSection(&ainput->lock);
153 }
154 return CHANNEL_RC_OK;
155}
156
157static UINT init_plugin_cb(GENERIC_DYNVC_PLUGIN* base, WINPR_ATTR_UNUSED rdpContext* rcontext,
158 WINPR_ATTR_UNUSED rdpSettings* settings)
159{
160 AINPUT_PLUGIN* ainput = (AINPUT_PLUGIN*)base;
161 AInputClientContext* context = (AInputClientContext*)calloc(1, sizeof(AInputClientContext));
162 if (!context)
163 return CHANNEL_RC_NO_MEMORY;
164
165 context->handle = (void*)base;
166 context->AInputSendInputEvent = ainput_send_input_event;
167
168 InitializeCriticalSection(&ainput->lock);
169
170 EnterCriticalSection(&ainput->lock);
171 ainput->context = context;
172 ainput->base.iface.pInterface = context;
173 LeaveCriticalSection(&ainput->lock);
174 return CHANNEL_RC_OK;
175}
176
177static void terminate_plugin_cb(GENERIC_DYNVC_PLUGIN* base)
178{
179 AINPUT_PLUGIN* ainput = (AINPUT_PLUGIN*)base;
180 WINPR_ASSERT(ainput);
181
182 DeleteCriticalSection(&ainput->lock);
183 free(ainput->context);
184}
185
186static const IWTSVirtualChannelCallback ainput_functions = { ainput_on_data_received,
187 NULL, /* Open */
188 ainput_on_close, NULL };
189
195FREERDP_ENTRY_POINT(UINT VCAPITYPE ainput_DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints))
196{
197 return freerdp_generic_DVCPluginEntry(pEntryPoints, TAG, AINPUT_DVC_CHANNEL_NAME,
198 sizeof(AINPUT_PLUGIN), sizeof(GENERIC_CHANNEL_CALLBACK),
199 &ainput_functions, init_plugin_cb, terminate_plugin_cb);
200}