diff --git a/src/gui/application/gui_application.cpp b/src/gui/application/gui_application.cpp index 163b05d..5cee3bd 100644 --- a/src/gui/application/gui_application.cpp +++ b/src/gui/application/gui_application.cpp @@ -50,8 +50,10 @@ #include "platform/tray/linux_tray.h" #endif #include "rd_log.h" +#include "server_window_state.h" #include "ui/ui_localization.h" #include "version_checker.h" +#include "window_geometry.h" namespace crossdesk { namespace { @@ -692,18 +694,43 @@ float StreamWindowLogicalHeight(bool custom_titlebar) { } SDL_DisplayID DisplayForSlintWindow(const slint::Window& window) { - const float scale = std::max(window.scale_factor(), 0.01f); const auto position = window.position(); const auto size = window.size(); +#if defined(__APPLE__) + const float scale = std::max(window.scale_factor(), 0.01f); const SDL_Point center{ static_cast( std::lround(position.x / scale + size.width / (2.0f * scale))), static_cast( std::lround(position.y / scale + size.height / (2.0f * scale)))}; +#else + const SDL_Point center{ + position.x + static_cast(size.width / 2), + position.y + static_cast(size.height / 2)}; +#endif const SDL_DisplayID display = SDL_GetDisplayForPoint(¢er); return display != 0 ? display : SDL_GetPrimaryDisplay(); } +#if !defined(__APPLE__) +window_geometry::PhysicalSize PhysicalWindowSize(const slint::Window& window, + float logical_width, + float logical_height) { + const auto size = window.size(); + if (size.width > 0 && size.height > 0) { + return {static_cast(size.width), static_cast(size.height)}; + } + + const float scale = std::max(window.scale_factor(), 0.01f); + return {std::max(1, static_cast(std::lround(logical_width * scale))), + std::max(1, static_cast(std::lround(logical_height * scale)))}; +} + +window_geometry::PhysicalRect PhysicalDisplayBounds(const SDL_Rect& bounds) { + return {bounds.x, bounds.y, bounds.w, bounds.h}; +} +#endif + bool PositionWindowAtCenter(slint::Window& window, const slint::Window& anchor, float logical_width, float logical_height) { if (!CanUseGlobalPointerPosition()) { @@ -718,6 +745,7 @@ bool PositionWindowAtCenter(slint::Window& window, const slint::Window& anchor, return false; } +#if defined(__APPLE__) const float target_x = static_cast(usable_bounds.x) + (static_cast(usable_bounds.w) - logical_width) / 2.0f; @@ -726,6 +754,13 @@ bool PositionWindowAtCenter(slint::Window& window, const slint::Window& anchor, (static_cast(usable_bounds.h) - logical_height) / 2.0f; window.set_position( slint::LogicalPosition(slint::Point{target_x, target_y})); +#else + const auto target = window_geometry::CenteredPosition( + PhysicalDisplayBounds(usable_bounds), + PhysicalWindowSize(window, logical_width, logical_height)); + window.set_position(slint::PhysicalPosition( + slint::Point{target.x, target.y})); +#endif return true; } @@ -735,16 +770,21 @@ bool PositionWindowAtBottomRight(slint::Window& window, float logical_width, return false; } - SDL_DisplayID display = 0; - const float scale = std::max(window.scale_factor(), 0.01f); const auto position = window.position(); const auto size = window.size(); +#if defined(__APPLE__) + const float scale = std::max(window.scale_factor(), 0.01f); const SDL_Point center{ static_cast( std::lround(position.x / scale + size.width / (2.0f * scale))), static_cast( std::lround(position.y / scale + size.height / (2.0f * scale)))}; - display = SDL_GetDisplayForPoint(¢er); +#else + const SDL_Point center{ + position.x + static_cast(size.width / 2), + position.y + static_cast(size.height / 2)}; +#endif + SDL_DisplayID display = SDL_GetDisplayForPoint(¢er); if (display == 0) { display = SDL_GetPrimaryDisplay(); } @@ -756,6 +796,7 @@ bool PositionWindowAtBottomRight(slint::Window& window, float logical_width, return false; } +#if defined(__APPLE__) const float target_x = static_cast(usable_bounds.x) + std::max(0.0f, static_cast(usable_bounds.w) - logical_width); @@ -764,9 +805,33 @@ bool PositionWindowAtBottomRight(slint::Window& window, float logical_width, std::max(0.0f, static_cast(usable_bounds.h) - logical_height); window.set_position( slint::LogicalPosition(slint::Point{target_x, target_y})); +#else + const auto target = window_geometry::BottomRightPosition( + PhysicalDisplayBounds(usable_bounds), + PhysicalWindowSize(window, logical_width, logical_height)); + window.set_position(slint::PhysicalPosition( + slint::Point{target.x, target.y})); +#endif return true; } +#if _WIN32 +bool ConfigureWindowsServerWindow(slint::Window& window) { + const HWND hwnd = window.win32_hwnd(); + if (!hwnd) { + return false; + } + + LONG_PTR ex_style = GetWindowLongPtr(hwnd, GWL_EXSTYLE); + ex_style |= WS_EX_TOOLWINDOW; + ex_style &= ~WS_EX_APPWINDOW; + SetWindowLongPtr(hwnd, GWL_EXSTYLE, ex_style); + return SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0, + SWP_NOMOVE | SWP_NOSIZE | SWP_FRAMECHANGED | + SWP_NOACTIVATE) != FALSE; +} +#endif + template void DragWindow(WindowHandle& component, int phase, float mouse_x, float mouse_y, WindowDragState& state) { @@ -897,6 +962,9 @@ struct GuiApplication::SlintUi { #endif int stream_initial_position_attempts = 0; int server_initial_position_attempts = 0; +#if _WIN32 + int server_native_window_attempts = 0; +#endif int localized_language = -1; std::chrono::steady_clock::time_point last_clipboard_poll{}; slint::Timer video_timer; @@ -3024,6 +3092,9 @@ void GuiApplication::SyncServerWindow() { // the right and bottom edges correct when the backend resolves DPI and // language-dependent preferred width asynchronously on first show. ui_->server_initial_position_attempts = 2; +#if _WIN32 + ui_->server_native_window_attempts = 30; +#endif server_window_created_ = true; server_window_inited_ = true; } @@ -3033,6 +3104,9 @@ void GuiApplication::SyncServerWindow() { server_window_created_ = false; server_window_inited_ = false; ui_->server_initial_position_attempts = 0; +#if _WIN32 + ui_->server_native_window_attempts = 0; +#endif } if (!ui_->server) { return; @@ -3060,18 +3134,19 @@ void GuiApplication::SyncServerWindow() { } ui_->controller_model->set_vector(std::move(controllers)); ui_->controller_name_model->set_vector(std::move(names)); - if (selected_server_remote_id_.empty() && !ui_->controller_ids.empty()) { - selected_server_remote_id_ = ui_->controller_ids.front(); - } - int selected = 0; - for (int i = 0; i < static_cast(ui_->controller_ids.size()); ++i) { - if (ui_->controller_ids[i] == selected_server_remote_id_) { - selected = i; - break; - } - } + const int selected = server_window_state::ReconcileSelectedController( + ui_->controller_ids, &selected_server_remote_id_); (*ui_->server)->set_selected_controller(selected); (*ui_->server)->set_language_index(localization_language_index_); +#if _WIN32 + if (ui_->server_native_window_attempts > 0) { + if (ConfigureWindowsServerWindow((*ui_->server)->window())) { + ui_->server_native_window_attempts = 0; + } else { + --ui_->server_native_window_attempts; + } + } +#endif if (ui_->server_initial_position_attempts > 0) { const float server_width = ServerWindowLogicalWidth(localization_language_index_); diff --git a/src/gui/application/server_window_state.h b/src/gui/application/server_window_state.h new file mode 100644 index 0000000..bc69300 --- /dev/null +++ b/src/gui/application/server_window_state.h @@ -0,0 +1,28 @@ +#pragma once + +#include +#include +#include +#include + +namespace crossdesk::server_window_state { + +inline int ReconcileSelectedController(const std::vector& ids, + std::string* selected_id) { + if (!selected_id) { + return 0; + } + if (ids.empty()) { + selected_id->clear(); + return 0; + } + + const auto selected = std::find(ids.begin(), ids.end(), *selected_id); + if (selected == ids.end()) { + *selected_id = ids.front(); + return 0; + } + return static_cast(std::distance(ids.begin(), selected)); +} + +} // namespace crossdesk::server_window_state diff --git a/src/gui/application/window_geometry.h b/src/gui/application/window_geometry.h new file mode 100644 index 0000000..f6b0f1f --- /dev/null +++ b/src/gui/application/window_geometry.h @@ -0,0 +1,40 @@ +#pragma once + +#include + +namespace crossdesk::window_geometry { + +struct PhysicalRect { + int x = 0; + int y = 0; + int width = 0; + int height = 0; +}; + +struct PhysicalSize { + int width = 0; + int height = 0; +}; + +struct PhysicalPosition { + int x = 0; + int y = 0; +}; + +constexpr PhysicalPosition CenteredPosition(const PhysicalRect& bounds, + const PhysicalSize& size) { + return { + bounds.x + (bounds.width - std::min(bounds.width, size.width)) / 2, + bounds.y + (bounds.height - std::min(bounds.height, size.height)) / 2, + }; +} + +constexpr PhysicalPosition BottomRightPosition(const PhysicalRect& bounds, + const PhysicalSize& size) { + return { + bounds.x + std::max(0, bounds.width - size.width), + bounds.y + std::max(0, bounds.height - size.height), + }; +} + +} // namespace crossdesk::window_geometry