Coverage Report

Created: 2025-07-01 06:46

/src/FreeRDP/channels/ainput/client/ainput_main.c
Line
Count
Source (jump to first uncovered line)
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
};
49
50
/**
51
 * Function description
52
 *
53
 * @return 0 on success, otherwise a Win32 error code
54
 */
55
static UINT ainput_on_data_received(IWTSVirtualChannelCallback* pChannelCallback, wStream* data)
56
0
{
57
0
  UINT16 type = 0;
58
0
  AINPUT_PLUGIN* ainput = NULL;
59
0
  GENERIC_CHANNEL_CALLBACK* callback = (GENERIC_CHANNEL_CALLBACK*)pChannelCallback;
60
61
0
  WINPR_ASSERT(callback);
62
0
  WINPR_ASSERT(data);
63
64
0
  ainput = (AINPUT_PLUGIN*)callback->plugin;
65
0
  WINPR_ASSERT(ainput);
66
67
0
  if (!Stream_CheckAndLogRequiredLength(TAG, data, 2))
68
0
    return ERROR_NO_DATA;
69
0
  Stream_Read_UINT16(data, type);
70
0
  switch (type)
71
0
  {
72
0
    case MSG_AINPUT_VERSION:
73
0
      if (!Stream_CheckAndLogRequiredLength(TAG, data, 8))
74
0
        return ERROR_NO_DATA;
75
0
      Stream_Read_UINT32(data, ainput->MajorVersion);
76
0
      Stream_Read_UINT32(data, ainput->MinorVersion);
77
0
      break;
78
0
    default:
79
0
      WLog_WARN(TAG, "Received unsupported message type 0x%04" PRIx16, type);
80
0
      break;
81
0
  }
82
83
0
  return CHANNEL_RC_OK;
84
0
}
85
86
static UINT ainput_send_input_event(AInputClientContext* context, UINT64 flags, INT32 x, INT32 y)
87
0
{
88
0
  AINPUT_PLUGIN* ainput = NULL;
89
0
  GENERIC_CHANNEL_CALLBACK* callback = NULL;
90
0
  BYTE buffer[32] = { 0 };
91
0
  UINT64 time = 0;
92
0
  wStream sbuffer = { 0 };
93
0
  wStream* s = Stream_StaticInit(&sbuffer, buffer, sizeof(buffer));
94
95
0
  WINPR_ASSERT(s);
96
0
  WINPR_ASSERT(context);
97
98
0
  time = GetTickCount64();
99
0
  ainput = (AINPUT_PLUGIN*)context->handle;
100
0
  WINPR_ASSERT(ainput);
101
102
0
  if (ainput->MajorVersion != AINPUT_VERSION_MAJOR)
103
0
  {
104
0
    WLog_WARN(TAG, "Unsupported channel version %" PRIu32 ".%" PRIu32 ", aborting.",
105
0
              ainput->MajorVersion, ainput->MinorVersion);
106
0
    return CHANNEL_RC_UNSUPPORTED_VERSION;
107
0
  }
108
0
  callback = ainput->base.listener_callback->channel_callback;
109
0
  WINPR_ASSERT(callback);
110
111
0
  {
112
0
    char ebuffer[128] = { 0 };
113
0
    WLog_VRB(TAG, "sending timestamp=0x%08" PRIx64 ", flags=%s, %" PRId32 "x%" PRId32, time,
114
0
             ainput_flags_to_string(flags, ebuffer, sizeof(ebuffer)), x, y);
115
0
  }
116
117
  /* Message type */
118
0
  Stream_Write_UINT16(s, MSG_AINPUT_MOUSE);
119
120
  /* Event data */
121
0
  Stream_Write_UINT64(s, time);
122
0
  Stream_Write_UINT64(s, flags);
123
0
  Stream_Write_INT32(s, x);
124
0
  Stream_Write_INT32(s, y);
125
0
  Stream_SealLength(s);
126
127
  /* ainput back what we have received. AINPUT does not have any message IDs. */
128
0
  WINPR_ASSERT(callback->channel);
129
0
  WINPR_ASSERT(callback->channel->Write);
130
0
  return callback->channel->Write(callback->channel, (ULONG)Stream_Length(s), Stream_Buffer(s),
131
0
                                  NULL);
132
0
}
133
134
/**
135
 * Function description
136
 *
137
 * @return 0 on success, otherwise a Win32 error code
138
 */
139
static UINT ainput_on_close(IWTSVirtualChannelCallback* pChannelCallback)
140
0
{
141
0
  GENERIC_CHANNEL_CALLBACK* callback = (GENERIC_CHANNEL_CALLBACK*)pChannelCallback;
142
143
0
  free(callback);
144
145
0
  return CHANNEL_RC_OK;
146
0
}
147
148
static UINT init_plugin_cb(GENERIC_DYNVC_PLUGIN* base, WINPR_ATTR_UNUSED rdpContext* rcontext,
149
                           WINPR_ATTR_UNUSED rdpSettings* settings)
150
0
{
151
0
  AINPUT_PLUGIN* ainput = (AINPUT_PLUGIN*)base;
152
0
  AInputClientContext* context = (AInputClientContext*)calloc(1, sizeof(AInputClientContext));
153
0
  if (!context)
154
0
    return CHANNEL_RC_NO_MEMORY;
155
156
0
  context->handle = (void*)base;
157
0
  context->AInputSendInputEvent = ainput_send_input_event;
158
159
0
  ainput->context = context;
160
0
  ainput->base.iface.pInterface = context;
161
0
  return CHANNEL_RC_OK;
162
0
}
163
164
static void terminate_plugin_cb(GENERIC_DYNVC_PLUGIN* base)
165
0
{
166
0
  AINPUT_PLUGIN* ainput = (AINPUT_PLUGIN*)base;
167
0
  free(ainput->context);
168
0
}
169
170
static const IWTSVirtualChannelCallback ainput_functions = { ainput_on_data_received,
171
                                                           NULL, /* Open */
172
                                                           ainput_on_close, NULL };
173
174
/**
175
 * Function description
176
 *
177
 * @return 0 on success, otherwise a Win32 error code
178
 */
179
FREERDP_ENTRY_POINT(UINT VCAPITYPE ainput_DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints))
180
0
{
181
0
  return freerdp_generic_DVCPluginEntry(pEntryPoints, TAG, AINPUT_DVC_CHANNEL_NAME,
182
0
                                        sizeof(AINPUT_PLUGIN), sizeof(GENERIC_CHANNEL_CALLBACK),
183
0
                                        &ainput_functions, init_plugin_cb, terminate_plugin_cb);
184
0
}