Coverage Report

Created: 2026-02-26 06:54

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}