21#include <winpr/sysinfo.h> 
   22#include <winpr/assert.h> 
   24#include <freerdp/gdi/gdi.h> 
   28#include "sdl_disp.hpp" 
   30#include "sdl_utils.hpp" 
   31#include "sdl_freerdp.hpp" 
   33#include <freerdp/log.h> 
   34#define TAG CLIENT_TAG("sdl.disp") 
   36static constexpr UINT64 RESIZE_MIN_DELAY = 200; 
 
   37static constexpr unsigned MAX_RETRIES = 5;
 
   42  if (a.Flags != b.Flags)
 
   48  if (a.Width != b.Width)
 
   50  if (a.Height != b.Height)
 
   52  if (a.PhysicalWidth != b.PhysicalWidth)
 
   54  if (a.PhysicalHeight != b.PhysicalHeight)
 
   56  if (a.Orientation != b.Orientation)
 
   58  if (a.DesktopScaleFactor != b.DesktopScaleFactor)
 
   60  if (a.DeviceScaleFactor != b.DeviceScaleFactor)
 
   65bool sdlDispContext::settings_changed(
const std::vector<DISPLAY_CONTROL_MONITOR_LAYOUT>& layout)
 
   67  return (layout != _last_sent_layout);
 
   70bool sdlDispContext::sendResize()
 
   72  auto settings = _sdl->context()->settings;
 
   77  if (!_activated || !_disp)
 
   80  if (GetTickCount64() - _lastSentDate < RESIZE_MIN_DELAY)
 
   83  _lastSentDate = GetTickCount64();
 
   86  auto monitors = 
static_cast<const rdpMonitor*
>(
 
   88  return sendLayout(monitors, mcount) != CHANNEL_RC_OK;
 
   91bool sdlDispContext::set_window_resizable()
 
   93  return _sdl->update_resizeable(
true);
 
   97                                   rdpSettings** ppSettings)
 
  102  auto sdl = get_context(context);
 
  106  if (!sdl->context()->settings)
 
  110  *ppsdlDisp = &sdl->disp;
 
  111  *ppSettings = sdl->context()->settings;
 
  115void sdlDispContext::OnActivated(
void* context, 
const ActivatedEventArgs* e)
 
  119  rdpSettings* settings = 
nullptr;
 
  121  if (!sdl_disp_check_context(context, &sdl, &sdlDisp, &settings))
 
  124  sdlDisp->_waitingResize = 
false;
 
  128    sdlDisp->set_window_resizable();
 
  130    if (e->firstActivation)
 
  137void sdlDispContext::OnGraphicsReset(
void* context, 
const GraphicsResetEventArgs* e)
 
  141  rdpSettings* settings = 
nullptr;
 
  144  if (!sdl_disp_check_context(context, &sdl, &sdlDisp, &settings))
 
  147  sdlDisp->_waitingResize = 
false;
 
  151    sdlDisp->set_window_resizable();
 
  156Uint32 sdlDispContext::OnTimer(
void* param, [[maybe_unused]] SDL_TimerID timerID, Uint32 interval)
 
  167  rdpSettings* settings = 
nullptr;
 
  169  if (!sdl_disp_check_context(sdl->context(), &sdl, &sdlDisp, &settings))
 
  172  WLog_Print(sdl->log, WLOG_TRACE, 
"checking for display changes...");
 
  176  auto rc = sdlDisp->sendResize();
 
  178    WLog_Print(sdl->log, WLOG_TRACE, 
"sent new display layout, result %d", rc);
 
  180  if (sdlDisp->_timer_retries++ >= MAX_RETRIES)
 
  182    WLog_Print(sdl->log, WLOG_TRACE, 
"deactivate timer, retries exceeded");
 
  186  WLog_Print(sdl->log, WLOG_TRACE, 
"fire timer one more time");
 
  190UINT sdlDispContext::sendLayout(
const rdpMonitor* monitors, 
size_t nmonitors)
 
  192  UINT ret = CHANNEL_RC_OK;
 
  194  WINPR_ASSERT(monitors);
 
  195  WINPR_ASSERT(nmonitors > 0);
 
  197  auto settings = _sdl->context()->settings;
 
  198  WINPR_ASSERT(settings);
 
  200  std::vector<DISPLAY_CONTROL_MONITOR_LAYOUT> layouts;
 
  201  layouts.reserve(nmonitors);
 
  203  for (
size_t i = 0; i < nmonitors; i++)
 
  205    auto monitor = &monitors[i];
 
  208    layout.Flags = (monitor->is_primary ? DISPLAY_CONTROL_MONITOR_PRIMARY : 0);
 
  209    layout.Left = monitor->x;
 
  210    layout.Top = monitor->y;
 
  211    layout.Width = WINPR_ASSERTING_INT_CAST(uint32_t, monitor->width);
 
  212    layout.Height = WINPR_ASSERTING_INT_CAST(uint32_t, monitor->height);
 
  213    layout.Orientation = ORIENTATION_LANDSCAPE;
 
  214    layout.PhysicalWidth = monitor->attributes.physicalWidth;
 
  215    layout.PhysicalHeight = monitor->attributes.physicalHeight;
 
  217    switch (monitor->attributes.orientation)
 
  219      case ORIENTATION_PORTRAIT:
 
  220        layout.Orientation = ORIENTATION_PORTRAIT;
 
  223      case ORIENTATION_LANDSCAPE_FLIPPED:
 
  224        layout.Orientation = ORIENTATION_LANDSCAPE_FLIPPED;
 
  227      case ORIENTATION_PORTRAIT_FLIPPED:
 
  228        layout.Orientation = ORIENTATION_PORTRAIT_FLIPPED;
 
  231      case ORIENTATION_LANDSCAPE:
 
  240        layout.Orientation = ORIENTATION_LANDSCAPE;
 
  244    layout.DesktopScaleFactor = monitor->attributes.desktopScaleFactor;
 
  245    layout.DeviceScaleFactor = monitor->attributes.deviceScaleFactor;
 
  248    if ((mask & FREERDP_MONITOR_OVERRIDE_ORIENTATION) != 0)
 
  250    if ((mask & FREERDP_MONITOR_OVERRIDE_DESKTOP_SCALE) != 0)
 
  251      layout.DesktopScaleFactor =
 
  253    if ((mask & FREERDP_MONITOR_OVERRIDE_DEVICE_SCALE) != 0)
 
  254      layout.DeviceScaleFactor =
 
  256    layouts.emplace_back(layout);
 
  259  if (!settings_changed(layouts))
 
  263  const size_t len = layouts.size();
 
  264  WINPR_ASSERT(len <= UINT32_MAX);
 
  265  ret = IFCALLRESULT(CHANNEL_RC_OK, _disp->SendMonitorLayout, _disp, 
static_cast<UINT32
>(len),
 
  267  if (ret != CHANNEL_RC_OK)
 
  269  _last_sent_layout = layouts;
 
  273bool sdlDispContext::addTimer()
 
  275  if (SDL_WasInit(SDL_INIT_EVENTS) == 0)
 
  278  SDL_RemoveTimer(_timer);
 
  279  WLog_Print(_sdl->log, WLOG_TRACE, 
"adding new display check timer");
 
  283  _timer = SDL_AddTimer(1000, sdlDispContext::OnTimer, 
this);
 
  287bool sdlDispContext::updateMonitor(SDL_WindowID 
id)
 
  289  auto settings = _sdl->context()->settings;
 
  291    return updateMonitors(SDL_EVENT_DISPLAY_CURRENT_MODE_CHANGED);
 
  296  const auto& window = _sdl->windows.at(
id);
 
  297  auto monitor = window.monitor();
 
  299  monitor.is_primary = TRUE;
 
  306bool sdlDispContext::updateMonitors(SDL_EventType type)
 
  310    case SDL_EVENT_DISPLAY_ADDED:
 
  311    case SDL_EVENT_DISPLAY_REMOVED:
 
  312    case SDL_EVENT_DISPLAY_MOVED:
 
  313      SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, 
"TODO [%s] Not fully supported yet",
 
  314                  sdl_event_type_str(type));
 
  320  auto settings = _sdl->context()->settings;
 
  327  std::vector<rdpMonitor> monitors;
 
  328  monitors.reserve(_sdl->windows.size());
 
  329  for (
auto& smon : _sdl->windows)
 
  331    monitors.emplace_back(smon.second.monitor());
 
  339bool sdlDispContext::handle_display_event(
const SDL_DisplayEvent* ev)
 
  345    case SDL_EVENT_DISPLAY_ADDED:
 
  346      SDL_Log(
"A new display with id %u was connected", ev->displayID);
 
  347      return updateMonitors(ev->type);
 
  348    case SDL_EVENT_DISPLAY_REMOVED:
 
  349      SDL_Log(
"The display with id %u was disconnected", ev->displayID);
 
  350      return updateMonitors(ev->type);
 
  351    case SDL_EVENT_DISPLAY_ORIENTATION:
 
  352      SDL_Log(
"The orientation of display with id %u was changed", ev->displayID);
 
  353      return updateMonitors(ev->type);
 
  354    case SDL_EVENT_DISPLAY_MOVED:
 
  355      SDL_Log(
"The display with id %u was moved", ev->displayID);
 
  356      return updateMonitors(ev->type);
 
  357    case SDL_EVENT_DISPLAY_CONTENT_SCALE_CHANGED:
 
  358      SDL_Log(
"The display with id %u changed scale", ev->displayID);
 
  359      return updateMonitors(ev->type);
 
  360    case SDL_EVENT_DISPLAY_CURRENT_MODE_CHANGED:
 
  361      SDL_Log(
"The display with id %u changed mode", ev->displayID);
 
  362      return updateMonitors(ev->type);
 
  363    case SDL_EVENT_DISPLAY_DESKTOP_MODE_CHANGED:
 
  364      SDL_Log(
"The display with id %u changed desktop mode", ev->displayID);
 
  365      return updateMonitors(ev->type);
 
  371bool sdlDispContext::handle_window_event(
const SDL_WindowEvent* ev)
 
  377  auto it = _sdl->windows.find(ev->windowID);
 
  378  if (it != _sdl->windows.end())
 
  379    it->second.setBordered(bordered);
 
  383    case SDL_EVENT_WINDOW_HIDDEN:
 
  384    case SDL_EVENT_WINDOW_MINIMIZED:
 
  385      return _sdl->redraw(
true);
 
  386    case SDL_EVENT_WINDOW_ENTER_FULLSCREEN:
 
  387      return updateMonitor(ev->windowID);
 
  388    case SDL_EVENT_WINDOW_LEAVE_FULLSCREEN:
 
  389      return updateMonitor(ev->windowID);
 
  391    case SDL_EVENT_WINDOW_EXPOSED:
 
  392    case SDL_EVENT_WINDOW_SHOWN:
 
  393    case SDL_EVENT_WINDOW_MAXIMIZED:
 
  394    case SDL_EVENT_WINDOW_RESTORED:
 
  400    case SDL_EVENT_WINDOW_DISPLAY_SCALE_CHANGED:
 
  401    case SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED:
 
  402    case SDL_EVENT_WINDOW_RESIZED:
 
  403      return updateMonitor(ev->windowID);
 
  404    case SDL_EVENT_WINDOW_MOUSE_LEAVE:
 
  406      _sdl->input.keyboard_grab(ev->windowID, 
false);
 
  408    case SDL_EVENT_WINDOW_MOUSE_ENTER:
 
  410      _sdl->input.keyboard_grab(ev->windowID, 
true);
 
  411      return _sdl->input.keyboard_focus_in();
 
  412    case SDL_EVENT_WINDOW_FOCUS_GAINED:
 
  413      return _sdl->input.keyboard_focus_in();
 
  420UINT sdlDispContext::DisplayControlCaps(DispClientContext* disp, UINT32 maxNumMonitors,
 
  421                                        UINT32 maxMonitorAreaFactorA, UINT32 maxMonitorAreaFactorB)
 
  427  return sdlDisp->DisplayControlCaps(maxNumMonitors, maxMonitorAreaFactorA,
 
  428                                     maxMonitorAreaFactorB);
 
  431UINT sdlDispContext::DisplayControlCaps(UINT32 maxNumMonitors, UINT32 maxMonitorAreaFactorA,
 
  432                                        UINT32 maxMonitorAreaFactorB)
 
  434  auto settings = _sdl->context()->settings;
 
  435  WINPR_ASSERT(settings);
 
  438           "DisplayControlCapsPdu: MaxNumMonitors: %" PRIu32 
" MaxMonitorAreaFactorA: %" PRIu32
 
  439           " MaxMonitorAreaFactorB: %" PRIu32 
"",
 
  440           maxNumMonitors, maxMonitorAreaFactorA, maxMonitorAreaFactorB);
 
  444    return CHANNEL_RC_OK;
 
  446  WLog_DBG(TAG, 
"DisplayControlCapsPdu: setting the window as resizable");
 
  447  return set_window_resizable() ? CHANNEL_RC_OK : CHANNEL_RC_NO_MEMORY;
 
  450bool sdlDispContext::init(DispClientContext* disp)
 
  455  auto settings = _sdl->context()->settings;
 
  465    disp->DisplayControlCaps = sdlDispContext::DisplayControlCaps;
 
  468  return _sdl->update_resizeable(
true);
 
  471bool sdlDispContext::uninit(DispClientContext* disp)
 
  477  return _sdl->update_resizeable(
false);
 
  480sdlDispContext::sdlDispContext(
SdlContext* sdl) : _sdl(sdl)
 
  483  WINPR_ASSERT(_sdl->context()->settings);
 
  484  WINPR_ASSERT(_sdl->context()->pubSub);
 
  486  auto pubSub = _sdl->context()->pubSub;
 
  488  PubSub_SubscribeActivated(pubSub, sdlDispContext::OnActivated);
 
  489  PubSub_SubscribeGraphicsReset(pubSub, sdlDispContext::OnGraphicsReset);
 
  493sdlDispContext::~sdlDispContext()
 
  495  wPubSub* pubSub = _sdl->context()->pubSub;
 
  496  WINPR_ASSERT(pubSub);
 
  498  PubSub_UnsubscribeActivated(pubSub, sdlDispContext::OnActivated);
 
  499  PubSub_UnsubscribeGraphicsReset(pubSub, sdlDispContext::OnGraphicsReset);
 
  500  SDL_RemoveTimer(_timer);
 
FREERDP_API UINT32 freerdp_settings_get_uint32(const rdpSettings *settings, FreeRDP_Settings_Keys_UInt32 id)
Returns a UINT32 settings value.
 
FREERDP_API BOOL freerdp_settings_get_bool(const rdpSettings *settings, FreeRDP_Settings_Keys_Bool id)
Returns a boolean settings value.
 
FREERDP_API UINT64 freerdp_settings_get_uint64(const rdpSettings *settings, FreeRDP_Settings_Keys_UInt64 id)
Returns a UINT64 settings value.
 
FREERDP_API BOOL freerdp_settings_set_monitor_def_array_sorted(rdpSettings *settings, const rdpMonitor *monitors, size_t count)
Sort monitor array according to:
 
FREERDP_API UINT16 freerdp_settings_get_uint16(const rdpSettings *settings, FreeRDP_Settings_Keys_UInt16 id)
Returns a UINT16 settings value.
 
FREERDP_API const void * freerdp_settings_get_pointer(const rdpSettings *settings, FreeRDP_Settings_Keys_Pointer id)
Returns a immutable pointer settings value.