mirror of
https://github.com/morgan9e/FreeRDP
synced 2026-04-14 00:14:11 +09:00
[client,sdl] create a map of pixel coordinates
We need pixel coordinates for each monitor, but SDL may return logical coordinates depending on HighDPI mode used by the system. This commit does: * Detect which HighDPI mode is in use isHighDPIWindowsMode * Creates a map of pixel coordinates for each monitor * recreated whenever a monitor changes) * Updates the window rdpMonitor data for existing windows
This commit is contained in:
@@ -16,6 +16,9 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "sdl_context.hpp"
|
||||
#include "sdl_config.hpp"
|
||||
#include "sdl_channels.hpp"
|
||||
@@ -808,6 +811,78 @@ void SdlContext::applyMonitorOffset(SDL_WindowID window, float& x, float& y) con
|
||||
y -= static_cast<float>(w->offsetY());
|
||||
}
|
||||
|
||||
static bool alignX(const SDL_Rect& a, const SDL_Rect& b)
|
||||
{
|
||||
if (a.x + a.w == b.x)
|
||||
return true;
|
||||
if (b.x + b.w == a.x)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool alignY(const SDL_Rect& a, const SDL_Rect& b)
|
||||
{
|
||||
if (a.y + a.h == b.y)
|
||||
return true;
|
||||
if (b.y + b.h == a.y)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<SDL_DisplayID>
|
||||
SdlContext::updateDisplayOffsetsForNeighbours(SDL_DisplayID id,
|
||||
const std::vector<SDL_DisplayID>& ignore)
|
||||
{
|
||||
auto first = _offsets.at(id);
|
||||
std::vector<SDL_DisplayID> neighbours;
|
||||
|
||||
for (auto& entry : _offsets)
|
||||
{
|
||||
if (entry.first == id)
|
||||
continue;
|
||||
if (std::find(ignore.begin(), ignore.end(), entry.first) != ignore.end())
|
||||
continue;
|
||||
|
||||
bool neighbor = false;
|
||||
if (alignX(entry.second.first, first.first))
|
||||
{
|
||||
if (entry.second.first.x < first.first.x)
|
||||
entry.second.second.x = first.second.x - entry.second.second.w;
|
||||
else
|
||||
entry.second.second.x = first.second.x + first.second.w;
|
||||
neighbor = true;
|
||||
}
|
||||
if (alignY(entry.second.first, first.first))
|
||||
{
|
||||
if (entry.second.first.y < first.first.y)
|
||||
entry.second.second.y = first.second.y - entry.second.second.h;
|
||||
else
|
||||
entry.second.second.y = first.second.y + first.second.h;
|
||||
neighbor = true;
|
||||
}
|
||||
|
||||
if (neighbor)
|
||||
neighbours.push_back(entry.first);
|
||||
}
|
||||
return neighbours;
|
||||
}
|
||||
|
||||
void SdlContext::updateMonitorDataFromOffsets()
|
||||
{
|
||||
for (auto& entry : _displays)
|
||||
{
|
||||
auto offsets = _offsets.at(entry.first);
|
||||
entry.second.x = offsets.second.x;
|
||||
entry.second.y = offsets.second.y;
|
||||
}
|
||||
|
||||
for (auto& entry : _windows)
|
||||
{
|
||||
const auto& monitor = _displays.at(entry.first);
|
||||
entry.second.setMonitor(monitor);
|
||||
}
|
||||
}
|
||||
|
||||
bool SdlContext::drawToWindow(SdlWindow& window, const std::vector<SDL_Rect>& rects)
|
||||
{
|
||||
if (!isConnected())
|
||||
@@ -897,8 +972,7 @@ bool SdlContext::detectDisplays()
|
||||
for (int x = 0; x < count; x++)
|
||||
{
|
||||
const auto id = display[x];
|
||||
auto monitor = SdlWindow::query(id, false);
|
||||
addOrUpdateDisplay(id, monitor);
|
||||
addOrUpdateDisplay(id);
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -912,6 +986,7 @@ rdpMonitor SdlContext::getDisplay(SDL_DisplayID id) const
|
||||
std::vector<SDL_DisplayID> SdlContext::getDisplayIds() const
|
||||
{
|
||||
std::vector<SDL_DisplayID> keys;
|
||||
keys.reserve(_displays.size());
|
||||
for (const auto& entry : _displays)
|
||||
{
|
||||
keys.push_back(entry.first);
|
||||
@@ -1122,9 +1197,52 @@ bool SdlContext::handleEvent(const SDL_TouchFingerEvent& ev)
|
||||
return SdlTouch::handleEvent(this, copy.tfinger);
|
||||
}
|
||||
|
||||
void SdlContext::addOrUpdateDisplay(SDL_DisplayID id, const rdpMonitor& monitor)
|
||||
void SdlContext::addOrUpdateDisplay(SDL_DisplayID id)
|
||||
{
|
||||
auto monitor = SdlWindow::query(id, false);
|
||||
_displays.emplace(id, monitor);
|
||||
|
||||
/* Update actual display rectangles:
|
||||
*
|
||||
* 1. Get logical display bounds
|
||||
* 2. Use already known pixel width and height
|
||||
* 3. Iterate over each display and update the x and y offsets by adding all monitor
|
||||
* widths/heights from the primary
|
||||
*/
|
||||
_offsets.clear();
|
||||
for (auto& entry : _displays)
|
||||
{
|
||||
SDL_Rect bounds{};
|
||||
std::ignore = SDL_GetDisplayBounds(entry.first, &bounds);
|
||||
|
||||
SDL_Rect pixel{};
|
||||
pixel.w = entry.second.width;
|
||||
pixel.h = entry.second.height;
|
||||
_offsets.emplace(entry.first, std::pair{ bounds, pixel });
|
||||
}
|
||||
|
||||
/* 1. Find primary and update all neighbors
|
||||
* 2. For each neighbor update all neighbors
|
||||
* 3. repeat until all displays updated.
|
||||
*/
|
||||
const auto primary = SDL_GetPrimaryDisplay();
|
||||
std::vector<SDL_DisplayID> handled;
|
||||
handled.push_back(primary);
|
||||
|
||||
auto neighbors = updateDisplayOffsetsForNeighbours(primary);
|
||||
while (!neighbors.empty())
|
||||
{
|
||||
auto neighbor = *neighbors.begin();
|
||||
neighbors.pop_back();
|
||||
|
||||
if (std::find(handled.begin(), handled.end(), neighbor) != handled.end())
|
||||
continue;
|
||||
handled.push_back(neighbor);
|
||||
|
||||
auto next = updateDisplayOffsetsForNeighbours(neighbor, handled);
|
||||
neighbors.insert(neighbors.end(), next.begin(), next.end());
|
||||
}
|
||||
updateMonitorDataFromOffsets();
|
||||
}
|
||||
|
||||
void SdlContext::deleteDisplay(SDL_DisplayID id)
|
||||
|
||||
@@ -165,7 +165,7 @@ class SdlContext
|
||||
[[nodiscard]] bool handleEvent(const SDL_MouseWheelEvent& ev);
|
||||
[[nodiscard]] bool handleEvent(const SDL_TouchFingerEvent& ev);
|
||||
|
||||
void addOrUpdateDisplay(SDL_DisplayID id, const rdpMonitor& monitor);
|
||||
void addOrUpdateDisplay(SDL_DisplayID id);
|
||||
void deleteDisplay(SDL_DisplayID id);
|
||||
|
||||
[[nodiscard]] bool createPrimary();
|
||||
@@ -180,6 +180,11 @@ class SdlContext
|
||||
|
||||
void applyMonitorOffset(SDL_WindowID window, float& x, float& y) const;
|
||||
|
||||
[[nodiscard]] std::vector<SDL_DisplayID>
|
||||
updateDisplayOffsetsForNeighbours(SDL_DisplayID id,
|
||||
const std::vector<SDL_DisplayID>& ignore = {});
|
||||
void updateMonitorDataFromOffsets();
|
||||
|
||||
rdpContext* _context = nullptr;
|
||||
wLog* _log = nullptr;
|
||||
|
||||
@@ -214,6 +219,7 @@ class SdlContext
|
||||
|
||||
std::map<SDL_DisplayID, rdpMonitor> _displays;
|
||||
std::map<SDL_WindowID, SdlWindow> _windows;
|
||||
std::map<SDL_DisplayID, std::pair<SDL_Rect, SDL_Rect>> _offsets;
|
||||
|
||||
uint32_t _windowWidth = 0;
|
||||
uint32_t _windowHeigth = 0;
|
||||
|
||||
@@ -172,13 +172,6 @@ int sdl_list_monitors([[maybe_unused]] SdlContext* sdl)
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
[[nodiscard]] static Uint32 scale(Uint32 val, float scale)
|
||||
{
|
||||
const auto dval = static_cast<float>(val);
|
||||
const auto sval = dval / scale;
|
||||
return static_cast<Uint32>(sval);
|
||||
}
|
||||
|
||||
[[nodiscard]] static BOOL sdl_apply_display_properties(SdlContext* sdl)
|
||||
{
|
||||
WINPR_ASSERT(sdl);
|
||||
@@ -242,7 +235,6 @@ int sdl_list_monitors([[maybe_unused]] SdlContext* sdl)
|
||||
sdl->setMonitorIds({ id });
|
||||
}
|
||||
|
||||
// TODO: Fill monitor struct
|
||||
if (!sdl_apply_display_properties(sdl))
|
||||
return FALSE;
|
||||
return sdl_apply_max_size(sdl, pMaxWidth, pMaxHeight);
|
||||
|
||||
@@ -56,6 +56,8 @@ SdlWindow::SdlWindow(SDL_DisplayID id, const std::string& title, const SDL_Rect&
|
||||
std::ignore = resize({ w, h });
|
||||
SDL_SetHint(SDL_HINT_APP_NAME, "");
|
||||
std::ignore = SDL_SyncWindow(_window);
|
||||
|
||||
_monitor = query(_window, id, true);
|
||||
}
|
||||
|
||||
SdlWindow::SdlWindow(SdlWindow&& other) noexcept
|
||||
@@ -129,7 +131,18 @@ Sint32 SdlWindow::offsetY() const
|
||||
|
||||
rdpMonitor SdlWindow::monitor(bool isPrimary) const
|
||||
{
|
||||
return query(_window, displayIndex(), isPrimary);
|
||||
auto m = _monitor;
|
||||
if (isPrimary)
|
||||
{
|
||||
m.x = 0;
|
||||
m.y = 0;
|
||||
}
|
||||
return m;
|
||||
}
|
||||
|
||||
void SdlWindow::setMonitor(rdpMonitor monitor)
|
||||
{
|
||||
_monitor = monitor;
|
||||
}
|
||||
|
||||
float SdlWindow::scale() const
|
||||
@@ -274,23 +287,21 @@ rdpMonitor SdlWindow::query(SDL_Window* window, SDL_DisplayID id, bool forceAsPr
|
||||
if (!window)
|
||||
return {};
|
||||
|
||||
const auto& r = rect(window);
|
||||
const auto& r = rect(window, forceAsPrimary);
|
||||
const float factor = SDL_GetWindowDisplayScale(window);
|
||||
const float dpi = std::roundf(factor * 100.0f);
|
||||
|
||||
WINPR_ASSERT(r.w > 0);
|
||||
WINPR_ASSERT(r.h > 0);
|
||||
|
||||
bool highDpi = dpi > 100;
|
||||
|
||||
const auto primary = SDL_GetPrimaryDisplay();
|
||||
const auto orientation = SDL_GetCurrentDisplayOrientation(id);
|
||||
const auto rdp_orientation = sdl::utils::orientaion_to_rdp(orientation);
|
||||
|
||||
rdpMonitor monitor{};
|
||||
monitor.orig_screen = id;
|
||||
monitor.x = forceAsPrimary ? 0 : r.x;
|
||||
monitor.y = forceAsPrimary ? 0 : r.y;
|
||||
monitor.x = r.x;
|
||||
monitor.y = r.y;
|
||||
monitor.width = r.w;
|
||||
monitor.height = r.h;
|
||||
monitor.is_primary = forceAsPrimary || (id == primary);
|
||||
@@ -316,20 +327,49 @@ rdpMonitor SdlWindow::query(SDL_Window* window, SDL_DisplayID id, bool forceAsPr
|
||||
return monitor;
|
||||
}
|
||||
|
||||
SDL_Rect SdlWindow::rect(SDL_Window* window)
|
||||
SDL_Rect SdlWindow::rect(SDL_Window* window, bool forceAsPrimary)
|
||||
{
|
||||
SDL_Rect rect = {};
|
||||
if (!window)
|
||||
return {};
|
||||
|
||||
if (!SDL_GetWindowPosition(window, &rect.x, &rect.y))
|
||||
return {};
|
||||
if (!forceAsPrimary)
|
||||
{
|
||||
if (!SDL_GetWindowPosition(window, &rect.x, &rect.y))
|
||||
return {};
|
||||
}
|
||||
|
||||
if (!SDL_GetWindowSizeInPixels(window, &rect.w, &rect.h))
|
||||
return {};
|
||||
|
||||
return rect;
|
||||
}
|
||||
|
||||
SdlWindow::HighDPIMode SdlWindow::isHighDPIWindowsMode(SDL_Window* window)
|
||||
{
|
||||
if (!window)
|
||||
return MODE_INVALID;
|
||||
|
||||
const auto id = SDL_GetDisplayForWindow(window);
|
||||
if (id == 0)
|
||||
return MODE_INVALID;
|
||||
|
||||
const auto cs = SDL_GetDisplayContentScale(id);
|
||||
const auto ds = SDL_GetWindowDisplayScale(window);
|
||||
const auto pd = SDL_GetWindowPixelDensity(window);
|
||||
|
||||
/* mac os x style, but no HighDPI display */
|
||||
if ((cs == 1.0f) && (ds == 1.0f) && (pd == 1.0f))
|
||||
return MODE_NONE;
|
||||
|
||||
/* mac os x style HighDPI */
|
||||
if ((cs == 1.0f) && (ds > 1.0f) && (pd > 1.0f))
|
||||
return MODE_MACOS;
|
||||
|
||||
/* rest is windows style */
|
||||
return MODE_WINDOWS;
|
||||
}
|
||||
|
||||
bool SdlWindow::blit(SDL_Surface* surface, const SDL_Rect& srcRect, SDL_Rect& dstRect)
|
||||
{
|
||||
auto screen = SDL_GetWindowSurface(_window);
|
||||
@@ -421,3 +461,22 @@ rdpMonitor SdlWindow::query(SDL_DisplayID id, bool forceAsPrimary)
|
||||
|
||||
return query(window.get(), id, forceAsPrimary);
|
||||
}
|
||||
|
||||
SDL_Rect SdlWindow::rect(SDL_DisplayID id, bool forceAsPrimary)
|
||||
{
|
||||
std::unique_ptr<SDL_Window, void (*)(SDL_Window*)> window(createDummy(id), SDL_DestroyWindow);
|
||||
if (!window)
|
||||
return {};
|
||||
|
||||
std::unique_ptr<SDL_Renderer, void (*)(SDL_Renderer*)> renderer(
|
||||
SDL_CreateRenderer(window.get(), nullptr), SDL_DestroyRenderer);
|
||||
|
||||
if (!SDL_SyncWindow(window.get()))
|
||||
return {};
|
||||
|
||||
SDL_Event event{};
|
||||
while (SDL_PollEvent(&event))
|
||||
;
|
||||
|
||||
return rect(window.get(), forceAsPrimary);
|
||||
}
|
||||
|
||||
@@ -53,6 +53,7 @@ class SdlWindow
|
||||
[[nodiscard]] Sint32 offsetY() const;
|
||||
|
||||
[[nodiscard]] rdpMonitor monitor(bool isPrimary) const;
|
||||
void setMonitor(rdpMonitor monitor);
|
||||
|
||||
[[nodiscard]] float scale() const;
|
||||
[[nodiscard]] SDL_DisplayOrientation orientation() const;
|
||||
@@ -87,11 +88,23 @@ class SdlWindow
|
||||
Uint8 b = 0x00, Uint8 a = 0xff);
|
||||
[[nodiscard]] static rdpMonitor query(SDL_Window* window, SDL_DisplayID id,
|
||||
bool forceAsPrimary = false);
|
||||
[[nodiscard]] static SDL_Rect rect(SDL_Window* window);
|
||||
[[nodiscard]] static SDL_Rect rect(SDL_Window* window, bool forceAsPrimary = false);
|
||||
[[nodiscard]] static SDL_Rect rect(SDL_DisplayID id, bool forceAsPrimary = false);
|
||||
|
||||
enum HighDPIMode
|
||||
{
|
||||
MODE_INVALID,
|
||||
MODE_NONE,
|
||||
MODE_WINDOWS,
|
||||
MODE_MACOS
|
||||
};
|
||||
|
||||
[[nodiscard]] static enum HighDPIMode isHighDPIWindowsMode(SDL_Window* window);
|
||||
|
||||
private:
|
||||
SDL_Window* _window = nullptr;
|
||||
SDL_DisplayID _displayID = 0;
|
||||
Sint32 _offset_x = 0;
|
||||
Sint32 _offset_y = 0;
|
||||
rdpMonitor _monitor{};
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user