Implement pid->workspace tracking
When you spawn a process with the exec command, sway now notes the workspace you had focused and the pid of the child process, then assigns that workspace to the child when its window appears. Some of this is carried over from sway 0.15, but with some major refactoring and centralization of state.
This commit is contained in:
parent
62a7b762ac
commit
acd79e1505
|
@ -16,7 +16,8 @@ set $right l
|
||||||
# Your preferred terminal emulator
|
# Your preferred terminal emulator
|
||||||
set $term urxvt
|
set $term urxvt
|
||||||
# Your preferred application launcher
|
# Your preferred application launcher
|
||||||
set $menu dmenu_run
|
# Note: it's recommended that you pass the final command to sway
|
||||||
|
set $menu dmenu_path | dmenu | xargs swaymsg exec
|
||||||
|
|
||||||
### Output configuration
|
### Output configuration
|
||||||
#
|
#
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
#ifndef _SWAY_CONFIG_H
|
#ifndef _SWAY_CONFIG_H
|
||||||
#define _SWAY_CONFIG_H
|
#define _SWAY_CONFIG_H
|
||||||
#define PID_WORKSPACE_TIMEOUT 60
|
|
||||||
#include <libinput.h>
|
#include <libinput.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
@ -143,12 +142,6 @@ struct workspace_output {
|
||||||
char *workspace;
|
char *workspace;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct pid_workspace {
|
|
||||||
pid_t *pid;
|
|
||||||
char *workspace;
|
|
||||||
time_t *time_added;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct bar_config {
|
struct bar_config {
|
||||||
/**
|
/**
|
||||||
* One of "dock", "hide", "invisible"
|
* One of "dock", "hide", "invisible"
|
||||||
|
@ -300,7 +293,6 @@ struct sway_config {
|
||||||
list_t *bars;
|
list_t *bars;
|
||||||
list_t *cmd_queue;
|
list_t *cmd_queue;
|
||||||
list_t *workspace_outputs;
|
list_t *workspace_outputs;
|
||||||
list_t *pid_workspaces;
|
|
||||||
list_t *output_configs;
|
list_t *output_configs;
|
||||||
list_t *input_configs;
|
list_t *input_configs;
|
||||||
list_t *seat_configs;
|
list_t *seat_configs;
|
||||||
|
@ -384,9 +376,6 @@ struct sway_config {
|
||||||
} handler_context;
|
} handler_context;
|
||||||
};
|
};
|
||||||
|
|
||||||
void pid_workspace_add(struct pid_workspace *pw);
|
|
||||||
void free_pid_workspace(struct pid_workspace *pw);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads the main config from the given path. is_active should be true when
|
* Loads the main config from the given path. is_active should be true when
|
||||||
* reloading the config.
|
* reloading the config.
|
||||||
|
|
|
@ -42,4 +42,9 @@ void workspace_output_add_priority(struct sway_container *workspace,
|
||||||
|
|
||||||
struct sway_container *workspace_output_get_highest_available(
|
struct sway_container *workspace_output_get_highest_available(
|
||||||
struct sway_container *ws, struct sway_container *exclude);
|
struct sway_container *ws, struct sway_container *exclude);
|
||||||
|
|
||||||
|
struct sway_container *workspace_for_pid(pid_t pid);
|
||||||
|
|
||||||
|
void workspace_record_pid(pid_t pid);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -76,7 +76,7 @@ struct cmd_results *cmd_exec_always(int argc, char **argv) {
|
||||||
waitpid(pid, NULL, 0);
|
waitpid(pid, NULL, 0);
|
||||||
if (*child > 0) {
|
if (*child > 0) {
|
||||||
wlr_log(L_DEBUG, "Child process created with pid %d", *child);
|
wlr_log(L_DEBUG, "Child process created with pid %d", *child);
|
||||||
// TODO: add PID to active workspace
|
workspace_record_pid(*child);
|
||||||
} else {
|
} else {
|
||||||
free(child);
|
free(child);
|
||||||
}
|
}
|
||||||
|
|
|
@ -86,7 +86,6 @@ void free_config(struct sway_config *config) {
|
||||||
}
|
}
|
||||||
list_free(config->cmd_queue);
|
list_free(config->cmd_queue);
|
||||||
list_free(config->workspace_outputs);
|
list_free(config->workspace_outputs);
|
||||||
list_free(config->pid_workspaces);
|
|
||||||
list_free(config->output_configs);
|
list_free(config->output_configs);
|
||||||
if (config->input_configs) {
|
if (config->input_configs) {
|
||||||
for (int i = 0; i < config->input_configs->length; i++) {
|
for (int i = 0; i < config->input_configs->length; i++) {
|
||||||
|
@ -145,7 +144,6 @@ static void config_defaults(struct sway_config *config) {
|
||||||
if (!(config->modes = create_list())) goto cleanup;
|
if (!(config->modes = create_list())) goto cleanup;
|
||||||
if (!(config->bars = create_list())) goto cleanup;
|
if (!(config->bars = create_list())) goto cleanup;
|
||||||
if (!(config->workspace_outputs = create_list())) goto cleanup;
|
if (!(config->workspace_outputs = create_list())) goto cleanup;
|
||||||
if (!(config->pid_workspaces = create_list())) goto cleanup;
|
|
||||||
if (!(config->criteria = create_list())) goto cleanup;
|
if (!(config->criteria = create_list())) goto cleanup;
|
||||||
if (!(config->no_focus = create_list())) goto cleanup;
|
if (!(config->no_focus = create_list())) goto cleanup;
|
||||||
if (!(config->input_configs = create_list())) goto cleanup;
|
if (!(config->input_configs = create_list())) goto cleanup;
|
||||||
|
|
|
@ -321,9 +321,6 @@ void handle_xdg_shell_surface(struct wl_listener *listener, void *data) {
|
||||||
view_init(&xdg_shell_view->view, SWAY_VIEW_XDG_SHELL, &view_impl);
|
view_init(&xdg_shell_view->view, SWAY_VIEW_XDG_SHELL, &view_impl);
|
||||||
xdg_shell_view->view.wlr_xdg_surface = xdg_surface;
|
xdg_shell_view->view.wlr_xdg_surface = xdg_surface;
|
||||||
|
|
||||||
// TODO:
|
|
||||||
// - Look up pid and open on appropriate workspace
|
|
||||||
|
|
||||||
xdg_shell_view->map.notify = handle_map;
|
xdg_shell_view->map.notify = handle_map;
|
||||||
wl_signal_add(&xdg_surface->events.map, &xdg_shell_view->map);
|
wl_signal_add(&xdg_surface->events.map, &xdg_shell_view->map);
|
||||||
|
|
||||||
|
|
|
@ -312,9 +312,6 @@ void handle_xdg_shell_v6_surface(struct wl_listener *listener, void *data) {
|
||||||
view_init(&xdg_shell_v6_view->view, SWAY_VIEW_XDG_SHELL_V6, &view_impl);
|
view_init(&xdg_shell_v6_view->view, SWAY_VIEW_XDG_SHELL_V6, &view_impl);
|
||||||
xdg_shell_v6_view->view.wlr_xdg_surface_v6 = xdg_surface;
|
xdg_shell_v6_view->view.wlr_xdg_surface_v6 = xdg_surface;
|
||||||
|
|
||||||
// TODO:
|
|
||||||
// - Look up pid and open on appropriate workspace
|
|
||||||
|
|
||||||
xdg_shell_v6_view->map.notify = handle_map;
|
xdg_shell_v6_view->map.notify = handle_map;
|
||||||
wl_signal_add(&xdg_surface->events.map, &xdg_shell_v6_view->map);
|
wl_signal_add(&xdg_surface->events.map, &xdg_shell_v6_view->map);
|
||||||
|
|
||||||
|
|
|
@ -446,9 +446,6 @@ void handle_xwayland_surface(struct wl_listener *listener, void *data) {
|
||||||
view_init(&xwayland_view->view, SWAY_VIEW_XWAYLAND, &view_impl);
|
view_init(&xwayland_view->view, SWAY_VIEW_XWAYLAND, &view_impl);
|
||||||
xwayland_view->view.wlr_xwayland_surface = xsurface;
|
xwayland_view->view.wlr_xwayland_surface = xsurface;
|
||||||
|
|
||||||
// TODO:
|
|
||||||
// - Look up pid and open on appropriate workspace
|
|
||||||
|
|
||||||
wl_signal_add(&xsurface->events.destroy, &xwayland_view->destroy);
|
wl_signal_add(&xsurface->events.destroy, &xwayland_view->destroy);
|
||||||
xwayland_view->destroy.notify = handle_destroy;
|
xwayland_view->destroy.notify = handle_destroy;
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
#include <wayland-server.h>
|
#include <wayland-server.h>
|
||||||
#include <wlr/render/wlr_renderer.h>
|
#include <wlr/render/wlr_renderer.h>
|
||||||
#include <wlr/types/wlr_output_layout.h>
|
#include <wlr/types/wlr_output_layout.h>
|
||||||
|
#include <wlr/xwayland.h>
|
||||||
#include "list.h"
|
#include "list.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "sway/criteria.h"
|
#include "sway/criteria.h"
|
||||||
|
@ -492,9 +493,21 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pid_t pid;
|
||||||
|
if (view->type == SWAY_VIEW_XWAYLAND) {
|
||||||
|
struct wlr_xwayland_surface *surf =
|
||||||
|
wlr_xwayland_surface_from_wlr_surface(wlr_surface);
|
||||||
|
pid = surf->pid;
|
||||||
|
} else {
|
||||||
|
struct wl_client *client =
|
||||||
|
wl_resource_get_client(wlr_surface->resource);
|
||||||
|
wl_client_get_credentials(client, &pid, NULL, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
struct sway_seat *seat = input_manager_current_seat(input_manager);
|
struct sway_seat *seat = input_manager_current_seat(input_manager);
|
||||||
struct sway_container *focus =
|
struct sway_container *target_sibling =
|
||||||
seat_get_focus_inactive(seat, &root_container);
|
seat_get_focus_inactive(seat, &root_container);
|
||||||
|
struct sway_container *prev_focus = target_sibling;
|
||||||
struct sway_container *cont = NULL;
|
struct sway_container *cont = NULL;
|
||||||
|
|
||||||
// Check if there's any `assign` criteria for the view
|
// Check if there's any `assign` criteria for the view
|
||||||
|
@ -508,18 +521,31 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface) {
|
||||||
if (!workspace) {
|
if (!workspace) {
|
||||||
workspace = workspace_create(NULL, criteria->target);
|
workspace = workspace_create(NULL, criteria->target);
|
||||||
}
|
}
|
||||||
focus = seat_get_focus_inactive(seat, workspace);
|
prev_focus = target_sibling;
|
||||||
|
target_sibling = seat_get_focus_inactive(seat, workspace);
|
||||||
} else {
|
} else {
|
||||||
// TODO: CT_ASSIGN_OUTPUT
|
// TODO: CT_ASSIGN_OUTPUT
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
list_free(criterias);
|
||||||
|
|
||||||
|
if (!workspace) {
|
||||||
|
workspace = workspace_for_pid(pid);
|
||||||
|
if (workspace) {
|
||||||
|
prev_focus = target_sibling;
|
||||||
|
target_sibling = seat_get_focus_inactive(seat, workspace);
|
||||||
|
}
|
||||||
|
}
|
||||||
// If we're about to launch the view into the floating container, then
|
// If we're about to launch the view into the floating container, then
|
||||||
// launch it as a tiled view in the root of the workspace instead.
|
// launch it as a tiled view in the root of the workspace instead.
|
||||||
if (container_is_floating(focus)) {
|
if (container_is_floating(target_sibling)) {
|
||||||
focus = focus->parent->parent;
|
if (prev_focus == target_sibling) {
|
||||||
|
prev_focus = target_sibling->parent->parent;
|
||||||
}
|
}
|
||||||
free(criterias);
|
target_sibling = target_sibling->parent->parent;
|
||||||
cont = container_view_create(focus, view);
|
}
|
||||||
|
|
||||||
|
cont = container_view_create(target_sibling, view);
|
||||||
|
|
||||||
view->surface = wlr_surface;
|
view->surface = wlr_surface;
|
||||||
view->swayc = cont;
|
view->swayc = cont;
|
||||||
|
@ -538,9 +564,8 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface) {
|
||||||
container_set_floating(view->swayc, true);
|
container_set_floating(view->swayc, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (prev_focus == target_sibling) {
|
||||||
input_manager_set_focus(input_manager, cont);
|
input_manager_set_focus(input_manager, cont);
|
||||||
if (workspace) {
|
|
||||||
workspace_switch(workspace);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
view_update_title(view, false);
|
view_update_title(view, false);
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
#include "sway/input/input-manager.h"
|
#include "sway/input/input-manager.h"
|
||||||
#include "sway/input/seat.h"
|
#include "sway/input/seat.h"
|
||||||
#include "sway/ipc-server.h"
|
#include "sway/ipc-server.h"
|
||||||
|
#include "sway/output.h"
|
||||||
#include "sway/tree/arrange.h"
|
#include "sway/tree/arrange.h"
|
||||||
#include "sway/tree/container.h"
|
#include "sway/tree/container.h"
|
||||||
#include "sway/tree/workspace.h"
|
#include "sway/tree/workspace.h"
|
||||||
|
@ -516,3 +517,114 @@ struct sway_container *workspace_output_get_highest_available(
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct pid_workspace {
|
||||||
|
pid_t pid;
|
||||||
|
char *workspace;
|
||||||
|
struct timespec time_added;
|
||||||
|
|
||||||
|
struct sway_container *output;
|
||||||
|
struct wl_listener output_destroy;
|
||||||
|
|
||||||
|
struct wl_list link;
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct wl_list pid_workspaces;
|
||||||
|
|
||||||
|
struct sway_container *workspace_for_pid(pid_t pid) {
|
||||||
|
if (!pid_workspaces.prev && !pid_workspaces.next) {
|
||||||
|
wl_list_init(&pid_workspaces);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct sway_container *ws = NULL;
|
||||||
|
struct pid_workspace *pw = NULL;
|
||||||
|
|
||||||
|
wlr_log(L_DEBUG, "Looking up workspace for pid %d", pid);
|
||||||
|
|
||||||
|
do {
|
||||||
|
struct pid_workspace *_pw = NULL;
|
||||||
|
wl_list_for_each(_pw, &pid_workspaces, link) {
|
||||||
|
if (pid == _pw->pid) {
|
||||||
|
pw = _pw;
|
||||||
|
wlr_log(L_DEBUG,
|
||||||
|
"found pid_workspace for pid %d, workspace %s",
|
||||||
|
pid, pw->workspace);
|
||||||
|
goto found;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pid = get_parent_pid(pid);
|
||||||
|
} while (pid > 1);
|
||||||
|
found:
|
||||||
|
|
||||||
|
if (pw && pw->workspace) {
|
||||||
|
ws = workspace_by_name(pw->workspace);
|
||||||
|
|
||||||
|
if (!ws) {
|
||||||
|
wlr_log(L_DEBUG,
|
||||||
|
"Creating workspace %s for pid %d because it disappeared",
|
||||||
|
pw->workspace, pid);
|
||||||
|
ws = workspace_create(pw->output, pw->workspace);
|
||||||
|
}
|
||||||
|
|
||||||
|
wl_list_remove(&pw->output_destroy.link);
|
||||||
|
wl_list_remove(&pw->link);
|
||||||
|
free(pw->workspace);
|
||||||
|
free(pw);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ws;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void pw_handle_output_destroy(struct wl_listener *listener, void *data) {
|
||||||
|
struct pid_workspace *pw = wl_container_of(listener, pw, output_destroy);
|
||||||
|
pw->output = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void workspace_record_pid(pid_t pid) {
|
||||||
|
wlr_log(L_DEBUG, "Recording workspace for process %d", pid);
|
||||||
|
if (!pid_workspaces.prev && !pid_workspaces.next) {
|
||||||
|
wl_list_init(&pid_workspaces);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct sway_seat *seat = input_manager_current_seat(input_manager);
|
||||||
|
struct sway_container *ws =
|
||||||
|
seat_get_focus_inactive(seat, &root_container);
|
||||||
|
if (ws && ws->type != C_WORKSPACE) {
|
||||||
|
ws = container_parent(ws, C_WORKSPACE);
|
||||||
|
}
|
||||||
|
if (!ws) {
|
||||||
|
wlr_log(L_DEBUG, "Bailing out, no workspace");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
struct sway_container *output = ws->parent;
|
||||||
|
if (!output) {
|
||||||
|
wlr_log(L_DEBUG, "Bailing out, no output");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct timespec now;
|
||||||
|
clock_gettime(CLOCK_MONOTONIC, &now);
|
||||||
|
|
||||||
|
// Remove expired entries
|
||||||
|
static const int timeout = 60;
|
||||||
|
struct pid_workspace *old, *_old;
|
||||||
|
wl_list_for_each_safe(old, _old, &pid_workspaces, link) {
|
||||||
|
if (now.tv_sec - old->time_added.tv_sec >= timeout) {
|
||||||
|
wl_list_remove(&old->output_destroy.link);
|
||||||
|
wl_list_remove(&old->link);
|
||||||
|
free(old->workspace);
|
||||||
|
free(old);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct pid_workspace *pw = calloc(1, sizeof(struct pid_workspace));
|
||||||
|
pw->workspace = strdup(ws->name);
|
||||||
|
pw->output = output;
|
||||||
|
pw->pid = pid;
|
||||||
|
memcpy(&pw->time_added, &now, sizeof(struct timespec));
|
||||||
|
pw->output_destroy.notify = pw_handle_output_destroy;
|
||||||
|
wl_signal_add(&output->sway_output->wlr_output->events.destroy,
|
||||||
|
&pw->output_destroy);
|
||||||
|
wl_list_insert(&pid_workspaces, &pw->link);
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue