Merge pull request #4313 from hardening/dyn_res_update

disp: implement dynamic resolution for X11
This commit is contained in:
akallabeth 2017-12-19 13:49:02 +01:00 committed by GitHub
commit 71fd6f3116
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 339 additions and 36 deletions

View File

@ -98,7 +98,6 @@ UINT disp_send_display_control_monitor_layout_pdu(DISP_CHANNEL_CALLBACK* callbac
type = DISPLAY_CONTROL_PDU_TYPE_MONITOR_LAYOUT;
s = Stream_New(NULL, length);
if(!s)
{
WLog_ERR(TAG, "Stream_New failed!");
@ -112,10 +111,9 @@ UINT disp_send_display_control_monitor_layout_pdu(DISP_CHANNEL_CALLBACK* callbac
NumMonitors = disp->MaxNumMonitors;
Stream_Write_UINT32(s, MonitorLayoutSize); /* MonitorLayoutSize (4 bytes) */
Stream_Write_UINT32(s, NumMonitors); /* NumMonitors (4 bytes) */
//WLog_ERR(TAG, "NumMonitors: %"PRIu32"", NumMonitors);
WLog_DBG(TAG, "disp_send_display_control_monitor_layout_pdu: NumMonitors=%"PRIu32"", NumMonitors);
for (index = 0; index < NumMonitors; index++)
{
@ -147,16 +145,11 @@ UINT disp_send_display_control_monitor_layout_pdu(DISP_CHANNEL_CALLBACK* callbac
Stream_Write_UINT32(s, Monitors[index].DesktopScaleFactor); /* DesktopScaleFactor (4 bytes) */
Stream_Write_UINT32(s, Monitors[index].DeviceScaleFactor); /* DeviceScaleFactor (4 bytes) */
#if 0
WLog_DBG(TAG, "\t: Flags: 0x%08"PRIX32"", Monitors[index].Flags);
WLog_DBG(TAG, "\t: Left: %"PRId32"", Monitors[index].Left);
WLog_DBG(TAG, "\t: Top: %"PRId32"", Monitors[index].Top);
WLog_DBG(TAG, "\t: Width: %"PRIu32"", Monitors[index].Width);
WLog_DBG(TAG, "\t: Height: %"PRIu32"", Monitors[index].Height);
WLog_DBG(TAG, "\t: PhysicalWidth: %"PRIu32"", Monitors[index].PhysicalWidth);
WLog_DBG(TAG, "\t: PhysicalHeight: %"PRIu32"", Monitors[index].PhysicalHeight);
WLog_DBG(TAG, "\t: Orientation: %"PRIu32"", Monitors[index].Orientation);
#endif
WLog_DBG(TAG, "\t%d : Flags: 0x%08"PRIX32" Left/Top: (%"PRId32",%"PRId32") W/H=%"PRIu32"x%"PRIu32")", index,
Monitors[index].Flags, Monitors[index].Left, Monitors[index].Top, Monitors[index].Width,
Monitors[index].Height);
WLog_DBG(TAG, "\t PhysicalWidth: %"PRIu32" PhysicalHeight: %"PRIu32" Orientation: %"PRIu32"",
Monitors[index].PhysicalWidth, Monitors[index].PhysicalHeight, Monitors[index].Orientation);
}
Stream_SealLength(s);
@ -364,11 +357,9 @@ UINT DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints)
DispClientContext* context;
disp = (DISP_PLUGIN*) pEntryPoints->GetPlugin(pEntryPoints, "disp");
if (!disp)
{
disp = (DISP_PLUGIN*) calloc(1, sizeof(DISP_PLUGIN));
if (!disp)
{
WLog_ERR(TAG, "calloc failed!");
@ -379,9 +370,11 @@ UINT DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints)
disp->iface.Connected = NULL;
disp->iface.Disconnected = NULL;
disp->iface.Terminated = disp_plugin_terminated;
disp->MaxNumMonitors = 16;
disp->MaxMonitorAreaFactorA = 8192;
disp->MaxMonitorAreaFactorB = 8192;
context = (DispClientContext*) calloc(1, sizeof(DispClientContext));
if (!context)
{
WLog_ERR(TAG, "calloc failed!");
@ -390,15 +383,10 @@ UINT DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints)
}
context->handle = (void*) disp;
context->SendMonitorLayout = disp_send_monitor_layout;
disp->iface.pInterface = (void*) context;
disp->MaxNumMonitors = 16;
disp->MaxMonitorAreaFactorA = 8192;
disp->MaxMonitorAreaFactorB = 8192;
error = pEntryPoints->RegisterPlugin(pEntryPoints, "disp", (IWTSPlugin*) disp);
}
else

View File

@ -44,6 +44,8 @@ set(${MODULE_PREFIX}_SRCS
xf_cliprdr.h
xf_monitor.c
xf_monitor.h
xf_disp.c
xf_disp.h
xf_graphics.c
xf_graphics.h
xf_keyboard.c

View File

@ -30,6 +30,8 @@
#include "xf_tsmf.h"
#include "xf_rail.h"
#include "xf_cliprdr.h"
#include "xf_disp.h"
void xf_OnChannelConnectedEventHandler(rdpContext* context, ChannelConnectedEventArgs* e)
{
@ -63,6 +65,10 @@ void xf_OnChannelConnectedEventHandler(rdpContext* context, ChannelConnectedEven
{
xf_encomsp_init(xfc, (EncomspClientContext*) e->pInterface);
}
else if (strcmp(e->name, DISP_DVC_CHANNEL_NAME) == 0)
{
xf_disp_init(xfc, (DispClientContext*)e->pInterface);
}
}
void xf_OnChannelDisconnectedEventHandler(rdpContext* context, ChannelDisconnectedEventArgs* e)

View File

@ -28,6 +28,7 @@
#include <freerdp/client/cliprdr.h>
#include <freerdp/client/rdpgfx.h>
#include <freerdp/client/encomsp.h>
#include <freerdp/client/disp.h>
int xf_on_channel_connected(freerdp* instance, const char* name, void* pInterface);
int xf_on_channel_disconnected(freerdp* instance, const char* name, void* pInterface);

View File

@ -94,6 +94,7 @@
#include "xf_event.h"
#include "xf_input.h"
#include "xf_cliprdr.h"
#include "xf_disp.h"
#include "xf_monitor.h"
#include "xf_graphics.h"
#include "xf_keyboard.h"
@ -222,8 +223,7 @@ void xf_draw_screen(xfContext* xfc, int x, int y, int w, int h)
}
#endif
XCopyArea(xfc->display, xfc->primary, xfc->window->handle, xfc->gc, x, y, w, h,
x, y);
XCopyArea(xfc->display, xfc->primary, xfc->window->handle, xfc->gc, x, y, w, h, x, y);
}
static BOOL xf_desktop_resize(rdpContext* context)
@ -259,8 +259,7 @@ static BOOL xf_desktop_resize(rdpContext* context)
if (!xfc->fullscreen)
{
xf_ResizeDesktopWindow(xfc, xfc->window, settings->DesktopWidth,
settings->DesktopHeight);
xf_ResizeDesktopWindow(xfc, xfc->window, settings->DesktopWidth, settings->DesktopHeight);
}
else
{
@ -282,6 +281,9 @@ static BOOL xf_desktop_resize(rdpContext* context)
xfc->window->height);
}
if (xfc->xfDisp)
xf_disp_resized(xfc->xfDisp);
return TRUE;
}
@ -1269,6 +1271,12 @@ static BOOL xf_post_connect(freerdp* instance)
if (!(xfc->clipboard = xf_clipboard_new(xfc)))
return FALSE;
if (!(xfc->xfDisp = xf_disp_new(xfc)))
{
xf_clipboard_free(xfc->clipboard);
return FALSE;
}
EventArgsInit(&e, "xfreerdp");
e.width = settings->DesktopWidth;
e.height = settings->DesktopHeight;
@ -1294,6 +1302,12 @@ static void xf_post_disconnect(freerdp* instance)
xfc->clipboard = NULL;
}
if (xfc->xfDisp)
{
xf_disp_free(xfc->xfDisp);
xfc->xfDisp = NULL;
}
xf_window_free(xfc);
xf_keyboard_free(xfc);
}
@ -1452,6 +1466,7 @@ static void* xf_client_thread(void* param)
HANDLE inputEvent = NULL;
HANDLE inputThread = NULL;
rdpSettings* settings;
exit_code = 0;
instance = (freerdp*) param;
context = instance->context;

211
client/X11/xf_disp.c Normal file
View File

@ -0,0 +1,211 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* X11 Display Control channel
*
* Copyright 2017 David Fort <contact@hardening-consulting.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <X11/Xutil.h>
#ifdef WITH_XRANDR
#include <X11/extensions/Xrandr.h>
#include <X11/extensions/randr.h>
#endif
#include "xf_disp.h"
#include "xf_monitor.h"
#define TAG CLIENT_TAG("x11disp")
struct _xfDispContext
{
xfContext *xfc;
BOOL haveXRandr;
int eventBase, errorBase;
int lastWidth, lastHeight;
BOOL activated;
BOOL waitingResize;
};
xfDispContext *xf_disp_new(xfContext* xfc)
{
xfDispContext *ret = calloc(1, sizeof(xfDispContext));
if (!ret)
return NULL;
ret->xfc = xfc;
#ifdef WITH_XRANDR
if (XRRQueryExtension(xfc->display, &ret->eventBase, &ret->errorBase))
{
ret->haveXRandr = TRUE;
}
#endif
ret->lastWidth = xfc->context.settings->DesktopWidth;
ret->lastHeight = xfc->context.settings->DesktopHeight;
return ret;
}
void xf_disp_free(xfDispContext *disp)
{
free(disp);
}
static UINT xf_disp_sendLayout(DispClientContext *disp, rdpMonitor *monitors, int nmonitors)
{
UINT ret = CHANNEL_RC_OK;
DISPLAY_CONTROL_MONITOR_LAYOUT *layouts;
int i;
layouts = calloc(nmonitors, sizeof(DISPLAY_CONTROL_MONITOR_LAYOUT));
if (!layouts)
return CHANNEL_RC_NO_MEMORY;
for (i = 0; i < nmonitors; i++)
{
layouts[i].Flags = (monitors[i].is_primary ? DISPLAY_CONTROL_MONITOR_PRIMARY : 0);
layouts[i].Left = monitors[i].x;
layouts[i].Top = monitors[i].y;
layouts[i].Width = monitors[i].width;
layouts[i].Height = monitors[i].height;
layouts[i].Orientation = ORIENTATION_LANDSCAPE;
layouts[i].PhysicalWidth = monitors[i].width;
layouts[i].PhysicalHeight = monitors[i].height;
layouts[i].DesktopScaleFactor = 100;
layouts[i].DeviceScaleFactor = 100;
}
ret = disp->SendMonitorLayout(disp, nmonitors, layouts);
free(layouts);
return ret;
}
BOOL xf_disp_handle_xevent(xfContext *xfc, XEvent *event)
{
xfDispContext *xfDisp = xfc->xfDisp;
rdpSettings *settings = xfc->context.settings;
UINT32 maxWidth, maxHeight;
if (!xfDisp->haveXRandr)
return TRUE;
#ifdef WITH_XRANDR
if (event->type != xfDisp->eventBase + RRScreenChangeNotify)
return TRUE;
#endif
xf_detect_monitors(xfc, &maxWidth, &maxHeight);
return xf_disp_sendLayout(xfc->disp, settings->MonitorDefArray, settings->MonitorCount) == CHANNEL_RC_OK;
}
BOOL xf_disp_handle_resize(xfContext *xfc, int width, int height)
{
DISPLAY_CONTROL_MONITOR_LAYOUT layout;
xfDispContext *xfDisp = xfc->xfDisp;
if (xfDisp->lastWidth == width && xfDisp->lastHeight == height)
return TRUE;
if (xfDisp->waitingResize || !xfDisp->activated)
return TRUE;
xfDisp->lastWidth = width;
xfDisp->lastHeight = height;
xfDisp->waitingResize = TRUE;
layout.Flags = DISPLAY_CONTROL_MONITOR_PRIMARY;
layout.Top = layout.Left = 0;
layout.Width = width;
layout.Height = height;
layout.Orientation = ORIENTATION_LANDSCAPE;
layout.DesktopScaleFactor = 100;
layout.DeviceScaleFactor = 100;
layout.PhysicalWidth = width;
layout.PhysicalHeight = height;
return xfc->disp->SendMonitorLayout(xfc->disp, 1, &layout) == CHANNEL_RC_OK;
}
BOOL xf_disp_set_window_resizable(xfDispContext *xfDisp)
{
XSizeHints *size_hints;
if (!(size_hints = XAllocSizeHints()))
return FALSE;
size_hints->flags = PMinSize | PMaxSize | PWinGravity;
size_hints->win_gravity = NorthWestGravity;
size_hints->min_width = size_hints->min_height = 320;
size_hints->max_width = size_hints->max_height = 8192;
XSetWMNormalHints(xfDisp->xfc->display, xfDisp->xfc->window->handle, size_hints);
XFree(size_hints);
return TRUE;
}
void xf_disp_resized(xfDispContext *xfDisp)
{
rdpSettings *settings = xfDisp->xfc->context.settings;
xfDisp->waitingResize = FALSE;
if (xfDisp->activated && !settings->Fullscreen)
{
xf_disp_set_window_resizable(xfDisp);
}
}
UINT xf_DisplayControlCaps(DispClientContext *disp, UINT32 maxNumMonitors, UINT32 maxMonitorAreaFactorA, UINT32 maxMonitorAreaFactorB)
{
/* we're called only if dynamic resolution update is activated */
xfDispContext *xfDisp = (xfDispContext *)disp->custom;
rdpSettings *settings = xfDisp->xfc->context.settings;
WLog_DBG(TAG, "DisplayControlCapsPdu: MaxNumMonitors: %"PRIu32" MaxMonitorAreaFactorA: %"PRIu32" MaxMonitorAreaFactorB: %"PRIu32"",
maxNumMonitors, maxMonitorAreaFactorA, maxMonitorAreaFactorB);
xfDisp->activated = TRUE;
if (settings->Fullscreen)
return CHANNEL_RC_OK;
WLog_DBG(TAG, "DisplayControlCapsPdu: setting the window as resizeable");
return xf_disp_set_window_resizable(xfDisp) ? CHANNEL_RC_OK : CHANNEL_RC_NO_MEMORY;
}
BOOL xf_disp_init(xfContext* xfc, DispClientContext *disp)
{
rdpSettings *settings = xfc->context.settings;
xfc->disp = disp;
disp->custom = (void*) xfc->xfDisp;
if (settings->DynamicResolutionUpdate)
{
disp->DisplayControlCaps = xf_DisplayControlCaps;
#ifdef WITH_XRANDR
if (settings->Fullscreen)
{
/* ask X11 to notify us of screen changes */
XRRSelectInput(xfc->display, DefaultRootWindow(xfc->display), RRScreenChangeNotifyMask);
}
#endif
}
return TRUE;
}

38
client/X11/xf_disp.h Normal file
View File

@ -0,0 +1,38 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* X11 Display Control channel
*
* Copyright 2017 David Fort <contact@hardening-consulting.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef FREERDP_CLIENT_X11_DISP_H
#define FREERDP_CLIENT_X11_DISP_H
#include <freerdp/types.h>
#include <freerdp/client/disp.h>
#include "xf_client.h"
#include "xfreerdp.h"
typedef struct _xfDispContext xfDispContext;
FREERDP_API BOOL xf_disp_init(xfContext* xfc, DispClientContext *disp);
xfDispContext *xf_disp_new(xfContext* xfc);
void xf_disp_free(xfDispContext *disp);
BOOL xf_disp_handle_xevent(xfContext *xfc, XEvent *event);
BOOL xf_disp_handle_resize(xfContext *xfc, int width, int height);
void xf_disp_resized(xfDispContext *disp);
#endif /* FREERDP_CLIENT_X11_DISP_H */

View File

@ -30,6 +30,7 @@
#include "xf_rail.h"
#include "xf_window.h"
#include "xf_cliprdr.h"
#include "xf_disp.h"
#include "xf_input.h"
#include "xf_gfx.h"
@ -664,6 +665,17 @@ static BOOL xf_event_ConfigureNotify(xfContext* xfc, XEvent* event, BOOL app)
#endif
}
if (settings->DynamicResolutionUpdate)
{
int alignedWidth, alignedHeight;
alignedWidth = (xfc->window->width / 2) * 2;
alignedHeight = (xfc->window->height / 2) * 2;
/* ask the server to resize using the display channel */
xf_disp_handle_resize(xfc, alignedWidth, alignedHeight);
}
return TRUE;
}
@ -948,6 +960,7 @@ BOOL xf_event_process(freerdp* instance, XEvent* event)
BOOL status = TRUE;
xfAppWindow* appWindow;
xfContext* xfc = (xfContext*) instance->context;
rdpSettings *settings = xfc->context.settings;
if (xfc->remote_app)
{
@ -1047,11 +1060,17 @@ BOOL xf_event_process(freerdp* instance, XEvent* event)
case PropertyNotify:
status = xf_event_PropertyNotify(xfc, event, xfc->remote_app);
break;
default:
if (settings->SupportDisplayControl && xfc->xfDisp)
xf_disp_handle_xevent(xfc, event);
break;
}
xf_cliprdr_handle_xevent(xfc, event);
xf_input_handle_event(xfc, event);
XSync(xfc->display, FALSE);
return status;
}

View File

@ -297,11 +297,10 @@ BOOL xf_detect_monitors(xfContext* xfc, UINT32* pMaxWidth, UINT32* pMaxHeight)
settings->MonitorDefArray[nmonitors].x = vscreen->monitors[i].area.left;
settings->MonitorDefArray[nmonitors].y = vscreen->monitors[i].area.top;
settings->MonitorDefArray[nmonitors].width = MIN(vscreen->monitors[i].area.right
- vscreen->monitors[i].area.left + 1, *pMaxWidth);
settings->MonitorDefArray[nmonitors].height = MIN(
vscreen->monitors[i].area.bottom - vscreen->monitors[i].area.top + 1,
*pMaxHeight);
settings->MonitorDefArray[nmonitors].width =
MIN(vscreen->monitors[i].area.right - vscreen->monitors[i].area.left + 1, *pMaxWidth);
settings->MonitorDefArray[nmonitors].height =
MIN(vscreen->monitors[i].area.bottom - vscreen->monitors[i].area.top + 1, *pMaxHeight);
settings->MonitorDefArray[nmonitors].orig_screen = i;
if (i == settings->MonitorIds[0])

View File

@ -480,6 +480,7 @@ void xf_ResizeDesktopWindow(xfContext* xfc, xfWindow* window, int width,
int height)
{
XSizeHints* size_hints;
rdpSettings *settings = xfc->context.settings;
if (!xfc || !window)
return;
@ -493,13 +494,15 @@ void xf_ResizeDesktopWindow(xfContext* xfc, xfWindow* window, int width,
size_hints->max_width = size_hints->max_height = 16384;
XSetWMNormalHints(xfc->display, window->handle, size_hints);
XResizeWindow(xfc->display, window->handle, width, height);
#ifdef WITH_XRENDER
if (!xfc->context.settings->SmartSizing)
#ifdef WITH_XRENDER
if (!settings->SmartSizing)
#endif
{
if (!xfc->fullscreen)
{
/* min == max is an hint for the WM to indicate that the window should
* not be resizable */
size_hints->min_width = size_hints->max_width = width;
size_hints->min_height = size_hints->max_height = height;
XSetWMNormalHints(xfc->display, window->handle, size_hints);

View File

@ -81,6 +81,7 @@ struct xf_glyph
typedef struct xf_glyph xfGlyph;
typedef struct xf_clipboard xfClipboard;
typedef struct _xfDispContext xfDispContext;
/* Value of the first logical button number in X11 which must be */
/* subtracted to go from a button number in X11 to an index into */
@ -171,9 +172,6 @@ struct xf_context
BOOL complex_regions;
VIRTUAL_SCREEN vscreen;
void* xv_context;
TsmfClientContext* tsmf;
xfClipboard* clipboard;
CliprdrClientContext* cliprdr;
Atom UTF8_STRING;
@ -209,9 +207,14 @@ struct xf_context
Atom WM_DELETE_WINDOW;
/* Channels */
TsmfClientContext* tsmf;
xfClipboard* clipboard;
CliprdrClientContext* cliprdr;
RdpeiClientContext* rdpei;
RdpgfxClientContext* gfx;
EncomspClientContext* encomsp;
xfDispContext *xfDisp;
DispClientContext *disp;
RailClientContext* rail;
wHashTable* railWindows;

View File

@ -88,6 +88,7 @@ static COMMAND_LINE_ARGUMENT_A args[] =
{ "drive", COMMAND_LINE_VALUE_REQUIRED, "<name>,<path>", NULL, NULL, -1, NULL, "Redirect directory <path> as named share <name>" },
{ "drives", COMMAND_LINE_VALUE_BOOL, NULL, BoolValueFalse, NULL, -1, NULL, "Redirect all mount points as shares" },
{ "dvc", COMMAND_LINE_VALUE_REQUIRED, "<channel>[,<options>]", NULL, NULL, -1, NULL, "Dynamic virtual channel" },
{ "dynamic-resolution", COMMAND_LINE_VALUE_FLAG, NULL, NULL, NULL, -1, NULL, "Send resolution updates when the window is resized" },
{ "echo", COMMAND_LINE_VALUE_FLAG, NULL, NULL, NULL, -1, "echo", "Echo channel" },
{ "encryption", COMMAND_LINE_VALUE_BOOL, NULL, BoolValueTrue, NULL, -1, NULL, "Encryption (experimental)" },
{ "encryption-methods", COMMAND_LINE_VALUE_REQUIRED, "[40,][56,][128,][FIPS]", NULL, NULL, -1, NULL, "RDP standard security encryption methods" },
@ -1752,8 +1753,24 @@ int freerdp_client_settings_parse_command_line_arguments(rdpSettings* settings,
{
settings->Decorations = arg->Value ? TRUE : FALSE;
}
CommandLineSwitchCase(arg, "dynamic-resolution")
{
if (settings->SmartSizing)
{
WLog_ERR(TAG, "Smart sizing and dynamic resolution are mutually exclusive options");
return COMMAND_LINE_ERROR_UNEXPECTED_VALUE;
}
settings->SupportDisplayControl = TRUE;
settings->DynamicResolutionUpdate = TRUE;
}
CommandLineSwitchCase(arg, "smart-sizing")
{
if (settings->DynamicResolutionUpdate)
{
WLog_ERR(TAG, "Smart sizing and dynamic resolution are mutually exclusive options");
return COMMAND_LINE_ERROR_UNEXPECTED_VALUE;
}
settings->SmartSizing = TRUE;
if (arg->Value)

View File

@ -1104,7 +1104,8 @@ struct rdp_settings
ALIGN64 UINT32 SmartSizingHeight; /* 1555 */
ALIGN64 BOOL PercentScreenUseWidth; /* 1556 */
ALIGN64 BOOL PercentScreenUseHeight; /* 1557 */
UINT64 padding1601[1601 - 1558]; /* 1558 */
ALIGN64 BOOL DynamicResolutionUpdate; /* 1558 */
UINT64 padding1601[1601 - 1559]; /* 1559 */
/* Miscellaneous */
ALIGN64 BOOL SoftwareGdi; /* 1601 */