/src/FreeRDP/channels/ainput/client/ainput_main.c
Line | Count | Source |
1 | | /** |
2 | | * FreeRDP: A Remote Desktop Protocol Implementation |
3 | | * Advanced Input Virtual Channel Extension |
4 | | * |
5 | | * Copyright 2022 Armin Novak <anovak@thincast.com> |
6 | | * Copyright 2022 Thincast Technologies GmbH |
7 | | * |
8 | | * Licensed under the Apache License, Version 2.0 (the "License"); |
9 | | * you may not use this file except in compliance with the License. |
10 | | * You may obtain a copy of the License at |
11 | | * |
12 | | * http://www.apache.org/licenses/LICENSE-2.0 |
13 | | * |
14 | | * Unless required by applicable law or agreed to in writing, software |
15 | | * distributed under the License is distributed on an "AS IS" BASIS, |
16 | | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
17 | | * See the License for the specific language governing permissions and |
18 | | * limitations under the License. |
19 | | */ |
20 | | |
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 | 0 | #define TAG CHANNELS_TAG("ainput.client") |
40 | | |
41 | | typedef struct AINPUT_PLUGIN_ AINPUT_PLUGIN; |
42 | | struct AINPUT_PLUGIN_ |
43 | | { |
44 | | GENERIC_DYNVC_PLUGIN base; |
45 | | AInputClientContext* context; |
46 | | UINT32 MajorVersion; |
47 | | UINT32 MinorVersion; |
48 | | CRITICAL_SECTION lock; |
49 | | }; |
50 | | |
51 | | /** |
52 | | * Function description |
53 | | * |
54 | | * @return 0 on success, otherwise a Win32 error code |
55 | | */ |
56 | | static UINT ainput_on_data_received(IWTSVirtualChannelCallback* pChannelCallback, wStream* data) |
57 | 0 | { |
58 | 0 | UINT16 type = 0; |
59 | 0 | AINPUT_PLUGIN* ainput = NULL; |
60 | 0 | GENERIC_CHANNEL_CALLBACK* callback = (GENERIC_CHANNEL_CALLBACK*)pChannelCallback; |
61 | |
|
62 | 0 | WINPR_ASSERT(callback); |
63 | 0 | WINPR_ASSERT(data); |
64 | |
|
65 | 0 | ainput = (AINPUT_PLUGIN*)callback->plugin; |
66 | 0 | WINPR_ASSERT(ainput); |
67 | |
|
68 | 0 | if (!Stream_CheckAndLogRequiredLength(TAG, data, 2)) |
69 | 0 | return ERROR_NO_DATA; |
70 | 0 | Stream_Read_UINT16(data, type); |
71 | 0 | switch (type) |
72 | 0 | { |
73 | 0 | case MSG_AINPUT_VERSION: |
74 | 0 | if (!Stream_CheckAndLogRequiredLength(TAG, data, 8)) |
75 | 0 | return ERROR_NO_DATA; |
76 | 0 | Stream_Read_UINT32(data, ainput->MajorVersion); |
77 | 0 | Stream_Read_UINT32(data, ainput->MinorVersion); |
78 | 0 | break; |
79 | 0 | default: |
80 | 0 | WLog_WARN(TAG, "Received unsupported message type 0x%04" PRIx16, type); |
81 | 0 | break; |
82 | 0 | } |
83 | | |
84 | 0 | return CHANNEL_RC_OK; |
85 | 0 | } |
86 | | |
87 | | static UINT ainput_send_input_event(AInputClientContext* context, UINT64 flags, INT32 x, INT32 y) |
88 | 0 | { |
89 | 0 | BYTE buffer[32] = WINPR_C_ARRAY_INIT; |
90 | 0 | wStream sbuffer = WINPR_C_ARRAY_INIT; |
91 | 0 | wStream* s = Stream_StaticInit(&sbuffer, buffer, sizeof(buffer)); |
92 | |
|
93 | 0 | WINPR_ASSERT(s); |
94 | 0 | WINPR_ASSERT(context); |
95 | |
|
96 | 0 | const UINT64 time = GetTickCount64(); |
97 | 0 | AINPUT_PLUGIN* ainput = (AINPUT_PLUGIN*)context->handle; |
98 | 0 | WINPR_ASSERT(ainput); |
99 | |
|
100 | 0 | if (ainput->MajorVersion != AINPUT_VERSION_MAJOR) |
101 | 0 | { |
102 | 0 | WLog_WARN(TAG, "Unsupported channel version %" PRIu32 ".%" PRIu32 ", aborting.", |
103 | 0 | ainput->MajorVersion, ainput->MinorVersion); |
104 | 0 | return CHANNEL_RC_UNSUPPORTED_VERSION; |
105 | 0 | } |
106 | | |
107 | 0 | { |
108 | 0 | char ebuffer[128] = WINPR_C_ARRAY_INIT; |
109 | 0 | WLog_VRB(TAG, "sending timestamp=0x%08" PRIx64 ", flags=%s, %" PRId32 "x%" PRId32, time, |
110 | 0 | ainput_flags_to_string(flags, ebuffer, sizeof(ebuffer)), x, y); |
111 | 0 | } |
112 | | |
113 | | /* Message type */ |
114 | 0 | Stream_Write_UINT16(s, MSG_AINPUT_MOUSE); |
115 | | |
116 | | /* Event data */ |
117 | 0 | Stream_Write_UINT64(s, time); |
118 | 0 | Stream_Write_UINT64(s, flags); |
119 | 0 | Stream_Write_INT32(s, x); |
120 | 0 | Stream_Write_INT32(s, y); |
121 | 0 | Stream_SealLength(s); |
122 | | |
123 | | /* ainput back what we have received. AINPUT does not have any message IDs. */ |
124 | 0 | EnterCriticalSection(&ainput->lock); |
125 | 0 | GENERIC_CHANNEL_CALLBACK* callback = ainput->base.listener_callback->channel_callback; |
126 | 0 | WINPR_ASSERT(callback); |
127 | 0 | WINPR_ASSERT(callback->channel); |
128 | 0 | WINPR_ASSERT(callback->channel->Write); |
129 | 0 | const UINT rc = callback->channel->Write(callback->channel, (ULONG)Stream_Length(s), |
130 | 0 | Stream_Buffer(s), NULL); |
131 | 0 | LeaveCriticalSection(&ainput->lock); |
132 | 0 | return rc; |
133 | 0 | } |
134 | | |
135 | | /** |
136 | | * Function description |
137 | | * |
138 | | * @return 0 on success, otherwise a Win32 error code |
139 | | */ |
140 | | static UINT ainput_on_close(IWTSVirtualChannelCallback* pChannelCallback) |
141 | 0 | { |
142 | 0 | GENERIC_CHANNEL_CALLBACK* callback = (GENERIC_CHANNEL_CALLBACK*)pChannelCallback; |
143 | |
|
144 | 0 | if (callback) |
145 | 0 | { |
146 | 0 | AINPUT_PLUGIN* ainput = (AINPUT_PLUGIN*)callback->plugin; |
147 | 0 | WINPR_ASSERT(ainput); |
148 | | |
149 | | /* Lock here to ensure that no ainput_send_input_event is in progress. */ |
150 | 0 | EnterCriticalSection(&ainput->lock); |
151 | 0 | free(callback); |
152 | 0 | LeaveCriticalSection(&ainput->lock); |
153 | 0 | } |
154 | 0 | return CHANNEL_RC_OK; |
155 | 0 | } |
156 | | |
157 | | static UINT init_plugin_cb(GENERIC_DYNVC_PLUGIN* base, WINPR_ATTR_UNUSED rdpContext* rcontext, |
158 | | WINPR_ATTR_UNUSED rdpSettings* settings) |
159 | 0 | { |
160 | 0 | AINPUT_PLUGIN* ainput = (AINPUT_PLUGIN*)base; |
161 | 0 | AInputClientContext* context = (AInputClientContext*)calloc(1, sizeof(AInputClientContext)); |
162 | 0 | if (!context) |
163 | 0 | return CHANNEL_RC_NO_MEMORY; |
164 | | |
165 | 0 | context->handle = (void*)base; |
166 | 0 | context->AInputSendInputEvent = ainput_send_input_event; |
167 | |
|
168 | 0 | InitializeCriticalSection(&ainput->lock); |
169 | |
|
170 | 0 | EnterCriticalSection(&ainput->lock); |
171 | 0 | ainput->context = context; |
172 | 0 | ainput->base.iface.pInterface = context; |
173 | 0 | LeaveCriticalSection(&ainput->lock); |
174 | 0 | return CHANNEL_RC_OK; |
175 | 0 | } |
176 | | |
177 | | static void terminate_plugin_cb(GENERIC_DYNVC_PLUGIN* base) |
178 | 0 | { |
179 | 0 | AINPUT_PLUGIN* ainput = (AINPUT_PLUGIN*)base; |
180 | 0 | WINPR_ASSERT(ainput); |
181 | |
|
182 | 0 | DeleteCriticalSection(&ainput->lock); |
183 | 0 | free(ainput->context); |
184 | 0 | } |
185 | | |
186 | | static const IWTSVirtualChannelCallback ainput_functions = { ainput_on_data_received, |
187 | | NULL, /* Open */ |
188 | | ainput_on_close, NULL }; |
189 | | |
190 | | /** |
191 | | * Function description |
192 | | * |
193 | | * @return 0 on success, otherwise a Win32 error code |
194 | | */ |
195 | | FREERDP_ENTRY_POINT(UINT VCAPITYPE ainput_DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints)) |
196 | 0 | { |
197 | 0 | return freerdp_generic_DVCPluginEntry(pEntryPoints, TAG, AINPUT_DVC_CHANNEL_NAME, |
198 | 0 | sizeof(AINPUT_PLUGIN), sizeof(GENERIC_CHANNEL_CALLBACK), |
199 | 0 | &ainput_functions, init_plugin_cb, terminate_plugin_cb); |
200 | 0 | } |