From 0e2e8290f2c272a4fe45b779ebdf8d237e65218a Mon Sep 17 00:00:00 2001 From: Jesse Luehrs Date: Sat, 20 Dec 2025 02:04:30 -0500 Subject: [PATCH] provide I3_WINDOW_ID to processes called with exec when possible (#6160) fixes #1729 --- include/startup.h | 2 +- src/commands.c | 4 +++- src/main.c | 6 +++--- src/startup.c | 12 +++++++++++- testcases/t/165-for_window.t | 25 +++++++++++++++++++++++++ 5 files changed, 43 insertions(+), 6 deletions(-) diff --git a/include/startup.h b/include/startup.h index a2b52c58..6c9bff56 100644 --- a/include/startup.h +++ b/include/startup.h @@ -30,7 +30,7 @@ * (and ID) should be created, which is the default and encouraged behavior. * */ -void start_application(const char *command, bool no_startup_id); +void start_application(const char *command, bool no_startup_id, xcb_window_t window_id); /** * Deletes a startup sequence, ignoring whether its timeout has elapsed. diff --git a/src/commands.c b/src/commands.c index b421abbd..8cd9cd7e 100644 --- a/src/commands.c +++ b/src/commands.c @@ -1262,7 +1262,9 @@ void cmd_exec(I3_CMD, const char *nosn, const char *command) { TAILQ_FOREACH (current, &OWINDOWS, owindows) { DLOG("should execute %s, no_startup_id = %d\n", command, no_startup_id); - start_application(command, no_startup_id); + start_application( + command, no_startup_id, + current->con->window == NULL ? XCB_NONE : current->con->window->id); } ysuccess(true); diff --git a/src/main.c b/src/main.c index acbd180d..05d33adc 100644 --- a/src/main.c +++ b/src/main.c @@ -1175,7 +1175,7 @@ int main(int argc, char *argv[]) { struct Autostart *exec = TAILQ_FIRST(&autostarts); LOG("auto-starting %s\n", exec->command); - start_application(exec->command, exec->no_startup_id); + start_application(exec->command, exec->no_startup_id, XCB_NONE); FREE(exec->command); TAILQ_REMOVE(&autostarts, exec, autostarts); @@ -1188,7 +1188,7 @@ int main(int argc, char *argv[]) { struct Autostart *exec_always = TAILQ_FIRST(&autostarts_always); LOG("auto-starting (always!) %s\n", exec_always->command); - start_application(exec_always->command, exec_always->no_startup_id); + start_application(exec_always->command, exec_always->no_startup_id, XCB_NONE); FREE(exec_always->command); TAILQ_REMOVE(&autostarts_always, exec_always, autostarts_always); @@ -1204,7 +1204,7 @@ int main(int argc, char *argv[]) { barconfig->verbose ? "-V" : "", barconfig->id, current_socketpath); LOG("Starting bar process: %s\n", command); - start_application(command, true); + start_application(command, true, XCB_NONE); free(command); } diff --git a/src/startup.c b/src/startup.c index 08377f8b..da8f265a 100644 --- a/src/startup.c +++ b/src/startup.c @@ -129,8 +129,11 @@ void startup_sequence_delete(struct Startup_Sequence *sequence) { * The no_startup_id flag determines whether a startup notification context * (and ID) should be created, which is the default and encouraged behavior. * + * The window_id, if given, will be provided to the child process via the + * I3_WINDOW_ID environment variable. + * */ -void start_application(const char *command, bool no_startup_id) { +void start_application(const char *command, bool no_startup_id, xcb_window_t window_id) { SnLauncherContext *context = NULL; if (!no_startup_id) { @@ -194,6 +197,13 @@ void start_application(const char *command, bool no_startup_id) { } setenv("I3SOCK", current_socketpath, 1); + if (window_id != XCB_NONE) { + char *window_id_str; + sasprintf(&window_id_str, "%d", window_id); + setenv("I3_WINDOW_ID", window_id_str, 1); + free(window_id_str); + } + execl(_PATH_BSHELL, _PATH_BSHELL, "-c", command, NULL); err(EXIT_FAILURE, "execl return"); /* only reached on error */ } diff --git a/testcases/t/165-for_window.t b/testcases/t/165-for_window.t index 155077e5..7efe89ac 100644 --- a/testcases/t/165-for_window.t +++ b/testcases/t/165-for_window.t @@ -15,10 +15,15 @@ # (unless you are already familiar with Perl) # use i3test i3_autostart => 0; +use POSIX qw(mkfifo); +use File::Temp qw(:POSIX); use X11::XCB qw(PROP_MODE_REPLACE); my (@nodes); +my $exec_tmp = tmpnam(); +mkfifo($exec_tmp, 0600) or BAIL_OUT "Could not create FIFO in $exec_tmp: $!"; + my $config = <<'EOT'; font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1 @@ -51,6 +56,11 @@ for_window [window_role="i3test"] border none for_window [workspace="trigger"] floating enable, mark triggered EOT +$config .= < "$exec_tmp" +EOT + # test all window types my %window_types = ( 'normal' => '_NET_WM_WINDOW_TYPE_NORMAL', @@ -390,6 +400,21 @@ is_deeply($nodes[0]->{nodes}[0]->{marks}, [ 'triggered' ], "mark set for workspa kill_all_windows; +############################################################## +# 13: check that we pass $I3_WINDOW_ID to exec'ed subprocesses +############################################################## + +$window = open_window(wm_class => 'exec'); + +open my $fh, '<', $exec_tmp + or die "couldn't open FIFO $exec_tmp for reading: $!"; +chomp(my $window_id = do { local $/; <$fh> }); +is($window_id, $window->id, "got the expected window id from \$I3_WINDOW_ID"); + +kill_all_windows; + +unlink($exec_tmp); + ############################################################## exit_gracefully($pid);