--- /dev/null
+/**
+ * FreeRDP: A Remote Desktop Protocol Implementation
+ * Wayland Display Control Channel
+ *
+ * Copyright 2018 Armin Novak <armin.novak@thincast.com>
+ * Copyright 2018 Thincast Technologies GmbH
+ *
+ * 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 <winpr/sysinfo.h>
+
+#include "wlf_disp.h"
+
+#define RESIZE_MIN_DELAY 200 /* minimum delay in ms between two resizes */
+
+struct _wlfDispContext
+{
+ wlfContext* wlc;
+ DispClientContext* disp;
+ BOOL haveXRandr;
+ int eventBase, errorBase;
+ int lastSentWidth, lastSentHeight;
+ UINT64 lastSentDate;
+ int targetWidth, targetHeight;
+ BOOL activated;
+ BOOL waitingResize;
+ BOOL fullscreen;
+ UINT16 lastSentDesktopOrientation;
+ UINT32 lastSentDesktopScaleFactor;
+ UINT32 lastSentDeviceScaleFactor;
+};
+
+static UINT wlf_disp_sendLayout(DispClientContext* disp, rdpMonitor* monitors, size_t nmonitors);
+
+static BOOL wlf_disp_settings_changed(wlfDispContext* wlfDisp)
+{
+ rdpSettings* settings = wlfDisp->wlc->context.settings;
+
+ if (wlfDisp->lastSentWidth != wlfDisp->targetWidth)
+ return TRUE;
+
+ if (wlfDisp->lastSentHeight != wlfDisp->targetHeight)
+ return TRUE;
+
+ if (wlfDisp->lastSentDesktopOrientation != settings->DesktopOrientation)
+ return TRUE;
+
+ if (wlfDisp->lastSentDesktopScaleFactor != settings->DesktopScaleFactor)
+ return TRUE;
+
+ if (wlfDisp->lastSentDeviceScaleFactor != settings->DeviceScaleFactor)
+ return TRUE;
+
+ if (wlfDisp->fullscreen != wlfDisp->wlc->fullscreen)
+ return TRUE;
+
+ return FALSE;
+}
+
+static BOOL wlf_update_last_sent(wlfDispContext* wlfDisp)
+{
+ rdpSettings* settings = wlfDisp->wlc->context.settings;
+ wlfDisp->lastSentWidth = wlfDisp->targetWidth;
+ wlfDisp->lastSentHeight = wlfDisp->targetHeight;
+ wlfDisp->lastSentDesktopOrientation = settings->DesktopOrientation;
+ wlfDisp->lastSentDesktopScaleFactor = settings->DesktopScaleFactor;
+ wlfDisp->lastSentDeviceScaleFactor = settings->DeviceScaleFactor;
+ wlfDisp->fullscreen = wlfDisp->wlc->fullscreen;
+ return TRUE;
+}
+
+static BOOL wlf_disp_sendResize(wlfDispContext* wlfDisp)
+{
+ DISPLAY_CONTROL_MONITOR_LAYOUT layout;
+ wlfContext* wlc;
+ rdpSettings* settings;
+
+ if (!wlfDisp || !wlfDisp->wlc)
+ return FALSE;
+
+ wlc = wlfDisp->wlc;
+ settings = wlc->context.settings;
+
+ if (!settings)
+ return FALSE;
+
+ if (!wlfDisp->activated || !wlfDisp->disp)
+ return TRUE;
+
+ if (GetTickCount64() - wlfDisp->lastSentDate < RESIZE_MIN_DELAY)
+ return TRUE;
+
+ wlfDisp->lastSentDate = GetTickCount64();
+
+ if (!wlf_disp_settings_changed(wlfDisp))
+ return TRUE;
+
+ /* TODO: Multimonitor support for wayland
+ if (wlc->fullscreen && (settings->MonitorCount > 0))
+ {
+ if (wlf_disp_sendLayout(wlfDisp->disp, settings->MonitorDefArray,
+ settings->MonitorCount) != CHANNEL_RC_OK)
+ return FALSE;
+ }
+ else
+ */
+ {
+ wlfDisp->waitingResize = TRUE;
+ layout.Flags = DISPLAY_CONTROL_MONITOR_PRIMARY;
+ layout.Top = layout.Left = 0;
+ layout.Width = wlfDisp->targetWidth;
+ layout.Height = wlfDisp->targetHeight;
+ layout.Orientation = settings->DesktopOrientation;
+ layout.DesktopScaleFactor = settings->DesktopScaleFactor;
+ layout.DeviceScaleFactor = settings->DeviceScaleFactor;
+ layout.PhysicalWidth = wlfDisp->targetWidth;
+ layout.PhysicalHeight = wlfDisp->targetHeight;
+
+ if (IFCALLRESULT(CHANNEL_RC_OK, wlfDisp->disp->SendMonitorLayout, wlfDisp->disp, 1,
+ &layout) != CHANNEL_RC_OK)
+ return FALSE;
+ }
+ return wlf_update_last_sent(wlfDisp);
+}
+
+static BOOL wlf_disp_set_window_resizable(wlfDispContext* wlfDisp)
+{
+#if 0 // TODO
+#endif
+ return TRUE;
+}
+
+static BOOL wlf_disp_check_context(void* context, wlfContext** ppwlc, wlfDispContext** ppwlfDisp,
+ rdpSettings** ppSettings)
+{
+ wlfContext* wlc;
+
+ if (!context)
+ return FALSE;
+
+ wlc = (wlfContext*)context;
+
+ if (!(wlc->disp))
+ return FALSE;
+
+ if (!wlc->context.settings)
+ return FALSE;
+
+ *ppwlc = wlc;
+ *ppwlfDisp = wlc->disp;
+ *ppSettings = wlc->context.settings;
+ return TRUE;
+}
+
+static void wlf_disp_OnActivated(void* context, ActivatedEventArgs* e)
+{
+ wlfContext* wlc;
+ wlfDispContext* wlfDisp;
+ rdpSettings* settings;
+
+ if (!wlf_disp_check_context(context, &wlc, &wlfDisp, &settings))
+ return;
+
+ wlfDisp->waitingResize = FALSE;
+
+ if (wlfDisp->activated && !settings->Fullscreen)
+ {
+ wlf_disp_set_window_resizable(wlfDisp);
+
+ if (e->firstActivation)
+ return;
+
+ wlf_disp_sendResize(wlfDisp);
+ }
+}
+
+static void wlf_disp_OnGraphicsReset(void* context, GraphicsResetEventArgs* e)
+{
+ wlfContext* wlc;
+ wlfDispContext* wlfDisp;
+ rdpSettings* settings;
+
+ if (!wlf_disp_check_context(context, &wlc, &wlfDisp, &settings))
+ return;
+
+ wlfDisp->waitingResize = FALSE;
+
+ if (wlfDisp->activated && !settings->Fullscreen)
+ {
+ wlf_disp_set_window_resizable(wlfDisp);
+ wlf_disp_sendResize(wlfDisp);
+ }
+}
+
+static void wlf_disp_OnTimer(void* context, TimerEventArgs* e)
+{
+ wlfContext* wlc;
+ wlfDispContext* wlfDisp;
+ rdpSettings* settings;
+
+ if (!wlf_disp_check_context(context, &wlc, &wlfDisp, &settings))
+ return;
+
+ if (!wlfDisp->activated || settings->Fullscreen)
+ return;
+
+ wlf_disp_sendResize(wlfDisp);
+}
+
+wlfDispContext* wlf_disp_new(wlfContext* wlc)
+{
+ wlfDispContext* ret;
+
+ if (!wlc || !wlc->context.settings || !wlc->context.pubSub)
+ return NULL;
+
+ ret = calloc(1, sizeof(wlfDispContext));
+
+ if (!ret)
+ return NULL;
+
+ ret->wlc = wlc;
+ ret->lastSentWidth = ret->targetWidth = wlc->context.settings->DesktopWidth;
+ ret->lastSentHeight = ret->targetHeight = wlc->context.settings->DesktopHeight;
+ PubSub_SubscribeActivated(wlc->context.pubSub, wlf_disp_OnActivated);
+ PubSub_SubscribeGraphicsReset(wlc->context.pubSub, wlf_disp_OnGraphicsReset);
+ PubSub_SubscribeTimer(wlc->context.pubSub, wlf_disp_OnTimer);
+ return ret;
+}
+
+void wlf_disp_free(wlfDispContext* disp)
+{
+ if (!disp)
+ return;
+
+ if (disp->wlc)
+ {
+ PubSub_UnsubscribeActivated(disp->wlc->context.pubSub, wlf_disp_OnActivated);
+ PubSub_UnsubscribeGraphicsReset(disp->wlc->context.pubSub, wlf_disp_OnGraphicsReset);
+ PubSub_UnsubscribeTimer(disp->wlc->context.pubSub, wlf_disp_OnTimer);
+ }
+
+ free(disp);
+}
+
+UINT wlf_disp_sendLayout(DispClientContext* disp, rdpMonitor* monitors, size_t nmonitors)
+{
+ UINT ret = CHANNEL_RC_OK;
+ DISPLAY_CONTROL_MONITOR_LAYOUT* layouts;
+ size_t i;
+ wlfDispContext* wlfDisp = (wlfDispContext*)disp->custom;
+ rdpSettings* settings = wlfDisp->wlc->context.settings;
+ 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].attributes.physicalWidth;
+ layouts[i].PhysicalHeight = monitors[i].attributes.physicalHeight;
+
+ switch (monitors[i].attributes.orientation)
+ {
+ case 90:
+ layouts[i].Orientation = ORIENTATION_PORTRAIT;
+ break;
+
+ case 180:
+ layouts[i].Orientation = ORIENTATION_LANDSCAPE_FLIPPED;
+ break;
+
+ case 270:
+ layouts[i].Orientation = ORIENTATION_PORTRAIT_FLIPPED;
+ break;
+
+ case 0:
+ default:
+ /* MS-RDPEDISP - 2.2.2.2.1:
+ * Orientation (4 bytes): A 32-bit unsigned integer that specifies the
+ * orientation of the monitor in degrees. Valid values are 0, 90, 180
+ * or 270
+ *
+ * So we default to ORIENTATION_LANDSCAPE
+ */
+ layouts[i].Orientation = ORIENTATION_LANDSCAPE;
+ break;
+ }
+
+ layouts[i].DesktopScaleFactor = settings->DesktopScaleFactor;
+ layouts[i].DeviceScaleFactor = settings->DeviceScaleFactor;
+ }
+
+ ret = IFCALLRESULT(CHANNEL_RC_OK, disp->SendMonitorLayout, disp, nmonitors, layouts);
+ free(layouts);
+ return ret;
+}
+
+BOOL wlf_disp_handle_configure(wlfDispContext* disp, int32_t width, int32_t height)
+{
+ if (!disp)
+ return FALSE;
+
+ if (!disp)
+ return FALSE;
+
+ disp->targetWidth = width;
+ disp->targetHeight = height;
+ return wlf_disp_sendResize(disp);
+}
+
+static UINT wlf_DisplayControlCaps(DispClientContext* disp, UINT32 maxNumMonitors,
+ UINT32 maxMonitorAreaFactorA, UINT32 maxMonitorAreaFactorB)
+{
+ /* we're called only if dynamic resolution update is activated */
+ wlfDispContext* wlfDisp = (wlfDispContext*)disp->custom;
+ rdpSettings* settings = wlfDisp->wlc->context.settings;
+ WLog_DBG(TAG,
+ "DisplayControlCapsPdu: MaxNumMonitors: %"PRIu32" MaxMonitorAreaFactorA: %"PRIu32" MaxMonitorAreaFactorB: %"PRIu32"",
+ maxNumMonitors, maxMonitorAreaFactorA, maxMonitorAreaFactorB);
+ wlfDisp->activated = TRUE;
+
+ if (settings->Fullscreen)
+ return CHANNEL_RC_OK;
+
+ WLog_DBG(TAG, "DisplayControlCapsPdu: setting the window as resizable");
+ return wlf_disp_set_window_resizable(wlfDisp) ? CHANNEL_RC_OK : CHANNEL_RC_NO_MEMORY;
+}
+
+BOOL wlf_disp_init(wlfDispContext* wlfDisp, DispClientContext* disp)
+{
+ rdpSettings* settings;
+
+ if (!wlfDisp || !wlfDisp->wlc || !disp)
+ return FALSE;
+
+ settings = wlfDisp->wlc->context.settings;
+
+ if (!settings)
+ return FALSE;
+
+ wlfDisp->disp = disp;
+ disp->custom = (void*) wlfDisp;
+
+ if (settings->DynamicResolutionUpdate)
+ {
+ disp->DisplayControlCaps = wlf_DisplayControlCaps;
+ }
+
+ return TRUE;
+}
+
+BOOL wlf_disp_uninit(wlfDispContext* wlfDisp, DispClientContext* disp)
+{
+ if (!wlfDisp || !disp)
+ return FALSE;
+
+ wlfDisp->disp = NULL;
+ return TRUE;
+}
#include "wlfreerdp.h"
#include "wlf_input.h"
+#include "wlf_disp.h"
#include "wlf_channels.h"
static BOOL wl_update_content(wlfContext* context_w)
instance->update->EndPaint = wl_end_paint;
instance->update->DesktopResize = wl_resize_display;
freerdp_keyboard_init(instance->context->settings->KeyboardLayout);
+
+ if (!(context->disp = wlf_disp_new(context)))
+ return FALSE;
+
return wl_update_buffer(context, 0, 0, gdi->width, gdi->height);
}
context = (wlfContext*) instance->context;
gdi_free(instance);
+ wlf_disp_free(context->disp);
if (context->window)
UwacDestroyWindow(&context->window);
break;
case UWAC_EVENT_CONFIGURE:
+ if (!wlf_disp_handle_configure(context->disp, event.configure.width, event.configure.height))
+ return FALSE;
+
if (!wl_refresh_display(context))
return FALSE;
if (!freerdp_connect(instance))
{
- printf("Failed to connect\n");
+ WLog_Print(context->log, WLOG_ERROR, "Failed to connect");
return -1;
}
if (count <= 1)
{
- printf("Failed to get FreeRDP file descriptor\n");
+ WLog_Print(context->log, WLOG_ERROR, "Failed to get FreeRDP file descriptor");
break;
}
if (WAIT_FAILED == status)
{
- printf("%s: WaitForMultipleObjects failed\n", __FUNCTION__);
+ WLog_Print(context->log, WLOG_ERROR, "%s: WaitForMultipleObjects failed", __FUNCTION__);
break;
}
if (!handle_uwac_events(instance, context->display))
{
- printf("error handling UWAC events\n");
+ WLog_Print(context->log, WLOG_ERROR, "error handling UWAC events");
break;
}
if (freerdp_check_event_handles(instance->context) != TRUE)
{
if (freerdp_get_last_error(instance->context) == FREERDP_ERROR_SUCCESS)
- printf("Failed to check FreeRDP file descriptor\n");
+ WLog_Print(context->log, WLOG_ERROR, "Failed to check FreeRDP file descriptor");
break;
}
return -1;
wlf = (wlfContext*) instance->context;
- WLog_INFO(TAG, "Logon Error Info %s [%s]", str_data, str_type);
+ WLog_Print(wlf->log, WLOG_INFO, "Logon Error Info %s [%s]", str_data, str_type);
return 1;
}
instance->VerifyCertificateEx = client_cli_verify_certificate_ex;
instance->VerifyChangedCertificateEx = client_cli_verify_changed_certificate_ex;
instance->LogonErrorInfo = wlf_logon_error_info;
+ wfl->log = WLog_Get(TAG);
wfl->display = UwacOpenDisplay(NULL, &status);
- if (!wfl->display || (status != UWAC_SUCCESS))
+ if (!wfl->display || (status != UWAC_SUCCESS) || !wfl->log)
return FALSE;
wfl->displayHandle = CreateFileDescriptorEvent(NULL, FALSE, FALSE,