FreeRDP
Loading...
Searching...
No Matches
client/disp_main.c
1
23#include <freerdp/config.h>
24
25#include <stdio.h>
26#include <stdlib.h>
27#include <string.h>
28
29#include <winpr/crt.h>
30#include <winpr/assert.h>
31#include <winpr/synch.h>
32#include <winpr/print.h>
33#include <winpr/thread.h>
34#include <winpr/stream.h>
35#include <winpr/sysinfo.h>
36#include <winpr/cmdline.h>
37#include <winpr/collections.h>
38
39#include <freerdp/addin.h>
40#include <freerdp/utils/string.h>
41#include <freerdp/client/channels.h>
42
43#include "disp_main.h"
44#include "../disp_common.h"
45
46typedef struct
47{
49
50 DispClientContext* context;
51 UINT32 MaxNumMonitors;
52 UINT32 MaxMonitorAreaFactorA;
53 UINT32 MaxMonitorAreaFactorB;
55} DISP_PLUGIN;
56
62static UINT
63disp_send_display_control_monitor_layout_pdu(GENERIC_CHANNEL_CALLBACK* callback, UINT32 NumMonitors,
64 const DISPLAY_CONTROL_MONITOR_LAYOUT* Monitors)
65{
66 const DWORD level = WLOG_DEBUG;
67 UINT status = 0;
68
69 WINPR_ASSERT(callback);
70 WINPR_ASSERT(Monitors || (NumMonitors == 0));
71
72 DISP_PLUGIN* disp = (DISP_PLUGIN*)callback->plugin;
73 WINPR_ASSERT(disp);
74
75 const UINT32 MonitorLayoutSize = DISPLAY_CONTROL_MONITOR_LAYOUT_SIZE;
76 const DISPLAY_CONTROL_HEADER header = { .length = 8 + 8 + (NumMonitors * MonitorLayoutSize),
77 .type = DISPLAY_CONTROL_PDU_TYPE_MONITOR_LAYOUT };
78
79 wStream* s = Stream_New(nullptr, header.length);
80
81 if (!s)
82 {
83 WLog_Print(disp->base.log, WLOG_ERROR, "Stream_New failed!");
84 return CHANNEL_RC_NO_MEMORY;
85 }
86
87 if ((status = disp_write_header(s, &header)))
88 {
89 WLog_Print(disp->base.log, WLOG_ERROR, "Failed to write header with error %" PRIu32 "!",
90 status);
91 goto out;
92 }
93
94 if (NumMonitors > disp->MaxNumMonitors)
95 NumMonitors = disp->MaxNumMonitors;
96
97 Stream_Write_UINT32(s, MonitorLayoutSize); /* MonitorLayoutSize (4 bytes) */
98 Stream_Write_UINT32(s, NumMonitors); /* NumMonitors (4 bytes) */
99 WLog_Print(disp->base.log, level, "NumMonitors=%" PRIu32 "", NumMonitors);
100
101 for (UINT32 index = 0; index < NumMonitors; index++)
102 {
103 DISPLAY_CONTROL_MONITOR_LAYOUT current = Monitors[index];
104 current.Width -= (current.Width % 2);
105
106 if (current.Width < 200)
107 current.Width = 200;
108
109 if (current.Width > 8192)
110 current.Width = 8192;
111
112 if (current.Width % 2)
113 current.Width++;
114
115 if (current.Height < 200)
116 current.Height = 200;
117
118 if (current.Height > 8192)
119 current.Height = 8192;
120
121 Stream_Write_UINT32(s, current.Flags); /* Flags (4 bytes) */
122 Stream_Write_INT32(s, current.Left); /* Left (4 bytes) */
123 Stream_Write_INT32(s, current.Top); /* Top (4 bytes) */
124 Stream_Write_UINT32(s, current.Width); /* Width (4 bytes) */
125 Stream_Write_UINT32(s, current.Height); /* Height (4 bytes) */
126 Stream_Write_UINT32(s, current.PhysicalWidth); /* PhysicalWidth (4 bytes) */
127 Stream_Write_UINT32(s, current.PhysicalHeight); /* PhysicalHeight (4 bytes) */
128 Stream_Write_UINT32(s, current.Orientation); /* Orientation (4 bytes) */
129 Stream_Write_UINT32(s, current.DesktopScaleFactor); /* DesktopScaleFactor (4 bytes) */
130 Stream_Write_UINT32(s, current.DeviceScaleFactor); /* DeviceScaleFactor (4 bytes) */
131 WLog_Print(disp->base.log, level,
132 "\t%" PRIu32 " : Flags: 0x%08" PRIX32 " Left/Top: (%" PRId32 ",%" PRId32
133 ") W/H=%" PRIu32 "x%" PRIu32 ")",
134 index, current.Flags, current.Left, current.Top, current.Width, current.Height);
135 WLog_Print(disp->base.log, level,
136 "\t PhysicalWidth: %" PRIu32 " PhysicalHeight: %" PRIu32
137 " Orientation: %s DesktopScaleFactor=%" PRIu32 " DeviceScaleFactor=%" PRIu32 "",
138 current.PhysicalWidth, current.PhysicalHeight,
139 freerdp_desktop_rotation_flags_to_string(current.Orientation),
140 current.DesktopScaleFactor, current.DeviceScaleFactor);
141 }
142
143out:
144 Stream_SealLength(s);
145 status = callback->channel->Write(callback->channel, (UINT32)Stream_Length(s), Stream_Buffer(s),
146 nullptr);
147 Stream_Free(s, TRUE);
148 return status;
149}
150
156static UINT disp_recv_display_control_caps_pdu(GENERIC_CHANNEL_CALLBACK* callback, wStream* s)
157{
158 DISP_PLUGIN* disp = nullptr;
159 DispClientContext* context = nullptr;
160 UINT ret = CHANNEL_RC_OK;
161
162 WINPR_ASSERT(callback);
163 WINPR_ASSERT(s);
164
165 disp = (DISP_PLUGIN*)callback->plugin;
166 WINPR_ASSERT(disp);
167
168 context = disp->context;
169 WINPR_ASSERT(context);
170
171 if (!Stream_CheckAndLogRequiredLength(TAG, s, 12))
172 return ERROR_INVALID_DATA;
173
174 Stream_Read_UINT32(s, disp->MaxNumMonitors); /* MaxNumMonitors (4 bytes) */
175 Stream_Read_UINT32(s, disp->MaxMonitorAreaFactorA); /* MaxMonitorAreaFactorA (4 bytes) */
176 Stream_Read_UINT32(s, disp->MaxMonitorAreaFactorB); /* MaxMonitorAreaFactorB (4 bytes) */
177
178 if (context->DisplayControlCaps)
179 ret = context->DisplayControlCaps(context, disp->MaxNumMonitors,
180 disp->MaxMonitorAreaFactorA, disp->MaxMonitorAreaFactorB);
181
182 return ret;
183}
184
190static UINT disp_recv_pdu(GENERIC_CHANNEL_CALLBACK* callback, wStream* s)
191{
192 UINT32 error = 0;
193 DISPLAY_CONTROL_HEADER header = WINPR_C_ARRAY_INIT;
194
195 WINPR_ASSERT(callback);
196 WINPR_ASSERT(s);
197
198 DISP_PLUGIN* disp = (DISP_PLUGIN*)callback->plugin;
199 WINPR_ASSERT(disp);
200
201 if ((error = disp_read_header(s, &header, nullptr)))
202 {
203 WLog_Print(disp->base.log, WLOG_ERROR, "disp_read_header failed with error %" PRIu32 "!",
204 error);
205 return error;
206 }
207
208 switch (header.type)
209 {
210 case DISPLAY_CONTROL_PDU_TYPE_CAPS:
211 return disp_recv_display_control_caps_pdu(callback, s);
212
213 default:
214 WLog_Print(disp->base.log, WLOG_ERROR, "Type %" PRIu32 " not recognized!", header.type);
215 return ERROR_INTERNAL_ERROR;
216 }
217}
218
224static UINT disp_on_data_received(IWTSVirtualChannelCallback* pChannelCallback, wStream* data)
225{
226 GENERIC_CHANNEL_CALLBACK* callback = (GENERIC_CHANNEL_CALLBACK*)pChannelCallback;
227 return disp_recv_pdu(callback, data);
228}
229
235static UINT disp_on_close(IWTSVirtualChannelCallback* pChannelCallback)
236{
237 GENERIC_CHANNEL_CALLBACK* callback = (GENERIC_CHANNEL_CALLBACK*)pChannelCallback;
238
239 if (callback)
240 {
241 DISP_PLUGIN* disp = (DISP_PLUGIN*)callback->plugin;
242 WINPR_ASSERT(disp);
243
244 /* Lock here to ensure that no disp_send_monitor_layout is in progress. */
245 EnterCriticalSection(&disp->lock);
246 if (disp->base.listener_callback->channel_callback == callback)
247 disp->base.listener_callback->channel_callback = nullptr;
248 free(callback);
249 LeaveCriticalSection(&disp->lock);
250 }
251 return CHANNEL_RC_OK;
252}
253
263static UINT disp_send_monitor_layout(DispClientContext* context, UINT32 NumMonitors,
265{
266 DISP_PLUGIN* disp = nullptr;
267 GENERIC_CHANNEL_CALLBACK* callback = nullptr;
268
269 WINPR_ASSERT(context);
270
271 disp = (DISP_PLUGIN*)context->handle;
272 WINPR_ASSERT(disp);
273
274 UINT rc = CHANNEL_RC_NOT_OPEN;
275 EnterCriticalSection(&disp->lock);
276 callback = disp->base.listener_callback->channel_callback;
277 /* The channel might have been closed by the server in the meantime */
278 if (callback)
279 rc = disp_send_display_control_monitor_layout_pdu(callback, NumMonitors, Monitors);
280 LeaveCriticalSection(&disp->lock);
281 return rc;
282}
283
289static UINT disp_plugin_initialize(GENERIC_DYNVC_PLUGIN* base,
290 WINPR_ATTR_UNUSED rdpContext* rcontext,
291 WINPR_ATTR_UNUSED rdpSettings* settings)
292{
293 DispClientContext* context = nullptr;
294 DISP_PLUGIN* disp = (DISP_PLUGIN*)base;
295
296 WINPR_ASSERT(disp);
297 disp->MaxNumMonitors = 16;
298 disp->MaxMonitorAreaFactorA = 8192;
299 disp->MaxMonitorAreaFactorB = 8192;
300 InitializeCriticalSection(&disp->lock);
301
302 context = (DispClientContext*)calloc(1, sizeof(DispClientContext));
303 if (!context)
304 {
305 WLog_Print(base->log, WLOG_ERROR, "unable to allocate DispClientContext");
306 return CHANNEL_RC_NO_MEMORY;
307 }
308
309 context->handle = (void*)disp;
310 context->SendMonitorLayout = disp_send_monitor_layout;
311
312 disp->base.iface.pInterface = disp->context = context;
313
314 return CHANNEL_RC_OK;
315}
316
317static void disp_plugin_terminated(GENERIC_DYNVC_PLUGIN* base)
318{
319 DISP_PLUGIN* disp = (DISP_PLUGIN*)base;
320
321 WINPR_ASSERT(disp);
322
323 DeleteCriticalSection(&disp->lock);
324 free(disp->context);
325}
326
327static const IWTSVirtualChannelCallback disp_callbacks = { disp_on_data_received,
328 nullptr, /* Open */
329 disp_on_close, nullptr };
330
336FREERDP_ENTRY_POINT(UINT VCAPITYPE disp_DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints))
337{
338 return freerdp_generic_DVCPluginEntry(pEntryPoints, TAG, DISP_DVC_CHANNEL_NAME,
339 sizeof(DISP_PLUGIN), sizeof(GENERIC_CHANNEL_CALLBACK),
340 &disp_callbacks, disp_plugin_initialize,
341 disp_plugin_terminated);
342}