FreeRDP/client/SDL/sdl_disp.cpp

420 lines
10 KiB
C++
Raw Normal View History

2022-12-30 18:25:28 +08:00
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* SDL Display Control Channel
*
* Copyright 2023 Armin Novak <armin.novak@thincast.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 <vector>
2022-12-30 18:25:28 +08:00
#include <winpr/sysinfo.h>
#include <winpr/assert.h>
#include <freerdp/gdi/gdi.h>
#include <SDL.h>
2023-04-14 14:36:05 +08:00
#include "sdl_disp.hpp"
#include "sdl_kbd.hpp"
#include "sdl_utils.hpp"
2023-04-14 15:16:50 +08:00
#include "sdl_freerdp.hpp"
2022-12-30 18:25:28 +08:00
#include <freerdp/log.h>
#define TAG CLIENT_TAG("sdl.disp")
#define RESIZE_MIN_DELAY 200 /* minimum delay in ms between two resizes */
BOOL sdlDispContext::settings_changed()
2022-12-30 18:25:28 +08:00
{
auto settings = _sdl->context()->settings;
2022-12-30 18:25:28 +08:00
WINPR_ASSERT(settings);
if (_lastSentWidth != _targetWidth)
2022-12-30 18:25:28 +08:00
return TRUE;
if (_lastSentHeight != _targetHeight)
2022-12-30 18:25:28 +08:00
return TRUE;
if (_lastSentDesktopOrientation != settings->DesktopOrientation)
2022-12-30 18:25:28 +08:00
return TRUE;
if (_lastSentDesktopScaleFactor != settings->DesktopScaleFactor)
2022-12-30 18:25:28 +08:00
return TRUE;
if (_lastSentDeviceScaleFactor != settings->DeviceScaleFactor)
2022-12-30 18:25:28 +08:00
return TRUE;
/* TODO
if (_fullscreen != _sdl->fullscreen)
2022-12-30 18:25:28 +08:00
return TRUE;
*/
return FALSE;
}
BOOL sdlDispContext::update_last_sent()
2022-12-30 18:25:28 +08:00
{
WINPR_ASSERT(_sdl);
2022-12-30 18:25:28 +08:00
auto settings = _sdl->context()->settings;
2022-12-30 18:25:28 +08:00
WINPR_ASSERT(settings);
_lastSentWidth = _targetWidth;
_lastSentHeight = _targetHeight;
_lastSentDesktopOrientation = settings->DesktopOrientation;
_lastSentDesktopScaleFactor = settings->DesktopScaleFactor;
_lastSentDeviceScaleFactor = settings->DeviceScaleFactor;
// TODO _fullscreen = _sdl->fullscreen;
2022-12-30 18:25:28 +08:00
return TRUE;
}
BOOL sdlDispContext::sendResize()
2022-12-30 18:25:28 +08:00
{
DISPLAY_CONTROL_MONITOR_LAYOUT layout = {};
auto settings = _sdl->context()->settings;
2022-12-30 18:25:28 +08:00
if (!settings)
return FALSE;
if (!_activated || !_disp)
2022-12-30 18:25:28 +08:00
return TRUE;
if (GetTickCount64() - _lastSentDate < RESIZE_MIN_DELAY)
2022-12-30 18:25:28 +08:00
return TRUE;
_lastSentDate = GetTickCount64();
2022-12-30 18:25:28 +08:00
if (!settings_changed())
2022-12-30 18:25:28 +08:00
return TRUE;
if (_sdl->fullscreen && (settings->MonitorCount > 0))
2022-12-30 18:25:28 +08:00
{
if (sendLayout(settings->MonitorDefArray, settings->MonitorCount) != CHANNEL_RC_OK)
return FALSE;
2022-12-30 18:25:28 +08:00
}
else
{
_waitingResize = TRUE;
2022-12-30 18:25:28 +08:00
layout.Flags = DISPLAY_CONTROL_MONITOR_PRIMARY;
layout.Top = layout.Left = 0;
layout.Width = _targetWidth;
layout.Height = _targetHeight;
2022-12-30 18:25:28 +08:00
layout.Orientation = settings->DesktopOrientation;
layout.DesktopScaleFactor = settings->DesktopScaleFactor;
layout.DeviceScaleFactor = settings->DeviceScaleFactor;
layout.PhysicalWidth = _targetWidth;
layout.PhysicalHeight = _targetHeight;
2022-12-30 18:25:28 +08:00
if (IFCALLRESULT(CHANNEL_RC_OK, _disp->SendMonitorLayout, _disp, 1, &layout) !=
CHANNEL_RC_OK)
2022-12-30 18:25:28 +08:00
return FALSE;
}
return update_last_sent();
2022-12-30 18:25:28 +08:00
}
BOOL sdlDispContext::set_window_resizable()
2022-12-30 18:25:28 +08:00
{
2023-04-14 17:55:29 +08:00
_sdl->update_resizeable(TRUE);
2022-12-30 18:25:28 +08:00
return TRUE;
}
static BOOL sdl_disp_check_context(void* context, SdlContext** ppsdl, sdlDispContext** ppsdlDisp,
2022-12-30 18:25:28 +08:00
rdpSettings** ppSettings)
{
if (!context)
return FALSE;
auto sdl = get_context(context);
2022-12-30 18:25:28 +08:00
if (!sdl->context()->settings)
2022-12-30 18:25:28 +08:00
return FALSE;
*ppsdl = sdl;
*ppsdlDisp = &sdl->disp;
*ppSettings = sdl->context()->settings;
2022-12-30 18:25:28 +08:00
return TRUE;
}
void sdlDispContext::OnActivated(void* context, const ActivatedEventArgs* e)
2022-12-30 18:25:28 +08:00
{
SdlContext* sdl = nullptr;
sdlDispContext* sdlDisp = nullptr;
rdpSettings* settings = nullptr;
2022-12-30 18:25:28 +08:00
if (!sdl_disp_check_context(context, &sdl, &sdlDisp, &settings))
return;
sdlDisp->_waitingResize = FALSE;
2022-12-30 18:25:28 +08:00
if (sdlDisp->_activated && !settings->Fullscreen)
2022-12-30 18:25:28 +08:00
{
sdlDisp->set_window_resizable();
2022-12-30 18:25:28 +08:00
if (e->firstActivation)
return;
sdlDisp->sendResize();
2022-12-30 18:25:28 +08:00
}
}
void sdlDispContext::OnGraphicsReset(void* context, const GraphicsResetEventArgs* e)
2022-12-30 18:25:28 +08:00
{
SdlContext* sdl = nullptr;
sdlDispContext* sdlDisp = nullptr;
rdpSettings* settings = nullptr;
2022-12-30 18:25:28 +08:00
WINPR_UNUSED(e);
if (!sdl_disp_check_context(context, &sdl, &sdlDisp, &settings))
return;
sdlDisp->_waitingResize = FALSE;
2022-12-30 18:25:28 +08:00
if (sdlDisp->_activated && !settings->Fullscreen)
2022-12-30 18:25:28 +08:00
{
sdlDisp->set_window_resizable();
sdlDisp->sendResize();
2022-12-30 18:25:28 +08:00
}
}
void sdlDispContext::OnTimer(void* context, const TimerEventArgs* e)
2022-12-30 18:25:28 +08:00
{
SdlContext* sdl = nullptr;
sdlDispContext* sdlDisp = nullptr;
rdpSettings* settings = nullptr;
2022-12-30 18:25:28 +08:00
WINPR_UNUSED(e);
if (!sdl_disp_check_context(context, &sdl, &sdlDisp, &settings))
return;
if (!sdlDisp->_activated || settings->Fullscreen)
2022-12-30 18:25:28 +08:00
return;
sdlDisp->sendResize();
2022-12-30 18:25:28 +08:00
}
UINT sdlDispContext::sendLayout(const rdpMonitor* monitors, size_t nmonitors)
2022-12-30 18:25:28 +08:00
{
UINT ret = CHANNEL_RC_OK;
WINPR_ASSERT(monitors);
WINPR_ASSERT(nmonitors > 0);
auto settings = _sdl->context()->settings;
2022-12-30 18:25:28 +08:00
WINPR_ASSERT(settings);
std::vector<DISPLAY_CONTROL_MONITOR_LAYOUT> layouts;
layouts.resize(nmonitors);
2022-12-30 18:25:28 +08:00
for (size_t i = 0; i < nmonitors; i++)
2022-12-30 18:25:28 +08:00
{
auto monitor = &monitors[i];
auto layout = &layouts[i];
2022-12-30 18:25:28 +08:00
layout->Flags = (monitor->is_primary ? DISPLAY_CONTROL_MONITOR_PRIMARY : 0);
layout->Left = monitor->x;
layout->Top = monitor->y;
layout->Width = monitor->width;
layout->Height = monitor->height;
layout->Orientation = ORIENTATION_LANDSCAPE;
layout->PhysicalWidth = monitor->attributes.physicalWidth;
layout->PhysicalHeight = monitor->attributes.physicalHeight;
switch (monitor->attributes.orientation)
{
case 90:
layout->Orientation = ORIENTATION_PORTRAIT;
break;
case 180:
layout->Orientation = ORIENTATION_LANDSCAPE_FLIPPED;
break;
case 270:
layout->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
*/
layout->Orientation = ORIENTATION_LANDSCAPE;
break;
}
layout->DesktopScaleFactor = settings->DesktopScaleFactor;
layout->DeviceScaleFactor = settings->DeviceScaleFactor;
}
WINPR_ASSERT(_disp);
ret = IFCALLRESULT(CHANNEL_RC_OK, _disp->SendMonitorLayout, _disp, layouts.size(),
layouts.data());
2022-12-30 18:25:28 +08:00
return ret;
}
#if SDL_VERSION_ATLEAST(2, 0, 10)
BOOL sdlDispContext::handle_display_event(const SDL_DisplayEvent* ev)
2022-12-30 18:25:28 +08:00
{
WINPR_ASSERT(ev);
switch (ev->event)
{
#if SDL_VERSION_ATLEAST(2, 0, 14)
case SDL_DISPLAYEVENT_CONNECTED:
SDL_Log("A new display with id %d was connected", ev->display);
return TRUE;
case SDL_DISPLAYEVENT_DISCONNECTED:
SDL_Log("The display with id %d was disconnected", ev->display);
return TRUE;
#endif
case SDL_DISPLAYEVENT_ORIENTATION:
SDL_Log("The orientation of display with id %d was changed", ev->display);
return TRUE;
default:
return TRUE;
}
}
#endif
BOOL sdlDispContext::handle_window_event(const SDL_WindowEvent* ev)
2022-12-30 18:25:28 +08:00
{
WINPR_ASSERT(ev);
switch (ev->event)
{
case SDL_WINDOWEVENT_HIDDEN:
case SDL_WINDOWEVENT_MINIMIZED:
gdi_send_suppress_output(_sdl->context()->gdi, TRUE);
2022-12-30 18:25:28 +08:00
return TRUE;
case SDL_WINDOWEVENT_EXPOSED:
case SDL_WINDOWEVENT_SHOWN:
case SDL_WINDOWEVENT_MAXIMIZED:
case SDL_WINDOWEVENT_RESTORED:
gdi_send_suppress_output(_sdl->context()->gdi, FALSE);
2022-12-30 18:25:28 +08:00
return TRUE;
case SDL_WINDOWEVENT_RESIZED:
case SDL_WINDOWEVENT_SIZE_CHANGED:
_targetWidth = ev->data1;
_targetHeight = ev->data2;
return sendResize();
2022-12-30 18:25:28 +08:00
case SDL_WINDOWEVENT_LEAVE:
2023-04-14 17:47:30 +08:00
WINPR_ASSERT(_sdl);
_sdl->input.keyboard_grab(ev->windowID, SDL_FALSE);
2022-12-30 18:25:28 +08:00
return TRUE;
case SDL_WINDOWEVENT_ENTER:
2023-04-14 17:47:30 +08:00
WINPR_ASSERT(_sdl);
_sdl->input.keyboard_grab(ev->windowID, SDL_TRUE);
return _sdl->input.keyboard_focus_in();
2022-12-30 18:25:28 +08:00
case SDL_WINDOWEVENT_FOCUS_GAINED:
case SDL_WINDOWEVENT_TAKE_FOCUS:
return _sdl->input.keyboard_focus_in();
2022-12-30 18:25:28 +08:00
default:
return TRUE;
}
}
UINT sdlDispContext::DisplayControlCaps(DispClientContext* disp, UINT32 maxNumMonitors,
UINT32 maxMonitorAreaFactorA, UINT32 maxMonitorAreaFactorB)
2022-12-30 18:25:28 +08:00
{
/* we're called only if dynamic resolution update is activated */
WINPR_ASSERT(disp);
auto sdlDisp = reinterpret_cast<sdlDispContext*>(disp->custom);
return sdlDisp->DisplayControlCaps(maxNumMonitors, maxMonitorAreaFactorA,
maxMonitorAreaFactorB);
}
2022-12-30 18:25:28 +08:00
UINT sdlDispContext::DisplayControlCaps(UINT32 maxNumMonitors, UINT32 maxMonitorAreaFactorA,
UINT32 maxMonitorAreaFactorB)
{
auto settings = _sdl->context()->settings;
2022-12-30 18:25:28 +08:00
WINPR_ASSERT(settings);
WLog_DBG(TAG,
"DisplayControlCapsPdu: MaxNumMonitors: %" PRIu32 " MaxMonitorAreaFactorA: %" PRIu32
" MaxMonitorAreaFactorB: %" PRIu32 "",
maxNumMonitors, maxMonitorAreaFactorA, maxMonitorAreaFactorB);
_activated = TRUE;
2022-12-30 18:25:28 +08:00
if (settings->Fullscreen)
return CHANNEL_RC_OK;
WLog_DBG(TAG, "DisplayControlCapsPdu: setting the window as resizable");
return set_window_resizable() ? CHANNEL_RC_OK : CHANNEL_RC_NO_MEMORY;
2022-12-30 18:25:28 +08:00
}
BOOL sdlDispContext::init(DispClientContext* disp)
2022-12-30 18:25:28 +08:00
{
if (!disp)
2022-12-30 18:25:28 +08:00
return FALSE;
auto settings = _sdl->context()->settings;
2022-12-30 18:25:28 +08:00
if (!settings)
return FALSE;
_disp = disp;
disp->custom = this;
2022-12-30 18:25:28 +08:00
if (settings->DynamicResolutionUpdate)
{
disp->DisplayControlCaps = sdlDispContext::DisplayControlCaps;
2022-12-30 18:25:28 +08:00
}
2023-04-14 17:55:29 +08:00
_sdl->update_resizeable(TRUE);
2022-12-30 18:25:28 +08:00
return TRUE;
}
BOOL sdlDispContext::uninit(DispClientContext* disp)
2022-12-30 18:25:28 +08:00
{
if (!disp)
2022-12-30 18:25:28 +08:00
return FALSE;
_disp = nullptr;
2023-04-14 17:55:29 +08:00
_sdl->update_resizeable(FALSE);
2022-12-30 18:25:28 +08:00
return TRUE;
}
sdlDispContext::sdlDispContext(SdlContext* sdl) : _sdl(sdl)
{
WINPR_ASSERT(_sdl);
WINPR_ASSERT(_sdl->context()->settings);
WINPR_ASSERT(_sdl->context()->pubSub);
auto settings = _sdl->context()->settings;
auto pubSub = _sdl->context()->pubSub;
_lastSentWidth = _targetWidth = settings->DesktopWidth;
_lastSentHeight = _targetHeight = settings->DesktopHeight;
PubSub_SubscribeActivated(pubSub, sdlDispContext::OnActivated);
PubSub_SubscribeGraphicsReset(pubSub, sdlDispContext::OnGraphicsReset);
PubSub_SubscribeTimer(pubSub, sdlDispContext::OnTimer);
}
sdlDispContext::~sdlDispContext()
{
wPubSub* pubSub = _sdl->context()->pubSub;
WINPR_ASSERT(pubSub);
PubSub_UnsubscribeActivated(pubSub, sdlDispContext::OnActivated);
PubSub_UnsubscribeGraphicsReset(pubSub, sdlDispContext::OnGraphicsReset);
PubSub_UnsubscribeTimer(pubSub, sdlDispContext::OnTimer);
}