Skip to main content

teksilo_platform/
window_activation.rs

1// SPDX-License-Identifier: MPL-2.0
2// SPDX-FileCopyrightText: 2026 FernTech
3
4//! Cross-platform window "raise / activate" — the per-OS backend behind
5//! `WindowState::focus()` / `WindowOps::focus_window`.
6//!
7//! winit's `Window::focus_window` already performs a real cross-process raise on
8//! X11 (`_NET_ACTIVE_WINDOW`), Windows (`SetForegroundWindow` + the SendInput
9//! foreground-lock defeat) and macOS (`activateIgnoringOtherApps` +
10//! `makeKeyAndOrderFront`) — including when the *target* window raises itself in
11//! response to a cross-process request. On **Wayland it is a hard no-op**, so a
12//! real raise there requires driving `xdg_activation_v1` over the window's raw
13//! `wl_surface` (see the private `wayland` submodule).
14//!
15//! Callers keep the single `focus()` API. The optional activation `token` — an
16//! opaque string minted by the focused requester and handed across the process
17//! boundary — is only ever consulted on Wayland; everywhere else it is ignored
18//! because `focus_window()` already suffices.
19
20use winit::window::Window;
21
22#[cfg(all(unix, not(target_os = "macos")))]
23mod wayland;
24
25/// Raise `window` above others and give it keyboard focus, best-effort.
26///
27/// `token` is only meaningful on Wayland (an `xdg_activation_v1` token). On
28/// Wayland *without* a token a genuine focus-steal is impossible, so this
29/// degrades to an attention request (urgency hint / taskbar highlight). On every
30/// other platform the token is ignored and winit's `focus_window()` performs the
31/// raise.
32pub fn raise(window: &Window, token: Option<&str>) {
33    #[cfg(all(unix, not(target_os = "macos")))]
34    if crate::active_window_system() == crate::WindowSystem::Wayland {
35        match token {
36            Some(token) => wayland::activate_with_token(window, token),
37            None => request_attention(window),
38        }
39        return;
40    }
41
42    let _ = token;
43    window.focus_window();
44}
45
46/// Consume an `xdg_activation_v1` startup token from the environment (set by the
47/// process that spawned us) and apply it to `attrs`, so the created window comes
48/// up focused on Wayland. Resets the env afterwards so later windows / grandchild
49/// processes don't reuse a stale token. No-op off Wayland/X11.
50pub fn apply_creation_token(
51    attrs: winit::window::WindowAttributes,
52    event_loop: &winit::event_loop::ActiveEventLoop,
53) -> winit::window::WindowAttributes {
54    #[cfg(all(unix, not(target_os = "macos")))]
55    {
56        use winit::platform::startup_notify::{
57            EventLoopExtStartupNotify, WindowAttributesExtStartupNotify, reset_activation_token_env,
58        };
59        if let Some(token) = event_loop.read_token_from_env() {
60            reset_activation_token_env();
61            return attrs.with_activation_token(token);
62        }
63    }
64    let _ = event_loop;
65    attrs
66}
67
68/// Ask the compositor for an `xdg_activation_v1` token for `window` (to hand to
69/// another window or a child process). Returns `true` if a request was issued —
70/// the token then arrives via `WindowEvent::ActivationTokenDone`. Returns `false`
71/// where unsupported (everything but Wayland/X11), so the caller treats "no
72/// token" as immediate.
73pub fn request_activation_token(window: &Window) -> bool {
74    #[cfg(all(unix, not(target_os = "macos")))]
75    {
76        use winit::platform::startup_notify::WindowExtStartupNotify;
77        window.request_activation_token().is_ok()
78    }
79    #[cfg(not(all(unix, not(target_os = "macos"))))]
80    {
81        let _ = window;
82        false
83    }
84}
85
86/// Inject an `xdg_activation_v1` `token` into a child process's environment so its
87/// first window (built with `WindowConfig::activate_from_env`) comes up focused on
88/// Wayland. Sets both the Wayland and X11 startup-notify variables that winit's
89/// `read_token_from_env` looks for. No-op off Wayland/X11.
90pub fn set_child_activation_env(cmd: &mut std::process::Command, token: &str) {
91    #[cfg(all(unix, not(target_os = "macos")))]
92    {
93        cmd.env("XDG_ACTIVATION_TOKEN", token);
94        cmd.env("DESKTOP_STARTUP_ID", token);
95    }
96    #[cfg(not(all(unix, not(target_os = "macos"))))]
97    {
98        let _ = (cmd, token);
99    }
100}
101
102/// The Wayland degrade (no token, or until the Tier-2 raise lands): a one-shot
103/// informational attention request. winit implements this on Wayland via
104/// `xdg_activation_v1` against the window's own surface.
105#[cfg(all(unix, not(target_os = "macos")))]
106fn request_attention(window: &Window) {
107    window.request_user_attention(Some(winit::window::UserAttentionType::Informational));
108}