-
Notifications
You must be signed in to change notification settings - Fork 909
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add platform::startup_notify
for Wayland/X11
#2955
Conversation
Draft, because I can't add just Wayland for that, since the way to inform is |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll make an X11 implementation after work today.
I took a quick look at the spec and.... uhm... it's extensible. 😅 . My TL;DR from reading the spec would be: If env variable And my TL;DR from looking at libstartup-notification: O Looking at the libsn git repo, https://cgit.freedesktop.org/startup-notification/tree/test/test-launchee-xcb.c seems relevant. This does:
Sooo... what does each of these functions do?
/**
* sn_launchee_context_complete:
* @context: an #SnLauncheeContext
*
* Called by the launchee when it is fully started up and the startup
* sequence should end.
*
**/
void
sn_launchee_context_complete (SnLauncheeContext *context)
{
char *keys[2];
char *vals[2];
char *message;
keys[0] = "ID";
keys[1] = NULL;
vals[0] = context->startup_id;
vals[1] = NULL;
message = sn_internal_serialize_message ("remove",
(const char**) keys,
(const char**) vals);
sn_internal_broadcast_xmessage (context->display,
context->screen,
sn_internal_get_net_startup_info_atom(context->display),
sn_internal_get_net_startup_info_begin_atom(context->display),
message);
sn_free (message);
} I tried to rust-ify this. No guarantees and no testing, but this is how I understand things: use std::os::unix::ffi::OsStrExt;
use x11rb::connection::Connection;
use x11rb::protocol::xproto::{
ClientMessageEvent, ConnectionExt as _, CreateWindowAux, EventMask, WindowClass, WindowWrapper,
};
fn main() -> Result<(), Box<dyn std::error::Error>> {
let startup_id = match std::env::var_os("DESKTOP_STARTUP_ID") {
Some(value) => value,
None => {
println!("No startup stuff going on");
return Ok(());
}
};
let startup_id = startup_id.as_bytes();
let (conn, screen) = x11rb::connect(None)?;
// Not shown here: Create a window, set the _NET_STARTUP_ID property on it, map it
finish_startup(&conn, screen, startup_id)
}
fn finish_startup(
conn: &impl Connection,
screen_idx: usize,
startup_id: &[u8],
) -> Result<(), Box<dyn std::error::Error>> {
let mut message = "remove: ID=".as_bytes().to_vec();
message.extend(startup_id);
message.push(0);
send_message(conn, screen_idx, &message)
}
fn send_message(
conn: &impl Connection,
screen_idx: usize,
message: &[u8],
) -> Result<(), Box<dyn std::error::Error>> {
let info_begin = conn
.intern_atom(false, b"_NET_STARTUP_INFO_BEGIN")?
.reply()?
.atom;
let info = conn.intern_atom(false, b"_NET_STARTUP_INFO")?.reply()?.atom;
let mut message_type = info_begin;
let screen = &conn.setup().roots[screen_idx];
// Yay, the spec wants us to create a new window for each message
let window = WindowWrapper::create_window(
conn,
screen.root_depth,
screen.root,
-100,
-100,
1,
1,
0,
WindowClass::INPUT_OUTPUT,
screen.root_visual,
&CreateWindowAux::new()
.override_redirect(1)
.event_mask(EventMask::STRUCTURE_NOTIFY | EventMask::PROPERTY_CHANGE),
)?;
let mut remaining_message = message;
while !remaining_message.is_empty() {
let mut data_to_send = [0; 20];
let to_send_len = remaining_message.len().min(data_to_send.len());
data_to_send[..to_send_len].copy_from_slice(&remaining_message[..to_send_len]);
remaining_message = &remaining_message[to_send_len..];
conn.send_event(
false,
screen.root,
EventMask::PROPERTY_CHANGE,
ClientMessageEvent::new(8, window.window(), message_type, data_to_send),
)?;
message_type = info;
}
conn.flush()?;
Ok(())
} |
After having written the above, I took another look at the current state of the PR.
|
@psychon inventing is not an issue, because inventing token and sending it back to user is fine, Wayland is more strict here. |
I think you can since GTK does that just fine, as well as firefox. They both use both protocols via the same internal C++ interfaces. It's true that X11 API is a bit weird here, but Wayland protocol links to X11 one for some stuff. It's true though, that I don't quite understand what X11 really wants here, I've tried to understand the GTK thing, but got confused what leader window is. Wayland spec is more clear here, you ask for token you activate with that token, dead simple. For X11 we at least need handling for startup case, as in where we pass the variable from the env to first winit window and then create it. From what I've read we must set do (taken from gtk, you've mentioned that in your rust code): XChangeProperty (display_x11->xdisplay,
display_x11->leader_window,
gdk_x11_get_xatom_by_name_for_display (display, "_NET_STARTUP_ID"),
gdk_x11_get_xatom_by_name_for_display (display, "UTF8_STRING"), 8,
PropModeReplace,
(guchar *)startup_id, strlen (startup_id)); We may leave request of the code alone, but I don't see any issue with inventing and sending it back via the event to the user keeping the async-ness of the API, since doing it sync on Wayland is not really an option (though, don't you need to broadcast your invented token somehow?). |
I think that refers to ICCCM window groups: https://tronche.com/gui/x/icccm/sec-4.html#s-4.1.11
(I don't think I ever saw any WM treat group leaders specially, but that doesn't mean much; I'm not WM-switching often)
Yup, looking again at the tests for libstartup-notification, there is a call to snprintf (s, len, "%s/%s/%d-%d-%s_TIME%lu",
canonicalized_launcher, canonicalized_launchee,
(int) getpid (), (int) sequence_number, hostbuf,
(unsigned long) timestamp);
++sequence_number; where So, that would be something like |
2f5a69a
to
3acc71c
Compare
22670a8
to
34cb588
Compare
34cb588
to
4175b6c
Compare
The utils in this module should help the users to activate the windows they create, as well as manage activation tokens environment variables. The API is essential for Wayland in the first place, since some compositors may decide initial focus of the window based on whether the activation token was during the window creation. Fixes rust-windowing#2279.
4175b6c
to
85ff752
Compare
The utils in this module should help the users to activate the windows they create, as well as manage activation tokens environment variables.
The API is essential for Wayland in the first place, since some compositors may decide initial focus of the window based on whether the activation token was during the window creation.
Fixes #2279.
CHANGELOG.md
if knowledge of this change could be valuable to users--
I'm not sure how to properly integrate it into the winit at this very moment, because we'll have a platform specific event. Well to be fair, we have them already for macOS backend and such (see gestures things).
@notgull do you want to look into impl of X11 part? I've linked the spec in the module, but I'm not sure how to implement it, there's a libstartup-notify, but I don't think we want it? Maybe x11rb could help us here?