Compare commits

...

33 Commits
master ... 1.8

Author SHA1 Message Date
Simon Ser b88b1b6302 build: bump version to 1.8 2022-12-25 16:59:56 +01:00
Simon Ser 0500cdbfce swaynag: fix NULL font description
The font description was only set if provided on the CLI. It was
left NULL for the defaults and when reading from the config file.

Closes: https://github.com/swaywm/sway/issues/7186
(cherry picked from commit fd0af78e43)
2022-12-22 17:45:33 +01:00
Simon Ser 1340910a24 build: bump version to 1.8-rc4 2022-12-19 10:40:02 +01:00
Kenny Levinsen 6b900bab60 seat: Set keyboard if seat keyboard is NULL
sway sends wl_keyboard.enter on seat focus change and when a keyboard
active on a seat is configured. If all keyboards are removed and a
keyboard is added back without changing the focused client, no new
notify event would be sent despite having keyboard focus. This could
lead to key events without notify, which is a protocol violation.

As a quick fix, when configuring a keyboard on a seat where no keyboard
is currently active, activate the keyboard so that a focused surface
will receive a notify event.

Regressed by: e1b268af98
Closes: https://github.com/swaywm/sway/issues/7330

(cherry picked from commit 1ade0ce753)
2022-12-19 10:39:53 +01:00
Ankit Pandey 51663bb120 commands/move: Warp cursor after moving workspace to another output
This makes sway's behavior consistent with i3 when `mouse_warping` is
set to any value besides `none`.

Fixes #7027.

(cherry picked from commit e3c63bf58d)
2022-12-19 10:39:53 +01:00
Simon Ser 54d1e0d568 build: bump version to 1.8-rc3 2022-12-12 11:26:40 +01:00
Ronan Pigott 8d78ab6a45 criteria: be lenient on window_role and instance too 2022-12-12 11:26:11 +01:00
Kenny Levinsen 817f1bbec3 seat: Avoid sending redundant keymaps on reload
When we reload the config, we reset every input device and re-apply
configuration from the config file. This means that the keyboard keymap
is updated at least once during config reload, more if the config file
contains keyboard configuration.

When they keyboard keymap changes and is updated through wlr_seat, the
keymap ends up sent to every keyboard bound in every client, seemingly
multiple times. On an x230 of mine with a keyboard layout set in the
config file, I see 42 keymap events sent to foot on config reload.

Reduce events from keyboard configurations by skipping all but the
currently active keyboard for the seat, and by clearing the active
keyboard during input manager device reset. After this change, I only
see a single just-in-time keymap event.

Fixes: https://github.com/swaywm/sway/issues/6654
2022-12-12 11:26:11 +01:00
Simon Ser fb1cb0aa3a build: bump version to 1.8-rc2 2022-12-03 17:56:30 +01:00
Simon Ser 194fdc6c35 build: fix have_xwayland when xcb-icccm is not found
xcb-icccm is required to build Xwayland support.

Backported from commit d41f11e6bd.
2022-12-03 17:48:41 +01:00
nerdopolis baf027fc5b Fix build on Debian Stable
(cherry picked from commit dca0bb5749)
2022-11-29 00:09:44 +01:00
Simon Ser 27a56e63d3 build: bump version to 1.8-rc1 2022-11-26 23:27:03 +01:00
Simon Ser 6afe74ffec build: drop unused wayland-egl dependency
(cherry picked from commit 37e4a3d637)
2022-11-26 23:26:47 +01:00
Joan Bruguera 0a9b468540 swaybar: Make hotspots block bar release bindings
The previous commit prioritized hotspots before bar bindings for press events,
which matches i3's behaviour. However, since hotspots don't need to do any
processing on release events, those were not handled, and simply fell through
to `bindsym --release` bar bindings (if any).

This is counter-intuitive, and doesn't match i3's behaviour. Instead in case
a hotspot handles the press event, it should also handle the release event,
doing nothing, but blocking the event from triggering a --release bar binding.

E.g., in Sway, without this commit, this config. shows a text on tray clicks:

    bar {
        # ...
        bindsym --release button1 exec swaynag -m I_got_the_release_event.
    }

But the same configuration in i3 (with i3-nagbar) doesn't show the text.

Signed-off-by: Joan Bruguera <joanbrugueram@gmail.com>
(cherry picked from commit 94b69acf0d)
2022-11-26 10:29:59 +01:00
Joan Bruguera b92af7e3ca swaybar: Prioritize hotspot events to bar bindings
This is consistent with i3bar's behaviour, and for example, allows binding a
command to button1, while still being able to click on tray icons or other
zones on the bar's status line which may have their own bindings.

E.g., in Sway, without this commit, this config. makes tray icons unclickable:

    bar {
        # ...
        bindsym button1 exec swaynag -m You_clicked_the_tray._Want_some_help?
    }

But the same configuration in i3 (with i3-nagbar) keeps tray items clickable.

Signed-off-by: Joan Bruguera <joanbrugueram@gmail.com>
(cherry picked from commit 53f9dbd424)
2022-11-26 10:29:59 +01:00
Ronan Pigott d5872d0880 launcher: export X startup ids and use them for workspace matching
(cherry picked from commit 28fda4c0d3)
2022-11-26 10:29:59 +01:00
Ronan Pigott 97423ca9c7 launcher: export xdga tokens and use them for workspace matching
(cherry picked from commit 30ad4dc4a5)
2022-11-26 10:29:59 +01:00
Ronan Pigott ee9266cf8c launcher: fudge the interface a bit
We want to create a context before knowing the pid it will match with.

(cherry picked from commit bdeb9f9565)
2022-11-26 10:29:59 +01:00
Ronan Pigott 66be031f6c launcher: initialize launcher_ctxs once on startup
(cherry picked from commit 66568508c0)
2022-11-26 10:29:59 +01:00
Ronan Pigott 5794a223ce view: associate launch contexts with views
Views now maintain a reference to a launch context which, as a last
resort, is populated at map time with a context associated with its pid.
This opens the possibility of populating it before map via another
source, e.g. xdga-tokens or configuration.

(cherry picked from commit 864b3a9a18)
2022-11-26 10:29:59 +01:00
Ronan Pigott 9d78ede905 launcher: rename pid_workspace to launcher_ctx
Soon we will match views with more than just a pid.

(cherry picked from commit d75c9f9722)
2022-11-26 10:29:59 +01:00
Ronan Pigott cb13b9d628 launcher: use xdga tokens
This reuses wlroots token tracking for workspace matching. It doesn't
export any xdga tokens for clients yet.

(cherry picked from commit bd66f4943d)
2022-11-26 10:29:59 +01:00
Ronan Pigott 69abc41d25 launcher: track workspaces by node
This removes the need to rename the pid_workspaces when a workspace
is renamed.

It also opens the possibility of tracking other node types. Tracking
containers would allow application to be placed correctly in the
container tree even if the user has moved their focus elsewhere since
it was launched.

(cherry picked from commit 3b49f2782e)
2022-11-26 10:29:59 +01:00
Ronan Pigott 16b391db48 node: prettify node type names
(cherry picked from commit 1c4b94ae3c)
2022-11-26 10:29:59 +01:00
Ronan Pigott 25f559dcde root: move the workspace matching code to its own file
This removes the pid_workspace bits from tree/root before it gets
too interesting.

No functional change.

(cherry picked from commit eb5021ef99)
2022-11-26 10:29:59 +01:00
Simon Ser 52166bc1f5 build: drop intermediate libraries for protocols
(cherry picked from commit af8a5a8918)
2022-11-26 10:29:58 +01:00
Simon Ser 2a6bcc6738 build: drop "server" from target name for protocol code
(cherry picked from commit e5475d9310)
2022-11-26 10:29:58 +01:00
Simon Ser 61e4e65ea6 build: unify server & client protocol generation
No need to make a difference here, let's just generate header
files for both.

(cherry picked from commit 5be5a038da)
2022-11-26 10:29:58 +01:00
Simon Ser ceece55850 build: drop wayland-scanner fallback
(cherry picked from commit 366f6ef3d3)
2022-11-26 10:29:58 +01:00
Manuel Stoeckl 9f4229827f Use shm_open instead of mkstemp
shm_open is more reliable because it does not require
a writeable filesystem folder, unlike mkstemp.

(cherry picked from commit e2bc8866f4)
2022-11-26 10:29:58 +01:00
Kirill Primak 68b4ed3a4a output: set damage ring bounds to pixel values
Fixes: https://github.com/swaywm/sway/issues/7254
(cherry picked from commit 85005b52fe)
2022-11-26 10:29:58 +01:00
Alexander Orzechowski 5a2563b1a4 workspace_create: Don't allow NULL name
(cherry picked from commit 34933bb843)
2022-11-26 10:29:58 +01:00
Simon Ser 0143c7ade8 ci: checkout wlroots 0.16.0 2022-11-11 22:30:10 +01:00
36 changed files with 483 additions and 322 deletions

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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;

View File

@ -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

View File

@ -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;

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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;
};

View File

@ -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()

View File

@ -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,
)

View File

@ -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");
}

View File

@ -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);
}

View File

@ -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;

View File

@ -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;

211
sway/desktop/launcher.c Normal file
View File

@ -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;
}

View File

@ -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);
}
}

View File

@ -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;

View File

@ -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) {

View File

@ -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);

View File

@ -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.

View File

@ -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],

View File

@ -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) {

View File

@ -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 "";
}

View File

@ -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);
}
}
}

View File

@ -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

View File

@ -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();

View File

@ -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;
}

View File

@ -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",

View File

@ -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;
}

View File

@ -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,

View File

@ -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;
}

View File

@ -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;

View File

@ -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,
],

View File

@ -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);
}