Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions Runtime/DisplayXRTargetDisplay.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ namespace DisplayXR
/// <para>
/// <b>Requires a runtime advertising <c>XR_DXR_display_info</c> v16+.</b> Against an
/// older one the panel's desktop position is simply not reported; the component logs
/// once and does nothing. It is also Windows-only today.
/// once and does nothing. It works on Windows and Linux (X11/XWayland); macOS is not
/// implemented yet.
/// </para>
///
/// <para>
Expand Down Expand Up @@ -66,7 +67,7 @@ void OnEnable()

/// <summary>
/// Move now, if the session is running. Returns false when it is not, when the
/// runtime does not report the panel position, or off Windows.
/// runtime does not report the panel position, or on macOS.
/// </summary>
public bool MoveNow()
{
Expand All @@ -87,7 +88,7 @@ public bool MoveNow()
Debug.LogWarning(
"[DisplayXR] TargetDisplay: the runtime did not report the panel's " +
"desktop position (needs XR_DXR_display_info v16+), so the window was " +
"left where Windows opened it. Making the panel the Windows main " +
"left where the OS opened it. Making the panel the main " +
"display remains the workaround.");
}
}
Expand Down
5 changes: 3 additions & 2 deletions Runtime/Provider/DisplayXRProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -142,8 +142,9 @@ public static bool RequestRenderingMode(uint modeIndex) =>
/// including the no-op case where it is already there.
/// <para>
/// Needs a runtime advertising <c>XR_DXR_display_info</c> v16+; returns false
/// against anything older, and false off Windows. Call it once the session is
/// running — the panel's desktop position is read at session start.
/// against anything older, and false on macOS (Windows and Linux are supported).
/// Call it once the session is running — the panel's desktop position is read at
/// session start.
/// </para>
/// <para>
/// The move is performed in native inside a per-monitor-DPI-aware context on
Expand Down
5 changes: 5 additions & 0 deletions native~/displayxr_exports.h
Original file line number Diff line number Diff line change
Expand Up @@ -408,6 +408,11 @@ DISPLAYXR_EXPORT int displayxr_linux_drag_window(int right_pressed);
/// (#332) 1 while the transparent overlay is up and the click-through exports act; 0 for
/// the opaque child or without X. Cached state only — cheap to poll every frame.
DISPLAYXR_EXPORT int displayxr_linux_click_through_active(void);

/// (#266) Centre Unity's window on the given rect (X root coordinates), unless it is
/// already there. Called by dxr_prov_move_window_to_display with the panel rect the
/// runtime reported. Returns 1 when the window is on (or was moved to) the rect.
DISPLAYXR_EXPORT int displayxr_linux_move_app_window_to_rect(int x, int y, int w, int h);
#endif

#ifdef __cplusplus
Expand Down
55 changes: 55 additions & 0 deletions native~/displayxr_linux.c
Original file line number Diff line number Diff line change
Expand Up @@ -1295,6 +1295,61 @@ displayxr_linux_drag_window(int right_pressed)
return active;
}

// ---------------------------------------------------------------------------
// Move the app window onto the 3D panel (#266)
// ---------------------------------------------------------------------------
//
// dxr_prov_move_window_to_display (DisplayXRProvider.MoveWindowToDisplay /
// DisplayXRTargetDisplay) hands us the panel rect the runtime reported, in X root
// coordinates. Centre Unity's window on it (its client area; the WM keeps any frame),
// clamped so a window larger than the panel keeps its top-left on the panel. Already
// on the panel (its centre inside the rect): leave it, as Windows does — a pointless
// move is a window jump the user did not have before.
//
// C# main thread: the main-thread connection and its mutex, never s_dpy (THREADS,
// above). Works for the opaque child path too; in transparent mode the overlay
// follows Unity's window in displayxr_linux_track_window.
DISPLAYXR_EXPORT int
displayxr_linux_move_app_window_to_rect(int px, int py, int pw, int ph)
{
if (!lin_load_xlib() || !s_win || pw <= 0 || ph <= 0) {
lin_log("[DisplayXR-LNX] move_to_display: no app window\n");
return 0;
}
int rc = 0;
pthread_mutex_lock(&s_hit_mutex);
if (!s_hit_dpy) s_hit_dpy = s_x.XOpenDisplay(NULL);
if (s_hit_dpy) {
XWin child = 0, gr = 0;
int cx = 0, cy = 0, gx = 0, gy = 0;
unsigned int cw = 0, ch = 0, gb = 0, gd = 0;
if (s_x.XTranslateCoordinates(s_hit_dpy, s_win, s_x.XDefaultRootWindow(s_hit_dpy), 0, 0,
&cx, &cy, &child) &&
s_x.XGetGeometry(s_hit_dpy, s_win, &gr, &gx, &gy, &cw, &ch, &gb, &gd)) {
char m[224];
int mx = cx + (int)cw / 2, my = cy + (int)ch / 2;
if (mx >= px && mx < px + pw && my >= py && my < py + ph) {
lin_log("[DisplayXR-LNX] move_to_display: already on the 3D display\n");
rc = 1;
} else {
int x = px + (pw - (int)cw) / 2;
int y = py + (ph - (int)ch) / 2;
if (x < px) x = px;
if (y < py) y = py;
lin_move_client(s_hit_dpy, x, y);
snprintf(m, sizeof(m),
"[DisplayXR-LNX] move_to_display: (%d,%d) %ux%u -> (%d,%d) on the panel "
"(%d,%d %dx%d)\n",
cx, cy, cw, ch, x, y, px, py, pw, ph);
lin_log(m);
rc = 1;
}
}
}
pthread_mutex_unlock(&s_hit_mutex);
return rc;
}

// ---------------------------------------------------------------------------
// Foreground query (#332)
// ---------------------------------------------------------------------------
Expand Down
29 changes: 29 additions & 0 deletions native~/displayxr_xrprovider/displayxr_provider_session.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -552,6 +552,7 @@ typedef struct ProviderSession {
// per-monitor-aware process sees them. Valid only against a runtime advertising
// XR_DXR_display_info v16+.
int32_t desktop_origin_x, desktop_origin_y;
uint32_t desktop_w, desktop_h; // panel extent (v18 only; 0 = unknown)
int desktop_origin_valid;
// (#266 / v18) desktop_origin_valid says the runtime gave us AN origin; this says
// it CONFIRMED a 3D panel is there (XrDisplayDesktopInfoDXR::isPanelConfirmed). v16
Expand Down Expand Up @@ -4866,6 +4867,8 @@ int dxr_prov_session_start(const char *runtime_json_path,
if (v18) {
s_ps.desktop_origin_x = ddi.desktopRect.offset.x;
s_ps.desktop_origin_y = ddi.desktopRect.offset.y;
s_ps.desktop_w = (uint32_t)ddi.desktopRect.extent.width;
s_ps.desktop_h = (uint32_t)ddi.desktopRect.extent.height;
s_ps.desktop_origin_valid = 1;
s_ps.panel_confirmed = (ddi.isPanelConfirmed == XR_TRUE) ? 1 : 0;
ps_log("[DisplayXR-PROV] Display desktop rect: (%d,%d %dx%d) confirmed=%d "
Expand All @@ -4877,6 +4880,7 @@ int dxr_prov_session_start(const char *runtime_json_path,
} else if (v16) {
s_ps.desktop_origin_x = ddp.left;
s_ps.desktop_origin_y = ddp.top;
s_ps.desktop_w = s_ps.desktop_h = 0;
s_ps.desktop_origin_valid = 1;
s_ps.panel_confirmed = -1; // v16 runtime: confirmation not available
ps_log("[DisplayXR-PROV] Display desktop origin: (%d,%d) [v16; no v18 "
Expand Down Expand Up @@ -7640,6 +7644,9 @@ uint32_t dxr_prov_get_active_mode_index(void) { return s_ps.active_mode_index; }
// inside a per-monitor-aware context removes that failure mode by construction rather
// than by remembering to convert at each call site.
extern "C" void *displayxr_find_unity_hwnd(void);
#if defined(__linux__) && !defined(__ANDROID__)
extern "C" int displayxr_linux_move_app_window_to_rect(int x, int y, int w, int h);
#endif

int dxr_prov_get_display_desktop_origin(int *out_x, int *out_y)
{
Expand Down Expand Up @@ -7721,6 +7728,28 @@ int dxr_prov_move_window_to_display(void)

if (prev) SetThreadDpiAwarenessContext(prev);
return rc;
#elif defined(__linux__) && !defined(__ANDROID__)
// (#266, Linux) The runtime reports the panel rect in X root coordinates (its desktop
// resolver matches the panel to an XRandR output), which is the space Unity's X11
// window lives in under X11 and XWayland alike — no DPI virtualisation to undo here.
// Same gating as Windows; the move itself is in displayxr_linux.c, on the main-thread
// X connection.
if (!s_ps.desktop_origin_valid) {
ps_log("[DisplayXR-PROV] move_window_to_display: no desktop origin (runtime < v16)\n");
return 0;
}
if (s_ps.panel_confirmed == 0) {
ps_log("[DisplayXR-PROV] move_window_to_display: runtime did not confirm a 3D panel "
"(fallback-to-primary rect) - leaving the window where it is (#266 v18)\n");
return 0;
}
uint32_t pw = s_ps.desktop_w, ph = s_ps.desktop_h;
if (pw == 0 || ph == 0) { // v16: no extent reported; the panel's native size is the best guess
pw = s_ps.display_info.pixel_width;
ph = s_ps.display_info.pixel_height;
}
return displayxr_linux_move_app_window_to_rect((int)s_ps.desktop_origin_x,
(int)s_ps.desktop_origin_y, (int)pw, (int)ph);
#else
return 0;
#endif
Expand Down
Loading