Resolution switching without reconnecting.
- Based on https://github.com/jsorg71/xrdp/tree/dynamic_monitor - Tested with xorgxrdp - Tested with vnc - Only works with single monitor. - Update documentation to clarify the difference between MSTSC and Microsoft Remote Desktop. - Does not include compatibility with /gfx at this time, which is still in testing. - Updates to include ms-rdpedisp.h header for the 2.2.2 specification of the protocol. - Adds new dynamic_monitor_layout struct that shares the number of monitors with xrdp_client_info.h - Does not allow for BPP changes because the RDP protocol doesn't support it. - Option to disable feature as NeutrinoRDP doesn't support it (It was based on FreeRDP 1.0.1 which didn't yet have this feature.) - Add CLIENT_MONITOR_DATA_MAXIMUM_MONITORS constant and reference spec definition. Depends on https://github.com/neutrinolabs/xorgxrdp/pull/183
This commit is contained in:
@@ -9,9 +9,15 @@
|
|||||||
## Overview
|
## Overview
|
||||||
|
|
||||||
**xrdp** provides a graphical login to remote machines using Microsoft
|
**xrdp** provides a graphical login to remote machines using Microsoft
|
||||||
Remote Desktop Protocol (RDP). xrdp accepts connections from a variety of
|
Remote Desktop Protocol (RDP). xrdp accepts connections from a variety of RDP clients:
|
||||||
RDP clients: FreeRDP, rdesktop, KRDC, NeutrinoRDP and Microsoft Remote Desktop
|
- FreeRDP
|
||||||
Client (for Windows, Mac OS, iOS and Android).
|
- rdesktop
|
||||||
|
- KRDC
|
||||||
|
- NeutrinoRDP
|
||||||
|
- Windows MSTSC (Microsoft Terminal Services Client)
|
||||||
|
- Microsoft Remote Desktop (which is distinct from MSTSC)
|
||||||
|
|
||||||
|
Many of these work on some or all of Windows, Mac OS, iOS, and/or Android.
|
||||||
|
|
||||||
RDP transport is encrypted using TLS by default.
|
RDP transport is encrypted using TLS by default.
|
||||||
|
|
||||||
@@ -24,7 +30,7 @@ RDP transport is encrypted using TLS by default.
|
|||||||
* Connect to a Linux desktop using RDP from anywhere (requires
|
* Connect to a Linux desktop using RDP from anywhere (requires
|
||||||
[xorgxrdp](https://github.com/neutrinolabs/xorgxrdp) Xorg module)
|
[xorgxrdp](https://github.com/neutrinolabs/xorgxrdp) Xorg module)
|
||||||
* Reconnect to an existing session
|
* Reconnect to an existing session
|
||||||
* Session resizing
|
* Session resizing on connect and while an existing session is active.
|
||||||
* RDP/VNC proxy (connect to another RDP/VNC server via xrdp)
|
* RDP/VNC proxy (connect to another RDP/VNC server via xrdp)
|
||||||
|
|
||||||
### Access to Remote Resources
|
### Access to Remote Resources
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ include_HEADERS = \
|
|||||||
ms-rdpegdi.h \
|
ms-rdpegdi.h \
|
||||||
ms-rdpele.h \
|
ms-rdpele.h \
|
||||||
ms-rdperp.h \
|
ms-rdperp.h \
|
||||||
|
ms-rdpedisp.h \
|
||||||
ms-smb2.h \
|
ms-smb2.h \
|
||||||
xrdp_client_info.h \
|
xrdp_client_info.h \
|
||||||
xrdp_constants.h \
|
xrdp_constants.h \
|
||||||
|
|||||||
+6
-1
@@ -78,7 +78,12 @@
|
|||||||
/* This isn't explicitly named in MS-RDPBCGR */
|
/* This isn't explicitly named in MS-RDPBCGR */
|
||||||
#define CHANNEL_NAME_LEN 7
|
#define CHANNEL_NAME_LEN 7
|
||||||
|
|
||||||
/* Oprions field */
|
/* 2.2.1.3.6 Client Monitor Data - */
|
||||||
|
/* monitorCount (4 bytes): A 32-bit, unsigned integer. The number of display */
|
||||||
|
/* monitor definitions in the monitorDefArray field (the maximum allowed is 16). */
|
||||||
|
#define CLIENT_MONITOR_DATA_MAXIMUM_MONITORS 16
|
||||||
|
|
||||||
|
/* Options field */
|
||||||
/* NOTE: XR_ prefixed to avoid conflict with FreeRDP */
|
/* NOTE: XR_ prefixed to avoid conflict with FreeRDP */
|
||||||
#define XR_CHANNEL_OPTION_INITIALIZED 0x80000000
|
#define XR_CHANNEL_OPTION_INITIALIZED 0x80000000
|
||||||
#define XR_CHANNEL_OPTION_ENCRYPT_RDP 0x40000000
|
#define XR_CHANNEL_OPTION_ENCRYPT_RDP 0x40000000
|
||||||
|
|||||||
@@ -0,0 +1,29 @@
|
|||||||
|
/**
|
||||||
|
* xrdp: A Remote Desktop Protocol server.
|
||||||
|
*
|
||||||
|
* MS-RDPEDISP : Definitions from [MS-RDPEDISP]
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* References to MS-RDPEDISP are currently correct for v20201030 of that
|
||||||
|
* document
|
||||||
|
*/
|
||||||
|
|
||||||
|
#if !defined(MS_RDPEDISP_H)
|
||||||
|
#define MS_RDPEDISP_H
|
||||||
|
|
||||||
|
/* Display Control Messages: Display Virtual Channel Extension (2.2.2) */
|
||||||
|
#define DISPLAYCONTROL_PDU_TYPE_MONITOR_LAYOUT 0x00000002
|
||||||
|
#define DISPLAYCONTROL_PDU_TYPE_CAPS 0x00000005
|
||||||
|
|
||||||
|
#endif /* MS_RDPEDISP_H */
|
||||||
@@ -19,6 +19,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "xrdp_constants.h"
|
#include "xrdp_constants.h"
|
||||||
|
#include "ms-rdpbcgr.h"
|
||||||
|
|
||||||
#if !defined(XRDP_CLIENT_INFO_H)
|
#if !defined(XRDP_CLIENT_INFO_H)
|
||||||
#define XRDP_CLIENT_INFO_H
|
#define XRDP_CLIENT_INFO_H
|
||||||
@@ -120,8 +121,8 @@ struct xrdp_client_info
|
|||||||
int security_layer; /* 0 = rdp, 1 = tls , 2 = hybrid */
|
int security_layer; /* 0 = rdp, 1 = tls , 2 = hybrid */
|
||||||
int multimon; /* 0 = deny , 1 = allow */
|
int multimon; /* 0 = deny , 1 = allow */
|
||||||
int monitorCount; /* number of monitors detected (max = 16) */
|
int monitorCount; /* number of monitors detected (max = 16) */
|
||||||
struct monitor_info minfo[16]; /* client monitor data */
|
struct monitor_info minfo[CLIENT_MONITOR_DATA_MAXIMUM_MONITORS]; /* client monitor data */
|
||||||
struct monitor_info minfo_wm[16]; /* client monitor data, non-negative values */
|
struct monitor_info minfo_wm[CLIENT_MONITOR_DATA_MAXIMUM_MONITORS]; /* client monitor data, non-negative values */
|
||||||
|
|
||||||
int keyboard_type;
|
int keyboard_type;
|
||||||
int keyboard_subtype;
|
int keyboard_subtype;
|
||||||
|
|||||||
@@ -776,7 +776,12 @@ xrdp_channel_drdynvc_start(struct xrdp_channel *self)
|
|||||||
struct mcs_channel_item *ci;
|
struct mcs_channel_item *ci;
|
||||||
struct mcs_channel_item *dci;
|
struct mcs_channel_item *dci;
|
||||||
|
|
||||||
|
LOG_DEVEL(LOG_LEVEL_INFO, "xrdp_channel_drdynvc_start: drdynvc_channel_id %d", self->drdynvc_channel_id);
|
||||||
|
if (self->drdynvc_channel_id != -1)
|
||||||
|
{
|
||||||
|
LOG_DEVEL(LOG_LEVEL_INFO, "xrdp_channel_drdynvc_start: already started");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
dci = NULL;
|
dci = NULL;
|
||||||
count = self->mcs_layer->channel_list->count;
|
count = self->mcs_layer->channel_list->count;
|
||||||
for (index = 0; index < count; index++)
|
for (index = 0; index < count; index++)
|
||||||
|
|||||||
@@ -492,6 +492,10 @@ lxrdp_set_param(struct mod *mod, const char *name, const char *value)
|
|||||||
{
|
{
|
||||||
settings->nla_security = g_text2bool(value);
|
settings->nla_security = g_text2bool(value);
|
||||||
}
|
}
|
||||||
|
else if (g_strcmp(name, "enable_dynamic_resizing") == 0)
|
||||||
|
{
|
||||||
|
settings->desktop_resize = g_text2bool(value);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
LOG(LOG_LEVEL_WARNING, "lxrdp_set_param: unknown name [%s] value [%s]", name, value);
|
LOG(LOG_LEVEL_WARNING, "lxrdp_set_param: unknown name [%s] value [%s]", name, value);
|
||||||
@@ -599,6 +603,27 @@ lxrdp_suppress_output(struct mod *mod, int suppress,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
static int
|
||||||
|
lxrdp_server_version_message(struct mod *mod)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
static int
|
||||||
|
lxrdp_server_monitor_resize(struct mod *mod, int width, int height)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
static int
|
||||||
|
lxrdp_server_monitor_full_invalidate(struct mod *mod, int width, int height)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
static void
|
static void
|
||||||
lfreerdp_begin_paint(rdpContext *context)
|
lfreerdp_begin_paint(rdpContext *context)
|
||||||
@@ -2074,6 +2099,9 @@ mod_init(void)
|
|||||||
mod->mod_check_wait_objs = lxrdp_check_wait_objs;
|
mod->mod_check_wait_objs = lxrdp_check_wait_objs;
|
||||||
mod->mod_frame_ack = lxrdp_frame_ack;
|
mod->mod_frame_ack = lxrdp_frame_ack;
|
||||||
mod->mod_suppress_output = lxrdp_suppress_output;
|
mod->mod_suppress_output = lxrdp_suppress_output;
|
||||||
|
mod->mod_server_version_message = lxrdp_server_version_message;
|
||||||
|
mod->mod_server_monitor_resize = lxrdp_server_monitor_resize;
|
||||||
|
mod->mod_server_monitor_full_invalidate = lxrdp_server_monitor_full_invalidate;
|
||||||
|
|
||||||
mod->inst = freerdp_new();
|
mod->inst = freerdp_new();
|
||||||
mod->inst->PreConnect = lfreerdp_pre_connect;
|
mod->inst->PreConnect = lfreerdp_pre_connect;
|
||||||
|
|||||||
@@ -82,8 +82,13 @@ struct mod
|
|||||||
int (*mod_frame_ack)(struct mod *mod, int flags, int frame_id);
|
int (*mod_frame_ack)(struct mod *mod, int flags, int frame_id);
|
||||||
int (*mod_suppress_output)(struct mod *mod, int suppress,
|
int (*mod_suppress_output)(struct mod *mod, int suppress,
|
||||||
int left, int top, int right, int bottom);
|
int left, int top, int right, int bottom);
|
||||||
tintptr mod_dumby[100 - 11]; /* align, 100 minus the number of mod
|
int (*mod_server_monitor_resize)(struct mod *mod,
|
||||||
functions above */
|
int width, int height);
|
||||||
|
int (*mod_server_monitor_full_invalidate)(struct mod *mod,
|
||||||
|
int width, int height);
|
||||||
|
int (*mod_server_version_message)(struct mod *mod);
|
||||||
|
tintptr mod_dumby[100 - 14]; /* align, 100 minus the number of mod
|
||||||
|
functions above */
|
||||||
/* server functions */
|
/* server functions */
|
||||||
int (*server_begin_update)(struct mod *v);
|
int (*server_begin_update)(struct mod *v);
|
||||||
int (*server_end_update)(struct mod *v);
|
int (*server_end_update)(struct mod *v);
|
||||||
|
|||||||
@@ -1224,7 +1224,7 @@ send_update_request_for_resize_status(struct vnc *v)
|
|||||||
make_stream(s);
|
make_stream(s);
|
||||||
init_stream(s, 8192);
|
init_stream(s, 8192);
|
||||||
|
|
||||||
switch (v->initial_resize_status)
|
switch (v->resize_status)
|
||||||
{
|
{
|
||||||
case VRS_WAITING_FOR_FIRST_UPDATE:
|
case VRS_WAITING_FOR_FIRST_UPDATE:
|
||||||
/*
|
/*
|
||||||
@@ -1364,14 +1364,14 @@ lib_framebuffer_first_update(struct vnc *v)
|
|||||||
{
|
{
|
||||||
LOG(LOG_LEVEL_DEBUG, "Server layout is the same "
|
LOG(LOG_LEVEL_DEBUG, "Server layout is the same "
|
||||||
"as the client layout");
|
"as the client layout");
|
||||||
v->initial_resize_status = VRS_DONE;
|
v->resize_status = VRS_DONE;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
LOG(LOG_LEVEL_DEBUG, "Server layout differs from "
|
LOG(LOG_LEVEL_DEBUG, "Server layout differs from "
|
||||||
"the client layout. Changing server layout");
|
"the client layout. Changing server layout");
|
||||||
error = send_set_desktop_size(v, &v->client_layout);
|
error = send_set_desktop_size(v, &v->client_layout);
|
||||||
v->initial_resize_status = VRS_WAITING_FOR_RESIZE_CONFIRM;
|
v->resize_status = VRS_WAITING_FOR_RESIZE_CONFIRM;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -1382,7 +1382,7 @@ lib_framebuffer_first_update(struct vnc *v)
|
|||||||
LOG(LOG_LEVEL_DEBUG, "Resizing client to server %dx%d",
|
LOG(LOG_LEVEL_DEBUG, "Resizing client to server %dx%d",
|
||||||
v->server_width, v->server_height);
|
v->server_width, v->server_height);
|
||||||
error = resize_client(v, 0, v->server_width, v->server_height);
|
error = resize_client(v, 0, v->server_width, v->server_height);
|
||||||
v->initial_resize_status = VRS_DONE;
|
v->resize_status = VRS_DONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
g_free(layout.s);
|
g_free(layout.s);
|
||||||
@@ -1437,7 +1437,7 @@ lib_framebuffer_waiting_for_resize_confirm(struct vnc *v)
|
|||||||
v->server_width, v->server_height);
|
v->server_width, v->server_height);
|
||||||
error = resize_client(v, 0, v->server_width, v->server_height);
|
error = resize_client(v, 0, v->server_width, v->server_height);
|
||||||
}
|
}
|
||||||
v->initial_resize_status = VRS_DONE;
|
v->resize_status = VRS_DONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
g_free(layout.s);
|
g_free(layout.s);
|
||||||
@@ -1777,7 +1777,7 @@ lib_mod_process_message(struct vnc *v, struct stream *s)
|
|||||||
{
|
{
|
||||||
if (type == S2C_FRAMEBUFFER_UPDATE)
|
if (type == S2C_FRAMEBUFFER_UPDATE)
|
||||||
{
|
{
|
||||||
switch (v->initial_resize_status)
|
switch (v->resize_status)
|
||||||
{
|
{
|
||||||
case VRS_WAITING_FOR_FIRST_UPDATE:
|
case VRS_WAITING_FOR_FIRST_UPDATE:
|
||||||
error = lib_framebuffer_first_update(v);
|
error = lib_framebuffer_first_update(v);
|
||||||
@@ -2237,7 +2237,7 @@ lib_mod_connect(struct vnc *v)
|
|||||||
|
|
||||||
if (error == 0)
|
if (error == 0)
|
||||||
{
|
{
|
||||||
v->initial_resize_status = VRS_WAITING_FOR_FIRST_UPDATE;
|
v->resize_status = VRS_WAITING_FOR_FIRST_UPDATE;
|
||||||
error = send_update_request_for_resize_status(v);
|
error = send_update_request_for_resize_status(v);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2465,6 +2465,34 @@ lib_mod_suppress_output(struct vnc *v, int suppress,
|
|||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
/* return error */
|
||||||
|
int
|
||||||
|
lib_mod_server_version_message(struct vnc *v)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
/* return error */
|
||||||
|
int
|
||||||
|
lib_mod_server_monitor_resize(struct vnc *v, int width, int height)
|
||||||
|
{
|
||||||
|
int error = 0;
|
||||||
|
set_single_screen_layout(&v->client_layout, width, height);
|
||||||
|
v->resize_status = VRS_WAITING_FOR_FIRST_UPDATE;
|
||||||
|
error = send_update_request_for_resize_status(v);
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
/* return error */
|
||||||
|
int
|
||||||
|
lib_mod_server_monitor_full_invalidate(struct vnc *v, int param1, int param2)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
tintptr EXPORT_CC
|
tintptr EXPORT_CC
|
||||||
mod_init(void)
|
mod_init(void)
|
||||||
@@ -2486,6 +2514,9 @@ mod_init(void)
|
|||||||
v->mod_check_wait_objs = lib_mod_check_wait_objs;
|
v->mod_check_wait_objs = lib_mod_check_wait_objs;
|
||||||
v->mod_frame_ack = lib_mod_frame_ack;
|
v->mod_frame_ack = lib_mod_frame_ack;
|
||||||
v->mod_suppress_output = lib_mod_suppress_output;
|
v->mod_suppress_output = lib_mod_suppress_output;
|
||||||
|
v->mod_server_monitor_resize = lib_mod_server_monitor_resize;
|
||||||
|
v->mod_server_monitor_full_invalidate = lib_mod_server_monitor_full_invalidate;
|
||||||
|
v->mod_server_version_message = lib_mod_server_version_message;
|
||||||
|
|
||||||
/* Member variables */
|
/* Member variables */
|
||||||
v->enabled_encodings_mask = -1;
|
v->enabled_encodings_mask = -1;
|
||||||
|
|||||||
@@ -77,7 +77,12 @@ struct vnc
|
|||||||
int (*mod_frame_ack)(struct vnc *v, int flags, int frame_id);
|
int (*mod_frame_ack)(struct vnc *v, int flags, int frame_id);
|
||||||
int (*mod_suppress_output)(struct vnc *v, int suppress,
|
int (*mod_suppress_output)(struct vnc *v, int suppress,
|
||||||
int left, int top, int right, int bottom);
|
int left, int top, int right, int bottom);
|
||||||
tintptr mod_dumby[100 - 11]; /* align, 100 minus the number of mod
|
int (*mod_server_monitor_resize)(struct vnc *v,
|
||||||
|
int width, int height);
|
||||||
|
int (*mod_server_monitor_full_invalidate)(struct vnc *v,
|
||||||
|
int width, int height);
|
||||||
|
int (*mod_server_version_message)(struct vnc *v);
|
||||||
|
tintptr mod_dumby[100 - 14]; /* align, 100 minus the number of mod
|
||||||
functions above */
|
functions above */
|
||||||
/* server functions */
|
/* server functions */
|
||||||
int (*server_begin_update)(struct vnc *v);
|
int (*server_begin_update)(struct vnc *v);
|
||||||
@@ -152,5 +157,5 @@ struct vnc
|
|||||||
unsigned int enabled_encodings_mask;
|
unsigned int enabled_encodings_mask;
|
||||||
/* Resizeable support */
|
/* Resizeable support */
|
||||||
struct vnc_screen_layout client_layout;
|
struct vnc_screen_layout client_layout;
|
||||||
enum vnc_resize_status initial_resize_status;
|
enum vnc_resize_status resize_status;
|
||||||
};
|
};
|
||||||
|
|||||||
+15
@@ -367,6 +367,21 @@ xrdp_bitmap_compress(char *in_data, int width, int height,
|
|||||||
int e);
|
int e);
|
||||||
|
|
||||||
/* xrdp_mm.c */
|
/* xrdp_mm.c */
|
||||||
|
|
||||||
|
struct dynamic_monitor_layout
|
||||||
|
{
|
||||||
|
int flags;
|
||||||
|
int left;
|
||||||
|
int top;
|
||||||
|
int width;
|
||||||
|
int height;
|
||||||
|
int physical_width;
|
||||||
|
int physical_height;
|
||||||
|
int orientation;
|
||||||
|
int desktop_scale_factor;
|
||||||
|
int device_scale_factor;
|
||||||
|
};
|
||||||
|
|
||||||
int
|
int
|
||||||
xrdp_mm_drdynvc_up(struct xrdp_mm *self);
|
xrdp_mm_drdynvc_up(struct xrdp_mm *self);
|
||||||
int
|
int
|
||||||
|
|||||||
@@ -235,6 +235,9 @@ ip=ask
|
|||||||
port=ask3389
|
port=ask3389
|
||||||
username=ask
|
username=ask
|
||||||
password=ask
|
password=ask
|
||||||
|
; Currently NeutrinoRDP doesn't support dynamic resizing. Uncomment
|
||||||
|
; this line if you're using a client which does.
|
||||||
|
#enable_dynamic_resizing=false
|
||||||
|
|
||||||
; You can override the common channel settings for each session type
|
; You can override the common channel settings for each session type
|
||||||
#channel.rdpdr=true
|
#channel.rdpdr=true
|
||||||
|
|||||||
+200
-2
@@ -24,6 +24,8 @@
|
|||||||
#include "xrdp.h"
|
#include "xrdp.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "string_calls.h"
|
#include "string_calls.h"
|
||||||
|
#include "ms-rdpedisp.h"
|
||||||
|
#include "ms-rdpbcgr.h"
|
||||||
|
|
||||||
#ifdef USE_PAM
|
#ifdef USE_PAM
|
||||||
#if defined(HAVE__PAM_TYPES_H)
|
#if defined(HAVE__PAM_TYPES_H)
|
||||||
@@ -992,12 +994,208 @@ xrdp_mm_process_rail_drawing_orders(struct xrdp_mm *self, struct stream *s)
|
|||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
static int
|
||||||
|
dynamic_monitor_open_response(intptr_t id, int chan_id, int creation_status)
|
||||||
|
{
|
||||||
|
struct xrdp_process *pro;
|
||||||
|
struct xrdp_wm *wm;
|
||||||
|
struct stream *s;
|
||||||
|
int bytes;
|
||||||
|
|
||||||
|
LOG_DEVEL(LOG_LEVEL_TRACE, "dynamic_monitor_open_response: chan_id %d creation_status 0x%8.8x", chan_id, creation_status);
|
||||||
|
if (creation_status != 0)
|
||||||
|
{
|
||||||
|
LOG(LOG_LEVEL_ERROR, "dynamic_monitor_open_response: error");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
pro = (struct xrdp_process *) id;
|
||||||
|
wm = pro->wm;
|
||||||
|
make_stream(s);
|
||||||
|
init_stream(s, 1024);
|
||||||
|
out_uint32_le(s, 5); /* DISPLAYCONTROL_PDU_TYPE_CAPS */
|
||||||
|
out_uint32_le(s, 8 + 12);
|
||||||
|
out_uint32_le(s, CLIENT_MONITOR_DATA_MAXIMUM_MONITORS); /* MaxNumMonitors */
|
||||||
|
out_uint32_le(s, 4096); /* MaxMonitorAreaFactorA */
|
||||||
|
out_uint32_le(s, 2048); /* MaxMonitorAreaFactorB */
|
||||||
|
s_mark_end(s);
|
||||||
|
bytes = (int) (s->end - s->data);
|
||||||
|
libxrdp_drdynvc_data(wm->session, chan_id, s->data, bytes);
|
||||||
|
free_stream(s);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
static int
|
||||||
|
dynamic_monitor_close_response(intptr_t id, int chan_id)
|
||||||
|
{
|
||||||
|
LOG_DEVEL(LOG_LEVEL_TRACE, "dynamic_monitor_close_response:");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
static int
|
||||||
|
dynamic_monitor_data_first(intptr_t id, int chan_id, char *data, int bytes,
|
||||||
|
int total_bytes)
|
||||||
|
{
|
||||||
|
LOG_DEVEL(LOG_LEVEL_TRACE, "dynamic_monitor_data_first:");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
static int
|
||||||
|
dynamic_monitor_data(intptr_t id, int chan_id, char *data, int bytes)
|
||||||
|
{
|
||||||
|
struct stream ls;
|
||||||
|
struct stream *s;
|
||||||
|
int msg_type;
|
||||||
|
int msg_length;
|
||||||
|
int monitor_index;
|
||||||
|
struct xrdp_process *pro;
|
||||||
|
struct xrdp_wm *wm;
|
||||||
|
|
||||||
|
int MonitorLayoutSize;
|
||||||
|
int NumMonitor;
|
||||||
|
|
||||||
|
struct dynamic_monitor_layout monitor_layouts[CLIENT_MONITOR_DATA_MAXIMUM_MONITORS];
|
||||||
|
struct dynamic_monitor_layout *monitor_layout;
|
||||||
|
|
||||||
|
struct xrdp_rect rect;
|
||||||
|
int session_width;
|
||||||
|
int session_height;
|
||||||
|
|
||||||
|
LOG_DEVEL(LOG_LEVEL_TRACE, "dynamic_monitor_data:");
|
||||||
|
pro = (struct xrdp_process *) id;
|
||||||
|
wm = pro->wm;
|
||||||
|
g_memset(&ls, 0, sizeof(ls));
|
||||||
|
ls.data = data;
|
||||||
|
ls.p = ls.data;
|
||||||
|
ls.size = bytes;
|
||||||
|
ls.end = ls.data + bytes;
|
||||||
|
s = &ls;
|
||||||
|
in_uint32_le(s, msg_type);
|
||||||
|
in_uint32_le(s, msg_length);
|
||||||
|
LOG(LOG_LEVEL_DEBUG, "dynamic_monitor_data: msg_type %d msg_length %d",
|
||||||
|
msg_type, msg_length);
|
||||||
|
|
||||||
|
rect.left = 8192;
|
||||||
|
rect.top = 8192;
|
||||||
|
rect.right = -8192;
|
||||||
|
rect.bottom = -8192;
|
||||||
|
|
||||||
|
if (msg_type == DISPLAYCONTROL_PDU_TYPE_MONITOR_LAYOUT)
|
||||||
|
{
|
||||||
|
in_uint32_le(s, MonitorLayoutSize);
|
||||||
|
in_uint32_le(s, NumMonitor);
|
||||||
|
LOG(LOG_LEVEL_DEBUG, " MonitorLayoutSize %d NumMonitor %d",
|
||||||
|
MonitorLayoutSize, NumMonitor);
|
||||||
|
for (monitor_index = 0; monitor_index < NumMonitor; monitor_index++)
|
||||||
|
{
|
||||||
|
monitor_layout = monitor_layouts + monitor_index;
|
||||||
|
in_uint32_le(s, monitor_layout->flags);
|
||||||
|
in_uint32_le(s, monitor_layout->left);
|
||||||
|
in_uint32_le(s, monitor_layout->top);
|
||||||
|
in_uint32_le(s, monitor_layout->width);
|
||||||
|
in_uint32_le(s, monitor_layout->height);
|
||||||
|
in_uint32_le(s, monitor_layout->physical_width);
|
||||||
|
in_uint32_le(s, monitor_layout->physical_height);
|
||||||
|
in_uint32_le(s, monitor_layout->orientation);
|
||||||
|
in_uint32_le(s, monitor_layout->desktop_scale_factor);
|
||||||
|
in_uint32_le(s, monitor_layout->device_scale_factor);
|
||||||
|
LOG_DEVEL(LOG_LEVEL_DEBUG, " Flags 0x%8.8x Left %d Top %d "
|
||||||
|
"Width %d Height %d PhysicalWidth %d PhysicalHeight %d "
|
||||||
|
"Orientation %d DesktopScaleFactor %d DeviceScaleFactor %d",
|
||||||
|
monitor_layout->flags, monitor_layout->left, monitor_layout->top,
|
||||||
|
monitor_layout->width, monitor_layout->height,
|
||||||
|
monitor_layout->physical_width, monitor_layout->physical_height,
|
||||||
|
monitor_layout->orientation, monitor_layout->desktop_scale_factor,
|
||||||
|
monitor_layout->device_scale_factor);
|
||||||
|
|
||||||
|
rect.left = MIN(monitor_layout->left, rect.left);
|
||||||
|
rect.top = MIN(monitor_layout->top, rect.top);
|
||||||
|
rect.right = MAX(rect.right, monitor_layout->left + monitor_layout->width);
|
||||||
|
rect.bottom = MAX(rect.bottom, monitor_layout->top + monitor_layout->height);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
session_width = rect.right - rect.left;
|
||||||
|
session_height = rect.bottom - rect.top;
|
||||||
|
if ((session_width > 0) && (session_height > 0))
|
||||||
|
{
|
||||||
|
// TODO: Unify this logic with server_reset
|
||||||
|
libxrdp_reset(wm->session, session_width, session_height, wm->screen->bpp);
|
||||||
|
/* reset cache */
|
||||||
|
xrdp_cache_reset(wm->cache, wm->client_info);
|
||||||
|
/* resize the main window */
|
||||||
|
xrdp_bitmap_resize(wm->screen, session_width, session_height);
|
||||||
|
/* load some stuff */
|
||||||
|
xrdp_wm_load_static_colors_plus(wm, 0);
|
||||||
|
xrdp_wm_load_static_pointers(wm);
|
||||||
|
/* redraw */
|
||||||
|
xrdp_bitmap_invalidate(wm->screen, 0);
|
||||||
|
|
||||||
|
struct xrdp_mod* v = wm->mm->mod;
|
||||||
|
if (v != 0) {
|
||||||
|
v->mod_server_version_message(v);
|
||||||
|
v->mod_server_monitor_resize(v, session_width, session_height);
|
||||||
|
v->mod_server_monitor_full_invalidate(v, session_width, session_height);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
int
|
||||||
|
dynamic_monitor_initialize(struct xrdp_mm *self)
|
||||||
|
{
|
||||||
|
struct xrdp_drdynvc_procs d_procs;
|
||||||
|
int flags;
|
||||||
|
int error;
|
||||||
|
|
||||||
|
LOG_DEVEL(LOG_LEVEL_TRACE, "dynamic_monitor_initialize:");
|
||||||
|
|
||||||
|
g_memset(&d_procs, 0, sizeof(d_procs));
|
||||||
|
d_procs.open_response = dynamic_monitor_open_response;
|
||||||
|
d_procs.close_response = dynamic_monitor_close_response;
|
||||||
|
d_procs.data_first = dynamic_monitor_data_first;
|
||||||
|
d_procs.data = dynamic_monitor_data;
|
||||||
|
flags = 0;
|
||||||
|
error = libxrdp_drdynvc_open(self->wm->session,
|
||||||
|
"Microsoft::Windows::RDS::DisplayControl",
|
||||||
|
flags, &d_procs,
|
||||||
|
&(self->dynamic_monitor_chanid));
|
||||||
|
if (error != 0)
|
||||||
|
{
|
||||||
|
LOG(LOG_LEVEL_ERROR, "xrdp_mm_drdynvc_up: "
|
||||||
|
"libxrdp_drdynvc_open failed %d", error);
|
||||||
|
}
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
int
|
int
|
||||||
xrdp_mm_drdynvc_up(struct xrdp_mm *self)
|
xrdp_mm_drdynvc_up(struct xrdp_mm *self)
|
||||||
{
|
{
|
||||||
LOG_DEVEL(LOG_LEVEL_INFO, "xrdp_mm_drdynvc_up:");
|
char enable_dynamic_resize[32];
|
||||||
return 0;
|
int error = 0;
|
||||||
|
|
||||||
|
LOG_DEVEL(LOG_LEVEL_TRACE, "xrdp_mm_drdynvc_up:");
|
||||||
|
|
||||||
|
xrdp_mm_get_value(self, "enable_dynamic_resizing",
|
||||||
|
enable_dynamic_resize,
|
||||||
|
sizeof(enable_dynamic_resize) - 1);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* User can disable dynamic resizing if necessary
|
||||||
|
*/
|
||||||
|
if (enable_dynamic_resize[0] != '\0' && !g_text2bool(enable_dynamic_resize))
|
||||||
|
{
|
||||||
|
LOG(LOG_LEVEL_INFO, "User has disabled dynamic resizing.");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
error = dynamic_monitor_initialize(self);
|
||||||
|
}
|
||||||
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|||||||
+7
-1
@@ -53,7 +53,12 @@ struct xrdp_mod
|
|||||||
int (*mod_frame_ack)(struct xrdp_mod *v, int flags, int frame_id);
|
int (*mod_frame_ack)(struct xrdp_mod *v, int flags, int frame_id);
|
||||||
int (*mod_suppress_output)(struct xrdp_mod *v, int suppress,
|
int (*mod_suppress_output)(struct xrdp_mod *v, int suppress,
|
||||||
int left, int top, int right, int bottom);
|
int left, int top, int right, int bottom);
|
||||||
tintptr mod_dumby[100 - 11]; /* align, 100 minus the number of mod
|
int (*mod_server_monitor_resize)(struct xrdp_mod* v,
|
||||||
|
int width, int height);
|
||||||
|
int (*mod_server_monitor_full_invalidate)(struct xrdp_mod* v,
|
||||||
|
int width, int height);
|
||||||
|
int (*mod_server_version_message)(struct xrdp_mod* v);
|
||||||
|
tintptr mod_dumby[100 - 14]; /* align, 100 minus the number of mod
|
||||||
functions above */
|
functions above */
|
||||||
/* server functions */
|
/* server functions */
|
||||||
int (*server_begin_update)(struct xrdp_mod *v);
|
int (*server_begin_update)(struct xrdp_mod *v);
|
||||||
@@ -302,6 +307,7 @@ struct xrdp_mm
|
|||||||
struct xrdp_encoder *encoder;
|
struct xrdp_encoder *encoder;
|
||||||
int cs2xr_cid_map[256];
|
int cs2xr_cid_map[256];
|
||||||
int xr2cr_cid_map[256];
|
int xr2cr_cid_map[256];
|
||||||
|
int dynamic_monitor_chanid;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct xrdp_key_info
|
struct xrdp_key_info
|
||||||
|
|||||||
@@ -27,6 +27,15 @@
|
|||||||
#include "trans.h"
|
#include "trans.h"
|
||||||
#include "string_calls.h"
|
#include "string_calls.h"
|
||||||
|
|
||||||
|
static int
|
||||||
|
send_server_monitor_resize(struct mod *mod, struct stream *s, int width, int height, int bpp);
|
||||||
|
|
||||||
|
static int
|
||||||
|
send_server_monitor_full_invalidate(struct mod *mod, struct stream *s, int width, int height);
|
||||||
|
|
||||||
|
static int
|
||||||
|
send_server_version_message(struct mod *v, struct stream *s);
|
||||||
|
|
||||||
static int
|
static int
|
||||||
lib_mod_process_message(struct mod *mod, struct stream *s);
|
lib_mod_process_message(struct mod *mod, struct stream *s);
|
||||||
|
|
||||||
@@ -139,7 +148,6 @@ int
|
|||||||
lib_mod_connect(struct mod *mod)
|
lib_mod_connect(struct mod *mod)
|
||||||
{
|
{
|
||||||
int error;
|
int error;
|
||||||
int len;
|
|
||||||
int i;
|
int i;
|
||||||
int use_uds;
|
int use_uds;
|
||||||
struct stream *s;
|
struct stream *s;
|
||||||
@@ -243,60 +251,18 @@ lib_mod_connect(struct mod *mod)
|
|||||||
|
|
||||||
if (error == 0)
|
if (error == 0)
|
||||||
{
|
{
|
||||||
/* send version message */
|
error = send_server_version_message(mod, s);
|
||||||
init_stream(s, 8192);
|
|
||||||
s_push_layer(s, iso_hdr, 4);
|
|
||||||
out_uint16_le(s, 103);
|
|
||||||
out_uint32_le(s, 301);
|
|
||||||
out_uint32_le(s, 0);
|
|
||||||
out_uint32_le(s, 0);
|
|
||||||
out_uint32_le(s, 0);
|
|
||||||
out_uint32_le(s, 1);
|
|
||||||
s_mark_end(s);
|
|
||||||
len = (int)(s->end - s->data);
|
|
||||||
s_pop_layer(s, iso_hdr);
|
|
||||||
out_uint32_le(s, len);
|
|
||||||
lib_send_copy(mod, s);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (error == 0)
|
if (error == 0)
|
||||||
{
|
{
|
||||||
/* send screen size message */
|
/* send screen size message */
|
||||||
init_stream(s, 8192);
|
error = send_server_monitor_resize(mod, s, mod->width, mod->height, mod->bpp);
|
||||||
s_push_layer(s, iso_hdr, 4);
|
|
||||||
out_uint16_le(s, 103);
|
|
||||||
out_uint32_le(s, 300);
|
|
||||||
out_uint32_le(s, mod->width);
|
|
||||||
out_uint32_le(s, mod->height);
|
|
||||||
out_uint32_le(s, mod->bpp);
|
|
||||||
out_uint32_le(s, 0);
|
|
||||||
s_mark_end(s);
|
|
||||||
len = (int)(s->end - s->data);
|
|
||||||
s_pop_layer(s, iso_hdr);
|
|
||||||
out_uint32_le(s, len);
|
|
||||||
lib_send_copy(mod, s);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (error == 0)
|
if (error == 0)
|
||||||
{
|
{
|
||||||
/* send invalidate message */
|
error = send_server_monitor_full_invalidate(mod, s, mod->width, mod->height);
|
||||||
init_stream(s, 8192);
|
|
||||||
s_push_layer(s, iso_hdr, 4);
|
|
||||||
out_uint16_le(s, 103);
|
|
||||||
out_uint32_le(s, 200);
|
|
||||||
/* x and y */
|
|
||||||
i = 0;
|
|
||||||
out_uint32_le(s, i);
|
|
||||||
/* width and height */
|
|
||||||
i = ((mod->width & 0xffff) << 16) | mod->height;
|
|
||||||
out_uint32_le(s, i);
|
|
||||||
out_uint32_le(s, 0);
|
|
||||||
out_uint32_le(s, 0);
|
|
||||||
s_mark_end(s);
|
|
||||||
len = (int)(s->end - s->data);
|
|
||||||
s_pop_layer(s, iso_hdr);
|
|
||||||
out_uint32_le(s, len);
|
|
||||||
lib_send_copy(mod, s);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
free_stream(s);
|
free_stream(s);
|
||||||
@@ -1292,6 +1258,124 @@ process_server_paint_rect_shmem_ex(struct mod *amod, struct stream *s)
|
|||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
/* return error */
|
||||||
|
static int
|
||||||
|
send_server_version_message(struct mod *mod, struct stream *s)
|
||||||
|
{
|
||||||
|
/* send version message */
|
||||||
|
init_stream(s, 8192);
|
||||||
|
s_push_layer(s, iso_hdr, 4);
|
||||||
|
out_uint16_le(s, 103);
|
||||||
|
out_uint32_le(s, 301);
|
||||||
|
out_uint32_le(s, 0);
|
||||||
|
out_uint32_le(s, 0);
|
||||||
|
out_uint32_le(s, 0);
|
||||||
|
out_uint32_le(s, 1);
|
||||||
|
s_mark_end(s);
|
||||||
|
int len = (int)(s->end - s->data);
|
||||||
|
s_pop_layer(s, iso_hdr);
|
||||||
|
out_uint32_le(s, len);
|
||||||
|
int rv = lib_send_copy(mod, s);
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
/* return error */
|
||||||
|
static int
|
||||||
|
send_server_monitor_resize(struct mod *mod, struct stream *s, int width, int height, int bpp)
|
||||||
|
{
|
||||||
|
/* send screen size message */
|
||||||
|
init_stream(s, 8192);
|
||||||
|
s_push_layer(s, iso_hdr, 4);
|
||||||
|
out_uint16_le(s, 103);
|
||||||
|
out_uint32_le(s, 300);
|
||||||
|
out_uint32_le(s, width);
|
||||||
|
out_uint32_le(s, height);
|
||||||
|
/*
|
||||||
|
TODO: The bpp here is only necessary for initial creation. We should modify XUP to require this
|
||||||
|
only on server initialization, but not on resize. Microsoft's RDP protocol does not support changing
|
||||||
|
the bpp on resize.
|
||||||
|
*/
|
||||||
|
out_uint32_le(s, bpp);
|
||||||
|
out_uint32_le(s, 0);
|
||||||
|
s_mark_end(s);
|
||||||
|
int len = (int)(s->end - s->data);
|
||||||
|
s_pop_layer(s, iso_hdr);
|
||||||
|
out_uint32_le(s, len);
|
||||||
|
int rv = lib_send_copy(mod, s);
|
||||||
|
LOG_DEVEL(LOG_LEVEL_DEBUG, "send_server_monitor_resize: sent resize message with following properties to xorgxrdp backend "
|
||||||
|
"width=%d, height=%d, bpp=%d, return value=%d",
|
||||||
|
width, height, bpp, rv);
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
send_server_monitor_full_invalidate(struct mod *mod, struct stream *s, int width, int height)
|
||||||
|
{
|
||||||
|
/* send invalidate message */
|
||||||
|
init_stream(s, 8192);
|
||||||
|
s_push_layer(s, iso_hdr, 4);
|
||||||
|
out_uint16_le(s, 103);
|
||||||
|
out_uint32_le(s, 200);
|
||||||
|
/* x and y */
|
||||||
|
int i = 0;
|
||||||
|
out_uint32_le(s, i);
|
||||||
|
/* width and height */
|
||||||
|
i = ((width & 0xffff) << 16) | height;
|
||||||
|
out_uint32_le(s, i);
|
||||||
|
out_uint32_le(s, 0);
|
||||||
|
out_uint32_le(s, 0);
|
||||||
|
s_mark_end(s);
|
||||||
|
int len = (int)(s->end - s->data);
|
||||||
|
s_pop_layer(s, iso_hdr);
|
||||||
|
out_uint32_le(s, len);
|
||||||
|
int rv = lib_send_copy(mod, s);
|
||||||
|
LOG_DEVEL(LOG_LEVEL_DEBUG, "send_server_monitor_full_invalidate: sent invalidate message with following properties to xorgxrdp backend "
|
||||||
|
"width=%d, height=%d, return value=%d",
|
||||||
|
width, height, rv);
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
/* return error */
|
||||||
|
static int
|
||||||
|
lib_send_server_version_message(struct mod *mod)
|
||||||
|
{
|
||||||
|
/* send server version message */
|
||||||
|
struct stream *s;
|
||||||
|
make_stream(s);
|
||||||
|
int rv = send_server_version_message(mod, s);
|
||||||
|
free_stream(s);
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
/* return error */
|
||||||
|
static int
|
||||||
|
lib_send_server_monitor_resize(struct mod *mod, int width, int height)
|
||||||
|
{
|
||||||
|
/* send screen size message */
|
||||||
|
struct stream *s;
|
||||||
|
make_stream(s);
|
||||||
|
int rv = send_server_monitor_resize(mod, s, width, height, mod->bpp);
|
||||||
|
free_stream(s);
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
/* return error */
|
||||||
|
static int
|
||||||
|
lib_send_server_monitor_full_invalidate(struct mod *mod, int width, int height)
|
||||||
|
{
|
||||||
|
/* send invalidate message */
|
||||||
|
struct stream *s;
|
||||||
|
make_stream(s);
|
||||||
|
int rv = send_server_monitor_full_invalidate(mod, s, width, height);
|
||||||
|
free_stream(s);
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
/* return error */
|
/* return error */
|
||||||
static int
|
static int
|
||||||
@@ -1636,6 +1720,9 @@ mod_init(void)
|
|||||||
mod->mod_check_wait_objs = lib_mod_check_wait_objs;
|
mod->mod_check_wait_objs = lib_mod_check_wait_objs;
|
||||||
mod->mod_frame_ack = lib_mod_frame_ack;
|
mod->mod_frame_ack = lib_mod_frame_ack;
|
||||||
mod->mod_suppress_output = lib_mod_suppress_output;
|
mod->mod_suppress_output = lib_mod_suppress_output;
|
||||||
|
mod->mod_server_monitor_resize = lib_send_server_monitor_resize;
|
||||||
|
mod->mod_server_monitor_full_invalidate = lib_send_server_monitor_full_invalidate;
|
||||||
|
mod->mod_server_version_message = lib_send_server_version_message;
|
||||||
return (tintptr) mod;
|
return (tintptr) mod;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -51,7 +51,12 @@ struct mod
|
|||||||
int (*mod_frame_ack)(struct mod *v, int flags, int frame_id);
|
int (*mod_frame_ack)(struct mod *v, int flags, int frame_id);
|
||||||
int (*mod_suppress_output)(struct mod *v, int suppress,
|
int (*mod_suppress_output)(struct mod *v, int suppress,
|
||||||
int left, int top, int right, int bottom);
|
int left, int top, int right, int bottom);
|
||||||
tintptr mod_dumby[100 - 11]; /* align, 100 minus the number of mod
|
int (*mod_server_monitor_resize)(struct mod* v,
|
||||||
|
int width, int height);
|
||||||
|
int (*mod_server_monitor_full_invalidate)(struct mod* v,
|
||||||
|
int width, int height);
|
||||||
|
int (*mod_server_version_message)(struct mod* v);
|
||||||
|
tintptr mod_dumby[100 - 14]; /* align, 100 minus the number of mod
|
||||||
functions above */
|
functions above */
|
||||||
/* server functions */
|
/* server functions */
|
||||||
int (*server_begin_update)(struct mod *v);
|
int (*server_begin_update)(struct mod *v);
|
||||||
|
|||||||
Reference in New Issue
Block a user