1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
//! Window startup notification to handle window raising.
//!
//! The [`ActivationToken`] is essential to ensure that your newly
//! created window will obtain the focus, otherwise the user could
//! be requered to click on the window.
//!
//! Such token is usually delivered via the environment variable and
//! could be read from it with the [`EventLoopExtStartupNotify::read_token_from_env`].
//!
//! Such token must also be reset after reading it from your environment with
//! [`reset_activation_token_env`] otherwise child processes could inherit it.
//!
//! When starting a new child process with a newly obtained [`ActivationToken`] from
//! [`WindowExtStartupNotify::request_activation_token`] the [`set_activation_token_env`]
//! must be used to propagate it to the child
//!
//! To ensure the delivery of such token by other processes to you, the user should
//! set `StartupNotify=true` inside the `.desktop` file of their application.
//!
//! The specification could be found [`here`].
//!
//! [`here`]: https://specifications.freedesktop.org/startup-notification-spec/startup-notification-latest.txt

use std::env;

use crate::error::NotSupportedError;
use crate::event_loop::{AsyncRequestSerial, EventLoopWindowTarget};
use crate::window::{ActivationToken, Window, WindowBuilder};

/// The variable which is used mostly on X11.
const X11_VAR: &str = "DESKTOP_STARTUP_ID";

/// The variable which is used mostly on Wayland.
const WAYLAND_VAR: &str = "XDG_ACTIVATION_TOKEN";

pub trait EventLoopExtStartupNotify {
    /// Read the token from the environment.
    ///
    /// It's recommended **to unset** this environment variable for child processes.
    fn read_token_from_env(&self) -> Option<ActivationToken>;
}

pub trait WindowExtStartupNotify {
    /// Request a new activation token.
    ///
    /// The token will be delivered inside
    fn request_activation_token(&self) -> Result<AsyncRequestSerial, NotSupportedError>;
}

pub trait WindowBuilderExtStartupNotify {
    /// Use this [`ActivationToken`] during window creation.
    ///
    /// Not using such a token upon a window could make your window not gaining
    /// focus until the user clicks on the window.
    fn with_activation_token(self, token: ActivationToken) -> Self;
}

impl<T> EventLoopExtStartupNotify for EventLoopWindowTarget<T> {
    fn read_token_from_env(&self) -> Option<ActivationToken> {
        match self.p {
            #[cfg(wayland_platform)]
            crate::platform_impl::EventLoopWindowTarget::Wayland(_) => env::var(WAYLAND_VAR),
            #[cfg(x11_platform)]
            crate::platform_impl::EventLoopWindowTarget::X(_) => env::var(X11_VAR),
        }
        .ok()
        .map(ActivationToken::_new)
    }
}

impl WindowExtStartupNotify for Window {
    fn request_activation_token(&self) -> Result<AsyncRequestSerial, NotSupportedError> {
        self.window.request_activation_token()
    }
}

impl WindowBuilderExtStartupNotify for WindowBuilder {
    fn with_activation_token(mut self, token: ActivationToken) -> Self {
        self.platform_specific.activation_token = Some(token);
        self
    }
}

/// Remove the activation environment variables from the current process.
///
/// This is wise to do before running child processes,
/// which may not to support the activation token.
pub fn reset_activation_token_env() {
    env::remove_var(X11_VAR);
    env::remove_var(WAYLAND_VAR);
}

/// Set environment variables responsible for activation token.
///
/// This could be used before running daemon processes.
pub fn set_activation_token_env(token: ActivationToken) {
    env::set_var(X11_VAR, &token._token);
    env::set_var(WAYLAND_VAR, token._token);
}