e8208d21d7
Signed-off-by: Derek Foreman <derek.foreman@collabora.com>
364 lines
9.5 KiB
C
364 lines
9.5 KiB
C
/*
|
|
* Copyright © 2020 Microsoft
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining
|
|
* a copy of this software and associated documentation files (the
|
|
* "Software"), to deal in the Software without restriction, including
|
|
* without limitation the rights to use, copy, modify, merge, publish,
|
|
* distribute, sublicense, and/or sell copies of the Software, and to
|
|
* permit persons to whom the Software is furnished to do so, subject to
|
|
* the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice (including the
|
|
* next paragraph) shall be included in all copies or substantial
|
|
* portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
|
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
|
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
* SOFTWARE.
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include <assert.h>
|
|
#include <stdint.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
#include <unistd.h>
|
|
#include <pthread.h>
|
|
|
|
#include <stdio.h>
|
|
#include <wchar.h>
|
|
#include <strings.h>
|
|
|
|
#include "rdp.h"
|
|
|
|
#include "shared/xalloc.h"
|
|
|
|
static bool
|
|
match_primary(struct rdp_backend *rdp, rdpMonitor *a, rdpMonitor *b)
|
|
{
|
|
if (a->is_primary && b->is_primary)
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
static bool
|
|
match_dimensions(struct rdp_backend *rdp, rdpMonitor *a, rdpMonitor *b)
|
|
{
|
|
int scale_a = a->attributes.desktopScaleFactor;
|
|
int scale_b = b->attributes.desktopScaleFactor;
|
|
|
|
|
|
if (a->width != b->width ||
|
|
a->height != b->height ||
|
|
scale_a != scale_b)
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool
|
|
match_position(struct rdp_backend *rdp, rdpMonitor *a, rdpMonitor *b)
|
|
{
|
|
if (a->x != b->x ||
|
|
a->y != b->y)
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool
|
|
match_exact(struct rdp_backend *rdp, rdpMonitor *a, rdpMonitor *b)
|
|
{
|
|
if (match_dimensions(rdp, a, b) &&
|
|
match_position(rdp, a, b))
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
static bool
|
|
match_any(struct rdp_backend *rdp, rdpMonitor *a, rdpMonitor *b)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
static void
|
|
update_head(struct rdp_backend *rdp, struct rdp_head *head, rdpMonitor *config)
|
|
{
|
|
struct weston_mode mode = {};
|
|
int scale;
|
|
bool changed = false;
|
|
|
|
head->matched = true;
|
|
scale = config->attributes.desktopScaleFactor / 100;
|
|
scale = scale ? scale : 1;
|
|
|
|
if (!match_position(rdp, &head->config, config))
|
|
changed = true;
|
|
|
|
if (!match_dimensions(rdp, &head->config, config)) {
|
|
mode.flags = WL_OUTPUT_MODE_PREFERRED;
|
|
mode.width = config->width;
|
|
mode.height = config->height;
|
|
mode.refresh = rdp->rdp_monitor_refresh_rate;
|
|
weston_output_mode_set_native(head->base.output,
|
|
&mode, scale);
|
|
changed = true;
|
|
}
|
|
|
|
if (changed) {
|
|
weston_head_set_device_changed(&head->base);
|
|
}
|
|
head->config = *config;
|
|
}
|
|
|
|
static void
|
|
match_heads(struct rdp_backend *rdp, rdpMonitor *config, uint32_t count,
|
|
int *done,
|
|
bool (*cmp)(struct rdp_backend *rdp, rdpMonitor *a, rdpMonitor *b))
|
|
{
|
|
struct weston_head *iter;
|
|
struct rdp_head *current;
|
|
uint32_t i;
|
|
|
|
wl_list_for_each(iter, &rdp->compositor->head_list, compositor_link) {
|
|
current = to_rdp_head(iter);
|
|
if (!current || current->matched)
|
|
continue;
|
|
|
|
for (i = 0; i < count; i++) {
|
|
if (*done & (1 << i))
|
|
continue;
|
|
|
|
if (cmp(rdp, ¤t->config, &config[i])) {
|
|
*done |= 1 << i;
|
|
update_head(rdp, current, &config[i]);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
disp_layout_change(freerdp_peer *client, rdpMonitor *config, UINT32 monitorCount)
|
|
{
|
|
RdpPeerContext *peerCtx = (RdpPeerContext *)client->context;
|
|
struct rdp_backend *b = peerCtx->rdpBackend;
|
|
struct rdp_head *current;
|
|
struct weston_head *iter, *tmp;
|
|
pixman_region32_t desktop;
|
|
int done = 0;
|
|
|
|
assert_compositor_thread(b);
|
|
|
|
pixman_region32_init(&desktop);
|
|
|
|
/* Prune heads that were never enabled, and flag heads as unmatched */
|
|
wl_list_for_each_safe(iter, tmp, &b->compositor->head_list, compositor_link) {
|
|
current = to_rdp_head(iter);
|
|
if (!current)
|
|
continue;
|
|
|
|
if (!iter->output) {
|
|
rdp_head_destroy(iter);
|
|
continue;
|
|
}
|
|
current->matched = false;
|
|
}
|
|
|
|
/* We want the primary head to remain primary - it
|
|
* should always be rdp-0.
|
|
*/
|
|
match_heads(b, config, monitorCount, &done, match_primary);
|
|
|
|
/* Look for any exact match */
|
|
match_heads(b, config, monitorCount, &done, match_exact);
|
|
|
|
/* Match first head with the same dimensions */
|
|
match_heads(b, config, monitorCount, &done, match_dimensions);
|
|
|
|
/* Match head with the same position */
|
|
match_heads(b, config, monitorCount, &done, match_position);
|
|
|
|
/* Pick any available head */
|
|
match_heads(b, config, monitorCount, &done, match_any);
|
|
|
|
/* Destroy any heads we won't be using */
|
|
wl_list_for_each_safe(iter, tmp, &b->compositor->head_list, compositor_link) {
|
|
current = to_rdp_head(iter);
|
|
if (!current)
|
|
continue;
|
|
|
|
if (!current->matched)
|
|
rdp_head_destroy(iter);
|
|
}
|
|
|
|
|
|
for (uint32_t i = 0; i < monitorCount; i++) {
|
|
/* accumulate monitor layout */
|
|
pixman_region32_union_rect(&desktop, &desktop,
|
|
config[i].x,
|
|
config[i].y,
|
|
config[i].width,
|
|
config[i].height);
|
|
|
|
/* Create new heads for any without matches */
|
|
if (!(done & (1 << i)))
|
|
rdp_head_create(b, &config[i]);
|
|
}
|
|
peerCtx->desktop_left = desktop.extents.x1;
|
|
peerCtx->desktop_top = desktop.extents.y1;
|
|
peerCtx->desktop_width = desktop.extents.x2 - desktop.extents.x1;
|
|
peerCtx->desktop_height = desktop.extents.y2 - desktop.extents.y1;
|
|
pixman_region32_fini(&desktop);
|
|
}
|
|
|
|
static bool
|
|
disp_sanity_check_layout(RdpPeerContext *peerCtx, rdpMonitor *config, uint32_t count)
|
|
{
|
|
struct rdp_backend *b = peerCtx->rdpBackend;
|
|
uint32_t primaryCount = 0;
|
|
uint32_t i;
|
|
|
|
/* dump client monitor topology */
|
|
rdp_debug(b, "%s:---INPUT---\n", __func__);
|
|
for (i = 0; i < count; i++) {
|
|
int scale = config[i].attributes.desktopScaleFactor / 100;
|
|
|
|
rdp_debug(b, " rdpMonitor[%d]: x:%d, y:%d, width:%d, height:%d, is_primary:%d\n",
|
|
i, config[i].x, config[i].y,
|
|
config[i].width, config[i].height,
|
|
config[i].is_primary);
|
|
rdp_debug(b, " rdpMonitor[%d]: physicalWidth:%d, physicalHeight:%d, orientation:%d\n",
|
|
i, config[i].attributes.physicalWidth,
|
|
config[i].attributes.physicalHeight,
|
|
config[i].attributes.orientation);
|
|
rdp_debug(b, " rdpMonitor[%d]: desktopScaleFactor:%d, deviceScaleFactor:%d\n",
|
|
i, config[i].attributes.desktopScaleFactor,
|
|
config[i].attributes.deviceScaleFactor);
|
|
|
|
rdp_debug(b, " rdpMonitor[%d]: scale:%d\n",
|
|
i, scale);
|
|
}
|
|
|
|
for (i = 0; i < count; i++) {
|
|
/* make sure there is only one primary and its position at client */
|
|
if (config[i].is_primary) {
|
|
/* count number of primary */
|
|
if (++primaryCount > 1) {
|
|
weston_log("%s: RDP client reported unexpected primary count (%d)\n",
|
|
__func__, primaryCount);
|
|
return false;
|
|
}
|
|
/* primary must be at (0,0) in client space */
|
|
if (config[i].x != 0 || config[i].y != 0) {
|
|
weston_log("%s: RDP client reported primary is not at (0,0) but (%d,%d).\n",
|
|
__func__, config[i].x, config[i].y);
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
handle_adjust_monitor_layout(freerdp_peer *client, int monitor_count, rdpMonitor *monitors)
|
|
{
|
|
RdpPeerContext *peerCtx = (RdpPeerContext *)client->context;
|
|
|
|
if (!disp_sanity_check_layout(peerCtx, monitors, monitor_count))
|
|
return true;
|
|
|
|
disp_layout_change(client, monitors, monitor_count);
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool
|
|
rect_contains(int32_t px, int32_t py, int32_t rx, int32_t ry,
|
|
int32_t width, int32_t height)
|
|
{
|
|
if (px < rx)
|
|
return false;
|
|
if (py < ry)
|
|
return false;
|
|
if (px >= rx + width)
|
|
return false;
|
|
if (py >= ry + height)
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool
|
|
rdp_head_contains(struct rdp_head *rdp_head, int32_t x, int32_t y)
|
|
{
|
|
rdpMonitor *config = &rdp_head->config;
|
|
|
|
/* If we're forcing RDP desktop size then we don't have
|
|
* useful information in the monitor structs, but we
|
|
* can rely on the output settings in that case.
|
|
*/
|
|
if (config->width == 0) {
|
|
struct weston_head *head = &rdp_head->base;
|
|
struct weston_output *output = head->output;
|
|
|
|
if (!output)
|
|
return false;
|
|
|
|
return rect_contains(x, y, output->pos.c.x, output->pos.c.y,
|
|
output->width * output->scale,
|
|
output->height * output->scale);
|
|
}
|
|
|
|
return rect_contains(x, y, config->x, config->y,
|
|
config->width, config->height);
|
|
}
|
|
|
|
/* Input x/y in client space, output x/y in weston space */
|
|
struct weston_output *
|
|
to_weston_coordinate(RdpPeerContext *peerContext, int32_t *x, int32_t *y)
|
|
{
|
|
struct rdp_backend *b = peerContext->rdpBackend;
|
|
int sx = *x, sy = *y;
|
|
struct weston_head *head_iter;
|
|
|
|
/* First, find which monitor contains this x/y. */
|
|
wl_list_for_each(head_iter, &b->compositor->head_list, compositor_link) {
|
|
struct rdp_head *head = to_rdp_head(head_iter);
|
|
|
|
if (!head)
|
|
continue;
|
|
|
|
if (rdp_head_contains(head, sx, sy)) {
|
|
struct weston_output *output = head->base.output;
|
|
float scale = 1.0f / head->base.output->scale;
|
|
|
|
sx -= head->config.x;
|
|
sy -= head->config.y;
|
|
/* scale x/y to client output space. */
|
|
sx *= scale;
|
|
sy *= scale;
|
|
/* translate x/y to offset of this output in weston space. */
|
|
sx += output->pos.c.x;
|
|
sy += output->pos.c.y;
|
|
rdp_debug_verbose(b, "%s: (x:%d, y:%d) -> (sx:%d, sy:%d) at head:%s\n",
|
|
__func__, *x, *y, sx, sy, head->base.name);
|
|
*x = sx;
|
|
*y = sy;
|
|
return output;
|
|
}
|
|
}
|
|
/* x/y is outside of any monitors. */
|
|
return NULL;
|
|
}
|