mirror of https://github.com/swaywm/sway.git
Compare commits
33 Commits
Author | SHA1 | Date |
---|---|---|
Simon Ser | b88b1b6302 | |
Simon Ser | 0500cdbfce | |
Simon Ser | 1340910a24 | |
Kenny Levinsen | 6b900bab60 | |
Ankit Pandey | 51663bb120 | |
Simon Ser | 54d1e0d568 | |
Ronan Pigott | 8d78ab6a45 | |
Kenny Levinsen | 817f1bbec3 | |
Simon Ser | fb1cb0aa3a | |
Simon Ser | 194fdc6c35 | |
nerdopolis | baf027fc5b | |
Simon Ser | 27a56e63d3 | |
Simon Ser | 6afe74ffec | |
Joan Bruguera | 0a9b468540 | |
Joan Bruguera | b92af7e3ca | |
Ronan Pigott | d5872d0880 | |
Ronan Pigott | 97423ca9c7 | |
Ronan Pigott | ee9266cf8c | |
Ronan Pigott | 66be031f6c | |
Ronan Pigott | 5794a223ce | |
Ronan Pigott | 9d78ede905 | |
Ronan Pigott | cb13b9d628 | |
Ronan Pigott | 69abc41d25 | |
Ronan Pigott | 16b391db48 | |
Ronan Pigott | 25f559dcde | |
Simon Ser | 52166bc1f5 | |
Simon Ser | 2a6bcc6738 | |
Simon Ser | 61e4e65ea6 | |
Simon Ser | ceece55850 | |
Manuel Stoeckl | 9f4229827f | |
Kirill Primak | 68b4ed3a4a | |
Alexander Orzechowski | 5a2563b1a4 | |
Simon Ser | 0143c7ade8 |
|
@ -23,7 +23,7 @@ packages:
|
|||
- hwdata
|
||||
sources:
|
||||
- https://github.com/swaywm/sway
|
||||
- https://gitlab.freedesktop.org/wlroots/wlroots.git
|
||||
- https://gitlab.freedesktop.org/wlroots/wlroots.git#0.16.0
|
||||
tasks:
|
||||
- wlroots: |
|
||||
cd wlroots
|
||||
|
|
|
@ -20,7 +20,7 @@ packages:
|
|||
- hwdata
|
||||
sources:
|
||||
- https://github.com/swaywm/sway
|
||||
- https://gitlab.freedesktop.org/wlroots/wlroots.git
|
||||
- https://gitlab.freedesktop.org/wlroots/wlroots.git#0.16.0
|
||||
tasks:
|
||||
- wlroots: |
|
||||
cd wlroots
|
||||
|
|
|
@ -28,7 +28,7 @@ packages:
|
|||
- misc/hwdata
|
||||
sources:
|
||||
- https://github.com/swaywm/sway
|
||||
- https://gitlab.freedesktop.org/wlroots/wlroots.git
|
||||
- https://gitlab.freedesktop.org/wlroots/wlroots.git#0.16.0
|
||||
tasks:
|
||||
- setup: |
|
||||
cd sway
|
||||
|
|
|
@ -1,50 +1,43 @@
|
|||
#define _POSIX_C_SOURCE 200809
|
||||
#include <assert.h>
|
||||
#include <cairo.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <pango/pangocairo.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/mman.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
#include <wayland-client.h>
|
||||
#include "config.h"
|
||||
#include "pool-buffer.h"
|
||||
#include "util.h"
|
||||
|
||||
static int create_pool_file(size_t size, char **name) {
|
||||
static const char template[] = "sway-client-XXXXXX";
|
||||
const char *path = getenv("XDG_RUNTIME_DIR");
|
||||
if (path == NULL) {
|
||||
fprintf(stderr, "XDG_RUNTIME_DIR is not set\n");
|
||||
return -1;
|
||||
}
|
||||
static int anonymous_shm_open(void) {
|
||||
int retries = 100;
|
||||
|
||||
size_t name_size = strlen(template) + 1 + strlen(path) + 1;
|
||||
*name = malloc(name_size);
|
||||
if (*name == NULL) {
|
||||
fprintf(stderr, "allocation failed\n");
|
||||
return -1;
|
||||
}
|
||||
snprintf(*name, name_size, "%s/%s", path, template);
|
||||
do {
|
||||
// try a probably-unique name
|
||||
struct timespec ts;
|
||||
clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||
pid_t pid = getpid();
|
||||
char name[50];
|
||||
snprintf(name, sizeof(name), "/sway-%x-%x",
|
||||
(unsigned int)pid, (unsigned int)ts.tv_nsec);
|
||||
|
||||
int fd = mkstemp(*name);
|
||||
if (fd < 0) {
|
||||
return -1;
|
||||
}
|
||||
// shm_open guarantees that O_CLOEXEC is set
|
||||
int fd = shm_open(name, O_RDWR | O_CREAT | O_EXCL, 0600);
|
||||
if (fd >= 0) {
|
||||
shm_unlink(name);
|
||||
return fd;
|
||||
}
|
||||
|
||||
if (!sway_set_cloexec(fd, true)) {
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
--retries;
|
||||
} while (retries > 0 && errno == EEXIST);
|
||||
|
||||
if (ftruncate(fd, size) < 0) {
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return fd;
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void buffer_release(void *data, struct wl_buffer *wl_buffer) {
|
||||
|
@ -62,17 +55,20 @@ static struct pool_buffer *create_buffer(struct wl_shm *shm,
|
|||
uint32_t stride = width * 4;
|
||||
size_t size = stride * height;
|
||||
|
||||
char *name;
|
||||
int fd = create_pool_file(size, &name);
|
||||
assert(fd != -1);
|
||||
int fd = anonymous_shm_open();
|
||||
if (fd == -1) {
|
||||
return NULL;
|
||||
}
|
||||
if (ftruncate(fd, size) < 0) {
|
||||
close(fd);
|
||||
return NULL;
|
||||
}
|
||||
void *data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
|
||||
struct wl_shm_pool *pool = wl_shm_create_pool(shm, fd, size);
|
||||
buf->buffer = wl_shm_pool_create_buffer(pool, 0,
|
||||
width, height, stride, format);
|
||||
wl_shm_pool_destroy(pool);
|
||||
close(fd);
|
||||
unlink(name);
|
||||
free(name);
|
||||
|
||||
buf->size = size;
|
||||
buf->width = width;
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
#ifndef _SWAY_LAUNCHER_H
|
||||
#define _SWAY_LAUNCHER_H
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
struct launcher_ctx {
|
||||
pid_t pid;
|
||||
char *name;
|
||||
struct wlr_xdg_activation_token_v1 *token;
|
||||
struct wl_listener token_destroy;
|
||||
|
||||
bool activated;
|
||||
|
||||
struct sway_node *node;
|
||||
struct wl_listener node_destroy;
|
||||
|
||||
struct wl_list link; // sway_server::pending_launcher_ctxs
|
||||
};
|
||||
|
||||
struct launcher_ctx *launcher_ctx_find_pid(pid_t pid);
|
||||
|
||||
struct sway_workspace *launcher_ctx_get_workspace(struct launcher_ctx *ctx);
|
||||
|
||||
void launcher_ctx_consume(struct launcher_ctx *ctx);
|
||||
|
||||
void launcher_ctx_destroy(struct launcher_ctx *ctx);
|
||||
|
||||
struct launcher_ctx *launcher_ctx_create(void);
|
||||
|
||||
const char *launcher_ctx_get_token_name(struct launcher_ctx *ctx);
|
||||
|
||||
#endif
|
|
@ -114,6 +114,8 @@ struct sway_server {
|
|||
struct wlr_xdg_activation_v1 *xdg_activation_v1;
|
||||
struct wl_listener xdg_activation_v1_request_activate;
|
||||
|
||||
struct wl_list pending_launcher_ctxs; // launcher_ctx::link
|
||||
|
||||
// The timeout for transactions, after which a transaction is applied
|
||||
// regardless of readiness.
|
||||
size_t txn_timeout_ms;
|
||||
|
|
|
@ -69,12 +69,6 @@ void root_scratchpad_show(struct sway_container *con);
|
|||
*/
|
||||
void root_scratchpad_hide(struct sway_container *con);
|
||||
|
||||
struct sway_workspace *root_workspace_for_pid(pid_t pid);
|
||||
|
||||
void root_record_workspace_pid(pid_t pid);
|
||||
|
||||
void root_remove_workspace_pid(pid_t pid);
|
||||
|
||||
void root_for_each_workspace(void (*f)(struct sway_workspace *ws, void *data),
|
||||
void *data);
|
||||
|
||||
|
@ -92,6 +86,4 @@ struct sway_container *root_find_container(
|
|||
|
||||
void root_get_box(struct sway_root *root, struct wlr_box *box);
|
||||
|
||||
void root_rename_pid_workspaces(const char *old_name, const char *new_name);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -74,6 +74,7 @@ struct sway_view {
|
|||
struct sway_xdg_decoration *xdg_decoration;
|
||||
|
||||
pid_t pid;
|
||||
struct launcher_ctx *ctx;
|
||||
|
||||
// The size the view would want to be if it weren't tiled.
|
||||
// Used when changing a view from tiled to floating.
|
||||
|
@ -155,6 +156,7 @@ struct sway_xwayland_view {
|
|||
struct wl_listener set_title;
|
||||
struct wl_listener set_class;
|
||||
struct wl_listener set_role;
|
||||
struct wl_listener set_startup_id;
|
||||
struct wl_listener set_window_type;
|
||||
struct wl_listener set_hints;
|
||||
struct wl_listener set_decorations;
|
||||
|
@ -372,4 +374,6 @@ void view_save_buffer(struct sway_view *view);
|
|||
|
||||
bool view_is_transient_for(struct sway_view *child, struct sway_view *ancestor);
|
||||
|
||||
void view_assign_ctx(struct sway_view *view, struct launcher_ctx *ctx);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -30,6 +30,6 @@ void i3bar_block_unref(struct i3bar_block *block);
|
|||
bool i3bar_handle_readable(struct status_line *status);
|
||||
enum hotspot_event_handling i3bar_block_send_click(struct status_line *status,
|
||||
struct i3bar_block *block, double x, double y, double rx, double ry,
|
||||
double w, double h, int scale, uint32_t button);
|
||||
double w, double h, int scale, uint32_t button, bool released);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -49,7 +49,7 @@ struct swaybar_hotspot {
|
|||
int x, y, width, height;
|
||||
enum hotspot_event_handling (*callback)(struct swaybar_output *output,
|
||||
struct swaybar_hotspot *hotspot, double x, double y, uint32_t button,
|
||||
void *data);
|
||||
bool released, void *data);
|
||||
void (*destroy)(void *data);
|
||||
void *data;
|
||||
};
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
project(
|
||||
'sway',
|
||||
'c',
|
||||
version: '1.8-dev',
|
||||
version: '1.8',
|
||||
license: 'MIT',
|
||||
meson_version: '>=0.60.0',
|
||||
default_options: [
|
||||
|
@ -49,7 +49,6 @@ pcre2 = dependency('libpcre2-8')
|
|||
wayland_server = dependency('wayland-server', version: '>=1.21.0')
|
||||
wayland_client = dependency('wayland-client')
|
||||
wayland_cursor = dependency('wayland-cursor')
|
||||
wayland_egl = dependency('wayland-egl')
|
||||
wayland_protos = dependency('wayland-protocols', version: '>=1.24')
|
||||
wlroots = dependency('wlroots', version: wlroots_version)
|
||||
xkbcommon = dependency('xkbcommon')
|
||||
|
@ -84,7 +83,7 @@ endforeach
|
|||
if get_option('xwayland').enabled() and not wlroots_features['xwayland']
|
||||
error('Cannot enable Xwayland in sway: wlroots has been built without Xwayland support')
|
||||
endif
|
||||
have_xwayland = xcb.found() and wlroots_features['xwayland']
|
||||
have_xwayland = xcb.found() and xcb_icccm.found() and wlroots_features['xwayland']
|
||||
|
||||
if get_option('sd-bus-provider') == 'auto'
|
||||
if not get_option('tray').disabled()
|
||||
|
|
|
@ -1,14 +1,10 @@
|
|||
wl_protocol_dir = wayland_protos.get_variable('pkgdatadir')
|
||||
|
||||
wayland_scanner_dep = dependency('wayland-scanner', required: false, native: true)
|
||||
if wayland_scanner_dep.found()
|
||||
wayland_scanner = find_program(
|
||||
wayland_scanner_dep.get_variable(pkgconfig: 'wayland_scanner'),
|
||||
native: true,
|
||||
)
|
||||
else
|
||||
wayland_scanner = find_program('wayland-scanner', native: true)
|
||||
endif
|
||||
wayland_scanner_dep = dependency('wayland-scanner', native: true)
|
||||
wayland_scanner = find_program(
|
||||
wayland_scanner_dep.get_variable(pkgconfig: 'wayland_scanner'),
|
||||
native: true,
|
||||
)
|
||||
|
||||
protocols = [
|
||||
wl_protocol_dir / 'stable/xdg-shell/xdg-shell.xml',
|
||||
|
@ -22,58 +18,25 @@ protocols = [
|
|||
'wlr-output-power-management-unstable-v1.xml',
|
||||
]
|
||||
|
||||
client_protocols = [
|
||||
wl_protocol_dir / 'stable/xdg-shell/xdg-shell.xml',
|
||||
wl_protocol_dir / 'unstable/xdg-output/xdg-output-unstable-v1.xml',
|
||||
'wlr-layer-shell-unstable-v1.xml',
|
||||
'wlr-input-inhibitor-unstable-v1.xml',
|
||||
]
|
||||
|
||||
wl_protos_src = []
|
||||
wl_protos_headers = []
|
||||
|
||||
foreach xml : protocols
|
||||
wl_protos_src += custom_target(
|
||||
xml.underscorify() + '_server_c',
|
||||
xml.underscorify() + '_c',
|
||||
input: xml,
|
||||
output: '@BASENAME@-protocol.c',
|
||||
command: [wayland_scanner, 'private-code', '@INPUT@', '@OUTPUT@'],
|
||||
)
|
||||
wl_protos_headers += custom_target(
|
||||
wl_protos_src += custom_target(
|
||||
xml.underscorify() + '_server_h',
|
||||
input: xml,
|
||||
output: '@BASENAME@-protocol.h',
|
||||
command: [wayland_scanner, 'server-header', '@INPUT@', '@OUTPUT@'],
|
||||
)
|
||||
endforeach
|
||||
|
||||
foreach xml : client_protocols
|
||||
wl_protos_headers += custom_target(
|
||||
wl_protos_src += custom_target(
|
||||
xml.underscorify() + '_client_h',
|
||||
input: xml,
|
||||
output: '@BASENAME@-client-protocol.h',
|
||||
command: [wayland_scanner, 'client-header', '@INPUT@', '@OUTPUT@'],
|
||||
)
|
||||
endforeach
|
||||
|
||||
lib_client_protos = static_library(
|
||||
'client_protos',
|
||||
wl_protos_src + wl_protos_headers,
|
||||
dependencies: wayland_client.partial_dependency(compile_args: true),
|
||||
)
|
||||
|
||||
client_protos = declare_dependency(
|
||||
link_with: lib_client_protos,
|
||||
sources: wl_protos_headers,
|
||||
)
|
||||
|
||||
lib_server_protos = static_library(
|
||||
'server_protos',
|
||||
wl_protos_src + wl_protos_headers,
|
||||
dependencies: wayland_server.partial_dependency(compile_args: true),
|
||||
)
|
||||
|
||||
server_protos = declare_dependency(
|
||||
link_with: lib_server_protos,
|
||||
sources: wl_protos_headers,
|
||||
)
|
||||
|
|
|
@ -8,6 +8,8 @@
|
|||
#include "sway/commands.h"
|
||||
#include "sway/config.h"
|
||||
#include "sway/server.h"
|
||||
#include "sway/desktop/launcher.h"
|
||||
#include "sway/server.h"
|
||||
#include "sway/tree/container.h"
|
||||
#include "sway/tree/root.h"
|
||||
#include "sway/tree/workspace.h"
|
||||
|
@ -25,11 +27,22 @@ struct cmd_results *cmd_exec_validate(int argc, char **argv) {
|
|||
return error;
|
||||
}
|
||||
|
||||
static void export_xdga_token(struct launcher_ctx *ctx) {
|
||||
const char *token = launcher_ctx_get_token_name(ctx);
|
||||
setenv("XDG_ACTIVATION_TOKEN", token, 1);
|
||||
}
|
||||
|
||||
static void export_startup_id(struct launcher_ctx *ctx) {
|
||||
const char *token = launcher_ctx_get_token_name(ctx);
|
||||
setenv("DESKTOP_STARTUP_ID", token, 1);
|
||||
}
|
||||
|
||||
struct cmd_results *cmd_exec_process(int argc, char **argv) {
|
||||
struct cmd_results *error = NULL;
|
||||
char *cmd = NULL;
|
||||
bool no_startup_id = false;
|
||||
if (strcmp(argv[0], "--no-startup-id") == 0) {
|
||||
sway_log(SWAY_INFO, "exec switch '--no-startup-id' not supported, ignored.");
|
||||
no_startup_id = true;
|
||||
--argc; ++argv;
|
||||
if ((error = checkarg(argc, argv[-1], EXPECTED_AT_LEAST, 1))) {
|
||||
return error;
|
||||
|
@ -51,6 +64,7 @@ struct cmd_results *cmd_exec_process(int argc, char **argv) {
|
|||
}
|
||||
|
||||
pid_t pid, child;
|
||||
struct launcher_ctx *ctx = launcher_ctx_create();
|
||||
// Fork process
|
||||
if ((pid = fork()) == 0) {
|
||||
// Fork child process again
|
||||
|
@ -63,6 +77,12 @@ struct cmd_results *cmd_exec_process(int argc, char **argv) {
|
|||
close(fd[0]);
|
||||
if ((child = fork()) == 0) {
|
||||
close(fd[1]);
|
||||
if (ctx) {
|
||||
export_xdga_token(ctx);
|
||||
}
|
||||
if (ctx && !no_startup_id) {
|
||||
export_startup_id(ctx);
|
||||
}
|
||||
execlp("sh", "sh", "-c", cmd, (void *)NULL);
|
||||
sway_log_errno(SWAY_ERROR, "execlp failed");
|
||||
_exit(1);
|
||||
|
@ -90,8 +110,12 @@ struct cmd_results *cmd_exec_process(int argc, char **argv) {
|
|||
waitpid(pid, NULL, 0);
|
||||
if (child > 0) {
|
||||
sway_log(SWAY_DEBUG, "Child process created with pid %d", child);
|
||||
root_record_workspace_pid(child);
|
||||
if (ctx != NULL) {
|
||||
sway_log(SWAY_DEBUG, "Recording workspace for process %d", child);
|
||||
ctx->pid = child;
|
||||
}
|
||||
} else {
|
||||
launcher_ctx_destroy(ctx);
|
||||
return cmd_results_new(CMD_FAILURE, "Second fork() failed");
|
||||
}
|
||||
|
||||
|
|
|
@ -686,6 +686,9 @@ static struct cmd_results *cmd_move_workspace(int argc, char **argv) {
|
|||
arrange_output(old_output);
|
||||
arrange_output(new_output);
|
||||
|
||||
struct sway_seat *seat = config->handler_context.seat;
|
||||
seat_consider_warp_to_focus(seat);
|
||||
|
||||
return cmd_results_new(CMD_SUCCESS, NULL);
|
||||
}
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#include "sway/config.h"
|
||||
#include "sway/ipc-server.h"
|
||||
#include "sway/output.h"
|
||||
#include "sway/desktop/launcher.h"
|
||||
#include "sway/tree/container.h"
|
||||
#include "sway/tree/workspace.h"
|
||||
#include "sway/tree/root.h"
|
||||
|
@ -91,8 +92,6 @@ struct cmd_results *cmd_rename(int argc, char **argv) {
|
|||
|
||||
sway_log(SWAY_DEBUG, "renaming workspace '%s' to '%s'", workspace->name, new_name);
|
||||
|
||||
root_rename_pid_workspaces(workspace->name, new_name);
|
||||
|
||||
free(workspace->name);
|
||||
workspace->name = new_name;
|
||||
|
||||
|
|
|
@ -287,7 +287,7 @@ static bool criteria_matches_view(struct criteria *criteria,
|
|||
|
||||
switch (criteria->instance->match_type) {
|
||||
case PATTERN_FOCUSED:
|
||||
if (focused && strcmp(instance, view_get_instance(focused))) {
|
||||
if (focused && lenient_strcmp(instance, view_get_instance(focused))) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
|
@ -307,7 +307,7 @@ static bool criteria_matches_view(struct criteria *criteria,
|
|||
|
||||
switch (criteria->window_role->match_type) {
|
||||
case PATTERN_FOCUSED:
|
||||
if (focused && strcmp(window_role, view_get_window_role(focused))) {
|
||||
if (focused && lenient_strcmp(window_role, view_get_window_role(focused))) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
|
|
|
@ -0,0 +1,211 @@
|
|||
#define _POSIX_C_SOURCE 200809L
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <wlr/types/wlr_xdg_activation_v1.h>
|
||||
#include "sway/input/seat.h"
|
||||
#include "sway/output.h"
|
||||
#include "sway/desktop/launcher.h"
|
||||
#include "sway/tree/node.h"
|
||||
#include "sway/tree/container.h"
|
||||
#include "sway/tree/workspace.h"
|
||||
#include "sway/tree/root.h"
|
||||
#include "log.h"
|
||||
|
||||
/**
|
||||
* Get the pid of a parent process given the pid of a child process.
|
||||
*
|
||||
* Returns the parent pid or NULL if the parent pid cannot be determined.
|
||||
*/
|
||||
static pid_t get_parent_pid(pid_t child) {
|
||||
pid_t parent = -1;
|
||||
char file_name[100];
|
||||
char *buffer = NULL;
|
||||
const char *sep = " ";
|
||||
FILE *stat = NULL;
|
||||
size_t buf_size = 0;
|
||||
|
||||
snprintf(file_name, sizeof(file_name), "/proc/%d/stat", child);
|
||||
|
||||
if ((stat = fopen(file_name, "r"))) {
|
||||
if (getline(&buffer, &buf_size, stat) != -1) {
|
||||
strtok(buffer, sep); // pid
|
||||
strtok(NULL, sep); // executable name
|
||||
strtok(NULL, sep); // state
|
||||
char *token = strtok(NULL, sep); // parent pid
|
||||
parent = strtol(token, NULL, 10);
|
||||
}
|
||||
free(buffer);
|
||||
fclose(stat);
|
||||
}
|
||||
|
||||
if (parent) {
|
||||
return (parent == child) ? -1 : parent;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
void launcher_ctx_consume(struct launcher_ctx *ctx) {
|
||||
// The view is now responsible for destroying this ctx
|
||||
wl_list_remove(&ctx->token_destroy.link);
|
||||
wl_list_init(&ctx->token_destroy.link);
|
||||
|
||||
if (!ctx->activated) {
|
||||
// An unactivated token hasn't been destroyed yet
|
||||
wlr_xdg_activation_token_v1_destroy(ctx->token);
|
||||
}
|
||||
ctx->token = NULL;
|
||||
|
||||
// Prevent additional matches
|
||||
wl_list_remove(&ctx->link);
|
||||
wl_list_init(&ctx->link);
|
||||
}
|
||||
|
||||
void launcher_ctx_destroy(struct launcher_ctx *ctx) {
|
||||
if (ctx == NULL) {
|
||||
return;
|
||||
}
|
||||
wl_list_remove(&ctx->node_destroy.link);
|
||||
wl_list_remove(&ctx->token_destroy.link);
|
||||
wl_list_remove(&ctx->link);
|
||||
wlr_xdg_activation_token_v1_destroy(ctx->token);
|
||||
free(ctx->name);
|
||||
free(ctx);
|
||||
}
|
||||
|
||||
struct launcher_ctx *launcher_ctx_find_pid(pid_t pid) {
|
||||
if (wl_list_empty(&server.pending_launcher_ctxs)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct launcher_ctx *ctx = NULL;
|
||||
sway_log(SWAY_DEBUG, "Looking up workspace for pid %d", pid);
|
||||
|
||||
do {
|
||||
struct launcher_ctx *_ctx = NULL;
|
||||
wl_list_for_each(_ctx, &server.pending_launcher_ctxs, link) {
|
||||
if (pid == _ctx->pid) {
|
||||
ctx = _ctx;
|
||||
sway_log(SWAY_DEBUG,
|
||||
"found %s match for pid %d: %s",
|
||||
node_type_to_str(ctx->node->type), pid, node_get_name(ctx->node));
|
||||
break;
|
||||
}
|
||||
}
|
||||
pid = get_parent_pid(pid);
|
||||
} while (pid > 1);
|
||||
|
||||
return ctx;
|
||||
}
|
||||
|
||||
struct sway_workspace *launcher_ctx_get_workspace(
|
||||
struct launcher_ctx *ctx) {
|
||||
struct sway_workspace *ws = NULL;
|
||||
struct sway_output *output = NULL;
|
||||
|
||||
switch (ctx->node->type) {
|
||||
case N_CONTAINER:
|
||||
// Unimplemented
|
||||
// TODO: add container matching?
|
||||
ws = ctx->node->sway_container->pending.workspace;
|
||||
break;
|
||||
case N_WORKSPACE:
|
||||
ws = ctx->node->sway_workspace;
|
||||
break;
|
||||
case N_OUTPUT:
|
||||
output = ctx->node->sway_output;
|
||||
ws = workspace_by_name(ctx->name);
|
||||
if (!ws) {
|
||||
sway_log(SWAY_DEBUG,
|
||||
"Creating workspace %s for pid %d because it disappeared",
|
||||
ctx->name, ctx->pid);
|
||||
if (!output->enabled) {
|
||||
sway_log(SWAY_DEBUG,
|
||||
"Workspace output %s is disabled, trying another one",
|
||||
output->wlr_output->name);
|
||||
output = NULL;
|
||||
}
|
||||
ws = workspace_create(output, ctx->name);
|
||||
}
|
||||
break;
|
||||
case N_ROOT:
|
||||
ws = workspace_create(NULL, ctx->name);
|
||||
break;
|
||||
}
|
||||
|
||||
return ws;
|
||||
}
|
||||
|
||||
static void ctx_handle_node_destroy(struct wl_listener *listener, void *data) {
|
||||
struct launcher_ctx *ctx = wl_container_of(listener, ctx, node_destroy);
|
||||
switch (ctx->node->type) {
|
||||
case N_CONTAINER:
|
||||
// Unimplemented
|
||||
break;
|
||||
case N_WORKSPACE:;
|
||||
struct sway_workspace *ws = ctx->node->sway_workspace;
|
||||
wl_list_remove(&ctx->node_destroy.link);
|
||||
wl_list_init(&ctx->node_destroy.link);
|
||||
// We want to save this ws name to recreate later, hopefully on the
|
||||
// same output
|
||||
free(ctx->name);
|
||||
ctx->name = strdup(ws->name);
|
||||
if (!ws->output || ws->output->node.destroying) {
|
||||
// If the output is being destroyed it would be pointless to track
|
||||
// If the output is being disabled, we'll find out if it's still
|
||||
// disabled when we try to match it.
|
||||
ctx->node = &root->node;
|
||||
break;
|
||||
}
|
||||
ctx->node = &ws->output->node;
|
||||
wl_signal_add(&ctx->node->events.destroy, &ctx->node_destroy);
|
||||
break;
|
||||
case N_OUTPUT:
|
||||
wl_list_remove(&ctx->node_destroy.link);
|
||||
wl_list_init(&ctx->node_destroy.link);
|
||||
// We'll make the ws ctx->name somewhere else
|
||||
ctx->node = &root->node;
|
||||
break;
|
||||
case N_ROOT:
|
||||
// Unreachable
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void token_handle_destroy(struct wl_listener *listener, void *data) {
|
||||
struct launcher_ctx *ctx = wl_container_of(listener, ctx, token_destroy);
|
||||
ctx->token = NULL;
|
||||
launcher_ctx_destroy(ctx);
|
||||
}
|
||||
|
||||
struct launcher_ctx *launcher_ctx_create() {
|
||||
struct sway_seat *seat = input_manager_current_seat();
|
||||
struct sway_workspace *ws = seat_get_focused_workspace(seat);
|
||||
if (!ws) {
|
||||
sway_log(SWAY_DEBUG, "Failed to create launch context. No workspace.");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct launcher_ctx *ctx = calloc(1, sizeof(struct launcher_ctx));
|
||||
struct wlr_xdg_activation_token_v1 *token =
|
||||
wlr_xdg_activation_token_v1_create(server.xdg_activation_v1);
|
||||
token->data = ctx;
|
||||
ctx->name = strdup(ws->name);
|
||||
ctx->token = token;
|
||||
ctx->node = &ws->node;
|
||||
|
||||
ctx->node_destroy.notify = ctx_handle_node_destroy;
|
||||
wl_signal_add(&ctx->node->events.destroy, &ctx->node_destroy);
|
||||
|
||||
ctx->token_destroy.notify = token_handle_destroy;
|
||||
wl_signal_add(&token->events.destroy, &ctx->token_destroy);
|
||||
|
||||
wl_list_init(&ctx->link);
|
||||
wl_list_insert(&server.pending_launcher_ctxs, &ctx->link);
|
||||
return ctx;
|
||||
}
|
||||
|
||||
const char *launcher_ctx_get_token_name(struct launcher_ctx *ctx) {
|
||||
const char *token = wlr_xdg_activation_token_v1_get_name(ctx->token);
|
||||
return token;
|
||||
}
|
|
@ -840,8 +840,9 @@ static void handle_mode(struct wl_listener *listener, void *data) {
|
|||
arrange_output(output);
|
||||
transaction_commit_dirty();
|
||||
|
||||
wlr_damage_ring_set_bounds(&output->damage_ring,
|
||||
output->width, output->height);
|
||||
int width, height;
|
||||
wlr_output_transformed_resolution(output->wlr_output, &width, &height);
|
||||
wlr_damage_ring_set_bounds(&output->damage_ring, width, height);
|
||||
wlr_output_schedule_frame(output->wlr_output);
|
||||
|
||||
update_output_manager_config(output->server);
|
||||
|
@ -872,11 +873,10 @@ static void handle_commit(struct wl_listener *listener, void *data) {
|
|||
update_output_manager_config(output->server);
|
||||
}
|
||||
|
||||
if (event->committed & (WLR_OUTPUT_STATE_MODE |
|
||||
WLR_OUTPUT_STATE_TRANSFORM |
|
||||
WLR_OUTPUT_STATE_SCALE)) {
|
||||
wlr_damage_ring_set_bounds(&output->damage_ring,
|
||||
output->width, output->height);
|
||||
if (event->committed & (WLR_OUTPUT_STATE_MODE | WLR_OUTPUT_STATE_TRANSFORM)) {
|
||||
int width, height;
|
||||
wlr_output_transformed_resolution(output->wlr_output, &width, &height);
|
||||
wlr_damage_ring_set_bounds(&output->damage_ring, width, height);
|
||||
wlr_output_schedule_frame(output->wlr_output);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#include <wayland-server-core.h>
|
||||
#include <wlr/types/wlr_output_layout.h>
|
||||
#include <wlr/types/wlr_output.h>
|
||||
#include <wlr/types/wlr_xdg_activation_v1.h>
|
||||
#include <wlr/xwayland.h>
|
||||
#include <xcb/xcb_icccm.h>
|
||||
#include "log.h"
|
||||
|
@ -16,6 +17,7 @@
|
|||
#include "sway/output.h"
|
||||
#include "sway/tree/arrange.h"
|
||||
#include "sway/tree/container.h"
|
||||
#include "sway/server.h"
|
||||
#include "sway/tree/view.h"
|
||||
#include "sway/tree/workspace.h"
|
||||
|
||||
|
@ -466,6 +468,7 @@ static void handle_destroy(struct wl_listener *listener, void *data) {
|
|||
wl_list_remove(&xwayland_view->set_title.link);
|
||||
wl_list_remove(&xwayland_view->set_class.link);
|
||||
wl_list_remove(&xwayland_view->set_role.link);
|
||||
wl_list_remove(&xwayland_view->set_startup_id.link);
|
||||
wl_list_remove(&xwayland_view->set_window_type.link);
|
||||
wl_list_remove(&xwayland_view->set_hints.link);
|
||||
wl_list_remove(&xwayland_view->set_decorations.link);
|
||||
|
@ -666,6 +669,31 @@ static void handle_set_role(struct wl_listener *listener, void *data) {
|
|||
view_execute_criteria(view);
|
||||
}
|
||||
|
||||
static void handle_set_startup_id(struct wl_listener *listener, void *data) {
|
||||
struct sway_xwayland_view *xwayland_view =
|
||||
wl_container_of(listener, xwayland_view, set_startup_id);
|
||||
struct sway_view *view = &xwayland_view->view;
|
||||
struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface;
|
||||
if (xsurface->startup_id == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
struct wlr_xdg_activation_token_v1 *token =
|
||||
wlr_xdg_activation_v1_find_token(
|
||||
server.xdg_activation_v1, xsurface->startup_id);
|
||||
if (token == NULL) {
|
||||
// Tried to activate with an unknown or expired token
|
||||
return;
|
||||
}
|
||||
|
||||
struct launcher_ctx *ctx = token->data;
|
||||
if (token->data == NULL) {
|
||||
// TODO: support external launchers in X
|
||||
return;
|
||||
}
|
||||
view_assign_ctx(view, ctx);
|
||||
}
|
||||
|
||||
static void handle_set_window_type(struct wl_listener *listener, void *data) {
|
||||
struct sway_xwayland_view *xwayland_view =
|
||||
wl_container_of(listener, xwayland_view, set_window_type);
|
||||
|
@ -751,6 +779,10 @@ struct sway_xwayland_view *create_xwayland_view(struct wlr_xwayland_surface *xsu
|
|||
wl_signal_add(&xsurface->events.set_role, &xwayland_view->set_role);
|
||||
xwayland_view->set_role.notify = handle_set_role;
|
||||
|
||||
wl_signal_add(&xsurface->events.set_startup_id,
|
||||
&xwayland_view->set_startup_id);
|
||||
xwayland_view->set_startup_id.notify = handle_set_startup_id;
|
||||
|
||||
wl_signal_add(&xsurface->events.set_window_type,
|
||||
&xwayland_view->set_window_type);
|
||||
xwayland_view->set_window_type.notify = handle_set_window_type;
|
||||
|
|
|
@ -575,6 +575,13 @@ void input_manager_reset_input(struct sway_input_device *input_device) {
|
|||
}
|
||||
|
||||
void input_manager_reset_all_inputs(void) {
|
||||
// Set the active keyboard to NULL to avoid spamming configuration updates
|
||||
// for all keyboard devices.
|
||||
struct sway_seat *seat;
|
||||
wl_list_for_each(seat, &server.input->seats, link) {
|
||||
wlr_seat_set_keyboard(seat->wlr_seat, NULL);
|
||||
}
|
||||
|
||||
struct sway_input_device *input_device = NULL;
|
||||
wl_list_for_each(input_device, &server.input->devices, link) {
|
||||
input_manager_reset_input(input_device);
|
||||
|
@ -583,7 +590,6 @@ void input_manager_reset_all_inputs(void) {
|
|||
// If there is at least one keyboard using the default keymap, repeat delay,
|
||||
// and repeat rate, then it is possible that there is a keyboard group that
|
||||
// need their keyboard disarmed.
|
||||
struct sway_seat *seat;
|
||||
wl_list_for_each(seat, &server.input->seats, link) {
|
||||
struct sway_keyboard_group *group;
|
||||
wl_list_for_each(group, &seat->keyboard_groups, link) {
|
||||
|
|
|
@ -1068,8 +1068,12 @@ void sway_keyboard_configure(struct sway_keyboard *keyboard) {
|
|||
}
|
||||
}
|
||||
|
||||
// If the seat has no active keyboard, set this one
|
||||
struct wlr_seat *seat = keyboard->seat_device->sway_seat->wlr_seat;
|
||||
wlr_seat_set_keyboard(seat, keyboard->wlr);
|
||||
struct wlr_keyboard *current_keyboard = seat->keyboard_state.keyboard;
|
||||
if (current_keyboard == NULL) {
|
||||
wlr_seat_set_keyboard(seat, keyboard->wlr);
|
||||
}
|
||||
|
||||
wl_list_remove(&keyboard->keyboard_key.link);
|
||||
wl_signal_add(&keyboard->wlr->events.key, &keyboard->keyboard_key);
|
||||
|
|
|
@ -814,8 +814,15 @@ static void seat_configure_keyboard(struct sway_seat *seat,
|
|||
sway_keyboard_create(seat, seat_device);
|
||||
}
|
||||
sway_keyboard_configure(seat_device->keyboard);
|
||||
wlr_seat_set_keyboard(seat->wlr_seat,
|
||||
wlr_keyboard_from_input_device(seat_device->input_device->wlr_device));
|
||||
|
||||
// We only need to update the current keyboard, as the rest will be updated
|
||||
// as they are activated.
|
||||
struct wlr_keyboard *wlr_keyboard =
|
||||
wlr_keyboard_from_input_device(seat_device->input_device->wlr_device);
|
||||
struct wlr_keyboard *current_keyboard = seat->wlr_seat->keyboard_state.keyboard;
|
||||
if (wlr_keyboard != current_keyboard) {
|
||||
return;
|
||||
}
|
||||
|
||||
// force notify reenter to pick up the new configuration. This reuses
|
||||
// the current focused surface to avoid breaking input grabs.
|
||||
|
|
|
@ -21,6 +21,7 @@ sway_sources = files(
|
|||
'desktop/surface.c',
|
||||
'desktop/transaction.c',
|
||||
'desktop/xdg_shell.c',
|
||||
'desktop/launcher.c',
|
||||
|
||||
'input/input-manager.c',
|
||||
'input/cursor.c',
|
||||
|
@ -222,7 +223,6 @@ sway_deps = [
|
|||
pcre2,
|
||||
glesv2,
|
||||
pixman,
|
||||
server_protos,
|
||||
threads,
|
||||
wayland_server,
|
||||
wlroots,
|
||||
|
@ -237,7 +237,7 @@ endif
|
|||
|
||||
executable(
|
||||
'sway',
|
||||
sway_sources,
|
||||
sway_sources + wl_protos_src,
|
||||
include_directories: [sway_inc],
|
||||
dependencies: sway_deps,
|
||||
link_with: [lib_sway_common],
|
||||
|
|
|
@ -215,6 +215,8 @@ bool server_init(struct sway_server *server) {
|
|||
wl_signal_add(&server->xdg_activation_v1->events.request_activate,
|
||||
&server->xdg_activation_v1_request_activate);
|
||||
|
||||
wl_list_init(&server->pending_launcher_ctxs);
|
||||
|
||||
// Avoid using "wayland-0" as display socket
|
||||
char name_candidate[16];
|
||||
for (unsigned int i = 1; i <= 32; ++i) {
|
||||
|
|
|
@ -18,13 +18,13 @@ void node_init(struct sway_node *node, enum sway_node_type type, void *thing) {
|
|||
const char *node_type_to_str(enum sway_node_type type) {
|
||||
switch (type) {
|
||||
case N_ROOT:
|
||||
return "N_ROOT";
|
||||
return "root";
|
||||
case N_OUTPUT:
|
||||
return "N_OUTPUT";
|
||||
return "output";
|
||||
case N_WORKSPACE:
|
||||
return "N_WORKSPACE";
|
||||
return "workspace";
|
||||
case N_CONTAINER:
|
||||
return "N_CONTAINER";
|
||||
return "container";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
|
180
sway/tree/root.c
180
sway/tree/root.c
|
@ -184,172 +184,6 @@ void root_scratchpad_hide(struct sway_container *con) {
|
|||
ipc_event_window(con, "move");
|
||||
}
|
||||
|
||||
struct pid_workspace {
|
||||
pid_t pid;
|
||||
char *workspace;
|
||||
struct timespec time_added;
|
||||
|
||||
struct sway_output *output;
|
||||
struct wl_listener output_destroy;
|
||||
|
||||
struct wl_list link;
|
||||
};
|
||||
|
||||
static struct wl_list pid_workspaces;
|
||||
|
||||
/**
|
||||
* Get the pid of a parent process given the pid of a child process.
|
||||
*
|
||||
* Returns the parent pid or NULL if the parent pid cannot be determined.
|
||||
*/
|
||||
static pid_t get_parent_pid(pid_t child) {
|
||||
pid_t parent = -1;
|
||||
char file_name[100];
|
||||
char *buffer = NULL;
|
||||
const char *sep = " ";
|
||||
FILE *stat = NULL;
|
||||
size_t buf_size = 0;
|
||||
|
||||
snprintf(file_name, sizeof(file_name), "/proc/%d/stat", child);
|
||||
|
||||
if ((stat = fopen(file_name, "r"))) {
|
||||
if (getline(&buffer, &buf_size, stat) != -1) {
|
||||
strtok(buffer, sep); // pid
|
||||
strtok(NULL, sep); // executable name
|
||||
strtok(NULL, sep); // state
|
||||
char *token = strtok(NULL, sep); // parent pid
|
||||
parent = strtol(token, NULL, 10);
|
||||
}
|
||||
free(buffer);
|
||||
fclose(stat);
|
||||
}
|
||||
|
||||
if (parent) {
|
||||
return (parent == child) ? -1 : parent;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void pid_workspace_destroy(struct pid_workspace *pw) {
|
||||
wl_list_remove(&pw->output_destroy.link);
|
||||
wl_list_remove(&pw->link);
|
||||
free(pw->workspace);
|
||||
free(pw);
|
||||
}
|
||||
|
||||
struct sway_workspace *root_workspace_for_pid(pid_t pid) {
|
||||
if (!pid_workspaces.prev && !pid_workspaces.next) {
|
||||
wl_list_init(&pid_workspaces);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct sway_workspace *ws = NULL;
|
||||
struct pid_workspace *pw = NULL;
|
||||
|
||||
sway_log(SWAY_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;
|
||||
sway_log(SWAY_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) {
|
||||
sway_log(SWAY_DEBUG,
|
||||
"Creating workspace %s for pid %d because it disappeared",
|
||||
pw->workspace, pid);
|
||||
|
||||
struct sway_output *output = pw->output;
|
||||
if (pw->output && !pw->output->enabled) {
|
||||
sway_log(SWAY_DEBUG,
|
||||
"Workspace output %s is disabled, trying another one",
|
||||
pw->output->wlr_output->name);
|
||||
output = NULL;
|
||||
}
|
||||
|
||||
ws = workspace_create(output, pw->workspace);
|
||||
}
|
||||
|
||||
pid_workspace_destroy(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;
|
||||
wl_list_remove(&pw->output_destroy.link);
|
||||
wl_list_init(&pw->output_destroy.link);
|
||||
}
|
||||
|
||||
void root_record_workspace_pid(pid_t pid) {
|
||||
sway_log(SWAY_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();
|
||||
struct sway_workspace *ws = seat_get_focused_workspace(seat);
|
||||
if (!ws) {
|
||||
sway_log(SWAY_DEBUG, "Bailing out, no workspace");
|
||||
return;
|
||||
}
|
||||
struct sway_output *output = ws->output;
|
||||
if (!output) {
|
||||
sway_log(SWAY_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) {
|
||||
pid_workspace_destroy(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->wlr_output->events.destroy, &pw->output_destroy);
|
||||
wl_list_insert(&pid_workspaces, &pw->link);
|
||||
}
|
||||
|
||||
void root_remove_workspace_pid(pid_t pid) {
|
||||
if (!pid_workspaces.prev || !pid_workspaces.next) {
|
||||
return;
|
||||
}
|
||||
|
||||
struct pid_workspace *pw, *tmp;
|
||||
wl_list_for_each_safe(pw, tmp, &pid_workspaces, link) {
|
||||
if (pid == pw->pid) {
|
||||
pid_workspace_destroy(pw);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void root_for_each_workspace(void (*f)(struct sway_workspace *ws, void *data),
|
||||
void *data) {
|
||||
for (int i = 0; i < root->outputs->length; ++i) {
|
||||
|
@ -444,17 +278,3 @@ void root_get_box(struct sway_root *root, struct wlr_box *box) {
|
|||
box->width = root->width;
|
||||
box->height = root->height;
|
||||
}
|
||||
|
||||
void root_rename_pid_workspaces(const char *old_name, const char *new_name) {
|
||||
if (!pid_workspaces.prev && !pid_workspaces.next) {
|
||||
wl_list_init(&pid_workspaces);
|
||||
}
|
||||
|
||||
struct pid_workspace *pw = NULL;
|
||||
wl_list_for_each(pw, &pid_workspaces, link) {
|
||||
if (strcmp(pw->workspace, old_name) == 0) {
|
||||
free(pw->workspace);
|
||||
pw->workspace = strdup(new_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include "sway/desktop.h"
|
||||
#include "sway/desktop/transaction.h"
|
||||
#include "sway/desktop/idle_inhibit_v1.h"
|
||||
#include "sway/desktop/launcher.h"
|
||||
#include "sway/input/cursor.h"
|
||||
#include "sway/ipc-server.h"
|
||||
#include "sway/output.h"
|
||||
|
@ -63,6 +64,8 @@ void view_destroy(struct sway_view *view) {
|
|||
}
|
||||
list_free(view->executed_criteria);
|
||||
|
||||
view_assign_ctx(view, NULL);
|
||||
|
||||
free(view->title_format);
|
||||
|
||||
if (view->impl->destroy) {
|
||||
|
@ -533,6 +536,20 @@ static void view_populate_pid(struct sway_view *view) {
|
|||
view->pid = pid;
|
||||
}
|
||||
|
||||
void view_assign_ctx(struct sway_view *view, struct launcher_ctx *ctx) {
|
||||
if (view->ctx) {
|
||||
// This ctx has been replaced
|
||||
launcher_ctx_destroy(view->ctx);
|
||||
view->ctx = NULL;
|
||||
}
|
||||
if (ctx == NULL) {
|
||||
return;
|
||||
}
|
||||
launcher_ctx_consume(ctx);
|
||||
|
||||
view->ctx = ctx;
|
||||
}
|
||||
|
||||
static struct sway_workspace *select_workspace(struct sway_view *view) {
|
||||
struct sway_seat *seat = input_manager_current_seat();
|
||||
|
||||
|
@ -568,13 +585,14 @@ static struct sway_workspace *select_workspace(struct sway_view *view) {
|
|||
}
|
||||
list_free(criterias);
|
||||
if (ws) {
|
||||
root_remove_workspace_pid(view->pid);
|
||||
view_assign_ctx(view, NULL);
|
||||
return ws;
|
||||
}
|
||||
|
||||
// Check if there's a PID mapping
|
||||
ws = root_workspace_for_pid(view->pid);
|
||||
ws = view->ctx ? launcher_ctx_get_workspace(view->ctx) : NULL;
|
||||
if (ws) {
|
||||
view_assign_ctx(view, NULL);
|
||||
return ws;
|
||||
}
|
||||
|
||||
|
@ -717,6 +735,13 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface,
|
|||
view_populate_pid(view);
|
||||
view->container = container_create(view);
|
||||
|
||||
if (view->ctx == NULL) {
|
||||
struct launcher_ctx *ctx = launcher_ctx_find_pid(view->pid);
|
||||
if (ctx != NULL) {
|
||||
view_assign_ctx(view, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
// If there is a request to be opened fullscreen on a specific output, try
|
||||
// to honor that request. Otherwise, fallback to assigns, pid mappings,
|
||||
// focused workspace, etc
|
||||
|
|
|
@ -56,6 +56,8 @@ struct sway_output *workspace_get_initial_output(const char *name) {
|
|||
|
||||
struct sway_workspace *workspace_create(struct sway_output *output,
|
||||
const char *name) {
|
||||
sway_assert(name, "NULL name given to workspace_create");
|
||||
|
||||
if (output == NULL) {
|
||||
output = workspace_get_initial_output(name);
|
||||
}
|
||||
|
@ -69,7 +71,7 @@ struct sway_workspace *workspace_create(struct sway_output *output,
|
|||
return NULL;
|
||||
}
|
||||
node_init(&ws->node, N_WORKSPACE, ws);
|
||||
ws->name = name ? strdup(name) : NULL;
|
||||
ws->name = strdup(name);
|
||||
ws->prev_split_layout = L_NONE;
|
||||
ws->layout = output_get_default_layout(output);
|
||||
ws->floating = create_list();
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#include <wlr/types/wlr_xdg_activation_v1.h>
|
||||
#include "sway/desktop/launcher.h"
|
||||
#include "sway/tree/view.h"
|
||||
|
||||
void xdg_activation_v1_handle_request_activate(struct wl_listener *listener,
|
||||
|
@ -15,7 +16,22 @@ void xdg_activation_v1_handle_request_activate(struct wl_listener *listener,
|
|||
return;
|
||||
}
|
||||
struct sway_view *view = xdg_surface->data;
|
||||
if (!xdg_surface->mapped || view == NULL) {
|
||||
if (view == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!xdg_surface->mapped) {
|
||||
// This is a startup notification. If we are tracking it, the data
|
||||
// field is a launcher_ctx.
|
||||
struct launcher_ctx *ctx = event->token->data;
|
||||
if (!ctx || ctx->activated) {
|
||||
// This ctx has already been activated and cannot be used again
|
||||
// for a startup notification. It will be destroyed
|
||||
return;
|
||||
} else {
|
||||
ctx->activated = true;
|
||||
view_assign_ctx(view, ctx);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -269,11 +269,16 @@ bool i3bar_handle_readable(struct status_line *status) {
|
|||
|
||||
enum hotspot_event_handling i3bar_block_send_click(struct status_line *status,
|
||||
struct i3bar_block *block, double x, double y, double rx, double ry,
|
||||
double w, double h, int scale, uint32_t button) {
|
||||
double w, double h, int scale, uint32_t button, bool released) {
|
||||
sway_log(SWAY_DEBUG, "block %s clicked", block->name);
|
||||
if (!block->name || !status->click_events) {
|
||||
return HOTSPOT_PROCESS;
|
||||
}
|
||||
if (released) {
|
||||
// Since we handle the pressed event, also handle the released event
|
||||
// to block it from falling through to a binding in the bar
|
||||
return HOTSPOT_IGNORE;
|
||||
}
|
||||
|
||||
struct json_object *event_json = json_object_new_object();
|
||||
json_object_object_add(event_json, "name",
|
||||
|
|
|
@ -141,14 +141,15 @@ static bool check_bindings(struct swaybar *bar, uint32_t button,
|
|||
}
|
||||
|
||||
static bool process_hotspots(struct swaybar_output *output,
|
||||
double x, double y, uint32_t button) {
|
||||
double x, double y, uint32_t button, uint32_t state) {
|
||||
bool released = state == WL_POINTER_BUTTON_STATE_RELEASED;
|
||||
struct swaybar_hotspot *hotspot;
|
||||
wl_list_for_each(hotspot, &output->hotspots, link) {
|
||||
if (x >= hotspot->x && y >= hotspot->y
|
||||
&& x < hotspot->x + hotspot->width
|
||||
&& y < hotspot->y + hotspot->height) {
|
||||
if (HOTSPOT_IGNORE == hotspot->callback(output, hotspot, x, y,
|
||||
button, hotspot->data)) {
|
||||
button, released, hotspot->data)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -166,14 +167,11 @@ static void wl_pointer_button(void *data, struct wl_pointer *wl_pointer,
|
|||
return;
|
||||
}
|
||||
|
||||
if (check_bindings(seat->bar, button, state)) {
|
||||
if (process_hotspots(output, pointer->x, pointer->y, button, state)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (state != WL_POINTER_BUTTON_STATE_PRESSED) {
|
||||
return;
|
||||
}
|
||||
process_hotspots(output, pointer->x, pointer->y, button);
|
||||
check_bindings(seat->bar, button, state);
|
||||
}
|
||||
|
||||
static void workspace_next(struct swaybar *bar, struct swaybar_output *output,
|
||||
|
@ -222,15 +220,15 @@ static void workspace_next(struct swaybar *bar, struct swaybar_output *output,
|
|||
static void process_discrete_scroll(struct swaybar_seat *seat,
|
||||
struct swaybar_output *output, struct swaybar_pointer *pointer,
|
||||
uint32_t axis, wl_fixed_t value) {
|
||||
// If there is a button press binding, execute it, skip default behavior,
|
||||
// and check button release bindings
|
||||
uint32_t button = wl_axis_to_button(axis, value);
|
||||
if (check_bindings(seat->bar, button, WL_POINTER_BUTTON_STATE_PRESSED)) {
|
||||
check_bindings(seat->bar, button, WL_POINTER_BUTTON_STATE_RELEASED);
|
||||
if (process_hotspots(output, pointer->x, pointer->y, button, WL_POINTER_BUTTON_STATE_PRESSED)) {
|
||||
// (Currently hotspots don't do anything on release events, so no need to emit one)
|
||||
return;
|
||||
}
|
||||
|
||||
if (process_hotspots(output, pointer->x, pointer->y, button)) {
|
||||
// If there is a button press binding, execute it, and check button release bindings
|
||||
if (check_bindings(seat->bar, button, WL_POINTER_BUTTON_STATE_PRESSED)) {
|
||||
check_bindings(seat->bar, button, WL_POINTER_BUTTON_STATE_RELEASED);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -403,7 +401,8 @@ static void wl_touch_up(void *data, struct wl_touch *wl_touch,
|
|||
}
|
||||
if (time - slot->time < 500) {
|
||||
// Tap, treat it like a pointer click
|
||||
process_hotspots(slot->output, slot->x, slot->y, BTN_LEFT);
|
||||
process_hotspots(slot->output, slot->x, slot->y, BTN_LEFT, WL_POINTER_BUTTON_STATE_PRESSED);
|
||||
// (Currently hotspots don't do anything on release events, so no need to emit one)
|
||||
}
|
||||
slot->output = NULL;
|
||||
}
|
||||
|
|
|
@ -8,7 +8,6 @@ tray_files = have_tray ? [
|
|||
|
||||
swaybar_deps = [
|
||||
cairo,
|
||||
client_protos,
|
||||
gdk_pixbuf,
|
||||
jsonc,
|
||||
math,
|
||||
|
@ -32,7 +31,8 @@ executable(
|
|||
'main.c',
|
||||
'render.c',
|
||||
'status_line.c',
|
||||
tray_files
|
||||
tray_files,
|
||||
wl_protos_src,
|
||||
],
|
||||
include_directories: [sway_inc],
|
||||
dependencies: swaybar_deps,
|
||||
|
|
|
@ -160,7 +160,7 @@ static void render_sharp_line(cairo_t *cairo, uint32_t color,
|
|||
|
||||
static enum hotspot_event_handling block_hotspot_callback(
|
||||
struct swaybar_output *output, struct swaybar_hotspot *hotspot,
|
||||
double x, double y, uint32_t button, void *data) {
|
||||
double x, double y, uint32_t button, bool released, void *data) {
|
||||
struct i3bar_block *block = data;
|
||||
struct status_line *status = output->bar->status;
|
||||
return i3bar_block_send_click(status, block, x, y,
|
||||
|
@ -168,7 +168,7 @@ static enum hotspot_event_handling block_hotspot_callback(
|
|||
y - (double)hotspot->y,
|
||||
(double)hotspot->width,
|
||||
(double)hotspot->height,
|
||||
output->scale, button);
|
||||
output->scale, button, released);
|
||||
}
|
||||
|
||||
static void i3bar_block_unref_callback(void *data) {
|
||||
|
@ -599,10 +599,15 @@ static uint32_t render_binding_mode_indicator(struct render_context *ctx,
|
|||
|
||||
static enum hotspot_event_handling workspace_hotspot_callback(
|
||||
struct swaybar_output *output, struct swaybar_hotspot *hotspot,
|
||||
double x, double y, uint32_t button, void *data) {
|
||||
double x, double y, uint32_t button, bool released, void *data) {
|
||||
if (button != BTN_LEFT) {
|
||||
return HOTSPOT_PROCESS;
|
||||
}
|
||||
if (released) {
|
||||
// Since we handle the pressed event, also handle the released event
|
||||
// to block it from falling through to a binding in the bar
|
||||
return HOTSPOT_IGNORE;
|
||||
}
|
||||
ipc_send_workspace_command(output->bar, (const char *)data);
|
||||
return HOTSPOT_IGNORE;
|
||||
}
|
||||
|
|
|
@ -385,13 +385,18 @@ static int cmp_sni_id(const void *item, const void *cmp_to) {
|
|||
|
||||
static enum hotspot_event_handling icon_hotspot_callback(
|
||||
struct swaybar_output *output, struct swaybar_hotspot *hotspot,
|
||||
double x, double y, uint32_t button, void *data) {
|
||||
double x, double y, uint32_t button, bool released, void *data) {
|
||||
sway_log(SWAY_DEBUG, "Clicked on %s", (char *)data);
|
||||
|
||||
struct swaybar_tray *tray = output->bar->tray;
|
||||
int idx = list_seq_find(tray->items, cmp_sni_id, data);
|
||||
|
||||
if (idx != -1) {
|
||||
if (released) {
|
||||
// Since we handle the pressed event, also handle the released event
|
||||
// to block it from falling through to a binding in the bar
|
||||
return HOTSPOT_IGNORE;
|
||||
}
|
||||
struct swaybar_sni *sni = tray->items->items[idx];
|
||||
// guess global position since wayland doesn't expose it
|
||||
struct swaybar_config *config = tray->bar->config;
|
||||
|
|
|
@ -5,13 +5,14 @@ executable(
|
|||
'render.c',
|
||||
'swaynag.c',
|
||||
'types.c',
|
||||
wl_protos_src,
|
||||
],
|
||||
include_directories: [sway_inc],
|
||||
dependencies: [
|
||||
cairo,
|
||||
client_protos,
|
||||
pango,
|
||||
pangocairo,
|
||||
rt,
|
||||
wayland_client,
|
||||
wayland_cursor,
|
||||
],
|
||||
|
|
|
@ -33,6 +33,8 @@ struct swaynag_type *swaynag_type_new(const char *name) {
|
|||
void swaynag_types_add_default(list_t *types) {
|
||||
struct swaynag_type *type_defaults = swaynag_type_new("<defaults>");
|
||||
type_defaults->font = strdup("pango:Monospace 10");
|
||||
type_defaults->font_description =
|
||||
pango_font_description_from_string(type_defaults->font);
|
||||
type_defaults->anchors = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP
|
||||
| ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT
|
||||
| ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT;
|
||||
|
@ -94,6 +96,10 @@ void swaynag_type_merge(struct swaynag_type *dest, struct swaynag_type *src) {
|
|||
dest->font = strdup(src->font);
|
||||
}
|
||||
|
||||
if (src->font_description) {
|
||||
dest->font_description = pango_font_description_copy(src->font_description);
|
||||
}
|
||||
|
||||
if (src->output) {
|
||||
dest->output = strdup(src->output);
|
||||
}
|
||||
|
@ -173,6 +179,7 @@ void swaynag_type_merge(struct swaynag_type *dest, struct swaynag_type *src) {
|
|||
void swaynag_type_free(struct swaynag_type *type) {
|
||||
free(type->name);
|
||||
free(type->font);
|
||||
pango_font_description_free(type->font_description);
|
||||
free(type->output);
|
||||
free(type);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue