This commit is contained in:
Will McKinnon 2023-01-04 18:07:53 -05:00
commit f934c74e97
123 changed files with 3613 additions and 1357 deletions

View file

@ -1,7 +1,6 @@
# SwayFX: A Beautiful Sway Fork # SwayFX: A Beautiful Sway Fork
![](assets/swayfx_screenshot.jpg) ![swayfx_screenshot](assets/swayfx_screenshot.jpg)
Sway is an incredible window manager, and certainly one of the most well established wayland window managers. However, it is restricted to only include the functionality that existed in i3. This fork ditches the simple wlr_renderer, and replaces it with our fx_renderer, capable of rendering with fancy GLES2 effects. This, along with a couple of minor changes, expands sway's featureset to include the following: Sway is an incredible window manager, and certainly one of the most well established wayland window managers. However, it is restricted to only include the functionality that existed in i3. This fork ditches the simple wlr_renderer, and replaces it with our fx_renderer, capable of rendering with fancy GLES2 effects. This, along with a couple of minor changes, expands sway's featureset to include the following:
+ **Anti-aliased rounded corners, borders, and titlebars** + **Anti-aliased rounded corners, borders, and titlebars**
@ -54,7 +53,7 @@ Install dependencies:
* wlroots * wlroots
* wayland * wayland
* wayland-protocols \* * wayland-protocols \*
* pcre * pcre2
* json-c * json-c
* pango * pango
* cairo * cairo
@ -81,9 +80,9 @@ SwayFX will drop root permissions shortly after startup.
SwayFX would love to receive any new features that you're willing to build! Generally, we'd like to focus on eye-candy type improvements to keep our scope appropriate. If you'd like to build something that you think may be out of that focus, please raise an issue and we can discuss whether or not it will fit within this project. SwayFX would love to receive any new features that you're willing to build! Generally, we'd like to focus on eye-candy type improvements to keep our scope appropriate. If you'd like to build something that you think may be out of that focus, please raise an issue and we can discuss whether or not it will fit within this project.
Here's a quick outline of where most of our changes lie vs the main sway repository: Here's a quick outline of where most of our changes lie vs the main sway repository:
+ `sway/desktop/render.c`: the file that handles calling `fx_renderer` to render to the screen, handles damage tracking and scaling + `sway/desktop/render.c`: the file that handles calling `fx_renderer` to render to the screen, handles damage tracking and scaling
+ `sway/desktop/fx_renderer.c`: the meat and potatoes of this project, structured as similarly to wlr_renderer as possible + `sway/desktop/fx_renderer.c`: the meat and potatoes of this project, structured as similarly to wlr_renderer as possible
+ `sway/desktop/shaders`: where all of the shaders that fx_renderer uses live + `sway/desktop/shaders`: where all of the shaders that fx_renderer uses live
Please join our (for the time being very small) Discord server for development chatter! https://discord.gg/jVjjFdcN Please join our (for the time being very small) Discord server for development chatter! https://discord.gg/jVjjFdcN

View file

@ -23,9 +23,9 @@ depends=(
"libxcb" "libxcb"
"libxkbcommon.so" "libxkbcommon.so"
"pango" "pango"
"pcre" "pcre2"
"ttf-font" "ttf-font"
"wlroots<0.16" "wlroots<0.17"
) )
optdepends=( optdepends=(
"alacritty: Terminal emulator used by the default config" "alacritty: Terminal emulator used by the default config"

View file

@ -23,9 +23,9 @@ depends=(
"libxcb" "libxcb"
"libxkbcommon.so" "libxkbcommon.so"
"pango" "pango"
"pcre" "pcre2"
"ttf-font" "ttf-font"
"wlroots<0.16" "wlroots<0.17"
) )
optdepends=( optdepends=(
"alacritty: Terminal emulator used by the default config" "alacritty: Terminal emulator used by the default config"

View file

@ -2,7 +2,7 @@
### CHANGE THESE VARIABLES BEFORE RELEASE: ### CHANGE THESE VARIABLES BEFORE RELEASE:
# Change to current Sway base version! # Change to current Sway base version!
%global SwayBaseVersion 1.7 %global SwayBaseVersion 1.8
# Change to current SwayFX tag! # Change to current SwayFX tag!
%global Tag 0.1.1 %global Tag 0.1.1
@ -23,8 +23,8 @@ BuildRequires: pkgconfig(gdk-pixbuf-2.0)
BuildRequires: pkgconfig(json-c) >= 0.13 BuildRequires: pkgconfig(json-c) >= 0.13
BuildRequires: pkgconfig(libdrm) BuildRequires: pkgconfig(libdrm)
BuildRequires: pkgconfig(libevdev) BuildRequires: pkgconfig(libevdev)
BuildRequires: pkgconfig(libinput) >= 1.6.0 BuildRequires: pkgconfig(libinput) >= 1.21.0
BuildRequires: pkgconfig(libpcre) BuildRequires: pkgconfig(libpcre2)
BuildRequires: pkgconfig(libsystemd) >= 239 BuildRequires: pkgconfig(libsystemd) >= 239
BuildRequires: pkgconfig(libudev) BuildRequires: pkgconfig(libudev)
BuildRequires: pkgconfig(pango) BuildRequires: pkgconfig(pango)
@ -35,7 +35,7 @@ BuildRequires: pkgconfig(wayland-cursor)
BuildRequires: pkgconfig(wayland-egl) BuildRequires: pkgconfig(wayland-egl)
BuildRequires: pkgconfig(wayland-server) >= 1.20.0 BuildRequires: pkgconfig(wayland-server) >= 1.20.0
BuildRequires: pkgconfig(wayland-protocols) >= 1.24 BuildRequires: pkgconfig(wayland-protocols) >= 1.24
BuildRequires: (pkgconfig(wlroots) >= 0.15.0 with pkgconfig(wlroots) < 0.16) BuildRequires: (pkgconfig(wlroots) >= 0.16.0 with pkgconfig(wlroots) < 0.17)
BuildRequires: pkgconfig(xcb) BuildRequires: pkgconfig(xcb)
BuildRequires: pkgconfig(xkbcommon) BuildRequires: pkgconfig(xkbcommon)
# Dmenu is the default launcher in sway # Dmenu is the default launcher in sway

View file

@ -1,50 +1,43 @@
#define _POSIX_C_SOURCE 200809 #define _POSIX_C_SOURCE 200809
#include <assert.h> #include <assert.h>
#include <cairo.h> #include <cairo.h>
#include <errno.h>
#include <fcntl.h> #include <fcntl.h>
#include <pango/pangocairo.h> #include <pango/pangocairo.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <sys/mman.h> #include <sys/mman.h>
#include <time.h>
#include <unistd.h> #include <unistd.h>
#include <wayland-client.h> #include <wayland-client.h>
#include "config.h" #include "config.h"
#include "pool-buffer.h" #include "pool-buffer.h"
#include "util.h" #include "util.h"
static int create_pool_file(size_t size, char **name) { static int anonymous_shm_open(void) {
static const char template[] = "sway-client-XXXXXX"; int retries = 100;
const char *path = getenv("XDG_RUNTIME_DIR");
if (path == NULL) {
fprintf(stderr, "XDG_RUNTIME_DIR is not set\n");
return -1;
}
size_t name_size = strlen(template) + 1 + strlen(path) + 1; do {
*name = malloc(name_size); // try a probably-unique name
if (*name == NULL) { struct timespec ts;
fprintf(stderr, "allocation failed\n"); clock_gettime(CLOCK_MONOTONIC, &ts);
return -1; pid_t pid = getpid();
} char name[50];
snprintf(*name, name_size, "%s/%s", path, template); snprintf(name, sizeof(name), "/sway-%x-%x",
(unsigned int)pid, (unsigned int)ts.tv_nsec);
int fd = mkstemp(*name); // shm_open guarantees that O_CLOEXEC is set
if (fd < 0) { int fd = shm_open(name, O_RDWR | O_CREAT | O_EXCL, 0600);
return -1; if (fd >= 0) {
} shm_unlink(name);
return fd;
}
if (!sway_set_cloexec(fd, true)) { --retries;
close(fd); } while (retries > 0 && errno == EEXIST);
return -1;
}
if (ftruncate(fd, size) < 0) { return -1;
close(fd);
return -1;
}
return fd;
} }
static void buffer_release(void *data, struct wl_buffer *wl_buffer) { 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; uint32_t stride = width * 4;
size_t size = stride * height; size_t size = stride * height;
char *name; int fd = anonymous_shm_open();
int fd = create_pool_file(size, &name); if (fd == -1) {
assert(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); 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); struct wl_shm_pool *pool = wl_shm_create_pool(shm, fd, size);
buf->buffer = wl_shm_pool_create_buffer(pool, 0, buf->buffer = wl_shm_pool_create_buffer(pool, 0,
width, height, stride, format); width, height, stride, format);
wl_shm_pool_destroy(pool); wl_shm_pool_destroy(pool);
close(fd); close(fd);
unlink(name);
free(name);
buf->size = size; buf->size = size;
buf->width = width; buf->width = width;

350
common/gesture.c Normal file
View file

@ -0,0 +1,350 @@
#define _POSIX_C_SOURCE 200809L
#include "gesture.h"
#include <math.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "list.h"
#include "log.h"
#include "stringop.h"
const uint8_t GESTURE_FINGERS_ANY = 0;
// Helper to easily allocate and format string
static char *strformat(const char *format, ...) {
va_list args;
va_start(args, format);
int length = vsnprintf(NULL, 0, format, args) + 1;
va_end(args);
char *result = malloc(length);
if (result) {
va_start(args, format);
vsnprintf(result, length, format, args);
va_end(args);
}
return result;
}
char *gesture_parse(const char *input, struct gesture *output) {
// Clear output in case of failure
output->type = GESTURE_TYPE_NONE;
output->fingers = GESTURE_FINGERS_ANY;
output->directions = GESTURE_DIRECTION_NONE;
// Split input type, fingers and directions
list_t *split = split_string(input, ":");
if (split->length < 1 || split->length > 3) {
return strformat(
"expected <gesture>[:<fingers>][:direction], got %s",
input);
}
// Parse gesture type
if (strcmp(split->items[0], "hold") == 0) {
output->type = GESTURE_TYPE_HOLD;
} else if (strcmp(split->items[0], "pinch") == 0) {
output->type = GESTURE_TYPE_PINCH;
} else if (strcmp(split->items[0], "swipe") == 0) {
output->type = GESTURE_TYPE_SWIPE;
} else {
return strformat("expected hold|pinch|swipe, got %s",
split->items[0]);
}
// Parse optional arguments
if (split->length > 1) {
char *next = split->items[1];
// Try to parse as finger count (1-9)
if (strlen(next) == 1 && '1' <= next[0] && next[0] <= '9') {
output->fingers = atoi(next);
// Move to next if available
next = split->length == 3 ? split->items[2] : NULL;
} else if (split->length == 3) {
// Fail here if argument can only be finger count
return strformat("expected 1-9, got %s", next);
}
// If there is an argument left, try to parse as direction
if (next) {
list_t *directions = split_string(next, "+");
for (int i = 0; i < directions->length; ++i) {
const char *item = directions->items[i];
if (strcmp(item, "any") == 0) {
continue;
} else if (strcmp(item, "up") == 0) {
output->directions |= GESTURE_DIRECTION_UP;
} else if (strcmp(item, "down") == 0) {
output->directions |= GESTURE_DIRECTION_DOWN;
} else if (strcmp(item, "left") == 0) {
output->directions |= GESTURE_DIRECTION_LEFT;
} else if (strcmp(item, "right") == 0) {
output->directions |= GESTURE_DIRECTION_RIGHT;
} else if (strcmp(item, "inward") == 0) {
output->directions |= GESTURE_DIRECTION_INWARD;
} else if (strcmp(item, "outward") == 0) {
output->directions |= GESTURE_DIRECTION_OUTWARD;
} else if (strcmp(item, "clockwise") == 0) {
output->directions |= GESTURE_DIRECTION_CLOCKWISE;
} else if (strcmp(item, "counterclockwise") == 0) {
output->directions |= GESTURE_DIRECTION_COUNTERCLOCKWISE;
} else {
return strformat("expected direction, got %s", item);
}
}
list_free_items_and_destroy(directions);
}
} // if optional args
list_free_items_and_destroy(split);
return NULL;
}
const char *gesture_type_string(enum gesture_type type) {
switch (type) {
case GESTURE_TYPE_NONE:
return "none";
case GESTURE_TYPE_HOLD:
return "hold";
case GESTURE_TYPE_PINCH:
return "pinch";
case GESTURE_TYPE_SWIPE:
return "swipe";
}
return NULL;
}
const char *gesture_direction_string(enum gesture_direction direction) {
switch (direction) {
case GESTURE_DIRECTION_NONE:
return "none";
case GESTURE_DIRECTION_UP:
return "up";
case GESTURE_DIRECTION_DOWN:
return "down";
case GESTURE_DIRECTION_LEFT:
return "left";
case GESTURE_DIRECTION_RIGHT:
return "right";
case GESTURE_DIRECTION_INWARD:
return "inward";
case GESTURE_DIRECTION_OUTWARD:
return "outward";
case GESTURE_DIRECTION_CLOCKWISE:
return "clockwise";
case GESTURE_DIRECTION_COUNTERCLOCKWISE:
return "counterclockwise";
}
return NULL;
}
// Helper to turn combination of directions flags into string representation.
static char *gesture_directions_to_string(uint32_t directions) {
char *result = NULL;
for (uint8_t bit = 0; bit < 32; bit++) {
uint32_t masked = directions & (1 << bit);
if (masked) {
const char *name = gesture_direction_string(masked);
if (!name) {
name = "unknown";
}
if (!result) {
result = strdup(name);
} else {
char *new = strformat("%s+%s", result, name);
free(result);
result = new;
}
}
}
if(!result) {
return strdup("any");
}
return result;
}
char *gesture_to_string(struct gesture *gesture) {
char *directions = gesture_directions_to_string(gesture->directions);
char *result = strformat("%s:%u:%s",
gesture_type_string(gesture->type),
gesture->fingers, directions);
free(directions);
return result;
}
bool gesture_check(struct gesture *target, enum gesture_type type, uint8_t fingers) {
// Check that gesture type matches
if (target->type != type) {
return false;
}
// Check that finger count matches
if (target->fingers != GESTURE_FINGERS_ANY && target->fingers != fingers) {
return false;
}
return true;
}
bool gesture_match(struct gesture *target, struct gesture *to_match, bool exact) {
// Check type and fingers
if (!gesture_check(target, to_match->type, to_match->fingers)) {
return false;
}
// Enforce exact matches ...
if (exact && target->directions != to_match->directions) {
return false;
}
// ... or ensure all target directions are matched
return (target->directions & to_match->directions) == target->directions;
}
bool gesture_equal(struct gesture *a, struct gesture *b) {
return a->type == b->type
&& a->fingers == b->fingers
&& a->directions == b->directions;
}
// Return count of set bits in directions bit field.
static uint8_t gesture_directions_count(uint32_t directions) {
uint8_t count = 0;
for (; directions; directions >>= 1) {
count += directions & 1;
}
return count;
}
// Compare direction bit count of two direction.
static int8_t gesture_directions_compare(uint32_t a, uint32_t b) {
return gesture_directions_count(a) - gesture_directions_count(b);
}
// Compare two direction based on their direction bit count
int8_t gesture_compare(struct gesture *a, struct gesture *b) {
return gesture_directions_compare(a->directions, b->directions);
}
void gesture_tracker_begin(struct gesture_tracker *tracker,
enum gesture_type type, uint8_t fingers) {
tracker->type = type;
tracker->fingers = fingers;
tracker->dx = 0.0;
tracker->dy = 0.0;
tracker->scale = 1.0;
tracker->rotation = 0.0;
sway_log(SWAY_DEBUG, "begin tracking %s:%u:? gesture",
gesture_type_string(type), fingers);
}
bool gesture_tracker_check(struct gesture_tracker *tracker, enum gesture_type type) {
return tracker->type == type;
}
void gesture_tracker_update(struct gesture_tracker *tracker,
double dx, double dy, double scale, double rotation) {
if (tracker->type == GESTURE_TYPE_HOLD) {
sway_assert(false, "hold does not update.");
return;
}
tracker->dx += dx;
tracker->dy += dy;
if (tracker->type == GESTURE_TYPE_PINCH) {
tracker->scale = scale;
tracker->rotation += rotation;
}
sway_log(SWAY_DEBUG, "update tracking %s:%u:? gesture: %f %f %f %f",
gesture_type_string(tracker->type),
tracker->fingers,
tracker->dx, tracker->dy,
tracker->scale, tracker->rotation);
}
void gesture_tracker_cancel(struct gesture_tracker *tracker) {
sway_log(SWAY_DEBUG, "cancel tracking %s:%u:? gesture",
gesture_type_string(tracker->type), tracker->fingers);
tracker->type = GESTURE_TYPE_NONE;
}
struct gesture *gesture_tracker_end(struct gesture_tracker *tracker) {
struct gesture *result = calloc(1, sizeof(struct gesture));
// Ignore gesture under some threshold
// TODO: Make configurable
const double min_rotation = 5;
const double min_scale_delta = 0.1;
// Determine direction
switch(tracker->type) {
// Gestures with scale and rotation
case GESTURE_TYPE_PINCH:
if (tracker->rotation > min_rotation) {
result->directions |= GESTURE_DIRECTION_CLOCKWISE;
}
if (tracker->rotation < -min_rotation) {
result->directions |= GESTURE_DIRECTION_COUNTERCLOCKWISE;
}
if (tracker->scale > (1.0 + min_scale_delta)) {
result->directions |= GESTURE_DIRECTION_OUTWARD;
}
if (tracker->scale < (1.0 - min_scale_delta)) {
result->directions |= GESTURE_DIRECTION_INWARD;
}
__attribute__ ((fallthrough));
// Gestures with dx and dy
case GESTURE_TYPE_SWIPE:
if (fabs(tracker->dx) > fabs(tracker->dy)) {
if (tracker->dx > 0) {
result->directions |= GESTURE_DIRECTION_RIGHT;
} else {
result->directions |= GESTURE_DIRECTION_LEFT;
}
} else {
if (tracker->dy > 0) {
result->directions |= GESTURE_DIRECTION_DOWN;
} else {
result->directions |= GESTURE_DIRECTION_UP;
}
}
// Gesture without any direction
case GESTURE_TYPE_HOLD:
break;
// Not tracking any gesture
case GESTURE_TYPE_NONE:
sway_assert(false, "Not tracking any gesture.");
return result;
}
result->type = tracker->type;
result->fingers = tracker->fingers;
char *description = gesture_to_string(result);
sway_log(SWAY_DEBUG, "end tracking gesture: %s", description);
free(description);
tracker->type = GESTURE_TYPE_NONE;
return result;
}

View file

@ -3,6 +3,7 @@ lib_sway_common = static_library(
files( files(
'background-image.c', 'background-image.c',
'cairo.c', 'cairo.c',
'gesture.c',
'ipc-client.c', 'ipc-client.c',
'log.c', 'log.c',
'loop.c', 'loop.c',

View file

@ -50,7 +50,7 @@ size_t escape_markup_text(const char *src, char *dest) {
return length; return length;
} }
PangoLayout *get_pango_layout(cairo_t *cairo, const char *font, PangoLayout *get_pango_layout(cairo_t *cairo, const PangoFontDescription *desc,
const char *text, double scale, bool markup) { const char *text, double scale, bool markup) {
PangoLayout *layout = pango_cairo_create_layout(cairo); PangoLayout *layout = pango_cairo_create_layout(cairo);
PangoAttrList *attrs; PangoAttrList *attrs;
@ -73,16 +73,14 @@ PangoLayout *get_pango_layout(cairo_t *cairo, const char *font,
} }
pango_attr_list_insert(attrs, pango_attr_scale_new(scale)); pango_attr_list_insert(attrs, pango_attr_scale_new(scale));
PangoFontDescription *desc = pango_font_description_from_string(font);
pango_layout_set_font_description(layout, desc); pango_layout_set_font_description(layout, desc);
pango_layout_set_single_paragraph_mode(layout, 1); pango_layout_set_single_paragraph_mode(layout, 1);
pango_layout_set_attributes(layout, attrs); pango_layout_set_attributes(layout, attrs);
pango_attr_list_unref(attrs); pango_attr_list_unref(attrs);
pango_font_description_free(desc);
return layout; return layout;
} }
void get_text_size(cairo_t *cairo, const char *font, int *width, int *height, void get_text_size(cairo_t *cairo, const PangoFontDescription *desc, int *width, int *height,
int *baseline, double scale, bool markup, const char *fmt, ...) { int *baseline, double scale, bool markup, const char *fmt, ...) {
va_list args; va_list args;
va_start(args, fmt); va_start(args, fmt);
@ -99,7 +97,7 @@ void get_text_size(cairo_t *cairo, const char *font, int *width, int *height,
vsnprintf(buf, length, fmt, args); vsnprintf(buf, length, fmt, args);
va_end(args); va_end(args);
PangoLayout *layout = get_pango_layout(cairo, font, buf, scale, markup); PangoLayout *layout = get_pango_layout(cairo, desc, buf, scale, markup);
pango_cairo_update_layout(cairo, layout); pango_cairo_update_layout(cairo, layout);
pango_layout_get_pixel_size(layout, width, height); pango_layout_get_pixel_size(layout, width, height);
if (baseline) { if (baseline) {
@ -109,10 +107,9 @@ void get_text_size(cairo_t *cairo, const char *font, int *width, int *height,
free(buf); free(buf);
} }
void get_text_metrics(const char *font, int *height, int *baseline) { void get_text_metrics(const PangoFontDescription *description, int *height, int *baseline) {
cairo_t *cairo = cairo_create(NULL); cairo_t *cairo = cairo_create(NULL);
PangoContext *pango = pango_cairo_create_context(cairo); PangoContext *pango = pango_cairo_create_context(cairo);
PangoFontDescription *description = pango_font_description_from_string(font);
// When passing NULL as a language, pango uses the current locale. // When passing NULL as a language, pango uses the current locale.
PangoFontMetrics *metrics = pango_context_get_metrics(pango, description, NULL); PangoFontMetrics *metrics = pango_context_get_metrics(pango, description, NULL);
@ -120,12 +117,11 @@ void get_text_metrics(const char *font, int *height, int *baseline) {
*height = *baseline + pango_font_metrics_get_descent(metrics) / PANGO_SCALE; *height = *baseline + pango_font_metrics_get_descent(metrics) / PANGO_SCALE;
pango_font_metrics_unref(metrics); pango_font_metrics_unref(metrics);
pango_font_description_free(description);
g_object_unref(pango); g_object_unref(pango);
cairo_destroy(cairo); cairo_destroy(cairo);
} }
void render_text(cairo_t *cairo, const char *font, void render_text(cairo_t *cairo, const PangoFontDescription *desc,
double scale, bool markup, const char *fmt, ...) { double scale, bool markup, const char *fmt, ...) {
va_list args; va_list args;
va_start(args, fmt); va_start(args, fmt);
@ -142,7 +138,7 @@ void render_text(cairo_t *cairo, const char *font,
vsnprintf(buf, length, fmt, args); vsnprintf(buf, length, fmt, args);
va_end(args); va_end(args);
PangoLayout *layout = get_pango_layout(cairo, font, buf, scale, markup); PangoLayout *layout = get_pango_layout(cairo, desc, buf, scale, markup);
cairo_font_options_t *fo = cairo_font_options_create(); cairo_font_options_t *fo = cairo_font_options_create();
cairo_get_font_options(cairo, fo); cairo_get_font_options(cairo, fo);
pango_cairo_context_set_font_options(pango_layout_get_context(layout), fo); pango_cairo_context_set_font_options(pango_layout_get_context(layout), fo);

View file

@ -2,7 +2,7 @@
_sway() _sway()
{ {
local cur prev local cur prev short long
_get_comp_words_by_ref cur prev _get_comp_words_by_ref cur prev
short=( short=(

View file

@ -2,7 +2,7 @@
_swaybar() _swaybar()
{ {
local cur prev local cur prev short long
_get_comp_words_by_ref cur prev _get_comp_words_by_ref cur prev
short=( short=(

View file

@ -2,7 +2,7 @@
_swaymsg() _swaymsg()
{ {
local cur prev local cur prev types short long
_get_comp_words_by_ref cur prev _get_comp_words_by_ref cur prev
types=( types=(

View file

@ -50,7 +50,7 @@ output * bg @datadir@/backgrounds/sway/Sway_Wallpaper_Blue_1920x1080.png fill
# #
# exec swayidle -w \ # exec swayidle -w \
# timeout 300 'swaylock -f -c 000000' \ # timeout 300 'swaylock -f -c 000000' \
# timeout 600 'swaymsg "output * dpms off"' resume 'swaymsg "output * dpms on"' \ # timeout 600 'swaymsg "output * power off"' resume 'swaymsg "output * power on"' \
# before-sleep 'swaylock -f -c 000000' # before-sleep 'swaylock -f -c 000000'
# #
# This will lock your screen after 300 seconds of inactivity, then turn off # This will lock your screen after 300 seconds of inactivity, then turn off

View file

@ -13,18 +13,32 @@
## See `man 1 grimshot` or `grimshot usage` for further details. ## See `man 1 grimshot` or `grimshot usage` for further details.
getTargetDirectory() { getTargetDirectory() {
test -f ${XDG_CONFIG_HOME:-~/.config}/user-dirs.dirs && \ test -f "${XDG_CONFIG_HOME:-$HOME/.config}/user-dirs.dirs" && \
. ${XDG_CONFIG_HOME:-~/.config}/user-dirs.dirs . "${XDG_CONFIG_HOME:-$HOME/.config}/user-dirs.dirs"
echo ${XDG_SCREENSHOTS_DIR:-${XDG_PICTURES_DIR:-$HOME}} echo "${XDG_SCREENSHOTS_DIR:-${XDG_PICTURES_DIR:-$HOME}}"
} }
if [ "$1" = "--notify" ]; then NOTIFY=no
NOTIFY=yes CURSOR=
shift 1
else while [ $# -gt 0 ]; do
NOTIFY=no key="$1"
fi
case $key in
-n|--notify)
NOTIFY=yes
shift # past argument
;;
-c|--cursor)
CURSOR=yes
shift # past argument
;;
*) # unknown option
break # done with parsing --flags
;;
esac
done
ACTION=${1:-usage} ACTION=${1:-usage}
SUBJECT=${2:-screen} SUBJECT=${2:-screen}
@ -32,7 +46,7 @@ FILE=${3:-$(getTargetDirectory)/$(date -Ins).png}
if [ "$ACTION" != "save" ] && [ "$ACTION" != "copy" ] && [ "$ACTION" != "check" ]; then if [ "$ACTION" != "save" ] && [ "$ACTION" != "copy" ] && [ "$ACTION" != "check" ]; then
echo "Usage:" echo "Usage:"
echo " grimshot [--notify] (copy|save) [active|screen|output|area|window] [FILE|-]" echo " grimshot [--notify] [--cursor] (copy|save) [active|screen|output|area|window] [FILE|-]"
echo " grimshot check" echo " grimshot check"
echo " grimshot usage" echo " grimshot usage"
echo "" echo ""
@ -67,7 +81,7 @@ notifyError() {
MESSAGE=${1:-"Error taking screenshot with grim"} MESSAGE=${1:-"Error taking screenshot with grim"}
notify -u critical "$TITLE" "$MESSAGE" notify -u critical "$TITLE" "$MESSAGE"
else else
echo $1 echo "$1"
fi fi
} }
@ -91,12 +105,12 @@ takeScreenshot() {
FILE=$1 FILE=$1
GEOM=$2 GEOM=$2
OUTPUT=$3 OUTPUT=$3
if [ ! -z "$OUTPUT" ]; then if [ -n "$OUTPUT" ]; then
grim -o "$OUTPUT" "$FILE" || die "Unable to invoke grim" grim ${CURSOR:+-c} -o "$OUTPUT" "$FILE" || die "Unable to invoke grim"
elif [ -z "$GEOM" ]; then elif [ -z "$GEOM" ]; then
grim "$FILE" || die "Unable to invoke grim" grim ${CURSOR:+-c} "$FILE" || die "Unable to invoke grim"
else else
grim -g "$GEOM" "$FILE" || die "Unable to invoke grim" grim ${CURSOR:+-c} -g "$GEOM" "$FILE" || die "Unable to invoke grim"
fi fi
} }
@ -147,7 +161,7 @@ else
TITLE="Screenshot of $SUBJECT" TITLE="Screenshot of $SUBJECT"
MESSAGE=$(basename "$FILE") MESSAGE=$(basename "$FILE")
notifyOk "$MESSAGE" "$TITLE" notifyOk "$MESSAGE" "$TITLE"
echo $FILE echo "$FILE"
else else
notifyError "Error taking screenshot with grim" notifyError "Error taking screenshot with grim"
fi fi

View file

@ -1,11 +1,11 @@
.\" Generated by scdoc 1.11.1 .\" Generated by scdoc 1.11.2
.\" Complete documentation for this program is not available as a GNU info page .\" Complete documentation for this program is not available as a GNU info page
.ie \n(.g .ds Aq \(aq .ie \n(.g .ds Aq \(aq
.el .ds Aq ' .el .ds Aq '
.nh .nh
.ad l .ad l
.\" Begin generated content: .\" Begin generated content:
.TH "grimshot" "1" "2021-02-23" .TH "grimshot" "1" "2022-03-31"
.P .P
.SH NAME .SH NAME
.P .P
@ -13,7 +13,7 @@ grimshot - a helper for screenshots within sway
.P .P
.SH SYNOPSIS .SH SYNOPSIS
.P .P
\fBgrimshot\fR [--notify] (copy|save) [TARGET] [FILE] \fBgrimshot\fR [--notify] [--cursor] (copy|save) [TARGET] [FILE]
.br .br
\fBgrimshot\fR check \fBgrimshot\fR check
.br .br
@ -26,12 +26,17 @@ grimshot - a helper for screenshots within sway
Show notifications to the user that a screenshot has been taken.\& Show notifications to the user that a screenshot has been taken.\&
.P .P
.RE .RE
\fB--cursor\fR
.RS 4
Include cursors in the screenshot.\&
.P
.RE
\fBsave\fR \fBsave\fR
.RS 4 .RS 4
Save the screenshot into a regular file.\& Grimshot will write images Save the screenshot into a regular file.\& Grimshot will write images
files to \fBXDG_SCREENSHOTS_DIR\fR if this is set (or defined files to \fBXDG_SCREENSHOTS_DIR\fR if this is set (or defined
in \fBuser-dirs.\&dir\fR), or otherwise fall back to \fBXDG_PICTURES_DIR\fR.\& in \fBuser-dirs.\&dir\fR), or otherwise fall back to \fBXDG_PICTURES_DIR\fR.\&
Set FILE to '-' to pipe the output to STDOUT.\& Set FILE to '\&-'\& to pipe the output to STDOUT.\&
.P .P
.RE .RE
\fBcopy\fR \fBcopy\fR

View file

@ -6,7 +6,7 @@ grimshot - a helper for screenshots within sway
# SYNOPSIS # SYNOPSIS
*grimshot* [--notify] (copy|save) [TARGET] [FILE]++ *grimshot* [--notify] [--cursor] (copy|save) [TARGET] [FILE]++
*grimshot* check++ *grimshot* check++
*grimshot* usage *grimshot* usage
@ -15,8 +15,11 @@ grimshot - a helper for screenshots within sway
*--notify* *--notify*
Show notifications to the user that a screenshot has been taken. Show notifications to the user that a screenshot has been taken.
*--cursor*
Include cursors in the screenshot.
*save* *save*
Save the screenshot into a regular file. Grimshot will write images Save the screenshot into a regular file. Grimshot will write image
files to *XDG_SCREENSHOTS_DIR* if this is set (or defined files to *XDG_SCREENSHOTS_DIR* if this is set (or defined
in *user-dirs.dir*), or otherwise fall back to *XDG_PICTURES_DIR*. in *user-dirs.dir*), or otherwise fall back to *XDG_PICTURES_DIR*.
Set FILE to '-' to pipe the output to STDOUT. Set FILE to '-' to pipe the output to STDOUT.

104
include/gesture.h Normal file
View file

@ -0,0 +1,104 @@
#ifndef _SWAY_GESTURE_H
#define _SWAY_GESTURE_H
#include <stdbool.h>
#include <stdint.h>
/**
* A gesture type used in binding.
*/
enum gesture_type {
GESTURE_TYPE_NONE = 0,
GESTURE_TYPE_HOLD,
GESTURE_TYPE_PINCH,
GESTURE_TYPE_SWIPE,
};
// Turns single type enum value to constant string representation.
const char *gesture_type_string(enum gesture_type direction);
// Value to use to accept any finger count
extern const uint8_t GESTURE_FINGERS_ANY;
/**
* A gesture direction used in binding.
*/
enum gesture_direction {
GESTURE_DIRECTION_NONE = 0,
// Directions based on delta x and y
GESTURE_DIRECTION_UP = 1 << 0,
GESTURE_DIRECTION_DOWN = 1 << 1,
GESTURE_DIRECTION_LEFT = 1 << 2,
GESTURE_DIRECTION_RIGHT = 1 << 3,
// Directions based on scale
GESTURE_DIRECTION_INWARD = 1 << 4,
GESTURE_DIRECTION_OUTWARD = 1 << 5,
// Directions based on rotation
GESTURE_DIRECTION_CLOCKWISE = 1 << 6,
GESTURE_DIRECTION_COUNTERCLOCKWISE = 1 << 7,
};
// Turns single direction enum value to constant string representation.
const char *gesture_direction_string(enum gesture_direction direction);
/**
* Struct representing a pointer gesture
*/
struct gesture {
enum gesture_type type;
uint8_t fingers;
uint32_t directions;
};
/**
* Parses gesture from <gesture>[:<fingers>][:<directions>] string.
*
* Return NULL on success, otherwise error message string
*/
char *gesture_parse(const char *input, struct gesture *output);
// Turns gesture into string representation
char *gesture_to_string(struct gesture *gesture);
// Check if gesture is of certain type and finger count.
bool gesture_check(struct gesture *target,
enum gesture_type type, uint8_t fingers);
// Check if a gesture target/binding is match by other gesture/input
bool gesture_match(struct gesture *target,
struct gesture *to_match, bool exact);
// Returns true if gesture are exactly the same
bool gesture_equal(struct gesture *a, struct gesture *b);
// Compare distance between two matched target gestures.
int8_t gesture_compare(struct gesture *a, struct gesture *b);
// Small helper struct to track gestures over time
struct gesture_tracker {
enum gesture_type type;
uint8_t fingers;
double dx, dy;
double scale;
double rotation;
};
// Begin gesture tracking
void gesture_tracker_begin(struct gesture_tracker *tracker,
enum gesture_type type, uint8_t fingers);
// Check if the provides type is currently being tracked
bool gesture_tracker_check(struct gesture_tracker *tracker,
enum gesture_type type);
// Update gesture track with new data point
void gesture_tracker_update(struct gesture_tracker *tracker, double dx,
double dy, double scale, double rotation);
// Reset tracker
void gesture_tracker_cancel(struct gesture_tracker *tracker);
// Reset tracker and return gesture tracked
struct gesture *gesture_tracker_end(struct gesture_tracker *tracker);
#endif

View file

@ -13,12 +13,12 @@
* escaped string to dest if provided. * escaped string to dest if provided.
*/ */
size_t escape_markup_text(const char *src, char *dest); size_t escape_markup_text(const char *src, char *dest);
PangoLayout *get_pango_layout(cairo_t *cairo, const char *font, PangoLayout *get_pango_layout(cairo_t *cairo, const PangoFontDescription *desc,
const char *text, double scale, bool markup); const char *text, double scale, bool markup);
void get_text_size(cairo_t *cairo, const char *font, int *width, int *height, void get_text_size(cairo_t *cairo, const PangoFontDescription *desc, int *width, int *height,
int *baseline, double scale, bool markup, const char *fmt, ...); int *baseline, double scale, bool markup, const char *fmt, ...);
void get_text_metrics(const char *font, int *height, int *baseline); void get_text_metrics(const PangoFontDescription *desc, int *height, int *baseline);
void render_text(cairo_t *cairo, const char *font, void render_text(cairo_t *cairo, PangoFontDescription *desc,
double scale, bool markup, const char *fmt, ...); double scale, bool markup, const char *fmt, ...);
#endif #endif

View file

@ -2,6 +2,7 @@
#define _SWAY_STRINGOP_H #define _SWAY_STRINGOP_H
#include <stdbool.h> #include <stdbool.h>
#include <stddef.h>
#include "list.h" #include "list.h"
void strip_whitespace(char *str); void strip_whitespace(char *str);

View file

@ -106,6 +106,7 @@ sway_cmd cmd_exec_process;
sway_cmd cmd_assign; sway_cmd cmd_assign;
sway_cmd cmd_bar; sway_cmd cmd_bar;
sway_cmd cmd_bindcode; sway_cmd cmd_bindcode;
sway_cmd cmd_bindgesture;
sway_cmd cmd_bindswitch; sway_cmd cmd_bindswitch;
sway_cmd cmd_bindsym; sway_cmd cmd_bindsym;
sway_cmd cmd_border; sway_cmd cmd_border;
@ -199,6 +200,7 @@ sway_cmd cmd_titlebar_border_thickness;
sway_cmd cmd_titlebar_padding; sway_cmd cmd_titlebar_padding;
sway_cmd cmd_unbindcode; sway_cmd cmd_unbindcode;
sway_cmd cmd_unbindswitch; sway_cmd cmd_unbindswitch;
sway_cmd cmd_unbindgesture;
sway_cmd cmd_unbindsym; sway_cmd cmd_unbindsym;
sway_cmd cmd_unmark; sway_cmd cmd_unmark;
sway_cmd cmd_urgent; sway_cmd cmd_urgent;
@ -258,6 +260,7 @@ sway_cmd input_cmd_click_method;
sway_cmd input_cmd_drag; sway_cmd input_cmd_drag;
sway_cmd input_cmd_drag_lock; sway_cmd input_cmd_drag_lock;
sway_cmd input_cmd_dwt; sway_cmd input_cmd_dwt;
sway_cmd input_cmd_dwtp;
sway_cmd input_cmd_events; sway_cmd input_cmd_events;
sway_cmd input_cmd_left_handed; sway_cmd input_cmd_left_handed;
sway_cmd input_cmd_map_from_region; sway_cmd input_cmd_map_from_region;
@ -293,12 +296,14 @@ sway_cmd output_cmd_max_render_time;
sway_cmd output_cmd_mode; sway_cmd output_cmd_mode;
sway_cmd output_cmd_modeline; sway_cmd output_cmd_modeline;
sway_cmd output_cmd_position; sway_cmd output_cmd_position;
sway_cmd output_cmd_power;
sway_cmd output_cmd_render_bit_depth; sway_cmd output_cmd_render_bit_depth;
sway_cmd output_cmd_scale; sway_cmd output_cmd_scale;
sway_cmd output_cmd_scale_filter; sway_cmd output_cmd_scale_filter;
sway_cmd output_cmd_subpixel; sway_cmd output_cmd_subpixel;
sway_cmd output_cmd_toggle; sway_cmd output_cmd_toggle;
sway_cmd output_cmd_transform; sway_cmd output_cmd_transform;
sway_cmd output_cmd_unplug;
sway_cmd seat_cmd_attach; sway_cmd seat_cmd_attach;
sway_cmd seat_cmd_cursor; sway_cmd seat_cmd_cursor;

View file

@ -10,12 +10,14 @@
#include <xkbcommon/xkbcommon.h> #include <xkbcommon/xkbcommon.h>
#include <xf86drmMode.h> #include <xf86drmMode.h>
#include "../include/config.h" #include "../include/config.h"
#include "gesture.h"
#include "list.h" #include "list.h"
#include "swaynag.h" #include "swaynag.h"
#include "tree/container.h" #include "tree/container.h"
#include "sway/input/tablet.h" #include "sway/input/tablet.h"
#include "sway/tree/root.h" #include "sway/tree/root.h"
#include "wlr-layer-shell-unstable-v1-protocol.h" #include "wlr-layer-shell-unstable-v1-protocol.h"
#include <pango/pangocairo.h>
// TODO: Refactor this shit // TODO: Refactor this shit
@ -32,7 +34,8 @@ enum binding_input_type {
BINDING_KEYSYM, BINDING_KEYSYM,
BINDING_MOUSECODE, BINDING_MOUSECODE,
BINDING_MOUSESYM, BINDING_MOUSESYM,
BINDING_SWITCH BINDING_SWITCH, // dummy, only used to call seat_execute_command
BINDING_GESTURE // dummy, only used to call seat_execute_command
}; };
enum binding_flags { enum binding_flags {
@ -45,10 +48,11 @@ enum binding_flags {
BINDING_RELOAD = 1 << 6, // switch only; (re)trigger binding on reload BINDING_RELOAD = 1 << 6, // switch only; (re)trigger binding on reload
BINDING_INHIBITED = 1 << 7, // keyboard only: ignore shortcut inhibitor BINDING_INHIBITED = 1 << 7, // keyboard only: ignore shortcut inhibitor
BINDING_NOREPEAT = 1 << 8, // keyboard only; do not trigger when repeating a held key BINDING_NOREPEAT = 1 << 8, // keyboard only; do not trigger when repeating a held key
BINDING_EXACT = 1 << 9, // gesture only; only trigger on exact match
}; };
/** /**
* A key binding and an associated command. * A key (or mouse) binding and an associated command.
*/ */
struct sway_binding { struct sway_binding {
enum binding_input_type type; enum binding_input_type type;
@ -62,12 +66,10 @@ struct sway_binding {
char *command; char *command;
}; };
/** enum sway_switch_trigger {
* A mouse binding and an associated command. SWAY_SWITCH_TRIGGER_OFF,
*/ SWAY_SWITCH_TRIGGER_ON,
struct sway_mouse_binding { SWAY_SWITCH_TRIGGER_TOGGLE,
uint32_t button;
char *command;
}; };
/** /**
@ -75,11 +77,21 @@ struct sway_mouse_binding {
*/ */
struct sway_switch_binding { struct sway_switch_binding {
enum wlr_switch_type type; enum wlr_switch_type type;
enum wlr_switch_state state; enum sway_switch_trigger trigger;
uint32_t flags; uint32_t flags;
char *command; char *command;
}; };
/**
* A gesture binding and an associated command.
*/
struct sway_gesture_binding {
char *input;
uint32_t flags;
struct gesture gesture;
char *command;
};
/** /**
* Focus on window activation. * Focus on window activation.
*/ */
@ -99,6 +111,7 @@ struct sway_mode {
list_t *keycode_bindings; list_t *keycode_bindings;
list_t *mouse_bindings; list_t *mouse_bindings;
list_t *switch_bindings; list_t *switch_bindings;
list_t *gesture_bindings;
bool pango; bool pango;
}; };
@ -137,6 +150,7 @@ struct input_config {
int drag; int drag;
int drag_lock; int drag_lock;
int dwt; int dwt;
int dwtp;
int left_handed; int left_handed;
int middle_emulation; int middle_emulation;
int natural_scroll; int natural_scroll;
@ -234,12 +248,6 @@ struct seat_config {
} xcursor_theme; } xcursor_theme;
}; };
enum config_dpms {
DPMS_IGNORE,
DPMS_ON,
DPMS_OFF,
};
enum scale_filter_mode { enum scale_filter_mode {
SCALE_FILTER_DEFAULT, // the default is currently smart SCALE_FILTER_DEFAULT, // the default is currently smart
SCALE_FILTER_LINEAR, SCALE_FILTER_LINEAR,
@ -261,6 +269,7 @@ enum render_bit_depth {
struct output_config { struct output_config {
char *name; char *name;
int enabled; int enabled;
int power;
int width, height; int width, height;
float refresh_rate; float refresh_rate;
int custom_mode; int custom_mode;
@ -277,7 +286,6 @@ struct output_config {
char *background; char *background;
char *background_option; char *background_option;
char *background_fallback; char *background_fallback;
enum config_dpms dpms_state;
}; };
/** /**
@ -504,7 +512,8 @@ struct sway_config {
char *floating_scroll_right_cmd; char *floating_scroll_right_cmd;
enum sway_container_layout default_orientation; enum sway_container_layout default_orientation;
enum sway_container_layout default_layout; enum sway_container_layout default_layout;
char *font; char *font; // Used for IPC.
PangoFontDescription *font_description; // Used internally for rendering and validating.
int font_height; int font_height;
int font_baseline; int font_baseline;
bool pango_markup; bool pango_markup;
@ -703,6 +712,8 @@ void free_sway_binding(struct sway_binding *sb);
void free_switch_binding(struct sway_switch_binding *binding); void free_switch_binding(struct sway_switch_binding *binding);
void free_gesture_binding(struct sway_gesture_binding *binding);
void seat_execute_command(struct sway_seat *seat, struct sway_binding *binding); void seat_execute_command(struct sway_seat *seat, struct sway_binding *binding);
void load_swaybar(struct bar_config *bar); void load_swaybar(struct bar_config *bar);

View file

@ -1,7 +1,8 @@
#ifndef _SWAY_CRITERIA_H #ifndef _SWAY_CRITERIA_H
#define _SWAY_CRITERIA_H #define _SWAY_CRITERIA_H
#include <pcre.h> #define PCRE2_CODE_UNIT_WIDTH 8
#include <pcre2.h>
#include "config.h" #include "config.h"
#include "list.h" #include "list.h"
#include "tree/view.h" #include "tree/view.h"
@ -15,13 +16,13 @@ enum criteria_type {
}; };
enum pattern_type { enum pattern_type {
PATTERN_PCRE, PATTERN_PCRE2,
PATTERN_FOCUSED, PATTERN_FOCUSED,
}; };
struct pattern { struct pattern {
enum pattern_type match_type; enum pattern_type match_type;
pcre *regex; pcre2_code *regex;
}; };
struct criteria { struct criteria {

View file

@ -1,4 +1,4 @@
#include <wlr/types/wlr_surface.h> #include <wlr/types/wlr_compositor.h>
struct sway_container; struct sway_container;
struct sway_view; struct sway_view;

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

@ -4,7 +4,7 @@
#include <stdint.h> #include <stdint.h>
#include <wlr/types/wlr_pointer_constraints_v1.h> #include <wlr/types/wlr_pointer_constraints_v1.h>
#include <wlr/types/wlr_pointer_gestures_v1.h> #include <wlr/types/wlr_pointer_gestures_v1.h>
#include <wlr/types/wlr_surface.h> #include <wlr/types/wlr_compositor.h>
#include "sway/input/seat.h" #include "sway/input/seat.h"
#include "config.h" #include "config.h"
@ -36,6 +36,8 @@ struct sway_cursor {
bool active_confine_requires_warp; bool active_confine_requires_warp;
struct wlr_pointer_gestures_v1 *pointer_gestures; struct wlr_pointer_gestures_v1 *pointer_gestures;
struct wl_listener hold_begin;
struct wl_listener hold_end;
struct wl_listener pinch_begin; struct wl_listener pinch_begin;
struct wl_listener pinch_update; struct wl_listener pinch_update;
struct wl_listener pinch_end; struct wl_listener pinch_end;
@ -110,7 +112,7 @@ void dispatch_cursor_button(struct sway_cursor *cursor,
enum wlr_button_state state); enum wlr_button_state state);
void dispatch_cursor_axis(struct sway_cursor *cursor, void dispatch_cursor_axis(struct sway_cursor *cursor,
struct wlr_event_pointer_axis *event); struct wlr_pointer_axis_event *event);
void cursor_set_image(struct sway_cursor *cursor, const char *image, void cursor_set_image(struct sway_cursor *cursor, const char *image,
struct wl_client *client); struct wl_client *client);

View file

@ -50,6 +50,7 @@ struct sway_shortcut_state {
struct sway_keyboard { struct sway_keyboard {
struct sway_seat_device *seat_device; struct sway_seat_device *seat_device;
struct wlr_keyboard *wlr;
struct xkb_keymap *keymap; struct xkb_keymap *keymap;
xkb_layout_index_t effective_layout; xkb_layout_index_t effective_layout;

View file

@ -2,7 +2,7 @@
#define _SWAY_INPUT_LIBINPUT_H #define _SWAY_INPUT_LIBINPUT_H
#include "sway/input/input-manager.h" #include "sway/input/input-manager.h"
void sway_input_configure_libinput_device(struct sway_input_device *device); bool sway_input_configure_libinput_device(struct sway_input_device *device);
void sway_input_reset_libinput_device(struct sway_input_device *device); void sway_input_reset_libinput_device(struct sway_input_device *device);

View file

@ -18,7 +18,23 @@ struct sway_seatop_impl {
enum wlr_button_state state); enum wlr_button_state state);
void (*pointer_motion)(struct sway_seat *seat, uint32_t time_msec); void (*pointer_motion)(struct sway_seat *seat, uint32_t time_msec);
void (*pointer_axis)(struct sway_seat *seat, void (*pointer_axis)(struct sway_seat *seat,
struct wlr_event_pointer_axis *event); struct wlr_pointer_axis_event *event);
void (*hold_begin)(struct sway_seat *seat,
struct wlr_pointer_hold_begin_event *event);
void (*hold_end)(struct sway_seat *seat,
struct wlr_pointer_hold_end_event *event);
void (*pinch_begin)(struct sway_seat *seat,
struct wlr_pointer_pinch_begin_event *event);
void (*pinch_update)(struct sway_seat *seat,
struct wlr_pointer_pinch_update_event *event);
void (*pinch_end)(struct sway_seat *seat,
struct wlr_pointer_pinch_end_event *event);
void (*swipe_begin)(struct sway_seat *seat,
struct wlr_pointer_swipe_begin_event *event);
void (*swipe_update)(struct sway_seat *seat,
struct wlr_pointer_swipe_update_event *event);
void (*swipe_end)(struct sway_seat *seat,
struct wlr_pointer_swipe_end_event *event);
void (*rebase)(struct sway_seat *seat, uint32_t time_msec); void (*rebase)(struct sway_seat *seat, uint32_t time_msec);
void (*tablet_tool_motion)(struct sway_seat *seat, void (*tablet_tool_motion)(struct sway_seat *seat,
struct sway_tablet_tool *tool, uint32_t time_msec); struct sway_tablet_tool *tool, uint32_t time_msec);
@ -274,7 +290,7 @@ void seatop_button(struct sway_seat *seat, uint32_t time_msec,
void seatop_pointer_motion(struct sway_seat *seat, uint32_t time_msec); void seatop_pointer_motion(struct sway_seat *seat, uint32_t time_msec);
void seatop_pointer_axis(struct sway_seat *seat, void seatop_pointer_axis(struct sway_seat *seat,
struct wlr_event_pointer_axis *event); struct wlr_pointer_axis_event *event);
void seatop_tablet_tool_tip(struct sway_seat *seat, void seatop_tablet_tool_tip(struct sway_seat *seat,
struct sway_tablet_tool *tool, uint32_t time_msec, struct sway_tablet_tool *tool, uint32_t time_msec,
@ -283,6 +299,25 @@ void seatop_tablet_tool_tip(struct sway_seat *seat,
void seatop_tablet_tool_motion(struct sway_seat *seat, void seatop_tablet_tool_motion(struct sway_seat *seat,
struct sway_tablet_tool *tool, uint32_t time_msec); struct sway_tablet_tool *tool, uint32_t time_msec);
void seatop_hold_begin(struct sway_seat *seat,
struct wlr_pointer_hold_begin_event *event);
void seatop_hold_end(struct sway_seat *seat,
struct wlr_pointer_hold_end_event *event);
void seatop_pinch_begin(struct sway_seat *seat,
struct wlr_pointer_pinch_begin_event *event);
void seatop_pinch_update(struct sway_seat *seat,
struct wlr_pointer_pinch_update_event *event);
void seatop_pinch_end(struct sway_seat *seat,
struct wlr_pointer_pinch_end_event *event);
void seatop_swipe_begin(struct sway_seat *seat,
struct wlr_pointer_swipe_begin_event *event);
void seatop_swipe_update(struct sway_seat *seat,
struct wlr_pointer_swipe_update_event *event);
void seatop_swipe_end(struct sway_seat *seat,
struct wlr_pointer_swipe_end_event *event);
void seatop_rebase(struct sway_seat *seat, uint32_t time_msec); void seatop_rebase(struct sway_seat *seat, uint32_t time_msec);
/** /**

View file

@ -5,6 +5,7 @@
struct sway_switch { struct sway_switch {
struct sway_seat_device *seat_device; struct sway_seat_device *seat_device;
struct wlr_switch *wlr;
enum wlr_switch_state state; enum wlr_switch_state state;
enum wlr_switch_type type; enum wlr_switch_type type;

View file

@ -32,6 +32,7 @@ struct sway_tablet_pad {
struct wl_list link; struct wl_list link;
struct sway_seat_device *seat_device; struct sway_seat_device *seat_device;
struct sway_tablet *tablet; struct sway_tablet *tablet;
struct wlr_tablet_pad *wlr;
struct wlr_tablet_v2_tablet_pad *tablet_v2_pad; struct wlr_tablet_v2_tablet_pad *tablet_v2_pad;
struct wl_listener attach; struct wl_listener attach;

View file

@ -3,7 +3,7 @@
#include <wlr/types/wlr_text_input_v3.h> #include <wlr/types/wlr_text_input_v3.h>
#include <wlr/types/wlr_input_method_v2.h> #include <wlr/types/wlr_input_method_v2.h>
#include <wlr/types/wlr_surface.h> #include <wlr/types/wlr_compositor.h>
#include "sway/input/seat.h" #include "sway/input/seat.h"
/** /**

View file

@ -1,6 +1,7 @@
#ifndef _SWAY_IPC_JSON_H #ifndef _SWAY_IPC_JSON_H
#define _SWAY_IPC_JSON_H #define _SWAY_IPC_JSON_H
#include <json.h> #include <json.h>
#include "sway/output.h"
#include "sway/tree/container.h" #include "sway/tree/container.h"
#include "sway/input/input-manager.h" #include "sway/input/input-manager.h"
@ -9,6 +10,7 @@ json_object *ipc_json_get_version(void);
json_object *ipc_json_get_binding_mode(void); json_object *ipc_json_get_binding_mode(void);
json_object *ipc_json_describe_disabled_output(struct sway_output *o); json_object *ipc_json_describe_disabled_output(struct sway_output *o);
json_object *ipc_json_describe_non_desktop_output(struct sway_output_non_desktop *o);
json_object *ipc_json_describe_node(struct sway_node *node); json_object *ipc_json_describe_node(struct sway_node *node);
json_object *ipc_json_describe_node_recursive(struct sway_node *node); json_object *ipc_json_describe_node_recursive(struct sway_node *node);
json_object *ipc_json_describe_input(struct sway_input_device *device); json_object *ipc_json_describe_input(struct sway_input_device *device);

View file

@ -1,7 +1,7 @@
#ifndef _SWAY_LAYERS_H #ifndef _SWAY_LAYERS_H
#define _SWAY_LAYERS_H #define _SWAY_LAYERS_H
#include <stdbool.h> #include <stdbool.h>
#include <wlr/types/wlr_surface.h> #include <wlr/types/wlr_compositor.h>
#include <wlr/types/wlr_layer_shell_v1.h> #include <wlr/types/wlr_layer_shell_v1.h>
enum layer_parent { enum layer_parent {

View file

@ -3,6 +3,7 @@
#include <time.h> #include <time.h>
#include <unistd.h> #include <unistd.h>
#include <wayland-server-core.h> #include <wayland-server-core.h>
#include <wlr/types/wlr_damage_ring.h>
#include <wlr/types/wlr_output.h> #include <wlr/types/wlr_output.h>
#include "config.h" #include "config.h"
#include "sway/desktop/fx_renderer.h" #include "sway/desktop/fx_renderer.h"
@ -27,13 +28,13 @@ struct sway_output {
struct wlr_box usable_area; struct wlr_box usable_area;
struct timespec last_frame; struct timespec last_frame;
struct wlr_output_damage *damage; struct wlr_damage_ring damage_ring;
int lx, ly; // layout coords int lx, ly; // layout coords
int width, height; // transformed buffer size int width, height; // transformed buffer size
enum wl_output_subpixel detected_subpixel; enum wl_output_subpixel detected_subpixel;
enum scale_filter_mode scale_filter; enum scale_filter_mode scale_filter;
// last applied mode when the output is DPMS'ed // last applied mode when the output is powered off
struct wlr_output_mode *current_mode; struct wlr_output_mode *current_mode;
bool enabling, enabled; bool enabling, enabled;
@ -45,8 +46,9 @@ struct sway_output {
struct wl_listener commit; struct wl_listener commit;
struct wl_listener mode; struct wl_listener mode;
struct wl_listener present; struct wl_listener present;
struct wl_listener damage_destroy; struct wl_listener damage;
struct wl_listener damage_frame; struct wl_listener frame;
struct wl_listener needs_frame;
struct { struct {
struct wl_signal disable; struct wl_signal disable;
@ -58,6 +60,12 @@ struct sway_output {
struct wl_event_source *repaint_timer; struct wl_event_source *repaint_timer;
}; };
struct sway_output_non_desktop {
struct wlr_output *wlr_output;
struct wl_listener destroy;
};
struct sway_output *output_create(struct wlr_output *wlr_output); struct sway_output *output_create(struct wlr_output *wlr_output);
void output_destroy(struct sway_output *output); void output_destroy(struct sway_output *output);
@ -184,4 +192,6 @@ void handle_output_manager_test(struct wl_listener *listener, void *data);
void handle_output_power_manager_set_mode(struct wl_listener *listener, void handle_output_power_manager_set_mode(struct wl_listener *listener,
void *data); void *data);
struct sway_output_non_desktop *output_non_desktop_create(struct wlr_output *wlr_output);
#endif #endif

View file

@ -16,6 +16,7 @@
#include <wlr/types/wlr_output_power_management_v1.h> #include <wlr/types/wlr_output_power_management_v1.h>
#include <wlr/types/wlr_presentation_time.h> #include <wlr/types/wlr_presentation_time.h>
#include <wlr/types/wlr_relative_pointer_v1.h> #include <wlr/types/wlr_relative_pointer_v1.h>
#include <wlr/types/wlr_session_lock_v1.h>
#include <wlr/types/wlr_server_decoration.h> #include <wlr/types/wlr_server_decoration.h>
#include <wlr/types/wlr_text_input_v3.h> #include <wlr/types/wlr_text_input_v3.h>
#include <wlr/types/wlr_xdg_shell.h> #include <wlr/types/wlr_xdg_shell.h>
@ -52,6 +53,7 @@ struct sway_server {
struct wl_listener output_layout_change; struct wl_listener output_layout_change;
struct wlr_idle *idle; struct wlr_idle *idle;
struct wlr_idle_notifier_v1 *idle_notifier_v1;
struct sway_idle_inhibit_manager_v1 *idle_inhibit_manager_v1; struct sway_idle_inhibit_manager_v1 *idle_inhibit_manager_v1;
struct wlr_layer_shell_v1 *layer_shell; struct wlr_layer_shell_v1 *layer_shell;
@ -90,6 +92,20 @@ struct sway_server {
struct wl_listener output_manager_apply; struct wl_listener output_manager_apply;
struct wl_listener output_manager_test; struct wl_listener output_manager_test;
struct {
bool locked;
struct wlr_session_lock_manager_v1 *manager;
struct wlr_session_lock_v1 *lock;
struct wlr_surface *focused;
struct wl_listener lock_new_surface;
struct wl_listener lock_unlock;
struct wl_listener lock_destroy;
struct wl_listener new_lock;
struct wl_listener manager_destroy;
} session_lock;
struct wlr_output_power_manager_v1 *output_power_manager_v1; struct wlr_output_power_manager_v1 *output_power_manager_v1;
struct wl_listener output_power_manager_set_mode; struct wl_listener output_power_manager_set_mode;
struct wlr_input_method_manager_v2 *input_method; struct wlr_input_method_manager_v2 *input_method;
@ -99,6 +115,8 @@ struct sway_server {
struct wlr_xdg_activation_v1 *xdg_activation_v1; struct wlr_xdg_activation_v1 *xdg_activation_v1;
struct wl_listener xdg_activation_v1_request_activate; 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 // The timeout for transactions, after which a transaction is applied
// regardless of readiness. // regardless of readiness.
size_t txn_timeout_ms; size_t txn_timeout_ms;
@ -135,8 +153,6 @@ struct sway_debug {
extern struct sway_debug debug; extern struct sway_debug debug;
/* Prepares an unprivileged server_init by performing all privileged operations in advance */
bool server_privileged_prepare(struct sway_server *server);
bool server_init(struct sway_server *server); bool server_init(struct sway_server *server);
void server_fini(struct sway_server *server); void server_fini(struct sway_server *server);
bool server_start(struct sway_server *server); bool server_start(struct sway_server *server);
@ -149,6 +165,7 @@ void handle_new_output(struct wl_listener *listener, void *data);
void handle_idle_inhibitor_v1(struct wl_listener *listener, void *data); void handle_idle_inhibitor_v1(struct wl_listener *listener, void *data);
void handle_layer_shell_surface(struct wl_listener *listener, void *data); void handle_layer_shell_surface(struct wl_listener *listener, void *data);
void sway_session_lock_init(void);
void handle_xdg_shell_surface(struct wl_listener *listener, void *data); void handle_xdg_shell_surface(struct wl_listener *listener, void *data);
#if HAVE_XWAYLAND #if HAVE_XWAYLAND
void handle_xwayland_surface(struct wl_listener *listener, void *data); void handle_xwayland_surface(struct wl_listener *listener, void *data);
@ -159,4 +176,6 @@ void handle_pointer_constraint(struct wl_listener *listener, void *data);
void xdg_activation_v1_handle_request_activate(struct wl_listener *listener, void xdg_activation_v1_handle_request_activate(struct wl_listener *listener,
void *data); void *data);
void set_rr_scheduling(void);
#endif #endif

View file

@ -1,6 +1,6 @@
#ifndef _SWAY_SURFACE_H #ifndef _SWAY_SURFACE_H
#define _SWAY_SURFACE_H #define _SWAY_SURFACE_H
#include <wlr/types/wlr_surface.h> #include <wlr/types/wlr_compositor.h>
struct sway_surface { struct sway_surface {
struct wlr_surface *wlr_surface; struct wlr_surface *wlr_surface;

View file

@ -2,7 +2,7 @@
#define _SWAY_CONTAINER_H #define _SWAY_CONTAINER_H
#include <stdint.h> #include <stdint.h>
#include <sys/types.h> #include <sys/types.h>
#include <wlr/types/wlr_surface.h> #include <wlr/types/wlr_compositor.h>
#include "list.h" #include "list.h"
#include "sway/tree/node.h" #include "sway/tree/node.h"
@ -370,7 +370,7 @@ bool container_is_sticky_or_child(struct sway_container *con);
* This will destroy pairs of redundant H/V splits * This will destroy pairs of redundant H/V splits
* e.g. H[V[H[app app]] app] -> H[app app app] * e.g. H[V[H[app app]] app] -> H[app app app]
* The middle "V[H[" are eliminated by a call to container_squash * The middle "V[H[" are eliminated by a call to container_squash
* on the V[ con. It's grandchildren are added to it's parent. * on the V[ con. It's grandchildren are added to its parent.
* *
* This function is roughly equivalent to i3's tree_flatten here: * This function is roughly equivalent to i3's tree_flatten here:
* https://github.com/i3/i3/blob/1f0c628cde40cf87371481041b7197344e0417c6/src/tree.c#L651 * https://github.com/i3/i3/blob/1f0c628cde40cf87371481041b7197344e0417c6/src/tree.c#L651

View file

@ -28,6 +28,7 @@ struct sway_root {
double width, height; double width, height;
list_t *outputs; // struct sway_output list_t *outputs; // struct sway_output
list_t *non_desktop_outputs; // struct sway_output_non_desktop
list_t *scratchpad; // struct sway_container list_t *scratchpad; // struct sway_container
// For when there's no connected outputs // For when there's no connected outputs
@ -68,12 +69,6 @@ void root_scratchpad_show(struct sway_container *con);
*/ */
void root_scratchpad_hide(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 root_for_each_workspace(void (*f)(struct sway_workspace *ws, void *data),
void *data); void *data);
@ -91,6 +86,4 @@ struct sway_container *root_find_container(
void root_get_box(struct sway_root *root, struct wlr_box *box); 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 #endif

View file

@ -1,7 +1,7 @@
#ifndef _SWAY_VIEW_H #ifndef _SWAY_VIEW_H
#define _SWAY_VIEW_H #define _SWAY_VIEW_H
#include <wayland-server-core.h> #include <wayland-server-core.h>
#include <wlr/types/wlr_surface.h> #include <wlr/types/wlr_compositor.h>
#include "config.h" #include "config.h"
#if HAVE_XWAYLAND #if HAVE_XWAYLAND
#include <wlr/xwayland.h> #include <wlr/xwayland.h>
@ -74,6 +74,7 @@ struct sway_view {
struct sway_xdg_decoration *xdg_decoration; struct sway_xdg_decoration *xdg_decoration;
pid_t pid; pid_t pid;
struct launcher_ctx *ctx;
// The size the view would want to be if it weren't tiled. // The size the view would want to be if it weren't tiled.
// Used when changing a view from tiled to floating. // Used when changing a view from tiled to floating.
@ -109,7 +110,7 @@ struct sway_view {
list_t *executed_criteria; // struct criteria * list_t *executed_criteria; // struct criteria *
union { union {
struct wlr_xdg_surface *wlr_xdg_surface; struct wlr_xdg_toplevel *wlr_xdg_toplevel;
#if HAVE_XWAYLAND #if HAVE_XWAYLAND
struct wlr_xwayland_surface *wlr_xwayland_surface; struct wlr_xwayland_surface *wlr_xwayland_surface;
#endif #endif
@ -132,6 +133,7 @@ struct sway_xdg_shell_view {
struct wl_listener commit; struct wl_listener commit;
struct wl_listener request_move; struct wl_listener request_move;
struct wl_listener request_resize; struct wl_listener request_resize;
struct wl_listener request_maximize;
struct wl_listener request_fullscreen; struct wl_listener request_fullscreen;
struct wl_listener set_title; struct wl_listener set_title;
struct wl_listener set_app_id; struct wl_listener set_app_id;
@ -155,6 +157,7 @@ struct sway_xwayland_view {
struct wl_listener set_title; struct wl_listener set_title;
struct wl_listener set_class; struct wl_listener set_class;
struct wl_listener set_role; struct wl_listener set_role;
struct wl_listener set_startup_id;
struct wl_listener set_window_type; struct wl_listener set_window_type;
struct wl_listener set_hints; struct wl_listener set_hints;
struct wl_listener set_decorations; struct wl_listener set_decorations;
@ -170,6 +173,7 @@ struct sway_xwayland_unmanaged {
int lx, ly; int lx, ly;
struct wl_listener request_activate;
struct wl_listener request_configure; struct wl_listener request_configure;
struct wl_listener request_fullscreen; struct wl_listener request_fullscreen;
struct wl_listener commit; struct wl_listener commit;
@ -217,7 +221,7 @@ struct sway_subsurface {
struct sway_xdg_popup { struct sway_xdg_popup {
struct sway_view_child child; struct sway_view_child child;
struct wlr_xdg_surface *wlr_xdg_surface; struct wlr_xdg_popup *wlr_xdg_popup;
struct wl_listener new_popup; struct wl_listener new_popup;
struct wl_listener destroy; struct wl_listener destroy;
@ -371,4 +375,6 @@ void view_save_buffer(struct sway_view *view);
bool view_is_transient_for(struct sway_view *child, struct sway_view *ancestor); 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 #endif

View file

@ -58,7 +58,6 @@ struct swaybar_output {
struct zxdg_output_v1 *xdg_output; struct zxdg_output_v1 *xdg_output;
struct wl_surface *surface; struct wl_surface *surface;
struct zwlr_layer_surface_v1 *layer_surface; struct zwlr_layer_surface_v1 *layer_surface;
struct wl_region *input_region;
uint32_t wl_name; uint32_t wl_name;
struct wl_list workspaces; // swaybar_workspace::link struct wl_list workspaces; // swaybar_workspace::link

View file

@ -6,6 +6,7 @@
#include "../include/config.h" #include "../include/config.h"
#include "list.h" #include "list.h"
#include "util.h" #include "util.h"
#include <pango/pangocairo.h>
struct box_colors { struct box_colors {
uint32_t border; uint32_t border;
@ -28,7 +29,7 @@ struct swaybar_config {
char *status_command; char *status_command;
bool pango_markup; bool pango_markup;
uint32_t position; // zwlr_layer_surface_v1_anchor uint32_t position; // zwlr_layer_surface_v1_anchor
char *font; PangoFontDescription *font_description;
char *sep_symbol; char *sep_symbol;
char *mode; char *mode;
char *hidden_state; char *hidden_state;

View file

@ -30,6 +30,6 @@ void i3bar_block_unref(struct i3bar_block *block);
bool i3bar_handle_readable(struct status_line *status); bool i3bar_handle_readable(struct status_line *status);
enum hotspot_event_handling i3bar_block_send_click(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, 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 #endif

View file

@ -49,7 +49,7 @@ struct swaybar_hotspot {
int x, y, width, height; int x, y, width, height;
enum hotspot_event_handling (*callback)(struct swaybar_output *output, enum hotspot_event_handling (*callback)(struct swaybar_output *output,
struct swaybar_hotspot *hotspot, double x, double y, uint32_t button, struct swaybar_hotspot *hotspot, double x, double y, uint32_t button,
void *data); bool released, void *data);
void (*destroy)(void *data); void (*destroy)(void *data);
void *data; void *data;
}; };

View file

@ -67,7 +67,7 @@ struct swaynag_details {
int offset; int offset;
int visible_lines; int visible_lines;
int total_lines; int total_lines;
struct swaynag_button *button_details; struct swaynag_button button_details;
struct swaynag_button button_up; struct swaynag_button button_up;
struct swaynag_button button_down; struct swaynag_button button_down;
}; };

View file

@ -4,7 +4,8 @@
struct swaynag_type { struct swaynag_type {
char *name; char *name;
char *font; char *font; // Used for debugging.
PangoFontDescription *font_description;
char *output; char *output;
uint32_t anchors; uint32_t anchors;
int32_t layer; // enum zwlr_layer_shell_v1_layer or -1 if unset int32_t layer; // enum zwlr_layer_shell_v1_layer or -1 if unset

View file

@ -35,14 +35,24 @@ if is_freebsd
add_project_arguments('-D_C11_SOURCE', language: 'c') add_project_arguments('-D_C11_SOURCE', language: 'c')
endif endif
# Execute the wlroots subproject, if any
wlroots_version = ['>=0.16.0', '<0.17.0']
subproject(
'wlroots',
default_options: ['examples=false'],
required: false,
version: wlroots_version,
)
jsonc = dependency('json-c', version: '>=0.13') jsonc = dependency('json-c', version: '>=0.13')
pcre = dependency('libpcre') pcre2 = dependency('libpcre2-8')
wayland_server = dependency('wayland-server', version: '>=1.20.0') wayland_server = dependency('wayland-server', version: '>=1.21.0')
wayland_client = dependency('wayland-client') wayland_client = dependency('wayland-client')
wayland_cursor = dependency('wayland-cursor') wayland_cursor = dependency('wayland-cursor')
wayland_egl = dependency('wayland-egl') wayland_egl = dependency('wayland-egl')
egl = dependency('egl') egl = dependency('egl')
wayland_protos = dependency('wayland-protocols', version: '>=1.24') wayland_protos = dependency('wayland-protocols', version: '>=1.24')
wlroots = dependency('wlroots', version: wlroots_version)
xkbcommon = dependency('xkbcommon') xkbcommon = dependency('xkbcommon')
cairo = dependency('cairo') cairo = dependency('cairo')
pango = dependency('pango') pango = dependency('pango')
@ -51,7 +61,7 @@ gdk_pixbuf = dependency('gdk-pixbuf-2.0', required: get_option('gdk-pixbuf'))
pixman = dependency('pixman-1') pixman = dependency('pixman-1')
glesv2 = dependency('glesv2') glesv2 = dependency('glesv2')
libevdev = dependency('libevdev') libevdev = dependency('libevdev')
libinput = dependency('libinput', version: '>=1.6.0') libinput = dependency('libinput', version: '>=1.21.0')
xcb = dependency('xcb', required: get_option('xwayland')) xcb = dependency('xcb', required: get_option('xwayland'))
drm_full = dependency('libdrm') # only needed for drm_fourcc.h drm_full = dependency('libdrm') # only needed for drm_fourcc.h
drm = drm_full.partial_dependency(compile_args: true, includes: true) drm = drm_full.partial_dependency(compile_args: true, includes: true)
@ -60,20 +70,8 @@ bash_comp = dependency('bash-completion', required: false)
fish_comp = dependency('fish', required: false) fish_comp = dependency('fish', required: false)
math = cc.find_library('m') math = cc.find_library('m')
rt = cc.find_library('rt') rt = cc.find_library('rt')
xcb_icccm = dependency('xcb-icccm', required: get_option('xwayland'))
# Try first to find wlroots as a subproject, then as a system dependency threads = dependency('threads') # for pthread_setschedparam
wlroots_version = ['>=0.15.0', '<0.16.0']
wlroots_proj = subproject(
'wlroots',
default_options: ['examples=false'],
required: false,
version: wlroots_version,
)
if wlroots_proj.found()
wlroots = wlroots_proj.get_variable('wlroots')
else
wlroots = dependency('wlroots', version: wlroots_version)
endif
wlroots_features = { wlroots_features = {
'xwayland': false, 'xwayland': false,
@ -87,7 +85,7 @@ endforeach
if get_option('xwayland').enabled() and not wlroots_features['xwayland'] if get_option('xwayland').enabled() and not wlroots_features['xwayland']
error('Cannot enable Xwayland in sway: wlroots has been built without Xwayland support') error('Cannot enable Xwayland in sway: wlroots has been built without Xwayland support')
endif 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 get_option('sd-bus-provider') == 'auto'
if not get_option('tray').disabled() if not get_option('tray').disabled()

View file

@ -1,81 +1,42 @@
wl_protocol_dir = wayland_protos.get_variable('pkgdatadir') wl_protocol_dir = wayland_protos.get_variable('pkgdatadir')
wayland_scanner_dep = dependency('wayland-scanner', required: false, native: true) wayland_scanner_dep = dependency('wayland-scanner', native: true)
if wayland_scanner_dep.found() wayland_scanner = find_program(
wayland_scanner = find_program( wayland_scanner_dep.get_variable(pkgconfig: 'wayland_scanner'),
wayland_scanner_dep.get_variable(pkgconfig: 'wayland_scanner'), native: true,
native: true, )
)
else
wayland_scanner = find_program('wayland-scanner', native: true)
endif
protocols = [ protocols = [
[wl_protocol_dir, 'stable/xdg-shell/xdg-shell.xml'], wl_protocol_dir / 'stable/xdg-shell/xdg-shell.xml',
[wl_protocol_dir, 'unstable/xdg-output/xdg-output-unstable-v1.xml'], wl_protocol_dir / 'unstable/xdg-output/xdg-output-unstable-v1.xml',
[wl_protocol_dir, 'unstable/pointer-constraints/pointer-constraints-unstable-v1.xml'], wl_protocol_dir / 'unstable/pointer-constraints/pointer-constraints-unstable-v1.xml',
[wl_protocol_dir, 'unstable/tablet/tablet-unstable-v2.xml'], wl_protocol_dir / 'unstable/tablet/tablet-unstable-v2.xml',
[wl_protocol_dir, 'unstable/linux-dmabuf/linux-dmabuf-unstable-v1.xml'], wl_protocol_dir / 'unstable/linux-dmabuf/linux-dmabuf-unstable-v1.xml',
['wlr-layer-shell-unstable-v1.xml'], 'wlr-layer-shell-unstable-v1.xml',
['idle.xml'], 'idle.xml',
['wlr-input-inhibitor-unstable-v1.xml'], 'wlr-input-inhibitor-unstable-v1.xml',
['wlr-output-power-management-unstable-v1.xml'], '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_src = []
wl_protos_headers = []
foreach p : protocols foreach xml : protocols
xml = join_paths(p)
wl_protos_src += custom_target( wl_protos_src += custom_target(
xml.underscorify() + '_server_c', xml.underscorify() + '_c',
input: xml, input: xml,
output: '@BASENAME@-protocol.c', output: '@BASENAME@-protocol.c',
command: [wayland_scanner, 'private-code', '@INPUT@', '@OUTPUT@'], command: [wayland_scanner, 'private-code', '@INPUT@', '@OUTPUT@'],
) )
wl_protos_headers += custom_target( wl_protos_src += custom_target(
xml.underscorify() + '_server_h', xml.underscorify() + '_server_h',
input: xml, input: xml,
output: '@BASENAME@-protocol.h', output: '@BASENAME@-protocol.h',
command: [wayland_scanner, 'server-header', '@INPUT@', '@OUTPUT@'], command: [wayland_scanner, 'server-header', '@INPUT@', '@OUTPUT@'],
) )
endforeach wl_protos_src += custom_target(
foreach p : client_protocols
xml = join_paths(p)
wl_protos_headers += custom_target(
xml.underscorify() + '_client_h', xml.underscorify() + '_client_h',
input: xml, input: xml,
output: '@BASENAME@-client-protocol.h', output: '@BASENAME@-client-protocol.h',
command: [wayland_scanner, 'client-header', '@INPUT@', '@OUTPUT@'], command: [wayland_scanner, 'client-header', '@INPUT@', '@OUTPUT@'],
) )
endforeach 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

@ -1,5 +1,5 @@
[Desktop Entry] [Desktop Entry]
Name=Sway Name=SwayFX
Comment=An i3-compatible Wayland compositor Comment=An i3-compatible Wayland compositor
Exec=sway Exec=sway
Type=Application Type=Application

View file

@ -46,6 +46,7 @@ static const struct cmd_handler handlers[] = {
{ "assign", cmd_assign }, { "assign", cmd_assign },
{ "bar", cmd_bar }, { "bar", cmd_bar },
{ "bindcode", cmd_bindcode }, { "bindcode", cmd_bindcode },
{ "bindgesture", cmd_bindgesture },
{ "bindswitch", cmd_bindswitch }, { "bindswitch", cmd_bindswitch },
{ "bindsym", cmd_bindsym }, { "bindsym", cmd_bindsym },
{ "client.background", cmd_client_noop }, { "client.background", cmd_client_noop },
@ -99,6 +100,7 @@ static const struct cmd_handler handlers[] = {
{ "titlebar_border_thickness", cmd_titlebar_border_thickness }, { "titlebar_border_thickness", cmd_titlebar_border_thickness },
{ "titlebar_padding", cmd_titlebar_padding }, { "titlebar_padding", cmd_titlebar_padding },
{ "unbindcode", cmd_unbindcode }, { "unbindcode", cmd_unbindcode },
{ "unbindgesture", cmd_unbindgesture },
{ "unbindswitch", cmd_unbindswitch }, { "unbindswitch", cmd_unbindswitch },
{ "unbindsym", cmd_unbindsym }, { "unbindsym", cmd_unbindsym },
{ "workspace", cmd_workspace }, { "workspace", cmd_workspace },
@ -415,6 +417,7 @@ struct cmd_results *config_command(char *exec, char **new_block) {
&& handler->handle != cmd_bindsym && handler->handle != cmd_bindsym
&& handler->handle != cmd_bindcode && handler->handle != cmd_bindcode
&& handler->handle != cmd_bindswitch && handler->handle != cmd_bindswitch
&& handler->handle != cmd_bindgesture
&& handler->handle != cmd_set && handler->handle != cmd_set
&& handler->handle != cmd_for_window && handler->handle != cmd_for_window
&& (*argv[i] == '\"' || *argv[i] == '\'')) { && (*argv[i] == '\"' || *argv[i] == '\'')) {
@ -490,13 +493,19 @@ struct cmd_results *cmd_results_new(enum cmd_status status,
} }
results->status = status; results->status = status;
if (format) { if (format) {
char *error = malloc(256); char *error = NULL;
va_list args; va_list args;
va_start(args, format); va_start(args, format);
if (error) { int slen = vsnprintf(NULL, 0, format, args);
vsnprintf(error, 256, format, args);
}
va_end(args); va_end(args);
if (slen > 0) {
error = malloc(slen + 1);
if (error != NULL) {
va_start(args, format);
vsnprintf(error, slen + 1, format, args);
va_end(args);
}
}
results->error = error; results->error = error;
} else { } else {
results->error = NULL; results->error = NULL;

View file

@ -47,7 +47,7 @@ static bool binding_switch_compare(struct sway_switch_binding *binding_a,
if (binding_a->type != binding_b->type) { if (binding_a->type != binding_b->type) {
return false; return false;
} }
if (binding_a->state != binding_b->state) { if (binding_a->trigger != binding_b->trigger) {
return false; return false;
} }
if ((binding_a->flags & BINDING_LOCKED) != if ((binding_a->flags & BINDING_LOCKED) !=
@ -372,6 +372,7 @@ static struct cmd_results *cmd_bindsym_or_bindcode(int argc, char **argv,
strlen("--input-device=")) == 0) { strlen("--input-device=")) == 0) {
free(binding->input); free(binding->input);
binding->input = strdup(argv[0] + strlen("--input-device=")); binding->input = strdup(argv[0] + strlen("--input-device="));
strip_quotes(binding->input);
} else if (strcmp("--no-warn", argv[0]) == 0) { } else if (strcmp("--no-warn", argv[0]) == 0) {
warn = false; warn = false;
} else if (strcmp("--no-repeat", argv[0]) == 0) { } else if (strcmp("--no-repeat", argv[0]) == 0) {
@ -551,11 +552,11 @@ struct cmd_results *cmd_bind_or_unbind_switch(int argc, char **argv,
"unknown switch %s)", bindtype, split->items[0]); "unknown switch %s)", bindtype, split->items[0]);
} }
if (strcmp(split->items[1], "on") == 0) { if (strcmp(split->items[1], "on") == 0) {
binding->state = WLR_SWITCH_STATE_ON; binding->trigger = SWAY_SWITCH_TRIGGER_ON;
} else if (strcmp(split->items[1], "off") == 0) { } else if (strcmp(split->items[1], "off") == 0) {
binding->state = WLR_SWITCH_STATE_OFF; binding->trigger = SWAY_SWITCH_TRIGGER_OFF;
} else if (strcmp(split->items[1], "toggle") == 0) { } else if (strcmp(split->items[1], "toggle") == 0) {
binding->state = WLR_SWITCH_STATE_TOGGLE; binding->trigger = SWAY_SWITCH_TRIGGER_TOGGLE;
} else { } else {
free_switch_binding(binding); free_switch_binding(binding);
return cmd_results_new(CMD_FAILURE, return cmd_results_new(CMD_FAILURE,

View file

@ -8,6 +8,8 @@
#include "sway/commands.h" #include "sway/commands.h"
#include "sway/config.h" #include "sway/config.h"
#include "sway/server.h" #include "sway/server.h"
#include "sway/desktop/launcher.h"
#include "sway/server.h"
#include "sway/tree/container.h" #include "sway/tree/container.h"
#include "sway/tree/root.h" #include "sway/tree/root.h"
#include "sway/tree/workspace.h" #include "sway/tree/workspace.h"
@ -25,11 +27,22 @@ struct cmd_results *cmd_exec_validate(int argc, char **argv) {
return error; 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 *cmd_exec_process(int argc, char **argv) {
struct cmd_results *error = NULL; struct cmd_results *error = NULL;
char *cmd = NULL; char *cmd = NULL;
bool no_startup_id = false;
if (strcmp(argv[0], "--no-startup-id") == 0) { 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; --argc; ++argv;
if ((error = checkarg(argc, argv[-1], EXPECTED_AT_LEAST, 1))) { if ((error = checkarg(argc, argv[-1], EXPECTED_AT_LEAST, 1))) {
return error; return error;
@ -51,6 +64,7 @@ struct cmd_results *cmd_exec_process(int argc, char **argv) {
} }
pid_t pid, child; pid_t pid, child;
struct launcher_ctx *ctx = launcher_ctx_create();
// Fork process // Fork process
if ((pid = fork()) == 0) { if ((pid = fork()) == 0) {
// Fork child process again // Fork child process again
@ -63,6 +77,12 @@ struct cmd_results *cmd_exec_process(int argc, char **argv) {
close(fd[0]); close(fd[0]);
if ((child = fork()) == 0) { if ((child = fork()) == 0) {
close(fd[1]); 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); execlp("sh", "sh", "-c", cmd, (void *)NULL);
sway_log_errno(SWAY_ERROR, "execlp failed"); sway_log_errno(SWAY_ERROR, "execlp failed");
_exit(1); _exit(1);
@ -90,8 +110,12 @@ struct cmd_results *cmd_exec_process(int argc, char **argv) {
waitpid(pid, NULL, 0); waitpid(pid, NULL, 0);
if (child > 0) { if (child > 0) {
sway_log(SWAY_DEBUG, "Child process created with pid %d", child); 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 { } else {
launcher_ctx_destroy(ctx);
return cmd_results_new(CMD_FAILURE, "Second fork() failed"); return cmd_results_new(CMD_FAILURE, "Second fork() failed");
} }

View file

@ -54,7 +54,7 @@ static bool get_direction_from_next_prev(struct sway_container *container,
} else { } else {
return false; return false;
} }
return true; return true;
} }
@ -285,7 +285,7 @@ static struct cmd_results *focus_mode(struct sway_workspace *ws,
} }
} else { } else {
return cmd_results_new(CMD_FAILURE, return cmd_results_new(CMD_FAILURE,
"Failed to find a %s container in workspace", "Failed to find a %s container in workspace.",
floating ? "floating" : "tiling"); floating ? "floating" : "tiling");
} }
return cmd_results_new(CMD_SUCCESS, NULL); return cmd_results_new(CMD_SUCCESS, NULL);
@ -295,7 +295,7 @@ static struct cmd_results *focus_output(struct sway_seat *seat,
int argc, char **argv) { int argc, char **argv) {
if (!argc) { if (!argc) {
return cmd_results_new(CMD_INVALID, return cmd_results_new(CMD_INVALID,
"Expected 'focus output <direction|name>'"); "Expected 'focus output <direction|name>'.");
} }
char *identifier = join_args(argv, argc); char *identifier = join_args(argv, argc);
struct sway_output *output = output_by_name_or_id(identifier); struct sway_output *output = output_by_name_or_id(identifier);
@ -305,13 +305,13 @@ static struct cmd_results *focus_output(struct sway_seat *seat,
if (!parse_direction(identifier, &direction)) { if (!parse_direction(identifier, &direction)) {
free(identifier); free(identifier);
return cmd_results_new(CMD_INVALID, return cmd_results_new(CMD_INVALID,
"There is no output with that name"); "There is no output with that name.");
} }
struct sway_workspace *ws = seat_get_focused_workspace(seat); struct sway_workspace *ws = seat_get_focused_workspace(seat);
if (!ws) { if (!ws) {
free(identifier); free(identifier);
return cmd_results_new(CMD_FAILURE, return cmd_results_new(CMD_FAILURE,
"No focused workspace to base directions off of"); "No focused workspace to base directions off of.");
} }
output = output_get_in_direction(ws->output, direction); output = output_get_in_direction(ws->output, direction);
@ -375,10 +375,14 @@ struct cmd_results *cmd_focus(int argc, char **argv) {
struct sway_seat *seat = config->handler_context.seat; struct sway_seat *seat = config->handler_context.seat;
if (node->type < N_WORKSPACE) { if (node->type < N_WORKSPACE) {
return cmd_results_new(CMD_FAILURE, return cmd_results_new(CMD_FAILURE,
"Command 'focus' cannot be used above the workspace level"); "Command 'focus' cannot be used above the workspace level.");
} }
if (argc == 0 && container) { if (argc == 0) {
if (!container) {
return cmd_results_new(CMD_FAILURE, "No container to focus was specified.");
}
if (container_is_scratchpad_hidden_or_child(container)) { if (container_is_scratchpad_hidden_or_child(container)) {
root_scratchpad_show(container); root_scratchpad_show(container);
} }

View file

@ -4,6 +4,7 @@
#include "sway/config.h" #include "sway/config.h"
#include "log.h" #include "log.h"
#include "stringop.h" #include "stringop.h"
#include <pango/pangocairo.h>
struct cmd_results *cmd_font(int argc, char **argv) { struct cmd_results *cmd_font(int argc, char **argv) {
struct cmd_results *error = NULL; struct cmd_results *error = NULL;
@ -16,12 +17,34 @@ struct cmd_results *cmd_font(int argc, char **argv) {
if (strncmp(font, "pango:", 6) == 0) { if (strncmp(font, "pango:", 6) == 0) {
config->pango_markup = true; config->pango_markup = true;
config->font = strdup(font + 6); config->font = strdup(font + 6);
free(font);
} else { } else {
config->pango_markup = false; config->pango_markup = false;
config->font = strdup(font); config->font = font;
} }
free(font); // Parse the font early so we can reject it if it's not valid for pango.
// Also avoids re-parsing each time we render text.
PangoFontDescription *font_description = pango_font_description_from_string(config->font);
const char *family = pango_font_description_get_family(font_description);
if (family == NULL) {
pango_font_description_free(font_description);
return cmd_results_new(CMD_FAILURE, "Invalid font family.");
}
const gint size = pango_font_description_get_size(font_description);
if (size == 0) {
pango_font_description_free(font_description);
return cmd_results_new(CMD_FAILURE, "Invalid font size.");
}
if (config->font_description != NULL) {
pango_font_description_free(config->font_description);
}
config->font_description = font_description;
config_update_font_height(); config_update_font_height();
return cmd_results_new(CMD_SUCCESS, NULL); return cmd_results_new(CMD_SUCCESS, NULL);
} }

166
sway/commands/gesture.c Normal file
View file

@ -0,0 +1,166 @@
#define _POSIX_C_SOURCE 200809L
#include "sway/config.h"
#include "gesture.h"
#include "log.h"
#include "stringop.h"
#include "sway/commands.h"
void free_gesture_binding(struct sway_gesture_binding *binding) {
if (!binding) {
return;
}
free(binding->input);
free(binding->command);
free(binding);
}
/**
* Returns true if the bindings have the same gesture type, direction, etc
*/
static bool binding_gesture_equal(struct sway_gesture_binding *binding_a,
struct sway_gesture_binding *binding_b) {
if (strcmp(binding_a->input, binding_b->input) != 0) {
return false;
}
if (!gesture_equal(&binding_a->gesture, &binding_b->gesture)) {
return false;
}
if ((binding_a->flags & BINDING_EXACT) !=
(binding_b->flags & BINDING_EXACT)) {
return false;
}
return true;
}
/**
* Add gesture binding to config
*/
static struct cmd_results *gesture_binding_add(
struct sway_gesture_binding *binding,
const char *gesturecombo, bool warn) {
list_t *mode_bindings = config->current_mode->gesture_bindings;
// overwrite the binding if it already exists
bool overwritten = false;
for (int i = 0; i < mode_bindings->length; ++i) {
struct sway_gesture_binding *config_binding = mode_bindings->items[i];
if (binding_gesture_equal(binding, config_binding)) {
sway_log(SWAY_INFO, "Overwriting binding '%s' to `%s` from `%s`",
gesturecombo, binding->command, config_binding->command);
if (warn) {
config_add_swaynag_warning("Overwriting binding"
"'%s' to `%s` from `%s`",
gesturecombo, binding->command,
config_binding->command);
}
free_gesture_binding(config_binding);
mode_bindings->items[i] = binding;
overwritten = true;
}
}
if (!overwritten) {
list_add(mode_bindings, binding);
sway_log(SWAY_DEBUG, "bindgesture - Bound %s to command `%s`",
gesturecombo, binding->command);
}
return cmd_results_new(CMD_SUCCESS, NULL);
}
/**
* Remove gesture binding from config
*/
static struct cmd_results *gesture_binding_remove(
struct sway_gesture_binding *binding, const char *gesturecombo) {
list_t *mode_bindings = config->current_mode->gesture_bindings;
for (int i = 0; i < mode_bindings->length; ++i) {
struct sway_gesture_binding *config_binding = mode_bindings->items[i];
if (binding_gesture_equal(binding, config_binding)) {
free_gesture_binding(config_binding);
free_gesture_binding(binding);
list_del(mode_bindings, i);
sway_log(SWAY_DEBUG, "unbindgesture - Unbound %s gesture",
gesturecombo);
return cmd_results_new(CMD_SUCCESS, NULL);
}
}
free_gesture_binding(binding);
return cmd_results_new(CMD_FAILURE, "Could not find gesture binding `%s`",
gesturecombo);
}
/**
* Parse and execute bindgesture or unbindgesture command.
*/
static struct cmd_results *cmd_bind_or_unbind_gesture(int argc, char **argv, bool unbind) {
int minargs = 2;
char *bindtype = "bindgesture";
if (unbind) {
minargs--;
bindtype = "unbindgesture";
}
struct cmd_results *error = NULL;
if ((error = checkarg(argc, bindtype, EXPECTED_AT_LEAST, minargs))) {
return error;
}
struct sway_gesture_binding *binding = calloc(1, sizeof(struct sway_gesture_binding));
if (!binding) {
return cmd_results_new(CMD_FAILURE, "Unable to allocate binding");
}
binding->input = strdup("*");
bool warn = true;
// Handle flags
while (argc > 0) {
if (strcmp("--exact", argv[0]) == 0) {
binding->flags |= BINDING_EXACT;
} else if (strcmp("--no-warn", argv[0]) == 0) {
warn = false;
} else if (strncmp("--input-device=", argv[0],
strlen("--input-device=")) == 0) {
free(binding->input);
binding->input = strdup(argv[0] + strlen("--input-device="));
} else {
break;
}
argv++;
argc--;
}
if (argc < minargs) {
free(binding);
return cmd_results_new(CMD_FAILURE,
"Invalid %s command (expected at least %d "
"non-option arguments, got %d)", bindtype, minargs, argc);
}
char* errmsg = NULL;
if ((errmsg = gesture_parse(argv[0], &binding->gesture))) {
free(binding);
struct cmd_results *final = cmd_results_new(CMD_FAILURE,
"Invalid %s command (%s)",
bindtype, errmsg);
free(errmsg);
return final;
}
if (unbind) {
return gesture_binding_remove(binding, argv[0]);
}
binding->command = join_args(argv + 1, argc - 1);
return gesture_binding_add(binding, argv[0], warn);
}
struct cmd_results *cmd_bindgesture(int argc, char **argv) {
return cmd_bind_or_unbind_gesture(argc, argv, false);
}
struct cmd_results *cmd_unbindgesture(int argc, char **argv) {
return cmd_bind_or_unbind_gesture(argc, argv, true);
}

View file

@ -14,6 +14,7 @@ static const struct cmd_handler input_handlers[] = {
{ "drag", input_cmd_drag }, { "drag", input_cmd_drag },
{ "drag_lock", input_cmd_drag_lock }, { "drag_lock", input_cmd_drag_lock },
{ "dwt", input_cmd_dwt }, { "dwt", input_cmd_dwt },
{ "dwtp", input_cmd_dwtp },
{ "events", input_cmd_events }, { "events", input_cmd_events },
{ "left_handed", input_cmd_left_handed }, { "left_handed", input_cmd_left_handed },
{ "map_from_region", input_cmd_map_from_region }, { "map_from_region", input_cmd_map_from_region },

View file

@ -0,0 +1,25 @@
#include <string.h>
#include <strings.h>
#include "sway/config.h"
#include "sway/commands.h"
#include "sway/input/input-manager.h"
#include "util.h"
struct cmd_results *input_cmd_dwtp(int argc, char **argv) {
struct cmd_results *error = NULL;
if ((error = checkarg(argc, "dwtp", EXPECTED_AT_LEAST, 1))) {
return error;
}
struct input_config *ic = config->handler_context.input_config;
if (!ic) {
return cmd_results_new(CMD_FAILURE, "No input device defined.");
}
if (parse_boolean(argv[0], true)) {
ic->dwtp = LIBINPUT_CONFIG_DWTP_ENABLED;
} else {
ic->dwtp = LIBINPUT_CONFIG_DWTP_DISABLED;
}
return cmd_results_new(CMD_SUCCESS, NULL);
}

View file

@ -1,10 +1,16 @@
#define _POSIX_C_SOURCE 200809L #define _POSIX_C_SOURCE 200809L
#include <assert.h> #include <assert.h>
#include <wlr/interfaces/wlr_keyboard.h>
#include "sway/config.h" #include "sway/config.h"
#include "sway/commands.h" #include "sway/commands.h"
#include "sway/input/input-manager.h" #include "sway/input/input-manager.h"
#include "log.h" #include "log.h"
struct xkb_switch_layout_action {
struct wlr_keyboard *keyboard;
xkb_layout_index_t layout;
};
static void switch_layout(struct wlr_keyboard *kbd, xkb_layout_index_t idx) { static void switch_layout(struct wlr_keyboard *kbd, xkb_layout_index_t idx) {
xkb_layout_index_t num_layouts = xkb_keymap_num_layouts(kbd->keymap); xkb_layout_index_t num_layouts = xkb_keymap_num_layouts(kbd->keymap);
if (idx >= num_layouts) { if (idx >= num_layouts) {
@ -28,10 +34,10 @@ static xkb_layout_index_t get_current_layout_index(struct wlr_keyboard *kbd) {
return layout_idx; return layout_idx;
} }
static void switch_layout_relative(struct wlr_keyboard *kbd, int dir) { static xkb_layout_index_t get_layout_relative(struct wlr_keyboard *kbd, int dir) {
xkb_layout_index_t num_layouts = xkb_keymap_num_layouts(kbd->keymap); xkb_layout_index_t num_layouts = xkb_keymap_num_layouts(kbd->keymap);
xkb_layout_index_t idx = get_current_layout_index(kbd); xkb_layout_index_t idx = get_current_layout_index(kbd);
switch_layout(kbd, (idx + num_layouts + dir) % num_layouts); return (idx + num_layouts + dir) % num_layouts;
} }
struct cmd_results *input_cmd_xkb_switch_layout(int argc, char **argv) { struct cmd_results *input_cmd_xkb_switch_layout(int argc, char **argv) {
@ -66,6 +72,18 @@ struct cmd_results *input_cmd_xkb_switch_layout(int argc, char **argv) {
relative = 0; relative = 0;
} }
struct xkb_switch_layout_action *actions = calloc(
wl_list_length(&server.input->devices),
sizeof(struct xkb_switch_layout_action));
size_t actions_len = 0;
if (!actions) {
return cmd_results_new(CMD_FAILURE, "Unable to allocate actions");
}
/* Calculate new indexes first because switching a layout in one
keyboard may result in a change on other keyboards as well because
of keyboard groups. */
struct sway_input_device *dev; struct sway_input_device *dev;
wl_list_for_each(dev, &server.input->devices, link) { wl_list_for_each(dev, &server.input->devices, link) {
if (strcmp(ic->identifier, "*") != 0 && if (strcmp(ic->identifier, "*") != 0 &&
@ -76,12 +94,22 @@ struct cmd_results *input_cmd_xkb_switch_layout(int argc, char **argv) {
if (dev->wlr_device->type != WLR_INPUT_DEVICE_KEYBOARD) { if (dev->wlr_device->type != WLR_INPUT_DEVICE_KEYBOARD) {
continue; continue;
} }
struct xkb_switch_layout_action *action =
&actions[actions_len++];
action->keyboard = wlr_keyboard_from_input_device(dev->wlr_device);
if (relative) { if (relative) {
switch_layout_relative(dev->wlr_device->keyboard, relative); action->layout = get_layout_relative(action->keyboard, relative);
} else { } else {
switch_layout(dev->wlr_device->keyboard, layout); action->layout = layout;
} }
} }
for (size_t i = 0; i < actions_len; i++) {
switch_layout(actions[i].keyboard, actions[i].layout);
}
free(actions);
return cmd_results_new(CMD_SUCCESS, NULL); return cmd_results_new(CMD_SUCCESS, NULL);
} }

View file

@ -11,10 +11,12 @@
// Must be in order for the bsearch // Must be in order for the bsearch
static const struct cmd_handler mode_handlers[] = { static const struct cmd_handler mode_handlers[] = {
{ "bindcode", cmd_bindcode }, { "bindcode", cmd_bindcode },
{ "bindgesture", cmd_bindgesture },
{ "bindswitch", cmd_bindswitch }, { "bindswitch", cmd_bindswitch },
{ "bindsym", cmd_bindsym }, { "bindsym", cmd_bindsym },
{ "set", cmd_set }, { "set", cmd_set },
{ "unbindcode", cmd_unbindcode }, { "unbindcode", cmd_unbindcode },
{ "unbindgesture", cmd_unbindgesture },
{ "unbindswitch", cmd_unbindswitch }, { "unbindswitch", cmd_unbindswitch },
{ "unbindsym", cmd_unbindsym }, { "unbindsym", cmd_unbindsym },
}; };
@ -59,6 +61,7 @@ struct cmd_results *cmd_mode(int argc, char **argv) {
mode->keycode_bindings = create_list(); mode->keycode_bindings = create_list();
mode->mouse_bindings = create_list(); mode->mouse_bindings = create_list();
mode->switch_bindings = create_list(); mode->switch_bindings = create_list();
mode->gesture_bindings = create_list();
mode->pango = pango; mode->pango = pango;
list_add(config->modes, mode); list_add(config->modes, mode);
} }

View file

@ -686,6 +686,9 @@ static struct cmd_results *cmd_move_workspace(int argc, char **argv) {
arrange_output(old_output); arrange_output(old_output);
arrange_output(new_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); return cmd_results_new(CMD_SUCCESS, NULL);
} }
@ -788,15 +791,15 @@ static struct cmd_results *cmd_move_to_position_pointer(
struct wlr_output *output = wlr_output_layout_output_at( struct wlr_output *output = wlr_output_layout_output_at(
root->output_layout, cursor->x, cursor->y); root->output_layout, cursor->x, cursor->y);
if (output) { if (output) {
struct wlr_box *box = struct wlr_box box;
wlr_output_layout_get_box(root->output_layout, output); wlr_output_layout_get_box(root->output_layout, output, &box);
lx = fmax(lx, box->x); lx = fmax(lx, box.x);
ly = fmax(ly, box->y); ly = fmax(ly, box.y);
if (lx + container->pending.width > box->x + box->width) { if (lx + container->pending.width > box.x + box.width) {
lx = box->x + box->width - container->pending.width; lx = box.x + box.width - container->pending.width;
} }
if (ly + container->pending.height > box->y + box->height) { if (ly + container->pending.height > box.y + box.height) {
ly = box->y + box->height - container->pending.height; ly = box.y + box.height - container->pending.height;
} }
} }

View file

@ -18,6 +18,7 @@ static const struct cmd_handler output_handlers[] = {
{ "modeline", output_cmd_modeline }, { "modeline", output_cmd_modeline },
{ "pos", output_cmd_position }, { "pos", output_cmd_position },
{ "position", output_cmd_position }, { "position", output_cmd_position },
{ "power", output_cmd_power },
{ "render_bit_depth", output_cmd_render_bit_depth }, { "render_bit_depth", output_cmd_render_bit_depth },
{ "res", output_cmd_mode }, { "res", output_cmd_mode },
{ "resolution", output_cmd_mode }, { "resolution", output_cmd_mode },
@ -26,6 +27,7 @@ static const struct cmd_handler output_handlers[] = {
{ "subpixel", output_cmd_subpixel }, { "subpixel", output_cmd_subpixel },
{ "toggle", output_cmd_toggle }, { "toggle", output_cmd_toggle },
{ "transform", output_cmd_transform }, { "transform", output_cmd_transform },
{ "unplug", output_cmd_unplug },
}; };
struct cmd_results *cmd_output(int argc, char **argv) { struct cmd_results *cmd_output(int argc, char **argv) {

View file

@ -102,19 +102,19 @@ struct cmd_results *output_cmd_background(int argc, char **argv) {
} }
char *conf_path = dirname(conf); char *conf_path = dirname(conf);
char *rel_path = src; char *real_src = malloc(strlen(conf_path) + strlen(src) + 2);
src = malloc(strlen(conf_path) + strlen(src) + 2); if (!real_src) {
if (!src) { free(src);
free(rel_path);
free(conf); free(conf);
sway_log(SWAY_ERROR, "Unable to allocate memory"); sway_log(SWAY_ERROR, "Unable to allocate memory");
return cmd_results_new(CMD_FAILURE, return cmd_results_new(CMD_FAILURE,
"Unable to allocate resources"); "Unable to allocate resources");
} }
sprintf(src, "%s/%s", conf_path, rel_path); snprintf(real_src, strlen(conf_path) + strlen(src) + 2, "%s/%s", conf_path, src);
free(rel_path); free(src);
free(conf); free(conf);
src = real_src;
} }
bool can_access = access(src, F_OK) != -1; bool can_access = access(src, F_OK) != -1;

View file

@ -1,45 +1,8 @@
#include "log.h"
#include "sway/commands.h" #include "sway/commands.h"
#include "sway/config.h"
#include "sway/output.h"
#include "util.h"
#include <strings.h>
struct cmd_results *output_cmd_dpms(int argc, char **argv) { struct cmd_results *output_cmd_dpms(int argc, char **argv) {
if (!config->handler_context.output_config) { sway_log(SWAY_INFO, "The \"output dpms\" command is deprecated, "
return cmd_results_new(CMD_FAILURE, "Missing output config"); "use \"output power\" instead");
} return output_cmd_power(argc, argv);
if (!argc) {
return cmd_results_new(CMD_INVALID, "Missing dpms argument.");
}
enum config_dpms current_dpms = DPMS_ON;
if (strcasecmp(argv[0], "toggle") == 0) {
const char *oc_name = config->handler_context.output_config->name;
if (strcmp(oc_name, "*") == 0) {
return cmd_results_new(CMD_INVALID,
"Cannot apply toggle to all outputs.");
}
struct sway_output *sway_output = all_output_by_name_or_id(oc_name);
if (!sway_output || !sway_output->wlr_output) {
return cmd_results_new(CMD_FAILURE,
"Cannot apply toggle to unknown output %s", oc_name);
}
if (sway_output->enabled && !sway_output->wlr_output->enabled) {
current_dpms = DPMS_OFF;
}
}
if (parse_boolean(argv[0], current_dpms == DPMS_ON)) {
config->handler_context.output_config->dpms_state = DPMS_ON;
} else {
config->handler_context.output_config->dpms_state = DPMS_OFF;
}
config->handler_context.leftovers.argc = argc - 1;
config->handler_context.leftovers.argv = argv + 1;
return NULL;
} }

View file

@ -0,0 +1,43 @@
#include <strings.h>
#include "sway/commands.h"
#include "sway/config.h"
#include "sway/output.h"
#include "util.h"
struct cmd_results *output_cmd_power(int argc, char **argv) {
if (!config->handler_context.output_config) {
return cmd_results_new(CMD_FAILURE, "Missing output config");
}
if (argc == 0) {
return cmd_results_new(CMD_INVALID, "Missing power argument");
}
bool current = true;
if (strcasecmp(argv[0], "toggle") == 0) {
const char *oc_name = config->handler_context.output_config->name;
if (strcmp(oc_name, "*") == 0) {
return cmd_results_new(CMD_INVALID,
"Cannot apply toggle to all outputs");
}
struct sway_output *sway_output = all_output_by_name_or_id(oc_name);
if (!sway_output || !sway_output->wlr_output) {
return cmd_results_new(CMD_FAILURE,
"Cannot apply toggle to unknown output %s", oc_name);
}
if (sway_output->enabled && !sway_output->wlr_output->enabled) {
current = false;
}
}
if (parse_boolean(argv[0], current)) {
config->handler_context.output_config->power = 1;
} else {
config->handler_context.output_config->power = 0;
}
config->handler_context.leftovers.argc = argc - 1;
config->handler_context.leftovers.argv = argv + 1;
return NULL;
}

View file

@ -0,0 +1,54 @@
#include <strings.h>
#include <wlr/config.h>
#include <wlr/backend/headless.h>
#include <wlr/backend/wayland.h>
#if WLR_HAS_X11_BACKEND
#include <wlr/backend/x11.h>
#endif
#include "sway/commands.h"
#include "sway/config.h"
#include "sway/output.h"
static bool is_backend_allowed(struct wlr_backend *backend) {
if (wlr_backend_is_headless(backend)) {
return true;
}
if (wlr_backend_is_wl(backend)) {
return true;
}
#if WLR_HAS_X11_BACKEND
if (wlr_backend_is_x11(backend)) {
return true;
}
#endif
return false;
}
/**
* This command is intended for developer use only.
*/
struct cmd_results *output_cmd_unplug(int argc, char **argv) {
if (!config->handler_context.output_config) {
return cmd_results_new(CMD_FAILURE, "Missing output config");
}
const char *oc_name = config->handler_context.output_config->name;
if (strcmp(oc_name, "*") == 0) {
return cmd_results_new(CMD_INVALID, "Won't unplug all outputs");
}
struct sway_output *sway_output = all_output_by_name_or_id(oc_name);
if (!sway_output) {
return cmd_results_new(CMD_INVALID,
"Cannot unplug unknown output %s", oc_name);
}
if (!is_backend_allowed(sway_output->wlr_output->backend)) {
return cmd_results_new(CMD_INVALID,
"Can only unplug outputs with headless, wayland or x11 backend");
}
wlr_output_destroy(sway_output->wlr_output);
return cmd_results_new(CMD_SUCCESS, NULL);
}

View file

@ -7,6 +7,7 @@
#include "sway/config.h" #include "sway/config.h"
#include "sway/ipc-server.h" #include "sway/ipc-server.h"
#include "sway/output.h" #include "sway/output.h"
#include "sway/desktop/launcher.h"
#include "sway/tree/container.h" #include "sway/tree/container.h"
#include "sway/tree/workspace.h" #include "sway/tree/workspace.h"
#include "sway/tree/root.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); sway_log(SWAY_DEBUG, "renaming workspace '%s' to '%s'", workspace->name, new_name);
root_rename_pid_workspaces(workspace->name, new_name);
free(workspace->name); free(workspace->name);
workspace->name = new_name; workspace->name = new_name;

View file

@ -111,8 +111,8 @@ static struct cmd_results *press_or_release(struct sway_cursor *cursor,
: WLR_AXIS_ORIENTATION_HORIZONTAL; : WLR_AXIS_ORIENTATION_HORIZONTAL;
double delta = (button == SWAY_SCROLL_UP || button == SWAY_SCROLL_LEFT) double delta = (button == SWAY_SCROLL_UP || button == SWAY_SCROLL_LEFT)
? -1 : 1; ? -1 : 1;
struct wlr_event_pointer_axis event = { struct wlr_pointer_axis_event event = {
.device = NULL, .pointer = NULL,
.time_msec = 0, .time_msec = 0,
.source = WLR_AXIS_SOURCE_WHEEL, .source = WLR_AXIS_SOURCE_WHEEL,
.orientation = orientation, .orientation = orientation,

View file

@ -82,6 +82,12 @@ static void free_mode(struct sway_mode *mode) {
} }
list_free(mode->switch_bindings); list_free(mode->switch_bindings);
} }
if (mode->gesture_bindings) {
for (int i = 0; i < mode->gesture_bindings->length; i++) {
free_gesture_binding(mode->gesture_bindings->items[i]);
}
list_free(mode->gesture_bindings);
}
free(mode); free(mode);
} }
@ -222,6 +228,7 @@ static void config_defaults(struct sway_config *config) {
if (!(config->current_mode->keycode_bindings = create_list())) goto cleanup; if (!(config->current_mode->keycode_bindings = create_list())) goto cleanup;
if (!(config->current_mode->mouse_bindings = create_list())) goto cleanup; if (!(config->current_mode->mouse_bindings = create_list())) goto cleanup;
if (!(config->current_mode->switch_bindings = create_list())) goto cleanup; if (!(config->current_mode->switch_bindings = create_list())) goto cleanup;
if (!(config->current_mode->gesture_bindings = create_list())) goto cleanup;
list_add(config->modes, config->current_mode); list_add(config->modes, config->current_mode);
config->floating_mod = 0; config->floating_mod = 0;
@ -236,6 +243,7 @@ static void config_defaults(struct sway_config *config) {
config->default_layout = L_NONE; config->default_layout = L_NONE;
config->default_orientation = L_NONE; config->default_orientation = L_NONE;
if (!(config->font = strdup("monospace 10"))) goto cleanup; if (!(config->font = strdup("monospace 10"))) goto cleanup;
config->font_description = pango_font_description_from_string(config->font);
config->urgent_timeout = 500; config->urgent_timeout = 500;
config->focus_on_window_activation = FOWA_URGENT; config->focus_on_window_activation = FOWA_URGENT;
config->popup_during_fullscreen = POPUP_SMART; config->popup_during_fullscreen = POPUP_SMART;
@ -1008,7 +1016,7 @@ int workspace_output_cmp_workspace(const void *a, const void *b) {
void config_update_font_height(void) { void config_update_font_height(void) {
int prev_max_height = config->font_height; int prev_max_height = config->font_height;
get_text_metrics(config->font, &config->font_height, &config->font_baseline); get_text_metrics(config->font_description, &config->font_height, &config->font_baseline);
if (config->font_height != prev_max_height) { if (config->font_height != prev_max_height) {
arrange_root(); arrange_root();

View file

@ -25,6 +25,7 @@ struct input_config *new_input_config(const char* identifier) {
input->drag = INT_MIN; input->drag = INT_MIN;
input->drag_lock = INT_MIN; input->drag_lock = INT_MIN;
input->dwt = INT_MIN; input->dwt = INT_MIN;
input->dwtp = INT_MIN;
input->send_events = INT_MIN; input->send_events = INT_MIN;
input->click_method = INT_MIN; input->click_method = INT_MIN;
input->middle_emulation = INT_MIN; input->middle_emulation = INT_MIN;
@ -61,6 +62,9 @@ void merge_input_config(struct input_config *dst, struct input_config *src) {
if (src->dwt != INT_MIN) { if (src->dwt != INT_MIN) {
dst->dwt = src->dwt; dst->dwt = src->dwt;
} }
if (src->dwtp != INT_MIN) {
dst->dwtp = src->dwtp;
}
if (src->left_handed != INT_MIN) { if (src->left_handed != INT_MIN) {
dst->left_handed = src->left_handed; dst->left_handed = src->left_handed;
} }

View file

@ -27,8 +27,10 @@ int output_name_cmp(const void *item, const void *data) {
void output_get_identifier(char *identifier, size_t len, void output_get_identifier(char *identifier, size_t len,
struct sway_output *output) { struct sway_output *output) {
struct wlr_output *wlr_output = output->wlr_output; struct wlr_output *wlr_output = output->wlr_output;
snprintf(identifier, len, "%s %s %s", wlr_output->make, wlr_output->model, snprintf(identifier, len, "%s %s %s",
wlr_output->serial); wlr_output->make ? wlr_output->make : "Unknown",
wlr_output->model ? wlr_output->model : "Unknown",
wlr_output->serial ? wlr_output->serial : "Unknown");
} }
const char *sway_output_scale_filter_to_string(enum scale_filter_mode scale_filter) { const char *sway_output_scale_filter_to_string(enum scale_filter_mode scale_filter) {
@ -69,6 +71,7 @@ struct output_config *new_output_config(const char *name) {
oc->max_render_time = -1; oc->max_render_time = -1;
oc->adaptive_sync = -1; oc->adaptive_sync = -1;
oc->render_bit_depth = RENDER_BIT_DEPTH_DEFAULT; oc->render_bit_depth = RENDER_BIT_DEPTH_DEFAULT;
oc->power = -1;
return oc; return oc;
} }
@ -130,8 +133,8 @@ void merge_output_config(struct output_config *dst, struct output_config *src) {
free(dst->background_fallback); free(dst->background_fallback);
dst->background_fallback = strdup(src->background_fallback); dst->background_fallback = strdup(src->background_fallback);
} }
if (src->dpms_state != 0) { if (src->power != -1) {
dst->dpms_state = src->dpms_state; dst->power = src->power;
} }
} }
@ -190,11 +193,11 @@ static void merge_id_on_name(struct output_config *oc) {
list_add(config->output_configs, ion_oc); list_add(config->output_configs, ion_oc);
sway_log(SWAY_DEBUG, "Generated id on name output config \"%s\"" sway_log(SWAY_DEBUG, "Generated id on name output config \"%s\""
" (enabled: %d) (%dx%d@%fHz position %d,%d scale %f " " (enabled: %d) (%dx%d@%fHz position %d,%d scale %f "
"transform %d) (bg %s %s) (dpms %d) (max render time: %d)", "transform %d) (bg %s %s) (power %d) (max render time: %d)",
ion_oc->name, ion_oc->enabled, ion_oc->width, ion_oc->height, ion_oc->name, ion_oc->enabled, ion_oc->width, ion_oc->height,
ion_oc->refresh_rate, ion_oc->x, ion_oc->y, ion_oc->scale, ion_oc->refresh_rate, ion_oc->x, ion_oc->y, ion_oc->scale,
ion_oc->transform, ion_oc->background, ion_oc->transform, ion_oc->background,
ion_oc->background_option, ion_oc->dpms_state, ion_oc->background_option, ion_oc->power,
ion_oc->max_render_time); ion_oc->max_render_time);
} }
} }
@ -235,18 +238,18 @@ struct output_config *store_output_config(struct output_config *oc) {
} }
sway_log(SWAY_DEBUG, "Config stored for output %s (enabled: %d) (%dx%d@%fHz " sway_log(SWAY_DEBUG, "Config stored for output %s (enabled: %d) (%dx%d@%fHz "
"position %d,%d scale %f subpixel %s transform %d) (bg %s %s) (dpms %d) " "position %d,%d scale %f subpixel %s transform %d) (bg %s %s) (power %d) "
"(max render time: %d)", "(max render time: %d)",
oc->name, oc->enabled, oc->width, oc->height, oc->refresh_rate, oc->name, oc->enabled, oc->width, oc->height, oc->refresh_rate,
oc->x, oc->y, oc->scale, sway_wl_output_subpixel_to_string(oc->subpixel), oc->x, oc->y, oc->scale, sway_wl_output_subpixel_to_string(oc->subpixel),
oc->transform, oc->background, oc->background_option, oc->dpms_state, oc->transform, oc->background, oc->background_option, oc->power,
oc->max_render_time); oc->max_render_time);
return oc; return oc;
} }
static void set_mode(struct wlr_output *output, int width, int height, static void set_mode(struct wlr_output *output, struct wlr_output_state *pending,
float refresh_rate, bool custom) { int width, int height, float refresh_rate, bool custom) {
// Not all floating point integers can be represented exactly // Not all floating point integers can be represented exactly
// as (int)(1000 * mHz / 1000.f) // as (int)(1000 * mHz / 1000.f)
// round() the result to avoid any error // round() the result to avoid any error
@ -254,7 +257,7 @@ static void set_mode(struct wlr_output *output, int width, int height,
if (wl_list_empty(&output->modes) || custom) { if (wl_list_empty(&output->modes) || custom) {
sway_log(SWAY_DEBUG, "Assigning custom mode to %s", output->name); sway_log(SWAY_DEBUG, "Assigning custom mode to %s", output->name);
wlr_output_set_custom_mode(output, width, height, wlr_output_state_set_custom_mode(pending, width, height,
refresh_rate > 0 ? mhz : 0); refresh_rate > 0 ? mhz : 0);
return; return;
} }
@ -278,10 +281,11 @@ static void set_mode(struct wlr_output *output, int width, int height,
} else { } else {
sway_log(SWAY_DEBUG, "Assigning configured mode to %s", output->name); sway_log(SWAY_DEBUG, "Assigning configured mode to %s", output->name);
} }
wlr_output_set_mode(output, best); wlr_output_state_set_mode(pending, best);
} }
static void set_modeline(struct wlr_output *output, drmModeModeInfo *drm_mode) { static void set_modeline(struct wlr_output *output,
struct wlr_output_state *pending, drmModeModeInfo *drm_mode) {
if (!wlr_output_is_drm(output)) { if (!wlr_output_is_drm(output)) {
sway_log(SWAY_ERROR, "Modeline can only be set to DRM output"); sway_log(SWAY_ERROR, "Modeline can only be set to DRM output");
return; return;
@ -289,7 +293,7 @@ static void set_modeline(struct wlr_output *output, drmModeModeInfo *drm_mode) {
sway_log(SWAY_DEBUG, "Assigning custom modeline to %s", output->name); sway_log(SWAY_DEBUG, "Assigning custom modeline to %s", output->name);
struct wlr_output_mode *mode = wlr_drm_connector_add_mode(output, drm_mode); struct wlr_output_mode *mode = wlr_drm_connector_add_mode(output, drm_mode);
if (mode) { if (mode) {
wlr_output_set_mode(output, mode); wlr_output_state_set_mode(pending, mode);
} }
} }
@ -311,23 +315,24 @@ static bool phys_size_is_aspect_ratio(struct wlr_output *output) {
// 1 inch = 25.4 mm // 1 inch = 25.4 mm
#define MM_PER_INCH 25.4 #define MM_PER_INCH 25.4
static int compute_default_scale(struct wlr_output *output) { static int compute_default_scale(struct wlr_output *output,
struct wlr_output_state *pending) {
struct wlr_box box = { .width = output->width, .height = output->height }; struct wlr_box box = { .width = output->width, .height = output->height };
if (output->pending.committed & WLR_OUTPUT_STATE_MODE) { if (pending->committed & WLR_OUTPUT_STATE_MODE) {
switch (output->pending.mode_type) { switch (pending->mode_type) {
case WLR_OUTPUT_STATE_MODE_FIXED: case WLR_OUTPUT_STATE_MODE_FIXED:
box.width = output->pending.mode->width; box.width = pending->mode->width;
box.height = output->pending.mode->height; box.height = pending->mode->height;
break; break;
case WLR_OUTPUT_STATE_MODE_CUSTOM: case WLR_OUTPUT_STATE_MODE_CUSTOM:
box.width = output->pending.custom_mode.width; box.width = pending->custom_mode.width;
box.height = output->pending.custom_mode.height; box.height = pending->custom_mode.height;
break; break;
} }
} }
enum wl_output_transform transform = output->transform; enum wl_output_transform transform = output->transform;
if (output->pending.committed & WLR_OUTPUT_STATE_TRANSFORM) { if (pending->committed & WLR_OUTPUT_STATE_TRANSFORM) {
transform = output->pending.transform; transform = pending->transform;
} }
wlr_box_transform(&box, &box, transform, box.width, box.height); wlr_box_transform(&box, &box, transform, box.width, box.height);
@ -374,38 +379,38 @@ static const uint32_t *bit_depth_preferences[] = {
}; };
static void queue_output_config(struct output_config *oc, static void queue_output_config(struct output_config *oc,
struct sway_output *output) { struct sway_output *output, struct wlr_output_state *pending) {
if (output == root->fallback_output) { if (output == root->fallback_output) {
return; return;
} }
struct wlr_output *wlr_output = output->wlr_output; struct wlr_output *wlr_output = output->wlr_output;
if (oc && (!oc->enabled || oc->dpms_state == DPMS_OFF)) { if (oc && (!oc->enabled || oc->power == 0)) {
sway_log(SWAY_DEBUG, "Turning off output %s", wlr_output->name); sway_log(SWAY_DEBUG, "Turning off output %s", wlr_output->name);
wlr_output_enable(wlr_output, false); wlr_output_state_set_enabled(pending, false);
return; return;
} }
sway_log(SWAY_DEBUG, "Turning on output %s", wlr_output->name); sway_log(SWAY_DEBUG, "Turning on output %s", wlr_output->name);
wlr_output_enable(wlr_output, true); wlr_output_state_set_enabled(pending, true);
if (oc && oc->drm_mode.type != 0 && oc->drm_mode.type != (uint32_t) -1) { if (oc && oc->drm_mode.type != 0 && oc->drm_mode.type != (uint32_t) -1) {
sway_log(SWAY_DEBUG, "Set %s modeline", sway_log(SWAY_DEBUG, "Set %s modeline",
wlr_output->name); wlr_output->name);
set_modeline(wlr_output, &oc->drm_mode); set_modeline(wlr_output, pending, &oc->drm_mode);
} else if (oc && oc->width > 0 && oc->height > 0) { } else if (oc && oc->width > 0 && oc->height > 0) {
sway_log(SWAY_DEBUG, "Set %s mode to %dx%d (%f Hz)", sway_log(SWAY_DEBUG, "Set %s mode to %dx%d (%f Hz)",
wlr_output->name, oc->width, oc->height, oc->refresh_rate); wlr_output->name, oc->width, oc->height, oc->refresh_rate);
set_mode(wlr_output, oc->width, oc->height, set_mode(wlr_output, pending, oc->width, oc->height,
oc->refresh_rate, oc->custom_mode == 1); oc->refresh_rate, oc->custom_mode == 1);
} else if (!wl_list_empty(&wlr_output->modes)) { } else if (!wl_list_empty(&wlr_output->modes)) {
sway_log(SWAY_DEBUG, "Set preferred mode"); sway_log(SWAY_DEBUG, "Set preferred mode");
struct wlr_output_mode *preferred_mode = struct wlr_output_mode *preferred_mode =
wlr_output_preferred_mode(wlr_output); wlr_output_preferred_mode(wlr_output);
wlr_output_set_mode(wlr_output, preferred_mode); wlr_output_state_set_mode(pending, preferred_mode);
if (!wlr_output_test(wlr_output)) { if (!wlr_output_test_state(wlr_output, pending)) {
sway_log(SWAY_DEBUG, "Preferred mode rejected, " sway_log(SWAY_DEBUG, "Preferred mode rejected, "
"falling back to another mode"); "falling back to another mode");
struct wlr_output_mode *mode; struct wlr_output_mode *mode;
@ -414,8 +419,8 @@ static void queue_output_config(struct output_config *oc,
continue; continue;
} }
wlr_output_set_mode(wlr_output, mode); wlr_output_state_set_mode(pending, mode);
if (wlr_output_test(wlr_output)) { if (wlr_output_test_state(wlr_output, pending)) {
break; break;
} }
} }
@ -425,7 +430,7 @@ static void queue_output_config(struct output_config *oc,
if (oc && (oc->subpixel != WL_OUTPUT_SUBPIXEL_UNKNOWN || config->reloading)) { if (oc && (oc->subpixel != WL_OUTPUT_SUBPIXEL_UNKNOWN || config->reloading)) {
sway_log(SWAY_DEBUG, "Set %s subpixel to %s", oc->name, sway_log(SWAY_DEBUG, "Set %s subpixel to %s", oc->name,
sway_wl_output_subpixel_to_string(oc->subpixel)); sway_wl_output_subpixel_to_string(oc->subpixel));
wlr_output_set_subpixel(wlr_output, oc->subpixel); wlr_output_state_set_subpixel(pending, oc->subpixel);
} }
enum wl_output_transform tr = WL_OUTPUT_TRANSFORM_NORMAL; enum wl_output_transform tr = WL_OUTPUT_TRANSFORM_NORMAL;
@ -437,7 +442,7 @@ static void queue_output_config(struct output_config *oc,
} }
if (wlr_output->transform != tr) { if (wlr_output->transform != tr) {
sway_log(SWAY_DEBUG, "Set %s transform to %d", oc->name, tr); sway_log(SWAY_DEBUG, "Set %s transform to %d", oc->name, tr);
wlr_output_set_transform(wlr_output, tr); wlr_output_state_set_transform(pending, tr);
} }
// Apply the scale last before the commit, because the scale auto-detection // Apply the scale last before the commit, because the scale auto-detection
@ -446,18 +451,22 @@ static void queue_output_config(struct output_config *oc,
if (oc && oc->scale > 0) { if (oc && oc->scale > 0) {
scale = oc->scale; scale = oc->scale;
} else { } else {
scale = compute_default_scale(wlr_output); scale = compute_default_scale(wlr_output, pending);
sway_log(SWAY_DEBUG, "Auto-detected output scale: %f", scale); sway_log(SWAY_DEBUG, "Auto-detected output scale: %f", scale);
} }
if (scale != wlr_output->scale) { if (scale != wlr_output->scale) {
sway_log(SWAY_DEBUG, "Set %s scale to %f", wlr_output->name, scale); sway_log(SWAY_DEBUG, "Set %s scale to %f", wlr_output->name, scale);
wlr_output_set_scale(wlr_output, scale); wlr_output_state_set_scale(pending, scale);
} }
if (oc && oc->adaptive_sync != -1) { if (oc && oc->adaptive_sync != -1) {
sway_log(SWAY_DEBUG, "Set %s adaptive sync to %d", wlr_output->name, sway_log(SWAY_DEBUG, "Set %s adaptive sync to %d", wlr_output->name,
oc->adaptive_sync); oc->adaptive_sync);
wlr_output_enable_adaptive_sync(wlr_output, oc->adaptive_sync == 1); wlr_output_state_set_adaptive_sync_enabled(pending, oc->adaptive_sync == 1);
if (oc->adaptive_sync == 1 && !wlr_output_test_state(wlr_output, pending)) {
sway_log(SWAY_DEBUG, "Adaptive sync failed, ignoring");
wlr_output_state_set_adaptive_sync_enabled(pending, false);
}
} }
if (oc && oc->render_bit_depth != RENDER_BIT_DEPTH_DEFAULT) { if (oc && oc->render_bit_depth != RENDER_BIT_DEPTH_DEFAULT) {
@ -465,8 +474,8 @@ static void queue_output_config(struct output_config *oc,
assert(fmts); assert(fmts);
for (size_t i = 0; fmts[i] != DRM_FORMAT_INVALID; i++) { for (size_t i = 0; fmts[i] != DRM_FORMAT_INVALID; i++) {
wlr_output_set_render_format(wlr_output, fmts[i]); wlr_output_state_set_render_format(pending, fmts[i]);
if (wlr_output_test(wlr_output)) { if (wlr_output_test_state(wlr_output, pending)) {
break; break;
} }
@ -487,14 +496,15 @@ bool apply_output_config(struct output_config *oc, struct sway_output *output) {
// Flag to prevent the output mode event handler from calling us // Flag to prevent the output mode event handler from calling us
output->enabling = (!oc || oc->enabled); output->enabling = (!oc || oc->enabled);
queue_output_config(oc, output); struct wlr_output_state pending = {0};
queue_output_config(oc, output, &pending);
if (!oc || oc->dpms_state != DPMS_OFF) { if (!oc || oc->power != 0) {
output->current_mode = wlr_output->pending.mode; output->current_mode = pending.mode;
} }
sway_log(SWAY_DEBUG, "Committing output %s", wlr_output->name); sway_log(SWAY_DEBUG, "Committing output %s", wlr_output->name);
if (!wlr_output_commit(wlr_output)) { if (!wlr_output_commit_state(wlr_output, &pending)) {
// Failed to commit output changes, maybe the output is missing a CRTC. // Failed to commit output changes, maybe the output is missing a CRTC.
// Leave the output disabled for now and try again when the output gets // Leave the output disabled for now and try again when the output gets
// the mode we asked for. // the mode we asked for.
@ -546,12 +556,12 @@ bool apply_output_config(struct output_config *oc, struct sway_output *output) {
} }
// Update output->{lx, ly, width, height} // Update output->{lx, ly, width, height}
struct wlr_box *output_box = struct wlr_box output_box;
wlr_output_layout_get_box(root->output_layout, wlr_output); wlr_output_layout_get_box(root->output_layout, wlr_output, &output_box);
output->lx = output_box->x; output->lx = output_box.x;
output->ly = output_box->y; output->ly = output_box.y;
output->width = output_box->width; output->width = output_box.width;
output->height = output_box->height; output->height = output_box.height;
if (!output->enabled) { if (!output->enabled) {
output_enable(output); output_enable(output);
@ -577,15 +587,15 @@ bool test_output_config(struct output_config *oc, struct sway_output *output) {
return false; return false;
} }
queue_output_config(oc, output); struct wlr_output_state pending = {0};
bool ok = wlr_output_test(output->wlr_output); queue_output_config(oc, output, &pending);
wlr_output_rollback(output->wlr_output); return wlr_output_test_state(output->wlr_output, &pending);
return ok;
} }
static void default_output_config(struct output_config *oc, static void default_output_config(struct output_config *oc,
struct wlr_output *wlr_output) { struct wlr_output *wlr_output) {
oc->enabled = 1; oc->enabled = 1;
oc->power = 1;
struct wlr_output_mode *mode = wlr_output_preferred_mode(wlr_output); struct wlr_output_mode *mode = wlr_output_preferred_mode(wlr_output);
if (mode != NULL) { if (mode != NULL) {
oc->width = mode->width; oc->width = mode->width;
@ -598,7 +608,6 @@ static void default_output_config(struct output_config *oc,
struct sway_output *output = wlr_output->data; struct sway_output *output = wlr_output->data;
oc->subpixel = output->detected_subpixel; oc->subpixel = output->detected_subpixel;
oc->transform = WL_OUTPUT_TRANSFORM_NORMAL; oc->transform = WL_OUTPUT_TRANSFORM_NORMAL;
oc->dpms_state = DPMS_ON;
oc->max_render_time = 0; oc->max_render_time = 0;
} }
@ -653,10 +662,10 @@ static struct output_config *get_output_config(char *identifier,
sway_log(SWAY_DEBUG, "Generated output config \"%s\" (enabled: %d)" sway_log(SWAY_DEBUG, "Generated output config \"%s\" (enabled: %d)"
" (%dx%d@%fHz position %d,%d scale %f transform %d) (bg %s %s)" " (%dx%d@%fHz position %d,%d scale %f transform %d) (bg %s %s)"
" (dpms %d) (max render time: %d)", result->name, result->enabled, " (power %d) (max render time: %d)", result->name, result->enabled,
result->width, result->height, result->refresh_rate, result->width, result->height, result->refresh_rate,
result->x, result->y, result->scale, result->transform, result->x, result->y, result->scale, result->transform,
result->background, result->background_option, result->dpms_state, result->background, result->background_option, result->power,
result->max_render_time); result->max_render_time);
} else if (oc_name) { } else if (oc_name) {
// No identifier config, just return a copy of the name config // No identifier config, just return a copy of the name config

View file

@ -3,7 +3,8 @@
#include <stdio.h> #include <stdio.h>
#include <stdbool.h> #include <stdbool.h>
#include <strings.h> #include <strings.h>
#include <pcre.h> #define PCRE2_CODE_UNIT_WIDTH 8
#include <pcre2.h>
#include "sway/criteria.h" #include "sway/criteria.h"
#include "sway/tree/container.h" #include "sway/tree/container.h"
#include "sway/config.h" #include "sway/config.h"
@ -40,17 +41,19 @@ bool criteria_is_empty(struct criteria *criteria) {
char *error = NULL; char *error = NULL;
// Returns error string on failure or NULL otherwise. // Returns error string on failure or NULL otherwise.
static bool generate_regex(pcre **regex, char *value) { static bool generate_regex(pcre2_code **regex, char *value) {
const char *reg_err; int errorcode;
int offset; PCRE2_SIZE offset;
*regex = pcre_compile(value, PCRE_UTF8 | PCRE_UCP, &reg_err, &offset, NULL);
*regex = pcre2_compile((PCRE2_SPTR)value, PCRE2_ZERO_TERMINATED, PCRE2_UTF | PCRE2_UCP, &errorcode, &offset, NULL);
if (!*regex) { if (!*regex) {
PCRE2_UCHAR buffer[256];
pcre2_get_error_message(errorcode, buffer, sizeof(buffer));
const char *fmt = "Regex compilation for '%s' failed: %s"; const char *fmt = "Regex compilation for '%s' failed: %s";
int len = strlen(fmt) + strlen(value) + strlen(reg_err) - 3; int len = strlen(fmt) + strlen(value) + strlen((char*) buffer) - 3;
error = malloc(len); error = malloc(len);
snprintf(error, len, fmt, value, reg_err); snprintf(error, len, fmt, value, buffer);
return false; return false;
} }
@ -66,7 +69,7 @@ static bool pattern_create(struct pattern **pattern, char *value) {
if (strcmp(value, "__focused__") == 0) { if (strcmp(value, "__focused__") == 0) {
(*pattern)->match_type = PATTERN_FOCUSED; (*pattern)->match_type = PATTERN_FOCUSED;
} else { } else {
(*pattern)->match_type = PATTERN_PCRE; (*pattern)->match_type = PATTERN_PCRE2;
if (!generate_regex(&(*pattern)->regex, value)) { if (!generate_regex(&(*pattern)->regex, value)) {
return false; return false;
}; };
@ -77,7 +80,7 @@ static bool pattern_create(struct pattern **pattern, char *value) {
static void pattern_destroy(struct pattern *pattern) { static void pattern_destroy(struct pattern *pattern) {
if (pattern) { if (pattern) {
if (pattern->regex) { if (pattern->regex) {
pcre_free(pattern->regex); pcre2_code_free(pattern->regex);
} }
free(pattern); free(pattern);
} }
@ -93,14 +96,18 @@ void criteria_destroy(struct criteria *criteria) {
pattern_destroy(criteria->window_role); pattern_destroy(criteria->window_role);
#endif #endif
pattern_destroy(criteria->con_mark); pattern_destroy(criteria->con_mark);
free(criteria->workspace); pattern_destroy(criteria->workspace);
free(criteria->target);
free(criteria->cmdlist); free(criteria->cmdlist);
free(criteria->raw); free(criteria->raw);
free(criteria); free(criteria);
} }
static int regex_cmp(const char *item, const pcre *regex) { static int regex_cmp(const char *item, const pcre2_code *regex) {
return pcre_exec(regex, NULL, item, strlen(item), 0, 0, NULL, 0); pcre2_match_data *match_data = pcre2_match_data_create_from_pattern(regex, NULL);
int result = pcre2_match(regex, (PCRE2_SPTR)item, strlen(item), 0, 0, match_data, NULL);
pcre2_match_data_free(match_data);
return result;
} }
#if HAVE_XWAYLAND #if HAVE_XWAYLAND
@ -155,7 +162,7 @@ static bool criteria_matches_container(struct criteria *criteria,
bool exists = false; bool exists = false;
struct sway_container *con = container; struct sway_container *con = container;
for (int i = 0; i < con->marks->length; ++i) { for (int i = 0; i < con->marks->length; ++i) {
if (regex_cmp(con->marks->items[i], criteria->con_mark->regex) == 0) { if (regex_cmp(con->marks->items[i], criteria->con_mark->regex) >= 0) {
exists = true; exists = true;
break; break;
} }
@ -183,7 +190,7 @@ static bool criteria_matches_view(struct criteria *criteria,
if (criteria->title) { if (criteria->title) {
const char *title = view_get_title(view); const char *title = view_get_title(view);
if (!title) { if (!title) {
return false; title = "";
} }
switch (criteria->title->match_type) { switch (criteria->title->match_type) {
@ -192,8 +199,8 @@ static bool criteria_matches_view(struct criteria *criteria,
return false; return false;
} }
break; break;
case PATTERN_PCRE: case PATTERN_PCRE2:
if (regex_cmp(title, criteria->title->regex) != 0) { if (regex_cmp(title, criteria->title->regex) < 0) {
return false; return false;
} }
break; break;
@ -203,7 +210,7 @@ static bool criteria_matches_view(struct criteria *criteria,
if (criteria->shell) { if (criteria->shell) {
const char *shell = view_get_shell(view); const char *shell = view_get_shell(view);
if (!shell) { if (!shell) {
return false; shell = "";
} }
switch (criteria->shell->match_type) { switch (criteria->shell->match_type) {
@ -212,8 +219,8 @@ static bool criteria_matches_view(struct criteria *criteria,
return false; return false;
} }
break; break;
case PATTERN_PCRE: case PATTERN_PCRE2:
if (regex_cmp(shell, criteria->shell->regex) != 0) { if (regex_cmp(shell, criteria->shell->regex) < 0) {
return false; return false;
} }
break; break;
@ -223,7 +230,7 @@ static bool criteria_matches_view(struct criteria *criteria,
if (criteria->app_id) { if (criteria->app_id) {
const char *app_id = view_get_app_id(view); const char *app_id = view_get_app_id(view);
if (!app_id) { if (!app_id) {
return false; app_id = "";
} }
switch (criteria->app_id->match_type) { switch (criteria->app_id->match_type) {
@ -232,8 +239,8 @@ static bool criteria_matches_view(struct criteria *criteria,
return false; return false;
} }
break; break;
case PATTERN_PCRE: case PATTERN_PCRE2:
if (regex_cmp(app_id, criteria->app_id->regex) != 0) { if (regex_cmp(app_id, criteria->app_id->regex) < 0) {
return false; return false;
} }
break; break;
@ -255,7 +262,7 @@ static bool criteria_matches_view(struct criteria *criteria,
if (criteria->class) { if (criteria->class) {
const char *class = view_get_class(view); const char *class = view_get_class(view);
if (!class) { if (!class) {
return false; class = "";
} }
switch (criteria->class->match_type) { switch (criteria->class->match_type) {
@ -264,8 +271,8 @@ static bool criteria_matches_view(struct criteria *criteria,
return false; return false;
} }
break; break;
case PATTERN_PCRE: case PATTERN_PCRE2:
if (regex_cmp(class, criteria->class->regex) != 0) { if (regex_cmp(class, criteria->class->regex) < 0) {
return false; return false;
} }
break; break;
@ -275,17 +282,17 @@ static bool criteria_matches_view(struct criteria *criteria,
if (criteria->instance) { if (criteria->instance) {
const char *instance = view_get_instance(view); const char *instance = view_get_instance(view);
if (!instance) { if (!instance) {
return false; instance = "";
} }
switch (criteria->instance->match_type) { switch (criteria->instance->match_type) {
case PATTERN_FOCUSED: case PATTERN_FOCUSED:
if (focused && strcmp(instance, view_get_instance(focused))) { if (focused && lenient_strcmp(instance, view_get_instance(focused))) {
return false; return false;
} }
break; break;
case PATTERN_PCRE: case PATTERN_PCRE2:
if (regex_cmp(instance, criteria->instance->regex) != 0) { if (regex_cmp(instance, criteria->instance->regex) < 0) {
return false; return false;
} }
break; break;
@ -295,17 +302,17 @@ static bool criteria_matches_view(struct criteria *criteria,
if (criteria->window_role) { if (criteria->window_role) {
const char *window_role = view_get_window_role(view); const char *window_role = view_get_window_role(view);
if (!window_role) { if (!window_role) {
return false; window_role = "";
} }
switch (criteria->window_role->match_type) { switch (criteria->window_role->match_type) {
case PATTERN_FOCUSED: 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; return false;
} }
break; break;
case PATTERN_PCRE: case PATTERN_PCRE2:
if (regex_cmp(window_role, criteria->window_role->regex) != 0) { if (regex_cmp(window_role, criteria->window_role->regex) < 0) {
return false; return false;
} }
break; break;
@ -363,8 +370,8 @@ static bool criteria_matches_view(struct criteria *criteria,
return false; return false;
} }
break; break;
case PATTERN_PCRE: case PATTERN_PCRE2:
if (regex_cmp(ws->name, criteria->workspace->regex) != 0) { if (regex_cmp(ws->name, criteria->workspace->regex) < 0) {
return false; return false;
} }
break; break;
@ -676,7 +683,7 @@ struct criteria *criteria_parse(char *raw, char **error_arg) {
} }
name = calloc(head - namestart + 1, 1); name = calloc(head - namestart + 1, 1);
if (head != namestart) { if (head != namestart) {
strncpy(name, namestart, head - namestart); memcpy(name, namestart, head - namestart);
} }
// Parse token value // Parse token value
skip_spaces(&head); skip_spaces(&head);
@ -703,7 +710,7 @@ struct criteria *criteria_parse(char *raw, char **error_arg) {
} }
} }
value = calloc(head - valuestart + 1, 1); value = calloc(head - valuestart + 1, 1);
strncpy(value, valuestart, head - valuestart); memcpy(value, valuestart, head - valuestart);
if (in_quotes) { if (in_quotes) {
++head; ++head;
in_quotes = false; in_quotes = false;
@ -734,7 +741,7 @@ struct criteria *criteria_parse(char *raw, char **error_arg) {
++head; ++head;
int len = head - raw; int len = head - raw;
criteria->raw = calloc(len + 1, 1); criteria->raw = calloc(len + 1, 1);
strncpy(criteria->raw, raw, len); memcpy(criteria->raw, raw, len);
return criteria; return criteria;
cleanup: cleanup:

View file

@ -6,10 +6,11 @@ void desktop_damage_surface(struct wlr_surface *surface, double lx, double ly,
bool whole) { bool whole) {
for (int i = 0; i < root->outputs->length; ++i) { for (int i = 0; i < root->outputs->length; ++i) {
struct sway_output *output = root->outputs->items[i]; struct sway_output *output = root->outputs->items[i];
struct wlr_box *output_box = wlr_output_layout_get_box( struct wlr_box output_box;
root->output_layout, output->wlr_output); wlr_output_layout_get_box(root->output_layout,
output_damage_surface(output, lx - output_box->x, output->wlr_output, &output_box);
ly - output_box->y, surface, whole); output_damage_surface(output, lx - output_box.x,
ly - output_box.y, surface, whole);
} }
} }

View file

@ -36,6 +36,71 @@ static const GLfloat verts[] = {
0, 1, // bottom left 0, 1, // bottom left
}; };
static const float transforms[][9] = {
[WL_OUTPUT_TRANSFORM_NORMAL] = {
1.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 1.0f,
},
[WL_OUTPUT_TRANSFORM_90] = {
0.0f, 1.0f, 0.0f,
-1.0f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f,
},
[WL_OUTPUT_TRANSFORM_180] = {
-1.0f, 0.0f, 0.0f,
0.0f, -1.0f, 0.0f,
0.0f, 0.0f, 1.0f,
},
[WL_OUTPUT_TRANSFORM_270] = {
0.0f, -1.0f, 0.0f,
1.0f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f,
},
[WL_OUTPUT_TRANSFORM_FLIPPED] = {
-1.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 1.0f,
},
[WL_OUTPUT_TRANSFORM_FLIPPED_90] = {
0.0f, 1.0f, 0.0f,
1.0f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f,
},
[WL_OUTPUT_TRANSFORM_FLIPPED_180] = {
1.0f, 0.0f, 0.0f,
0.0f, -1.0f, 0.0f,
0.0f, 0.0f, 1.0f,
},
[WL_OUTPUT_TRANSFORM_FLIPPED_270] = {
0.0f, -1.0f, 0.0f,
-1.0f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f,
},
};
static void matrix_projection(float mat[static 9], int width, int height,
enum wl_output_transform transform) {
memset(mat, 0, sizeof(*mat) * 9);
const float *t = transforms[transform];
float x = 2.0f / width;
float y = 2.0f / height;
// Rotation + reflection
mat[0] = x * t[0];
mat[1] = x * t[1];
mat[3] = y * -t[3];
mat[4] = y * -t[4];
// Translation
mat[2] = -copysign(1.0f, mat[0] + mat[1]);
mat[5] = -copysign(1.0f, mat[3] + mat[4]);
// Identity
mat[8] = 1.0f;
}
static GLuint compile_shader(GLuint type, const GLchar *src) { static GLuint compile_shader(GLuint type, const GLchar *src) {
GLuint shader = glCreateShader(type); GLuint shader = glCreateShader(type);
glShaderSource(shader, 1, &src, NULL); glShaderSource(shader, 1, &src, NULL);
@ -159,7 +224,8 @@ struct fx_renderer *fx_renderer_create(struct wlr_egl *egl) {
// TODO: wlr_egl_make_current or eglMakeCurrent? // TODO: wlr_egl_make_current or eglMakeCurrent?
// TODO: assert instead of conditional statement? // TODO: assert instead of conditional statement?
if (!wlr_egl_make_current(egl)) { if (!eglMakeCurrent(wlr_egl_get_display(egl), EGL_NO_SURFACE, EGL_NO_SURFACE,
wlr_egl_get_context(egl))) {
sway_log(SWAY_ERROR, "GLES2 RENDERER: Could not make EGL current"); sway_log(SWAY_ERROR, "GLES2 RENDERER: Could not make EGL current");
return NULL; return NULL;
} }
@ -259,7 +325,11 @@ struct fx_renderer *fx_renderer_create(struct wlr_egl *egl) {
goto error; goto error;
} }
wlr_egl_unset_current(renderer->egl); if (!eglMakeCurrent(wlr_egl_get_display(renderer->egl),
EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)) {
sway_log(SWAY_ERROR, "GLES2 RENDERER: Could not unset current EGL");
goto error;
}
sway_log(SWAY_INFO, "GLES2 RENDERER: Shaders Initialized Successfully"); sway_log(SWAY_INFO, "GLES2 RENDERER: Shaders Initialized Successfully");
return renderer; return renderer;
@ -275,7 +345,10 @@ error:
glDeleteProgram(renderer->shaders.tex_rgbx.program); glDeleteProgram(renderer->shaders.tex_rgbx.program);
glDeleteProgram(renderer->shaders.tex_ext.program); glDeleteProgram(renderer->shaders.tex_ext.program);
wlr_egl_unset_current(renderer->egl); if (!eglMakeCurrent(wlr_egl_get_display(renderer->egl),
EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)) {
sway_log(SWAY_ERROR, "GLES2 RENDERER: Could not unset current EGL");
}
// TODO: more freeing? // TODO: more freeing?
free(renderer); free(renderer);
@ -297,7 +370,7 @@ void fx_renderer_begin(struct fx_renderer *renderer, uint32_t width, uint32_t he
glViewport(0, 0, width, height); glViewport(0, 0, width, height);
// refresh projection matrix // refresh projection matrix
wlr_matrix_projection(renderer->projection, width, height, matrix_projection(renderer->projection, width, height,
WL_OUTPUT_TRANSFORM_FLIPPED_180); WL_OUTPUT_TRANSFORM_FLIPPED_180);
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);

View file

@ -1,5 +1,6 @@
#include <stdlib.h> #include <stdlib.h>
#include <wlr/types/wlr_idle.h> #include <wlr/types/wlr_idle.h>
#include <wlr/types/wlr_idle_notify_v1.h>
#include "log.h" #include "log.h"
#include "sway/desktop/idle_inhibit_v1.h" #include "sway/desktop/idle_inhibit_v1.h"
#include "sway/input/seat.h" #include "sway/input/seat.h"
@ -140,6 +141,7 @@ void sway_idle_inhibit_v1_check_active(
} }
} }
wlr_idle_set_enabled(manager->idle, NULL, !inhibited); wlr_idle_set_enabled(manager->idle, NULL, !inhibited);
wlr_idle_notifier_v1_set_inhibited(server.idle_notifier_v1, inhibited);
} }
struct sway_idle_inhibit_manager_v1 *sway_idle_inhibit_manager_v1_create( struct sway_idle_inhibit_manager_v1 *sway_idle_inhibit_manager_v1_create(

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

@ -3,8 +3,8 @@
#include <string.h> #include <string.h>
#include <wayland-server-core.h> #include <wayland-server-core.h>
#include <wlr/types/wlr_layer_shell_v1.h> #include <wlr/types/wlr_layer_shell_v1.h>
#include <wlr/types/wlr_output_damage.h>
#include <wlr/types/wlr_output.h> #include <wlr/types/wlr_output.h>
#include <wlr/types/wlr_subcompositor.h>
#include "log.h" #include "log.h"
#include "sway/desktop/transaction.h" #include "sway/desktop/transaction.h"
#include "sway/input/cursor.h" #include "sway/input/cursor.h"
@ -270,10 +270,6 @@ static void handle_output_destroy(struct wl_listener *listener, void *data) {
wl_resource_get_client(sway_layer->layer_surface->resource); wl_resource_get_client(sway_layer->layer_surface->resource);
bool set_focus = seat->exclusive_client == client; bool set_focus = seat->exclusive_client == client;
wl_list_remove(&sway_layer->output_destroy.link);
wl_list_remove(&sway_layer->link);
wl_list_init(&sway_layer->link);
if (set_focus) { if (set_focus) {
struct sway_layer_surface *layer = struct sway_layer_surface *layer =
find_mapped_layer_by_client(client, sway_layer->layer_surface->output); find_mapped_layer_by_client(client, sway_layer->layer_surface->output);
@ -282,7 +278,6 @@ static void handle_output_destroy(struct wl_listener *listener, void *data) {
} }
} }
sway_layer->layer_surface->output = NULL;
wlr_layer_surface_v1_destroy(sway_layer->layer_surface); wlr_layer_surface_v1_destroy(sway_layer->layer_surface);
} }
@ -291,10 +286,7 @@ static void handle_surface_commit(struct wl_listener *listener, void *data) {
wl_container_of(listener, layer, surface_commit); wl_container_of(listener, layer, surface_commit);
struct wlr_layer_surface_v1 *layer_surface = layer->layer_surface; struct wlr_layer_surface_v1 *layer_surface = layer->layer_surface;
struct wlr_output *wlr_output = layer_surface->output; struct wlr_output *wlr_output = layer_surface->output;
if (wlr_output == NULL) { sway_assert(wlr_output, "wlr_layer_surface_v1 has null output");
return;
}
struct sway_output *output = wlr_output->data; struct sway_output *output = wlr_output->data;
struct wlr_box old_extent = layer->extent; struct wlr_box old_extent = layer->extent;
@ -341,13 +333,8 @@ static void unmap(struct sway_layer_surface *sway_layer) {
cursor_rebase_all(); cursor_rebase_all();
struct wlr_output *wlr_output = sway_layer->layer_surface->output; struct wlr_output *wlr_output = sway_layer->layer_surface->output;
if (wlr_output == NULL) { sway_assert(wlr_output, "wlr_layer_surface_v1 has null output");
return;
}
struct sway_output *output = wlr_output->data; struct sway_output *output = wlr_output->data;
if (output == NULL) {
return;
}
output_damage_surface(output, sway_layer->geo.x, sway_layer->geo.y, output_damage_surface(output, sway_layer->geo.x, sway_layer->geo.y,
sway_layer->layer_surface->surface, true); sway_layer->layer_surface->surface, true);
} }
@ -375,22 +362,24 @@ static void handle_destroy(struct wl_listener *listener, void *data) {
wl_list_remove(&sway_layer->surface_commit.link); wl_list_remove(&sway_layer->surface_commit.link);
wl_list_remove(&sway_layer->new_popup.link); wl_list_remove(&sway_layer->new_popup.link);
wl_list_remove(&sway_layer->new_subsurface.link); wl_list_remove(&sway_layer->new_subsurface.link);
if (sway_layer->layer_surface->output != NULL) {
struct sway_output *output = sway_layer->layer_surface->output->data; struct wlr_output *wlr_output = sway_layer->layer_surface->output;
if (output != NULL) { sway_assert(wlr_output, "wlr_layer_surface_v1 has null output");
arrange_layers(output); struct sway_output *output = wlr_output->data;
transaction_commit_dirty(); arrange_layers(output);
} transaction_commit_dirty();
wl_list_remove(&sway_layer->output_destroy.link); wl_list_remove(&sway_layer->output_destroy.link);
sway_layer->layer_surface->output = NULL; sway_layer->layer_surface->output = NULL;
}
free(sway_layer); free(sway_layer);
} }
static void handle_map(struct wl_listener *listener, void *data) { static void handle_map(struct wl_listener *listener, void *data) {
struct sway_layer_surface *sway_layer = wl_container_of(listener, struct sway_layer_surface *sway_layer = wl_container_of(listener,
sway_layer, map); sway_layer, map);
struct sway_output *output = sway_layer->layer_surface->output->data; struct wlr_output *wlr_output = sway_layer->layer_surface->output;
sway_assert(wlr_output, "wlr_layer_surface_v1 has null output");
struct sway_output *output = wlr_output->data;
output_damage_surface(output, sway_layer->geo.x, sway_layer->geo.y, output_damage_surface(output, sway_layer->geo.x, sway_layer->geo.y,
sway_layer->layer_surface->surface, true); sway_layer->layer_surface->surface, true);
wlr_surface_send_enter(sway_layer->layer_surface->surface, wlr_surface_send_enter(sway_layer->layer_surface->surface,
@ -408,9 +397,7 @@ static void subsurface_damage(struct sway_layer_subsurface *subsurface,
bool whole) { bool whole) {
struct sway_layer_surface *layer = subsurface->layer_surface; struct sway_layer_surface *layer = subsurface->layer_surface;
struct wlr_output *wlr_output = layer->layer_surface->output; struct wlr_output *wlr_output = layer->layer_surface->output;
if (!wlr_output) { sway_assert(wlr_output, "wlr_layer_surface_v1 has null output");
return;
}
struct sway_output *output = wlr_output->data; struct sway_output *output = wlr_output->data;
int ox = subsurface->wlr_subsurface->current.x + layer->geo.x; int ox = subsurface->wlr_subsurface->current.x + layer->geo.x;
int oy = subsurface->wlr_subsurface->current.y + layer->geo.y; int oy = subsurface->wlr_subsurface->current.y + layer->geo.y;
@ -496,15 +483,15 @@ static struct sway_layer_surface *popup_get_layer(
static void popup_damage(struct sway_layer_popup *layer_popup, bool whole) { static void popup_damage(struct sway_layer_popup *layer_popup, bool whole) {
struct wlr_xdg_popup *popup = layer_popup->wlr_popup; struct wlr_xdg_popup *popup = layer_popup->wlr_popup;
struct wlr_surface *surface = popup->base->surface; struct wlr_surface *surface = popup->base->surface;
int popup_sx = popup->geometry.x - popup->base->current.geometry.x; int popup_sx = popup->current.geometry.x - popup->base->current.geometry.x;
int popup_sy = popup->geometry.y - popup->base->current.geometry.y; int popup_sy = popup->current.geometry.y - popup->base->current.geometry.y;
int ox = popup_sx, oy = popup_sy; int ox = popup_sx, oy = popup_sy;
struct sway_layer_surface *layer; struct sway_layer_surface *layer;
while (true) { while (true) {
if (layer_popup->parent_type == LAYER_PARENT_POPUP) { if (layer_popup->parent_type == LAYER_PARENT_POPUP) {
layer_popup = layer_popup->parent_popup; layer_popup = layer_popup->parent_popup;
ox += layer_popup->wlr_popup->geometry.x; ox += layer_popup->wlr_popup->current.geometry.x;
oy += layer_popup->wlr_popup->geometry.y; oy += layer_popup->wlr_popup->current.geometry.y;
} else { } else {
layer = layer_popup->parent_layer; layer = layer_popup->parent_layer;
ox += layer->geo.x; ox += layer->geo.x;
@ -513,6 +500,7 @@ static void popup_damage(struct sway_layer_popup *layer_popup, bool whole) {
} }
} }
struct wlr_output *wlr_output = layer->layer_surface->output; struct wlr_output *wlr_output = layer->layer_surface->output;
sway_assert(wlr_output, "wlr_layer_surface_v1 has null output");
struct sway_output *output = wlr_output->data; struct sway_output *output = wlr_output->data;
output_damage_surface(output, ox, oy, surface, whole); output_damage_surface(output, ox, oy, surface, whole);
} }
@ -521,6 +509,7 @@ static void popup_handle_map(struct wl_listener *listener, void *data) {
struct sway_layer_popup *popup = wl_container_of(listener, popup, map); struct sway_layer_popup *popup = wl_container_of(listener, popup, map);
struct sway_layer_surface *layer = popup_get_layer(popup); struct sway_layer_surface *layer = popup_get_layer(popup);
struct wlr_output *wlr_output = layer->layer_surface->output; struct wlr_output *wlr_output = layer->layer_surface->output;
sway_assert(wlr_output, "wlr_layer_surface_v1 has null output");
wlr_surface_send_enter(popup->wlr_popup->base->surface, wlr_output); wlr_surface_send_enter(popup->wlr_popup->base->surface, wlr_output);
popup_damage(popup, true); popup_damage(popup, true);
} }
@ -550,7 +539,9 @@ static void popup_unconstrain(struct sway_layer_popup *popup) {
struct sway_layer_surface *layer = popup_get_layer(popup); struct sway_layer_surface *layer = popup_get_layer(popup);
struct wlr_xdg_popup *wlr_popup = popup->wlr_popup; struct wlr_xdg_popup *wlr_popup = popup->wlr_popup;
struct sway_output *output = layer->layer_surface->output->data; struct wlr_output *wlr_output = layer->layer_surface->output;
sway_assert(wlr_output, "wlr_layer_surface_v1 has null output");
struct sway_output *output = wlr_output->data;
// the output box expressed in the coordinate system of the toplevel parent // the output box expressed in the coordinate system of the toplevel parent
// of the popup // of the popup
@ -642,6 +633,10 @@ void handle_layer_shell_surface(struct wl_listener *listener, void *data) {
sway_log(SWAY_ERROR, sway_log(SWAY_ERROR,
"no output to auto-assign layer surface '%s' to", "no output to auto-assign layer surface '%s' to",
layer_surface->namespace); layer_surface->namespace);
// Note that layer_surface->output can be NULL
// here, but none of our destroy callbacks are
// registered yet so we don't have to make them
// handle that case.
wlr_layer_surface_v1_destroy(layer_surface); wlr_layer_surface_v1_destroy(layer_surface);
return; return;
} }

View file

@ -10,11 +10,10 @@
#include <wlr/types/wlr_buffer.h> #include <wlr/types/wlr_buffer.h>
#include <wlr/types/wlr_drm_lease_v1.h> #include <wlr/types/wlr_drm_lease_v1.h>
#include <wlr/types/wlr_matrix.h> #include <wlr/types/wlr_matrix.h>
#include <wlr/types/wlr_output_damage.h>
#include <wlr/types/wlr_output_layout.h> #include <wlr/types/wlr_output_layout.h>
#include <wlr/types/wlr_output.h> #include <wlr/types/wlr_output.h>
#include <wlr/types/wlr_presentation_time.h> #include <wlr/types/wlr_presentation_time.h>
#include <wlr/types/wlr_surface.h> #include <wlr/types/wlr_compositor.h>
#include <wlr/util/region.h> #include <wlr/util/region.h>
#include "config.h" #include "config.h"
#include "log.h" #include "log.h"
@ -276,6 +275,25 @@ static void for_each_surface_container_iterator(struct sway_container *con,
static void output_for_each_surface(struct sway_output *output, static void output_for_each_surface(struct sway_output *output,
sway_surface_iterator_func_t iterator, void *user_data) { sway_surface_iterator_func_t iterator, void *user_data) {
if (server.session_lock.locked) {
if (server.session_lock.lock == NULL) {
return;
}
struct wlr_session_lock_surface_v1 *lock_surface;
wl_list_for_each(lock_surface, &server.session_lock.lock->surfaces, link) {
if (lock_surface->output != output->wlr_output) {
continue;
}
if (!lock_surface->mapped) {
continue;
}
output_surface_for_each_surface(output, lock_surface->surface,
0.0, 0.0, iterator, user_data);
}
return;
}
if (output_has_opaque_overlay_layer_surface(output)) { if (output_has_opaque_overlay_layer_surface(output)) {
goto overlay; goto overlay;
} }
@ -435,6 +453,10 @@ static bool scan_out_fullscreen_view(struct sway_output *output,
return false; return false;
} }
if (server.session_lock.locked) {
return false;
}
if (!wl_list_empty(&view->saved_buffers)) { if (!wl_list_empty(&view->saved_buffers)) {
return false; return false;
} }
@ -533,31 +555,43 @@ static int output_repaint_timer_handler(void *data) {
} }
} }
bool needs_frame; int buffer_age;
pixman_region32_t damage; if (!wlr_output_attach_render(output->wlr_output, &buffer_age)) {
pixman_region32_init(&damage);
if (!wlr_output_damage_attach_render(output->damage,
&needs_frame, &damage)) {
return 0; return 0;
} }
if (needs_frame) { pixman_region32_t damage;
struct timespec now; pixman_region32_init(&damage);
clock_gettime(CLOCK_MONOTONIC, &now); wlr_damage_ring_get_buffer_damage(&output->damage_ring, buffer_age, &damage);
if (!output->wlr_output->needs_frame &&
output_render(output, &now, &damage); !pixman_region32_not_empty(&output->damage_ring.current)) {
} else { pixman_region32_fini(&damage);
wlr_output_rollback(output->wlr_output); wlr_output_rollback(output->wlr_output);
return 0;
} }
struct timespec now;
clock_gettime(CLOCK_MONOTONIC, &now);
output_render(output, &now, &damage);
pixman_region32_fini(&damage); pixman_region32_fini(&damage);
return 0; return 0;
} }
static void damage_handle_frame(struct wl_listener *listener, void *user_data) { static void handle_damage(struct wl_listener *listener, void *user_data) {
struct sway_output *output = struct sway_output *output =
wl_container_of(listener, output, damage_frame); wl_container_of(listener, output, damage);
struct wlr_output_event_damage *event = user_data;
if (wlr_damage_ring_add(&output->damage_ring, event->damage)) {
wlr_output_schedule_frame(output->wlr_output);
}
}
static void handle_frame(struct wl_listener *listener, void *user_data) {
struct sway_output *output =
wl_container_of(listener, output, frame);
if (!output->enabled || !output->wlr_output->enabled) { if (!output->enabled || !output->wlr_output->enabled) {
return; return;
} }
@ -620,11 +654,18 @@ static void damage_handle_frame(struct wl_listener *listener, void *user_data) {
send_frame_done(output, &data); send_frame_done(output, &data);
} }
static void handle_needs_frame(struct wl_listener *listener, void *user_data) {
struct sway_output *output =
wl_container_of(listener, output, needs_frame);
wlr_output_schedule_frame(output->wlr_output);
}
void output_damage_whole(struct sway_output *output) { void output_damage_whole(struct sway_output *output) {
// The output can exist with no wlr_output if it's just been disconnected // The output can exist with no wlr_output if it's just been disconnected
// and the transaction to evacuate it has't completed yet. // and the transaction to evacuate it has't completed yet.
if (output && output->wlr_output && output->damage) { if (output != NULL && output->wlr_output != NULL) {
wlr_output_damage_add_whole(output->damage); wlr_damage_ring_add_whole(&output->damage_ring);
wlr_output_schedule_frame(output->wlr_output);
} }
} }
@ -648,11 +689,15 @@ static void damage_surface_iterator(struct sway_output *output,
ceil(output->wlr_output->scale) - surface->current.scale); ceil(output->wlr_output->scale) - surface->current.scale);
} }
pixman_region32_translate(&damage, box.x, box.y); pixman_region32_translate(&damage, box.x, box.y);
wlr_output_damage_add(output->damage, &damage); if (wlr_damage_ring_add(&output->damage_ring, &damage)) {
wlr_output_schedule_frame(output->wlr_output);
}
pixman_region32_fini(&damage); pixman_region32_fini(&damage);
if (whole) { if (whole) {
wlr_output_damage_add_box(output->damage, &box); if (wlr_damage_ring_add_box(&output->damage_ring, &box)) {
wlr_output_schedule_frame(output->wlr_output);
}
} }
if (!wl_list_empty(&surface->current.frame_callback_list)) { if (!wl_list_empty(&surface->current.frame_callback_list)) {
@ -682,7 +727,9 @@ void output_damage_box(struct sway_output *output, struct wlr_box *_box) {
box.x -= output->lx; box.x -= output->lx;
box.y -= output->ly; box.y -= output->ly;
scale_box(&box, output->wlr_output->scale); scale_box(&box, output->wlr_output->scale);
wlr_output_damage_add_box(output->damage, &box); if (wlr_damage_ring_add_box(&output->damage_ring, &box)) {
wlr_output_schedule_frame(output->wlr_output);
}
} }
static void damage_child_views_iterator(struct sway_container *con, static void damage_child_views_iterator(struct sway_container *con,
@ -707,7 +754,9 @@ void output_damage_whole_container(struct sway_output *output,
.height = con->current.height + 2 + shadow_sigma * 2, .height = con->current.height + 2 + shadow_sigma * 2,
}; };
scale_box(&box, output->wlr_output->scale); scale_box(&box, output->wlr_output->scale);
wlr_output_damage_add_box(output->damage, &box); if (wlr_damage_ring_add_box(&output->damage_ring, &box)) {
wlr_output_schedule_frame(output->wlr_output);
}
// Damage subsurfaces as well, which may extend outside the box // Damage subsurfaces as well, which may extend outside the box
if (con->view) { if (con->view) {
damage_child_views_iterator(con, output); damage_child_views_iterator(con, output);
@ -716,20 +765,6 @@ void output_damage_whole_container(struct sway_output *output,
} }
} }
static void damage_handle_destroy(struct wl_listener *listener, void *data) {
struct sway_output *output =
wl_container_of(listener, output, damage_destroy);
if (!output->enabled) {
return;
}
output_disable(output);
wl_list_remove(&output->damage_destroy.link);
wl_list_remove(&output->damage_frame.link);
transaction_commit_dirty();
}
static void update_output_manager_config(struct sway_server *server) { static void update_output_manager_config(struct sway_server *server) {
struct wlr_output_configuration_v1 *config = struct wlr_output_configuration_v1 *config =
wlr_output_configuration_v1_create(); wlr_output_configuration_v1_create();
@ -741,14 +776,15 @@ static void update_output_manager_config(struct sway_server *server) {
} }
struct wlr_output_configuration_head_v1 *config_head = struct wlr_output_configuration_head_v1 *config_head =
wlr_output_configuration_head_v1_create(config, output->wlr_output); wlr_output_configuration_head_v1_create(config, output->wlr_output);
struct wlr_box *output_box = wlr_output_layout_get_box( struct wlr_box output_box;
root->output_layout, output->wlr_output); wlr_output_layout_get_box(root->output_layout,
// We mark the output enabled even if it is switched off by DPMS output->wlr_output, &output_box);
// We mark the output enabled when it's switched off but not disabled
config_head->state.enabled = output->current_mode != NULL && output->enabled; config_head->state.enabled = output->current_mode != NULL && output->enabled;
config_head->state.mode = output->current_mode; config_head->state.mode = output->current_mode;
if (output_box) { if (!wlr_box_empty(&output_box)) {
config_head->state.x = output_box->x; config_head->state.x = output_box.x;
config_head->state.y = output_box->y; config_head->state.y = output_box.y;
} }
} }
@ -758,18 +794,24 @@ static void update_output_manager_config(struct sway_server *server) {
static void handle_destroy(struct wl_listener *listener, void *data) { static void handle_destroy(struct wl_listener *listener, void *data) {
struct sway_output *output = wl_container_of(listener, output, destroy); struct sway_output *output = wl_container_of(listener, output, destroy);
struct sway_server *server = output->server; struct sway_server *server = output->server;
output_begin_destroy(output);
if (output->enabled) { if (output->enabled) {
output_disable(output); output_disable(output);
} }
output_begin_destroy(output);
wl_list_remove(&output->link); wl_list_remove(&output->link);
wl_list_remove(&output->destroy.link); wl_list_remove(&output->destroy.link);
wl_list_remove(&output->commit.link); wl_list_remove(&output->commit.link);
wl_list_remove(&output->mode.link); wl_list_remove(&output->mode.link);
wl_list_remove(&output->present.link); wl_list_remove(&output->present.link);
wl_list_remove(&output->damage.link);
wl_list_remove(&output->frame.link);
wl_list_remove(&output->needs_frame.link);
wlr_damage_ring_finish(&output->damage_ring);
output->wlr_output->data = NULL; output->wlr_output->data = NULL;
output->wlr_output = NULL; output->wlr_output = NULL;
@ -797,10 +839,16 @@ static void handle_mode(struct wl_listener *listener, void *data) {
if (!output->enabled) { if (!output->enabled) {
return; return;
} }
arrange_layers(output); arrange_layers(output);
arrange_output(output); arrange_output(output);
transaction_commit_dirty(); transaction_commit_dirty();
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); update_output_manager_config(output->server);
} }
@ -828,6 +876,13 @@ static void handle_commit(struct wl_listener *listener, void *data) {
update_output_manager_config(output->server); update_output_manager_config(output->server);
} }
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);
}
} }
static void handle_present(struct wl_listener *listener, void *data) { static void handle_present(struct wl_listener *listener, void *data) {
@ -863,10 +918,12 @@ void handle_new_output(struct wl_listener *listener, void *data) {
if (wlr_output->non_desktop) { if (wlr_output->non_desktop) {
sway_log(SWAY_DEBUG, "Not configuring non-desktop output"); sway_log(SWAY_DEBUG, "Not configuring non-desktop output");
struct sway_output_non_desktop *non_desktop = output_non_desktop_create(wlr_output);
if (server->drm_lease_manager) { if (server->drm_lease_manager) {
wlr_drm_lease_v1_manager_offer_output(server->drm_lease_manager, wlr_drm_lease_v1_manager_offer_output(server->drm_lease_manager,
wlr_output); wlr_output);
} }
list_add(root->non_desktop_outputs, non_desktop);
return; return;
} }
@ -881,7 +938,7 @@ void handle_new_output(struct wl_listener *listener, void *data) {
return; return;
} }
output->server = server; output->server = server;
output->damage = wlr_output_damage_create(wlr_output); wlr_damage_ring_init(&output->damage_ring);
wl_signal_add(&wlr_output->events.destroy, &output->destroy); wl_signal_add(&wlr_output->events.destroy, &output->destroy);
output->destroy.notify = handle_destroy; output->destroy.notify = handle_destroy;
@ -891,10 +948,12 @@ void handle_new_output(struct wl_listener *listener, void *data) {
output->mode.notify = handle_mode; output->mode.notify = handle_mode;
wl_signal_add(&wlr_output->events.present, &output->present); wl_signal_add(&wlr_output->events.present, &output->present);
output->present.notify = handle_present; output->present.notify = handle_present;
wl_signal_add(&output->damage->events.frame, &output->damage_frame); wl_signal_add(&wlr_output->events.damage, &output->damage);
output->damage_frame.notify = damage_handle_frame; output->damage.notify = handle_damage;
wl_signal_add(&output->damage->events.destroy, &output->damage_destroy); wl_signal_add(&wlr_output->events.frame, &output->frame);
output->damage_destroy.notify = damage_handle_destroy; output->frame.notify = handle_frame;
wl_signal_add(&wlr_output->events.needs_frame, &output->needs_frame);
output->needs_frame.notify = handle_needs_frame;
output->repaint_timer = wl_event_loop_add_timer(server->wl_event_loop, output->repaint_timer = wl_event_loop_add_timer(server->wl_event_loop,
output_repaint_timer_handler, output); output_repaint_timer_handler, output);
@ -1008,10 +1067,10 @@ void handle_output_power_manager_set_mode(struct wl_listener *listener,
struct output_config *oc = new_output_config(output->wlr_output->name); struct output_config *oc = new_output_config(output->wlr_output->name);
switch (event->mode) { switch (event->mode) {
case ZWLR_OUTPUT_POWER_V1_MODE_OFF: case ZWLR_OUTPUT_POWER_V1_MODE_OFF:
oc->dpms_state = DPMS_OFF; oc->power = 0;
break; break;
case ZWLR_OUTPUT_POWER_V1_MODE_ON: case ZWLR_OUTPUT_POWER_V1_MODE_ON:
oc->dpms_state = DPMS_ON; oc->power = 1;
break; break;
} }
oc = store_output_config(oc); oc = store_output_config(oc);

View file

@ -9,11 +9,11 @@
#include <wlr/render/gles2.h> #include <wlr/render/gles2.h>
#include <wlr/render/wlr_renderer.h> #include <wlr/render/wlr_renderer.h>
#include <wlr/types/wlr_buffer.h> #include <wlr/types/wlr_buffer.h>
#include <wlr/types/wlr_damage_ring.h>
#include <wlr/types/wlr_matrix.h> #include <wlr/types/wlr_matrix.h>
#include <wlr/types/wlr_output_damage.h>
#include <wlr/types/wlr_output_layout.h> #include <wlr/types/wlr_output_layout.h>
#include <wlr/types/wlr_output.h> #include <wlr/types/wlr_output.h>
#include <wlr/types/wlr_surface.h> #include <wlr/types/wlr_compositor.h>
#include <wlr/util/region.h> #include <wlr/util/region.h>
#include "log.h" #include "log.h"
#include "config.h" #include "config.h"
@ -1420,6 +1420,41 @@ void output_render(struct sway_output *output, struct timespec *when,
fx_renderer_clear((float[]){1, 1, 0, 1}); fx_renderer_clear((float[]){1, 1, 0, 1});
} }
if (server.session_lock.locked) {
float clear_color[] = {0.0f, 0.0f, 0.0f, 1.0f};
if (server.session_lock.lock == NULL) {
// abandoned lock -> red BG
clear_color[0] = 1.f;
}
int nrects;
pixman_box32_t *rects = pixman_region32_rectangles(damage, &nrects);
for (int i = 0; i < nrects; ++i) {
scissor_output(wlr_output, &rects[i]);
fx_renderer_clear(clear_color);
}
if (server.session_lock.lock != NULL) {
struct render_data data = {
.damage = damage,
.deco_data = get_undecorated_decoration_data(),
};
struct wlr_session_lock_surface_v1 *lock_surface;
wl_list_for_each(lock_surface, &server.session_lock.lock->surfaces, link) {
if (lock_surface->output != wlr_output) {
continue;
}
if (!lock_surface->mapped) {
continue;
}
output_surface_for_each_surface(output, lock_surface->surface,
0.0, 0.0, render_surface_iterator, &data);
}
}
goto renderer_end;
}
if (output_has_opaque_overlay_layer_surface(output)) { if (output_has_opaque_overlay_layer_surface(output)) {
goto render_overlay; goto render_overlay;
} }
@ -1528,7 +1563,7 @@ renderer_end:
enum wl_output_transform transform = enum wl_output_transform transform =
wlr_output_transform_invert(wlr_output->transform); wlr_output_transform_invert(wlr_output->transform);
wlr_region_transform(&frame_damage, &output->damage->current, wlr_region_transform(&frame_damage, &output->damage_ring.current,
transform, width, height); transform, width, height);
if (debug.damage != DAMAGE_DEFAULT) { if (debug.damage != DAMAGE_DEFAULT) {
@ -1542,5 +1577,7 @@ renderer_end:
if (!wlr_output_commit(wlr_output)) { if (!wlr_output_commit(wlr_output)) {
return; return;
} }
wlr_damage_ring_rotate(&output->damage_ring);
output->last_frame = *when; output->last_frame = *when;
} }

View file

@ -1,7 +1,7 @@
#define _POSIX_C_SOURCE 200112L #define _POSIX_C_SOURCE 200112L
#include <stdlib.h> #include <stdlib.h>
#include <time.h> #include <time.h>
#include <wlr/types/wlr_surface.h> #include <wlr/types/wlr_compositor.h>
#include "sway/server.h" #include "sway/server.h"
#include "sway/surface.h" #include "sway/surface.h"

View file

@ -24,11 +24,11 @@ static const struct sway_view_child_impl popup_impl;
static void popup_get_view_coords(struct sway_view_child *child, static void popup_get_view_coords(struct sway_view_child *child,
int *sx, int *sy) { int *sx, int *sy) {
struct sway_xdg_popup *popup = (struct sway_xdg_popup *)child; struct sway_xdg_popup *popup = (struct sway_xdg_popup *)child;
struct wlr_xdg_surface *surface = popup->wlr_xdg_surface; struct wlr_xdg_popup *wlr_popup = popup->wlr_xdg_popup;
wlr_xdg_popup_get_toplevel_coords(surface->popup, wlr_xdg_popup_get_toplevel_coords(wlr_popup,
surface->popup->geometry.x - surface->current.geometry.x, wlr_popup->current.geometry.x - wlr_popup->base->current.geometry.x,
surface->popup->geometry.y - surface->current.geometry.y, wlr_popup->current.geometry.y - wlr_popup->base->current.geometry.y,
sx, sy); sx, sy);
} }
@ -65,7 +65,7 @@ static void popup_handle_destroy(struct wl_listener *listener, void *data) {
static void popup_unconstrain(struct sway_xdg_popup *popup) { static void popup_unconstrain(struct sway_xdg_popup *popup) {
struct sway_view *view = popup->child.view; struct sway_view *view = popup->child.view;
struct wlr_xdg_popup *wlr_popup = popup->wlr_xdg_surface->popup; struct wlr_xdg_popup *wlr_popup = popup->wlr_xdg_popup;
struct sway_output *output = view->container->pending.workspace->output; struct sway_output *output = view->container->pending.workspace->output;
@ -91,7 +91,7 @@ static struct sway_xdg_popup *popup_create(
return NULL; return NULL;
} }
view_child_init(&popup->child, &popup_impl, view, xdg_surface->surface); view_child_init(&popup->child, &popup_impl, view, xdg_surface->surface);
popup->wlr_xdg_surface = xdg_surface; popup->wlr_xdg_popup = xdg_surface->popup;
wl_signal_add(&xdg_surface->events.new_popup, &popup->new_popup); wl_signal_add(&xdg_surface->events.new_popup, &popup->new_popup);
popup->new_popup.notify = popup_handle_new_popup; popup->new_popup.notify = popup_handle_new_popup;
@ -119,7 +119,7 @@ static struct sway_xdg_shell_view *xdg_shell_view_from_view(
static void get_constraints(struct sway_view *view, double *min_width, static void get_constraints(struct sway_view *view, double *min_width,
double *max_width, double *min_height, double *max_height) { double *max_width, double *min_height, double *max_height) {
struct wlr_xdg_toplevel_state *state = struct wlr_xdg_toplevel_state *state =
&view->wlr_xdg_surface->toplevel->current; &view->wlr_xdg_toplevel->current;
*min_width = state->min_width > 0 ? state->min_width : DBL_MIN; *min_width = state->min_width > 0 ? state->min_width : DBL_MIN;
*max_width = state->max_width > 0 ? state->max_width : DBL_MAX; *max_width = state->max_width > 0 ? state->max_width : DBL_MAX;
*min_height = state->min_height > 0 ? state->min_height : DBL_MIN; *min_height = state->min_height > 0 ? state->min_height : DBL_MIN;
@ -133,9 +133,9 @@ static const char *get_string_prop(struct sway_view *view,
} }
switch (prop) { switch (prop) {
case VIEW_PROP_TITLE: case VIEW_PROP_TITLE:
return view->wlr_xdg_surface->toplevel->title; return view->wlr_xdg_toplevel->title;
case VIEW_PROP_APP_ID: case VIEW_PROP_APP_ID:
return view->wlr_xdg_surface->toplevel->app_id; return view->wlr_xdg_toplevel->app_id;
default: default:
return NULL; return NULL;
} }
@ -148,50 +148,45 @@ static uint32_t configure(struct sway_view *view, double lx, double ly,
if (xdg_shell_view == NULL) { if (xdg_shell_view == NULL) {
return 0; return 0;
} }
return wlr_xdg_toplevel_set_size(view->wlr_xdg_surface, width, height); return wlr_xdg_toplevel_set_size(view->wlr_xdg_toplevel,
width, height);
} }
static void set_activated(struct sway_view *view, bool activated) { static void set_activated(struct sway_view *view, bool activated) {
if (xdg_shell_view_from_view(view) == NULL) { if (xdg_shell_view_from_view(view) == NULL) {
return; return;
} }
struct wlr_xdg_surface *surface = view->wlr_xdg_surface; wlr_xdg_toplevel_set_activated(view->wlr_xdg_toplevel, activated);
if (surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL) {
wlr_xdg_toplevel_set_activated(surface, activated);
}
} }
static void set_tiled(struct sway_view *view, bool tiled) { static void set_tiled(struct sway_view *view, bool tiled) {
if (xdg_shell_view_from_view(view) == NULL) { if (xdg_shell_view_from_view(view) == NULL) {
return; return;
} }
struct wlr_xdg_surface *surface = view->wlr_xdg_surface;
enum wlr_edges edges = WLR_EDGE_NONE; enum wlr_edges edges = WLR_EDGE_NONE;
if (tiled) { if (tiled) {
edges = WLR_EDGE_LEFT | WLR_EDGE_RIGHT | WLR_EDGE_TOP | edges = WLR_EDGE_LEFT | WLR_EDGE_RIGHT | WLR_EDGE_TOP |
WLR_EDGE_BOTTOM; WLR_EDGE_BOTTOM;
} }
wlr_xdg_toplevel_set_tiled(surface, edges); wlr_xdg_toplevel_set_tiled(view->wlr_xdg_toplevel, edges);
} }
static void set_fullscreen(struct sway_view *view, bool fullscreen) { static void set_fullscreen(struct sway_view *view, bool fullscreen) {
if (xdg_shell_view_from_view(view) == NULL) { if (xdg_shell_view_from_view(view) == NULL) {
return; return;
} }
struct wlr_xdg_surface *surface = view->wlr_xdg_surface; wlr_xdg_toplevel_set_fullscreen(view->wlr_xdg_toplevel, fullscreen);
wlr_xdg_toplevel_set_fullscreen(surface, fullscreen);
} }
static void set_resizing(struct sway_view *view, bool resizing) { static void set_resizing(struct sway_view *view, bool resizing) {
if (xdg_shell_view_from_view(view) == NULL) { if (xdg_shell_view_from_view(view) == NULL) {
return; return;
} }
struct wlr_xdg_surface *surface = view->wlr_xdg_surface; wlr_xdg_toplevel_set_resizing(view->wlr_xdg_toplevel, resizing);
wlr_xdg_toplevel_set_resizing(surface, resizing);
} }
static bool wants_floating(struct sway_view *view) { static bool wants_floating(struct sway_view *view) {
struct wlr_xdg_toplevel *toplevel = view->wlr_xdg_surface->toplevel; struct wlr_xdg_toplevel *toplevel = view->wlr_xdg_toplevel;
struct wlr_xdg_toplevel_state *state = &toplevel->current; struct wlr_xdg_toplevel_state *state = &toplevel->current;
return (state->min_width != 0 && state->min_height != 0 return (state->min_width != 0 && state->min_height != 0
&& (state->min_width == state->max_width && (state->min_width == state->max_width
@ -204,7 +199,7 @@ static void for_each_surface(struct sway_view *view,
if (xdg_shell_view_from_view(view) == NULL) { if (xdg_shell_view_from_view(view) == NULL) {
return; return;
} }
wlr_xdg_surface_for_each_surface(view->wlr_xdg_surface, iterator, wlr_xdg_surface_for_each_surface(view->wlr_xdg_toplevel->base, iterator,
user_data); user_data);
} }
@ -213,8 +208,8 @@ static void for_each_popup_surface(struct sway_view *view,
if (xdg_shell_view_from_view(view) == NULL) { if (xdg_shell_view_from_view(view) == NULL) {
return; return;
} }
wlr_xdg_surface_for_each_popup_surface(view->wlr_xdg_surface, iterator, wlr_xdg_surface_for_each_popup_surface(view->wlr_xdg_toplevel->base,
user_data); iterator, user_data);
} }
static bool is_transient_for(struct sway_view *child, static bool is_transient_for(struct sway_view *child,
@ -222,12 +217,12 @@ static bool is_transient_for(struct sway_view *child,
if (xdg_shell_view_from_view(child) == NULL) { if (xdg_shell_view_from_view(child) == NULL) {
return false; return false;
} }
struct wlr_xdg_surface *surface = child->wlr_xdg_surface; struct wlr_xdg_toplevel *toplevel = child->wlr_xdg_toplevel;
while (surface && surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL) { while (toplevel) {
if (surface->toplevel->parent == ancestor->wlr_xdg_surface) { if (toplevel->parent == ancestor->wlr_xdg_toplevel) {
return true; return true;
} }
surface = surface->toplevel->parent; toplevel = toplevel->parent;
} }
return false; return false;
} }
@ -236,17 +231,13 @@ static void _close(struct sway_view *view) {
if (xdg_shell_view_from_view(view) == NULL) { if (xdg_shell_view_from_view(view) == NULL) {
return; return;
} }
struct wlr_xdg_surface *surface = view->wlr_xdg_surface; wlr_xdg_toplevel_send_close(view->wlr_xdg_toplevel);
if (surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL
&& surface->toplevel) {
wlr_xdg_toplevel_send_close(surface);
}
} }
static void close_popups(struct sway_view *view) { static void close_popups(struct sway_view *view) {
struct wlr_xdg_popup *popup, *tmp; struct wlr_xdg_popup *popup, *tmp;
wl_list_for_each_safe(popup, tmp, &view->wlr_xdg_surface->popups, link) { wl_list_for_each_safe(popup, tmp, &view->wlr_xdg_toplevel->base->popups, link) {
wlr_xdg_popup_destroy(popup->base); wlr_xdg_popup_destroy(popup);
} }
} }
@ -280,7 +271,7 @@ static void handle_commit(struct wl_listener *listener, void *data) {
struct sway_xdg_shell_view *xdg_shell_view = struct sway_xdg_shell_view *xdg_shell_view =
wl_container_of(listener, xdg_shell_view, commit); wl_container_of(listener, xdg_shell_view, commit);
struct sway_view *view = &xdg_shell_view->view; struct sway_view *view = &xdg_shell_view->view;
struct wlr_xdg_surface *xdg_surface = view->wlr_xdg_surface; struct wlr_xdg_surface *xdg_surface = view->wlr_xdg_toplevel->base;
struct wlr_box new_geo; struct wlr_box new_geo;
wlr_xdg_surface_get_geometry(xdg_surface, &new_geo); wlr_xdg_surface_get_geometry(xdg_surface, &new_geo);
@ -334,26 +325,27 @@ static void handle_new_popup(struct wl_listener *listener, void *data) {
popup_create(wlr_popup, &xdg_shell_view->view); popup_create(wlr_popup, &xdg_shell_view->view);
} }
static void handle_request_maximize(struct wl_listener *listener, void *data) {
struct sway_xdg_shell_view *xdg_shell_view =
wl_container_of(listener, xdg_shell_view, request_maximize);
struct wlr_xdg_toplevel *toplevel = xdg_shell_view->view.wlr_xdg_toplevel;
wlr_xdg_surface_schedule_configure(toplevel->base);
}
static void handle_request_fullscreen(struct wl_listener *listener, void *data) { static void handle_request_fullscreen(struct wl_listener *listener, void *data) {
struct sway_xdg_shell_view *xdg_shell_view = struct sway_xdg_shell_view *xdg_shell_view =
wl_container_of(listener, xdg_shell_view, request_fullscreen); wl_container_of(listener, xdg_shell_view, request_fullscreen);
struct wlr_xdg_toplevel_set_fullscreen_event *e = data; struct wlr_xdg_toplevel *toplevel = xdg_shell_view->view.wlr_xdg_toplevel;
struct wlr_xdg_surface *xdg_surface =
xdg_shell_view->view.wlr_xdg_surface;
struct sway_view *view = &xdg_shell_view->view; struct sway_view *view = &xdg_shell_view->view;
if (!sway_assert(xdg_surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL, if (!toplevel->base->mapped) {
"xdg_shell requested fullscreen of surface with role %i",
xdg_surface->role)) {
return;
}
if (!xdg_surface->mapped) {
return; return;
} }
struct sway_container *container = view->container; struct sway_container *container = view->container;
if (e->fullscreen && e->output && e->output->data) { struct wlr_xdg_toplevel_requested *req = &toplevel->requested;
struct sway_output *output = e->output->data; if (req->fullscreen && req->fullscreen_output && req->fullscreen_output->data) {
struct sway_output *output = req->fullscreen_output->data;
struct sway_workspace *ws = output_get_active_workspace(output); struct sway_workspace *ws = output_get_active_workspace(output);
if (ws && !container_is_scratchpad_hidden(container) && if (ws && !container_is_scratchpad_hidden(container) &&
container->pending.workspace != ws) { container->pending.workspace != ws) {
@ -365,7 +357,7 @@ static void handle_request_fullscreen(struct wl_listener *listener, void *data)
} }
} }
container_set_fullscreen(container, e->fullscreen); container_set_fullscreen(container, req->fullscreen);
arrange_root(); arrange_root();
transaction_commit_dirty(); transaction_commit_dirty();
@ -375,7 +367,8 @@ static void handle_request_move(struct wl_listener *listener, void *data) {
struct sway_xdg_shell_view *xdg_shell_view = struct sway_xdg_shell_view *xdg_shell_view =
wl_container_of(listener, xdg_shell_view, request_move); wl_container_of(listener, xdg_shell_view, request_move);
struct sway_view *view = &xdg_shell_view->view; struct sway_view *view = &xdg_shell_view->view;
if (!container_is_floating(view->container)) { if (!container_is_floating(view->container) ||
view->container->pending.fullscreen_mode) {
return; return;
} }
struct wlr_xdg_toplevel_move_event *e = data; struct wlr_xdg_toplevel_move_event *e = data;
@ -412,6 +405,7 @@ static void handle_unmap(struct wl_listener *listener, void *data) {
wl_list_remove(&xdg_shell_view->commit.link); wl_list_remove(&xdg_shell_view->commit.link);
wl_list_remove(&xdg_shell_view->new_popup.link); wl_list_remove(&xdg_shell_view->new_popup.link);
wl_list_remove(&xdg_shell_view->request_maximize.link);
wl_list_remove(&xdg_shell_view->request_fullscreen.link); wl_list_remove(&xdg_shell_view->request_fullscreen.link);
wl_list_remove(&xdg_shell_view->request_move.link); wl_list_remove(&xdg_shell_view->request_move.link);
wl_list_remove(&xdg_shell_view->request_resize.link); wl_list_remove(&xdg_shell_view->request_resize.link);
@ -423,13 +417,13 @@ static void handle_map(struct wl_listener *listener, void *data) {
struct sway_xdg_shell_view *xdg_shell_view = struct sway_xdg_shell_view *xdg_shell_view =
wl_container_of(listener, xdg_shell_view, map); wl_container_of(listener, xdg_shell_view, map);
struct sway_view *view = &xdg_shell_view->view; struct sway_view *view = &xdg_shell_view->view;
struct wlr_xdg_surface *xdg_surface = view->wlr_xdg_surface; struct wlr_xdg_toplevel *toplevel = view->wlr_xdg_toplevel;
view->natural_width = view->wlr_xdg_surface->current.geometry.width; view->natural_width = toplevel->base->current.geometry.width;
view->natural_height = view->wlr_xdg_surface->current.geometry.height; view->natural_height = toplevel->base->current.geometry.height;
if (!view->natural_width && !view->natural_height) { if (!view->natural_width && !view->natural_height) {
view->natural_width = view->wlr_xdg_surface->surface->current.width; view->natural_width = toplevel->base->surface->current.width;
view->natural_height = view->wlr_xdg_surface->surface->current.height; view->natural_height = toplevel->base->surface->current.height;
} }
bool csd = false; bool csd = false;
@ -440,44 +434,48 @@ static void handle_map(struct wl_listener *listener, void *data) {
csd = mode == WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE; csd = mode == WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE;
} else { } else {
struct sway_server_decoration *deco = struct sway_server_decoration *deco =
decoration_from_surface(xdg_surface->surface); decoration_from_surface(toplevel->base->surface);
csd = !deco || deco->wlr_server_decoration->mode == csd = !deco || deco->wlr_server_decoration->mode ==
WLR_SERVER_DECORATION_MANAGER_MODE_CLIENT; WLR_SERVER_DECORATION_MANAGER_MODE_CLIENT;
} }
view_map(view, view->wlr_xdg_surface->surface, view_map(view, toplevel->base->surface,
xdg_surface->toplevel->requested.fullscreen, toplevel->requested.fullscreen,
xdg_surface->toplevel->requested.fullscreen_output, toplevel->requested.fullscreen_output,
csd); csd);
transaction_commit_dirty(); transaction_commit_dirty();
xdg_shell_view->commit.notify = handle_commit; xdg_shell_view->commit.notify = handle_commit;
wl_signal_add(&xdg_surface->surface->events.commit, wl_signal_add(&toplevel->base->surface->events.commit,
&xdg_shell_view->commit); &xdg_shell_view->commit);
xdg_shell_view->new_popup.notify = handle_new_popup; xdg_shell_view->new_popup.notify = handle_new_popup;
wl_signal_add(&xdg_surface->events.new_popup, wl_signal_add(&toplevel->base->events.new_popup,
&xdg_shell_view->new_popup); &xdg_shell_view->new_popup);
xdg_shell_view->request_maximize.notify = handle_request_maximize;
wl_signal_add(&toplevel->events.request_maximize,
&xdg_shell_view->request_maximize);
xdg_shell_view->request_fullscreen.notify = handle_request_fullscreen; xdg_shell_view->request_fullscreen.notify = handle_request_fullscreen;
wl_signal_add(&xdg_surface->toplevel->events.request_fullscreen, wl_signal_add(&toplevel->events.request_fullscreen,
&xdg_shell_view->request_fullscreen); &xdg_shell_view->request_fullscreen);
xdg_shell_view->request_move.notify = handle_request_move; xdg_shell_view->request_move.notify = handle_request_move;
wl_signal_add(&xdg_surface->toplevel->events.request_move, wl_signal_add(&toplevel->events.request_move,
&xdg_shell_view->request_move); &xdg_shell_view->request_move);
xdg_shell_view->request_resize.notify = handle_request_resize; xdg_shell_view->request_resize.notify = handle_request_resize;
wl_signal_add(&xdg_surface->toplevel->events.request_resize, wl_signal_add(&toplevel->events.request_resize,
&xdg_shell_view->request_resize); &xdg_shell_view->request_resize);
xdg_shell_view->set_title.notify = handle_set_title; xdg_shell_view->set_title.notify = handle_set_title;
wl_signal_add(&xdg_surface->toplevel->events.set_title, wl_signal_add(&toplevel->events.set_title,
&xdg_shell_view->set_title); &xdg_shell_view->set_title);
xdg_shell_view->set_app_id.notify = handle_set_app_id; xdg_shell_view->set_app_id.notify = handle_set_app_id;
wl_signal_add(&xdg_surface->toplevel->events.set_app_id, wl_signal_add(&toplevel->events.set_app_id,
&xdg_shell_view->set_app_id); &xdg_shell_view->set_app_id);
} }
@ -491,7 +489,7 @@ static void handle_destroy(struct wl_listener *listener, void *data) {
wl_list_remove(&xdg_shell_view->destroy.link); wl_list_remove(&xdg_shell_view->destroy.link);
wl_list_remove(&xdg_shell_view->map.link); wl_list_remove(&xdg_shell_view->map.link);
wl_list_remove(&xdg_shell_view->unmap.link); wl_list_remove(&xdg_shell_view->unmap.link);
view->wlr_xdg_surface = NULL; view->wlr_xdg_toplevel = NULL;
if (view->xdg_decoration) { if (view->xdg_decoration) {
view->xdg_decoration->view = NULL; view->xdg_decoration->view = NULL;
} }
@ -522,7 +520,7 @@ void handle_xdg_shell_surface(struct wl_listener *listener, void *data) {
} }
view_init(&xdg_shell_view->view, SWAY_VIEW_XDG_SHELL, &view_impl); view_init(&xdg_shell_view->view, SWAY_VIEW_XDG_SHELL, &view_impl);
xdg_shell_view->view.wlr_xdg_surface = xdg_surface; xdg_shell_view->view.wlr_xdg_toplevel = xdg_surface->toplevel;
xdg_shell_view->map.notify = handle_map; xdg_shell_view->map.notify = handle_map;
wl_signal_add(&xdg_surface->events.map, &xdg_shell_view->map); wl_signal_add(&xdg_surface->events.map, &xdg_shell_view->map);

View file

@ -5,7 +5,9 @@
#include <wayland-server-core.h> #include <wayland-server-core.h>
#include <wlr/types/wlr_output_layout.h> #include <wlr/types/wlr_output_layout.h>
#include <wlr/types/wlr_output.h> #include <wlr/types/wlr_output.h>
#include <wlr/types/wlr_xdg_activation_v1.h>
#include <wlr/xwayland.h> #include <wlr/xwayland.h>
#include <xcb/xcb_icccm.h>
#include "log.h" #include "log.h"
#include "sway/desktop.h" #include "sway/desktop.h"
#include "sway/desktop/transaction.h" #include "sway/desktop/transaction.h"
@ -15,6 +17,7 @@
#include "sway/output.h" #include "sway/output.h"
#include "sway/tree/arrange.h" #include "sway/tree/arrange.h"
#include "sway/tree/container.h" #include "sway/tree/container.h"
#include "sway/server.h"
#include "sway/tree/view.h" #include "sway/tree/view.h"
#include "sway/tree/workspace.h" #include "sway/tree/workspace.h"
@ -121,6 +124,20 @@ static void unmanaged_handle_unmap(struct wl_listener *listener, void *data) {
} }
} }
static void unmanaged_handle_request_activate(struct wl_listener *listener, void *data) {
struct wlr_xwayland_surface *xsurface = data;
if (!xsurface->mapped) {
return;
}
struct sway_seat *seat = input_manager_current_seat();
struct sway_container *focus = seat_get_focused_container(seat);
if (focus && focus->view && focus->view->pid != xsurface->pid) {
return;
}
seat_set_focus_surface(seat, xsurface->surface, false);
}
static void unmanaged_handle_destroy(struct wl_listener *listener, void *data) { static void unmanaged_handle_destroy(struct wl_listener *listener, void *data) {
struct sway_xwayland_unmanaged *surface = struct sway_xwayland_unmanaged *surface =
wl_container_of(listener, surface, destroy); wl_container_of(listener, surface, destroy);
@ -129,6 +146,7 @@ static void unmanaged_handle_destroy(struct wl_listener *listener, void *data) {
wl_list_remove(&surface->unmap.link); wl_list_remove(&surface->unmap.link);
wl_list_remove(&surface->destroy.link); wl_list_remove(&surface->destroy.link);
wl_list_remove(&surface->override_redirect.link); wl_list_remove(&surface->override_redirect.link);
wl_list_remove(&surface->request_activate.link);
free(surface); free(surface);
} }
@ -176,6 +194,8 @@ static struct sway_xwayland_unmanaged *create_unmanaged(
surface->destroy.notify = unmanaged_handle_destroy; surface->destroy.notify = unmanaged_handle_destroy;
wl_signal_add(&xsurface->events.set_override_redirect, &surface->override_redirect); wl_signal_add(&xsurface->events.set_override_redirect, &surface->override_redirect);
surface->override_redirect.notify = unmanaged_handle_override_redirect; surface->override_redirect.notify = unmanaged_handle_override_redirect;
wl_signal_add(&xsurface->events.request_activate, &surface->request_activate);
surface->request_activate.notify = unmanaged_handle_request_activate;
return surface; return surface;
} }
@ -294,7 +314,7 @@ static bool wants_floating(struct sway_view *view) {
} }
} }
struct wlr_xwayland_surface_size_hints *size_hints = surface->size_hints; xcb_size_hints_t *size_hints = surface->size_hints;
if (size_hints != NULL && if (size_hints != NULL &&
size_hints->min_width > 0 && size_hints->min_height > 0 && size_hints->min_width > 0 && size_hints->min_height > 0 &&
(size_hints->max_width == size_hints->min_width || (size_hints->max_width == size_hints->min_width ||
@ -348,7 +368,7 @@ static void destroy(struct sway_view *view) {
static void get_constraints(struct sway_view *view, double *min_width, static void get_constraints(struct sway_view *view, double *min_width,
double *max_width, double *min_height, double *max_height) { double *max_width, double *min_height, double *max_height) {
struct wlr_xwayland_surface *surface = view->wlr_xwayland_surface; struct wlr_xwayland_surface *surface = view->wlr_xwayland_surface;
struct wlr_xwayland_surface_size_hints *size_hints = surface->size_hints; xcb_size_hints_t *size_hints = surface->size_hints;
if (size_hints == NULL) { if (size_hints == NULL) {
*min_width = DBL_MIN; *min_width = DBL_MIN;
@ -448,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_title.link);
wl_list_remove(&xwayland_view->set_class.link); wl_list_remove(&xwayland_view->set_class.link);
wl_list_remove(&xwayland_view->set_role.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_window_type.link);
wl_list_remove(&xwayland_view->set_hints.link); wl_list_remove(&xwayland_view->set_hints.link);
wl_list_remove(&xwayland_view->set_decorations.link); wl_list_remove(&xwayland_view->set_decorations.link);
@ -577,7 +598,8 @@ static void handle_request_move(struct wl_listener *listener, void *data) {
if (!xsurface->mapped) { if (!xsurface->mapped) {
return; return;
} }
if (!container_is_floating(view->container)) { if (!container_is_floating(view->container) ||
view->container->pending.fullscreen_mode) {
return; return;
} }
struct sway_seat *seat = input_manager_current_seat(); struct sway_seat *seat = input_manager_current_seat();
@ -647,6 +669,31 @@ static void handle_set_role(struct wl_listener *listener, void *data) {
view_execute_criteria(view); 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) { static void handle_set_window_type(struct wl_listener *listener, void *data) {
struct sway_xwayland_view *xwayland_view = struct sway_xwayland_view *xwayland_view =
wl_container_of(listener, xwayland_view, set_window_type); wl_container_of(listener, xwayland_view, set_window_type);
@ -666,14 +713,15 @@ static void handle_set_hints(struct wl_listener *listener, void *data) {
if (!xsurface->mapped) { if (!xsurface->mapped) {
return; return;
} }
if (!xsurface->hints_urgency && view->urgent_timer) { const bool hints_urgency = xcb_icccm_wm_hints_get_urgency(xsurface->hints);
if (!hints_urgency && view->urgent_timer) {
// The view is in the timeout period. We'll ignore the request to // The view is in the timeout period. We'll ignore the request to
// unset urgency so that the view remains urgent until the timer clears // unset urgency so that the view remains urgent until the timer clears
// it. // it.
return; return;
} }
if (view->allow_request_urgent) { if (view->allow_request_urgent) {
view_set_urgent(view, (bool)xsurface->hints_urgency); view_set_urgent(view, hints_urgency);
} }
} }
@ -731,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); wl_signal_add(&xsurface->events.set_role, &xwayland_view->set_role);
xwayland_view->set_role.notify = handle_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, wl_signal_add(&xsurface->events.set_window_type,
&xwayland_view->set_window_type); &xwayland_view->set_window_type);
xwayland_view->set_window_type.notify = handle_set_window_type; xwayland_view->set_window_type.notify = handle_set_window_type;

View file

@ -386,28 +386,29 @@ static void pointer_motion(struct sway_cursor *cursor, uint32_t time_msec,
static void handle_pointer_motion_relative( static void handle_pointer_motion_relative(
struct wl_listener *listener, void *data) { struct wl_listener *listener, void *data) {
struct sway_cursor *cursor = wl_container_of(listener, cursor, motion); struct sway_cursor *cursor = wl_container_of(listener, cursor, motion);
struct wlr_event_pointer_motion *e = data; struct wlr_pointer_motion_event *e = data;
cursor_handle_activity_from_device(cursor, e->device); cursor_handle_activity_from_device(cursor, &e->pointer->base);
pointer_motion(cursor, e->time_msec, e->device, e->delta_x, e->delta_y, pointer_motion(cursor, e->time_msec, &e->pointer->base, e->delta_x,
e->unaccel_dx, e->unaccel_dy); e->delta_y, e->unaccel_dx, e->unaccel_dy);
} }
static void handle_pointer_motion_absolute( static void handle_pointer_motion_absolute(
struct wl_listener *listener, void *data) { struct wl_listener *listener, void *data) {
struct sway_cursor *cursor = struct sway_cursor *cursor =
wl_container_of(listener, cursor, motion_absolute); wl_container_of(listener, cursor, motion_absolute);
struct wlr_event_pointer_motion_absolute *event = data; struct wlr_pointer_motion_absolute_event *event = data;
cursor_handle_activity_from_device(cursor, event->device); cursor_handle_activity_from_device(cursor, &event->pointer->base);
double lx, ly; double lx, ly;
wlr_cursor_absolute_to_layout_coords(cursor->cursor, event->device, wlr_cursor_absolute_to_layout_coords(cursor->cursor, &event->pointer->base,
event->x, event->y, &lx, &ly); event->x, event->y, &lx, &ly);
double dx = lx - cursor->cursor->x; double dx = lx - cursor->cursor->x;
double dy = ly - cursor->cursor->y; double dy = ly - cursor->cursor->y;
pointer_motion(cursor, event->time_msec, event->device, dx, dy, dx, dy); pointer_motion(cursor, event->time_msec, &event->pointer->base, dx, dy,
dx, dy);
} }
void dispatch_cursor_button(struct sway_cursor *cursor, void dispatch_cursor_button(struct sway_cursor *cursor,
@ -422,7 +423,7 @@ void dispatch_cursor_button(struct sway_cursor *cursor,
static void handle_pointer_button(struct wl_listener *listener, void *data) { static void handle_pointer_button(struct wl_listener *listener, void *data) {
struct sway_cursor *cursor = wl_container_of(listener, cursor, button); struct sway_cursor *cursor = wl_container_of(listener, cursor, button);
struct wlr_event_pointer_button *event = data; struct wlr_pointer_button_event *event = data;
if (event->state == WLR_BUTTON_PRESSED) { if (event->state == WLR_BUTTON_PRESSED) {
cursor->pressed_button_count++; cursor->pressed_button_count++;
@ -434,20 +435,20 @@ static void handle_pointer_button(struct wl_listener *listener, void *data) {
} }
} }
cursor_handle_activity_from_device(cursor, event->device); cursor_handle_activity_from_device(cursor, &event->pointer->base);
dispatch_cursor_button(cursor, event->device, dispatch_cursor_button(cursor, &event->pointer->base,
event->time_msec, event->button, event->state); event->time_msec, event->button, event->state);
} }
void dispatch_cursor_axis(struct sway_cursor *cursor, void dispatch_cursor_axis(struct sway_cursor *cursor,
struct wlr_event_pointer_axis *event) { struct wlr_pointer_axis_event *event) {
seatop_pointer_axis(cursor->seat, event); seatop_pointer_axis(cursor->seat, event);
} }
static void handle_pointer_axis(struct wl_listener *listener, void *data) { static void handle_pointer_axis(struct wl_listener *listener, void *data) {
struct sway_cursor *cursor = wl_container_of(listener, cursor, axis); struct sway_cursor *cursor = wl_container_of(listener, cursor, axis);
struct wlr_event_pointer_axis *event = data; struct wlr_pointer_axis_event *event = data;
cursor_handle_activity_from_device(cursor, event->device); cursor_handle_activity_from_device(cursor, &event->pointer->base);
dispatch_cursor_axis(cursor, event); dispatch_cursor_axis(cursor, event);
} }
@ -458,8 +459,8 @@ static void handle_pointer_frame(struct wl_listener *listener, void *data) {
static void handle_touch_down(struct wl_listener *listener, void *data) { static void handle_touch_down(struct wl_listener *listener, void *data) {
struct sway_cursor *cursor = wl_container_of(listener, cursor, touch_down); struct sway_cursor *cursor = wl_container_of(listener, cursor, touch_down);
struct wlr_event_touch_down *event = data; struct wlr_touch_down_event *event = data;
cursor_handle_activity_from_device(cursor, event->device); cursor_handle_activity_from_device(cursor, &event->touch->base);
cursor_hide(cursor); cursor_hide(cursor);
struct sway_seat *seat = cursor->seat; struct sway_seat *seat = cursor->seat;
@ -467,7 +468,7 @@ static void handle_touch_down(struct wl_listener *listener, void *data) {
struct wlr_surface *surface = NULL; struct wlr_surface *surface = NULL;
double lx, ly; double lx, ly;
wlr_cursor_absolute_to_layout_coords(cursor->cursor, event->device, wlr_cursor_absolute_to_layout_coords(cursor->cursor, &event->touch->base,
event->x, event->y, &lx, &ly); event->x, event->y, &lx, &ly);
double sx, sy; double sx, sy;
struct sway_node *focused_node = node_at_coords(seat, lx, ly, &surface, &sx, &sy); struct sway_node *focused_node = node_at_coords(seat, lx, ly, &surface, &sx, &sy);
@ -495,24 +496,25 @@ static void handle_touch_down(struct wl_listener *listener, void *data) {
double dx, dy; double dx, dy;
dx = lx - cursor->cursor->x; dx = lx - cursor->cursor->x;
dy = ly - cursor->cursor->y; dy = ly - cursor->cursor->y;
pointer_motion(cursor, event->time_msec, event->device, dx, dy, dx, dy); pointer_motion(cursor, event->time_msec, &event->touch->base, dx, dy,
dispatch_cursor_button(cursor, event->device, event->time_msec, dx, dy);
dispatch_cursor_button(cursor, &event->touch->base, event->time_msec,
BTN_LEFT, WLR_BUTTON_PRESSED); BTN_LEFT, WLR_BUTTON_PRESSED);
} }
} }
static void handle_touch_up(struct wl_listener *listener, void *data) { static void handle_touch_up(struct wl_listener *listener, void *data) {
struct sway_cursor *cursor = wl_container_of(listener, cursor, touch_up); struct sway_cursor *cursor = wl_container_of(listener, cursor, touch_up);
struct wlr_event_touch_up *event = data; struct wlr_touch_up_event *event = data;
cursor_handle_activity_from_device(cursor, event->device); cursor_handle_activity_from_device(cursor, &event->touch->base);
struct wlr_seat *wlr_seat = cursor->seat->wlr_seat; struct wlr_seat *wlr_seat = cursor->seat->wlr_seat;
if (cursor->simulating_pointer_from_touch) { if (cursor->simulating_pointer_from_touch) {
if (cursor->pointer_touch_id == cursor->seat->touch_id) { if (cursor->pointer_touch_id == cursor->seat->touch_id) {
cursor->pointer_touch_up = true; cursor->pointer_touch_up = true;
dispatch_cursor_button(cursor, event->device, event->time_msec, dispatch_cursor_button(cursor, &event->touch->base,
BTN_LEFT, WLR_BUTTON_RELEASED); event->time_msec, BTN_LEFT, WLR_BUTTON_RELEASED);
} }
} else { } else {
wlr_seat_touch_notify_up(wlr_seat, event->time_msec, event->touch_id); wlr_seat_touch_notify_up(wlr_seat, event->time_msec, event->touch_id);
@ -522,15 +524,15 @@ static void handle_touch_up(struct wl_listener *listener, void *data) {
static void handle_touch_motion(struct wl_listener *listener, void *data) { static void handle_touch_motion(struct wl_listener *listener, void *data) {
struct sway_cursor *cursor = struct sway_cursor *cursor =
wl_container_of(listener, cursor, touch_motion); wl_container_of(listener, cursor, touch_motion);
struct wlr_event_touch_motion *event = data; struct wlr_touch_motion_event *event = data;
cursor_handle_activity_from_device(cursor, event->device); cursor_handle_activity_from_device(cursor, &event->touch->base);
struct sway_seat *seat = cursor->seat; struct sway_seat *seat = cursor->seat;
struct wlr_seat *wlr_seat = seat->wlr_seat; struct wlr_seat *wlr_seat = seat->wlr_seat;
struct wlr_surface *surface = NULL; struct wlr_surface *surface = NULL;
double lx, ly; double lx, ly;
wlr_cursor_absolute_to_layout_coords(cursor->cursor, event->device, wlr_cursor_absolute_to_layout_coords(cursor->cursor, &event->touch->base,
event->x, event->y, &lx, &ly); event->x, event->y, &lx, &ly);
double sx, sy; double sx, sy;
node_at_coords(cursor->seat, lx, ly, &surface, &sx, &sy); node_at_coords(cursor->seat, lx, ly, &surface, &sx, &sy);
@ -552,7 +554,8 @@ static void handle_touch_motion(struct wl_listener *listener, void *data) {
double dx, dy; double dx, dy;
dx = lx - cursor->cursor->x; dx = lx - cursor->cursor->x;
dy = ly - cursor->cursor->y; dy = ly - cursor->cursor->y;
pointer_motion(cursor, event->time_msec, event->device, dx, dy, dx, dy); pointer_motion(cursor, event->time_msec, &event->touch->base,
dx, dy, dx, dy);
} }
} else if (surface) { } else if (surface) {
wlr_seat_touch_notify_motion(wlr_seat, event->time_msec, wlr_seat_touch_notify_motion(wlr_seat, event->time_msec,
@ -591,14 +594,15 @@ static void apply_mapping_from_region(struct wlr_input_device *device,
double x1 = region->x1, x2 = region->x2; double x1 = region->x1, x2 = region->x2;
double y1 = region->y1, y2 = region->y2; double y1 = region->y1, y2 = region->y2;
if (region->mm) { if (region->mm && device->type == WLR_INPUT_DEVICE_TABLET_TOOL) {
if (device->width_mm == 0 || device->height_mm == 0) { struct wlr_tablet *tablet = wlr_tablet_from_input_device(device);
if (tablet->width_mm == 0 || tablet->height_mm == 0) {
return; return;
} }
x1 /= device->width_mm; x1 /= tablet->width_mm;
x2 /= device->width_mm; x2 /= tablet->width_mm;
y1 /= device->height_mm; y1 /= tablet->height_mm;
y2 /= device->height_mm; y2 /= tablet->height_mm;
} }
*x = apply_mapping_from_coord(x1, x2, *x); *x = apply_mapping_from_coord(x1, x2, *x);
@ -660,8 +664,8 @@ static void handle_tablet_tool_position(struct sway_cursor *cursor,
static void handle_tool_axis(struct wl_listener *listener, void *data) { static void handle_tool_axis(struct wl_listener *listener, void *data) {
struct sway_cursor *cursor = wl_container_of(listener, cursor, tool_axis); struct sway_cursor *cursor = wl_container_of(listener, cursor, tool_axis);
struct wlr_event_tablet_tool_axis *event = data; struct wlr_tablet_tool_axis_event *event = data;
cursor_handle_activity_from_device(cursor, event->device); cursor_handle_activity_from_device(cursor, &event->tablet->base);
struct sway_tablet_tool *sway_tool = event->tool->data; struct sway_tablet_tool *sway_tool = event->tool->data;
if (!sway_tool) { if (!sway_tool) {
@ -716,8 +720,8 @@ static void handle_tool_axis(struct wl_listener *listener, void *data) {
static void handle_tool_tip(struct wl_listener *listener, void *data) { static void handle_tool_tip(struct wl_listener *listener, void *data) {
struct sway_cursor *cursor = wl_container_of(listener, cursor, tool_tip); struct sway_cursor *cursor = wl_container_of(listener, cursor, tool_tip);
struct wlr_event_tablet_tool_tip *event = data; struct wlr_tablet_tool_tip_event *event = data;
cursor_handle_activity_from_device(cursor, event->device); cursor_handle_activity_from_device(cursor, &event->tablet->base);
struct sway_tablet_tool *sway_tool = event->tool->data; struct sway_tablet_tool *sway_tool = event->tool->data;
struct wlr_tablet_v2_tablet *tablet_v2 = sway_tool->tablet->tablet_v2; struct wlr_tablet_v2_tablet *tablet_v2 = sway_tool->tablet->tablet_v2;
@ -732,7 +736,7 @@ static void handle_tool_tip(struct wl_listener *listener, void *data) {
if (cursor->simulating_pointer_from_tool_tip && if (cursor->simulating_pointer_from_tool_tip &&
event->state == WLR_TABLET_TOOL_TIP_UP) { event->state == WLR_TABLET_TOOL_TIP_UP) {
cursor->simulating_pointer_from_tool_tip = false; cursor->simulating_pointer_from_tool_tip = false;
dispatch_cursor_button(cursor, event->device, event->time_msec, dispatch_cursor_button(cursor, &event->tablet->base, event->time_msec,
BTN_LEFT, WLR_BUTTON_RELEASED); BTN_LEFT, WLR_BUTTON_RELEASED);
wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat); wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat);
} else if (!surface || !wlr_surface_accepts_tablet_v2(tablet_v2, surface)) { } else if (!surface || !wlr_surface_accepts_tablet_v2(tablet_v2, surface)) {
@ -744,8 +748,8 @@ static void handle_tool_tip(struct wl_listener *listener, void *data) {
WLR_TABLET_TOOL_TIP_UP); WLR_TABLET_TOOL_TIP_UP);
} else { } else {
cursor->simulating_pointer_from_tool_tip = true; cursor->simulating_pointer_from_tool_tip = true;
dispatch_cursor_button(cursor, event->device, event->time_msec, dispatch_cursor_button(cursor, &event->tablet->base,
BTN_LEFT, WLR_BUTTON_PRESSED); event->time_msec, BTN_LEFT, WLR_BUTTON_PRESSED);
wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat); wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat);
} }
} else { } else {
@ -767,12 +771,13 @@ static struct sway_tablet *get_tablet_for_device(struct sway_cursor *cursor,
static void handle_tool_proximity(struct wl_listener *listener, void *data) { static void handle_tool_proximity(struct wl_listener *listener, void *data) {
struct sway_cursor *cursor = struct sway_cursor *cursor =
wl_container_of(listener, cursor, tool_proximity); wl_container_of(listener, cursor, tool_proximity);
struct wlr_event_tablet_tool_proximity *event = data; struct wlr_tablet_tool_proximity_event *event = data;
cursor_handle_activity_from_device(cursor, event->device); cursor_handle_activity_from_device(cursor, &event->tablet->base);
struct wlr_tablet_tool *tool = event->tool; struct wlr_tablet_tool *tool = event->tool;
if (!tool->data) { if (!tool->data) {
struct sway_tablet *tablet = get_tablet_for_device(cursor, event->device); struct sway_tablet *tablet = get_tablet_for_device(cursor,
&event->tablet->base);
if (!tablet) { if (!tablet) {
sway_log(SWAY_ERROR, "no tablet for tablet tool"); sway_log(SWAY_ERROR, "no tablet for tablet tool");
return; return;
@ -797,8 +802,8 @@ static void handle_tool_proximity(struct wl_listener *listener, void *data) {
static void handle_tool_button(struct wl_listener *listener, void *data) { static void handle_tool_button(struct wl_listener *listener, void *data) {
struct sway_cursor *cursor = wl_container_of(listener, cursor, tool_button); struct sway_cursor *cursor = wl_container_of(listener, cursor, tool_button);
struct wlr_event_tablet_tool_button *event = data; struct wlr_tablet_tool_button_event *event = data;
cursor_handle_activity_from_device(cursor, event->device); cursor_handle_activity_from_device(cursor, &event->tablet->base);
struct sway_tablet_tool *sway_tool = event->tool->data; struct sway_tablet_tool *sway_tool = event->tool->data;
if (!sway_tool) { if (!sway_tool) {
@ -819,14 +824,14 @@ static void handle_tool_button(struct wl_listener *listener, void *data) {
switch (event->state) { switch (event->state) {
case WLR_BUTTON_PRESSED: case WLR_BUTTON_PRESSED:
if (cursor->tool_buttons == 0) { if (cursor->tool_buttons == 0) {
dispatch_cursor_button(cursor, event->device, dispatch_cursor_button(cursor, &event->tablet->base,
event->time_msec, BTN_RIGHT, event->state); event->time_msec, BTN_RIGHT, event->state);
} }
cursor->tool_buttons++; cursor->tool_buttons++;
break; break;
case WLR_BUTTON_RELEASED: case WLR_BUTTON_RELEASED:
if (cursor->tool_buttons == 1) { if (cursor->tool_buttons == 1) {
dispatch_cursor_button(cursor, event->device, dispatch_cursor_button(cursor, &event->tablet->base,
event->time_msec, BTN_RIGHT, event->state); event->time_msec, BTN_RIGHT, event->state);
} }
cursor->tool_buttons--; cursor->tool_buttons--;
@ -923,65 +928,68 @@ static void handle_request_pointer_set_cursor(struct wl_listener *listener,
event->hotspot_y, focused_client); event->hotspot_y, focused_client);
} }
static void handle_pointer_hold_begin(struct wl_listener *listener, void *data) {
struct sway_cursor *cursor = wl_container_of(
listener, cursor, hold_begin);
struct wlr_pointer_hold_begin_event *event = data;
cursor_handle_activity_from_device(cursor, &event->pointer->base);
seatop_hold_begin(cursor->seat, event);
}
static void handle_pointer_hold_end(struct wl_listener *listener, void *data) {
struct sway_cursor *cursor = wl_container_of(
listener, cursor, hold_end);
struct wlr_pointer_hold_end_event *event = data;
cursor_handle_activity_from_device(cursor, &event->pointer->base);
seatop_hold_end(cursor->seat, event);
}
static void handle_pointer_pinch_begin(struct wl_listener *listener, void *data) { static void handle_pointer_pinch_begin(struct wl_listener *listener, void *data) {
struct sway_cursor *cursor = wl_container_of( struct sway_cursor *cursor = wl_container_of(
listener, cursor, pinch_begin); listener, cursor, pinch_begin);
struct wlr_event_pointer_pinch_begin *event = data; struct wlr_pointer_pinch_begin_event *event = data;
cursor_handle_activity_from_device(cursor, event->device); cursor_handle_activity_from_device(cursor, &event->pointer->base);
wlr_pointer_gestures_v1_send_pinch_begin( seatop_pinch_begin(cursor->seat, event);
cursor->pointer_gestures, cursor->seat->wlr_seat,
event->time_msec, event->fingers);
} }
static void handle_pointer_pinch_update(struct wl_listener *listener, void *data) { static void handle_pointer_pinch_update(struct wl_listener *listener, void *data) {
struct sway_cursor *cursor = wl_container_of( struct sway_cursor *cursor = wl_container_of(
listener, cursor, pinch_update); listener, cursor, pinch_update);
struct wlr_event_pointer_pinch_update *event = data; struct wlr_pointer_pinch_update_event *event = data;
cursor_handle_activity_from_device(cursor, event->device); cursor_handle_activity_from_device(cursor, &event->pointer->base);
wlr_pointer_gestures_v1_send_pinch_update( seatop_pinch_update(cursor->seat, event);
cursor->pointer_gestures, cursor->seat->wlr_seat,
event->time_msec, event->dx, event->dy,
event->scale, event->rotation);
} }
static void handle_pointer_pinch_end(struct wl_listener *listener, void *data) { static void handle_pointer_pinch_end(struct wl_listener *listener, void *data) {
struct sway_cursor *cursor = wl_container_of( struct sway_cursor *cursor = wl_container_of(
listener, cursor, pinch_end); listener, cursor, pinch_end);
struct wlr_event_pointer_pinch_end *event = data; struct wlr_pointer_pinch_end_event *event = data;
cursor_handle_activity_from_device(cursor, event->device); cursor_handle_activity_from_device(cursor, &event->pointer->base);
wlr_pointer_gestures_v1_send_pinch_end( seatop_pinch_end(cursor->seat, event);
cursor->pointer_gestures, cursor->seat->wlr_seat,
event->time_msec, event->cancelled);
} }
static void handle_pointer_swipe_begin(struct wl_listener *listener, void *data) { static void handle_pointer_swipe_begin(struct wl_listener *listener, void *data) {
struct sway_cursor *cursor = wl_container_of( struct sway_cursor *cursor = wl_container_of(
listener, cursor, swipe_begin); listener, cursor, swipe_begin);
struct wlr_event_pointer_swipe_begin *event = data; struct wlr_pointer_swipe_begin_event *event = data;
cursor_handle_activity_from_device(cursor, event->device); cursor_handle_activity_from_device(cursor, &event->pointer->base);
wlr_pointer_gestures_v1_send_swipe_begin( seatop_swipe_begin(cursor->seat, event);
cursor->pointer_gestures, cursor->seat->wlr_seat,
event->time_msec, event->fingers);
} }
static void handle_pointer_swipe_update(struct wl_listener *listener, void *data) { static void handle_pointer_swipe_update(struct wl_listener *listener, void *data) {
struct sway_cursor *cursor = wl_container_of( struct sway_cursor *cursor = wl_container_of(
listener, cursor, swipe_update); listener, cursor, swipe_update);
struct wlr_event_pointer_swipe_update *event = data; struct wlr_pointer_swipe_update_event *event = data;
cursor_handle_activity_from_device(cursor, event->device); cursor_handle_activity_from_device(cursor, &event->pointer->base);
wlr_pointer_gestures_v1_send_swipe_update( seatop_swipe_update(cursor->seat, event);
cursor->pointer_gestures, cursor->seat->wlr_seat,
event->time_msec, event->dx, event->dy);
} }
static void handle_pointer_swipe_end(struct wl_listener *listener, void *data) { static void handle_pointer_swipe_end(struct wl_listener *listener, void *data) {
struct sway_cursor *cursor = wl_container_of( struct sway_cursor *cursor = wl_container_of(
listener, cursor, swipe_end); listener, cursor, swipe_end);
struct wlr_event_pointer_swipe_end *event = data; struct wlr_pointer_swipe_end_event *event = data;
cursor_handle_activity_from_device(cursor, event->device); cursor_handle_activity_from_device(cursor, &event->pointer->base);
wlr_pointer_gestures_v1_send_swipe_end( seatop_swipe_end(cursor->seat, event);
cursor->pointer_gestures, cursor->seat->wlr_seat,
event->time_msec, event->cancelled);
} }
static void handle_image_surface_destroy(struct wl_listener *listener, static void handle_image_surface_destroy(struct wl_listener *listener,
@ -1055,6 +1063,8 @@ void sway_cursor_destroy(struct sway_cursor *cursor) {
wl_event_source_remove(cursor->hide_source); wl_event_source_remove(cursor->hide_source);
wl_list_remove(&cursor->image_surface_destroy.link); wl_list_remove(&cursor->image_surface_destroy.link);
wl_list_remove(&cursor->hold_begin.link);
wl_list_remove(&cursor->hold_end.link);
wl_list_remove(&cursor->pinch_begin.link); wl_list_remove(&cursor->pinch_begin.link);
wl_list_remove(&cursor->pinch_update.link); wl_list_remove(&cursor->pinch_update.link);
wl_list_remove(&cursor->pinch_end.link); wl_list_remove(&cursor->pinch_end.link);
@ -1104,19 +1114,27 @@ struct sway_cursor *sway_cursor_create(struct sway_seat *seat) {
wl_list_init(&cursor->image_surface_destroy.link); wl_list_init(&cursor->image_surface_destroy.link);
cursor->image_surface_destroy.notify = handle_image_surface_destroy; cursor->image_surface_destroy.notify = handle_image_surface_destroy;
// gesture events
cursor->pointer_gestures = wlr_pointer_gestures_v1_create(server.wl_display); cursor->pointer_gestures = wlr_pointer_gestures_v1_create(server.wl_display);
cursor->pinch_begin.notify = handle_pointer_pinch_begin;
wl_signal_add(&wlr_cursor->events.hold_begin, &cursor->hold_begin);
cursor->hold_begin.notify = handle_pointer_hold_begin;
wl_signal_add(&wlr_cursor->events.hold_end, &cursor->hold_end);
cursor->hold_end.notify = handle_pointer_hold_end;
wl_signal_add(&wlr_cursor->events.pinch_begin, &cursor->pinch_begin); wl_signal_add(&wlr_cursor->events.pinch_begin, &cursor->pinch_begin);
cursor->pinch_update.notify = handle_pointer_pinch_update; cursor->pinch_begin.notify = handle_pointer_pinch_begin;
wl_signal_add(&wlr_cursor->events.pinch_update, &cursor->pinch_update); wl_signal_add(&wlr_cursor->events.pinch_update, &cursor->pinch_update);
cursor->pinch_end.notify = handle_pointer_pinch_end; cursor->pinch_update.notify = handle_pointer_pinch_update;
wl_signal_add(&wlr_cursor->events.pinch_end, &cursor->pinch_end); wl_signal_add(&wlr_cursor->events.pinch_end, &cursor->pinch_end);
cursor->swipe_begin.notify = handle_pointer_swipe_begin; cursor->pinch_end.notify = handle_pointer_pinch_end;
wl_signal_add(&wlr_cursor->events.swipe_begin, &cursor->swipe_begin); wl_signal_add(&wlr_cursor->events.swipe_begin, &cursor->swipe_begin);
cursor->swipe_update.notify = handle_pointer_swipe_update; cursor->swipe_begin.notify = handle_pointer_swipe_begin;
wl_signal_add(&wlr_cursor->events.swipe_update, &cursor->swipe_update); wl_signal_add(&wlr_cursor->events.swipe_update, &cursor->swipe_update);
cursor->swipe_end.notify = handle_pointer_swipe_end; cursor->swipe_update.notify = handle_pointer_swipe_update;
wl_signal_add(&wlr_cursor->events.swipe_end, &cursor->swipe_end); wl_signal_add(&wlr_cursor->events.swipe_end, &cursor->swipe_end);
cursor->swipe_end.notify = handle_pointer_swipe_end;
// input events // input events
wl_signal_add(&wlr_cursor->events.motion, &cursor->motion); wl_signal_add(&wlr_cursor->events.motion, &cursor->motion);
@ -1354,12 +1372,9 @@ void handle_pointer_constraint(struct wl_listener *listener, void *data) {
sway_constraint->destroy.notify = handle_constraint_destroy; sway_constraint->destroy.notify = handle_constraint_destroy;
wl_signal_add(&constraint->events.destroy, &sway_constraint->destroy); wl_signal_add(&constraint->events.destroy, &sway_constraint->destroy);
struct sway_node *focus = seat_get_focus(seat); struct wlr_surface *surface = seat->wlr_seat->keyboard_state.focused_surface;
if (focus && node_is_view(focus)) { if (surface && surface == constraint->surface) {
struct wlr_surface *surface = focus->sway_container->view->surface; sway_cursor_constrain(seat->cursor, constraint);
if (surface == constraint->surface) {
sway_cursor_constrain(seat->cursor, constraint);
}
} }
} }

View file

@ -236,7 +236,7 @@ static void handle_new_input(struct wl_listener *listener, void *data) {
apply_input_type_config(input_device); apply_input_type_config(input_device);
sway_input_configure_libinput_device(input_device); bool config_changed = sway_input_configure_libinput_device(input_device);
wl_signal_add(&device->events.destroy, &input_device->device_destroy); wl_signal_add(&device->events.destroy, &input_device->device_destroy);
input_device->device_destroy.notify = handle_device_destroy; input_device->device_destroy.notify = handle_device_destroy;
@ -274,6 +274,10 @@ static void handle_new_input(struct wl_listener *listener, void *data) {
} }
ipc_event_input("added", input_device); ipc_event_input("added", input_device);
if (config_changed) {
ipc_event_input("libinput_config", input_device);
}
} }
static void handle_inhibit_activate(struct wl_listener *listener, void *data) { static void handle_inhibit_activate(struct wl_listener *listener, void *data) {
@ -289,6 +293,10 @@ static void handle_inhibit_deactivate(struct wl_listener *listener, void *data)
struct sway_input_manager *input_manager = wl_container_of( struct sway_input_manager *input_manager = wl_container_of(
listener, input_manager, inhibit_deactivate); listener, input_manager, inhibit_deactivate);
struct sway_seat *seat; struct sway_seat *seat;
if (server.session_lock.locked) {
// Don't deactivate the grab of a screenlocker
return;
}
wl_list_for_each(seat, &input_manager->seats, link) { wl_list_for_each(seat, &input_manager->seats, link) {
seat_set_exclusive_client(seat, NULL); seat_set_exclusive_client(seat, NULL);
struct sway_node *previous = seat_get_focus(seat); struct sway_node *previous = seat_get_focus(seat);
@ -377,7 +385,7 @@ void handle_virtual_keyboard(struct wl_listener *listener, void *data) {
struct sway_input_manager *input_manager = struct sway_input_manager *input_manager =
wl_container_of(listener, input_manager, virtual_keyboard_new); wl_container_of(listener, input_manager, virtual_keyboard_new);
struct wlr_virtual_keyboard_v1 *keyboard = data; struct wlr_virtual_keyboard_v1 *keyboard = data;
struct wlr_input_device *device = &keyboard->input_device; struct wlr_input_device *device = &keyboard->keyboard.base;
// TODO: Amend protocol to allow NULL seat // TODO: Amend protocol to allow NULL seat
struct sway_seat *seat = keyboard->seat ? struct sway_seat *seat = keyboard->seat ?
@ -410,7 +418,7 @@ void handle_virtual_pointer(struct wl_listener *listener, void *data) {
wl_container_of(listener, input_manager, virtual_pointer_new); wl_container_of(listener, input_manager, virtual_pointer_new);
struct wlr_virtual_pointer_v1_new_pointer_event *event = data; struct wlr_virtual_pointer_v1_new_pointer_event *event = data;
struct wlr_virtual_pointer_v1 *pointer = event->new_pointer; struct wlr_virtual_pointer_v1 *pointer = event->new_pointer;
struct wlr_input_device *device = &pointer->input_device; struct wlr_input_device *device = &pointer->pointer.base;
struct sway_seat *seat = event->suggested_seat ? struct sway_seat *seat = event->suggested_seat ?
input_manager_sway_seat_from_wlr_seat(event->suggested_seat) : input_manager_sway_seat_from_wlr_seat(event->suggested_seat) :
@ -524,11 +532,14 @@ static void retranslate_keysyms(struct input_config *input_config) {
static void input_manager_configure_input( static void input_manager_configure_input(
struct sway_input_device *input_device) { struct sway_input_device *input_device) {
sway_input_configure_libinput_device(input_device); bool config_changed = sway_input_configure_libinput_device(input_device);
struct sway_seat *seat = NULL; struct sway_seat *seat = NULL;
wl_list_for_each(seat, &server.input->seats, link) { wl_list_for_each(seat, &server.input->seats, link) {
seat_configure_device(seat, input_device); seat_configure_device(seat, input_device);
} }
if (config_changed) {
ipc_event_input("libinput_config", input_device);
}
} }
void input_manager_configure_all_inputs(void) { void input_manager_configure_all_inputs(void) {
@ -564,6 +575,13 @@ void input_manager_reset_input(struct sway_input_device *input_device) {
} }
void input_manager_reset_all_inputs(void) { 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; struct sway_input_device *input_device = NULL;
wl_list_for_each(input_device, &server.input->devices, link) { wl_list_for_each(input_device, &server.input->devices, link) {
input_manager_reset_input(input_device); input_manager_reset_input(input_device);
@ -572,7 +590,6 @@ void input_manager_reset_all_inputs(void) {
// If there is at least one keyboard using the default keymap, repeat delay, // 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 // and repeat rate, then it is possible that there is a keyboard group that
// need their keyboard disarmed. // need their keyboard disarmed.
struct sway_seat *seat;
wl_list_for_each(seat, &server.input->seats, link) { wl_list_for_each(seat, &server.input->seats, link) {
struct sway_keyboard_group *group; struct sway_keyboard_group *group;
wl_list_for_each(group, &seat->keyboard_groups, link) { wl_list_for_each(group, &seat->keyboard_groups, link) {

View file

@ -291,14 +291,12 @@ static bool keyboard_execute_compositor_binding(struct sway_keyboard *keyboard,
static size_t keyboard_keysyms_translated(struct sway_keyboard *keyboard, static size_t keyboard_keysyms_translated(struct sway_keyboard *keyboard,
xkb_keycode_t keycode, const xkb_keysym_t **keysyms, xkb_keycode_t keycode, const xkb_keysym_t **keysyms,
uint32_t *modifiers) { uint32_t *modifiers) {
struct wlr_input_device *device = *modifiers = wlr_keyboard_get_modifiers(keyboard->wlr);
keyboard->seat_device->input_device->wlr_device;
*modifiers = wlr_keyboard_get_modifiers(device->keyboard);
xkb_mod_mask_t consumed = xkb_state_key_get_consumed_mods2( xkb_mod_mask_t consumed = xkb_state_key_get_consumed_mods2(
device->keyboard->xkb_state, keycode, XKB_CONSUMED_MODE_XKB); keyboard->wlr->xkb_state, keycode, XKB_CONSUMED_MODE_XKB);
*modifiers = *modifiers & ~consumed; *modifiers = *modifiers & ~consumed;
return xkb_state_key_get_syms(device->keyboard->xkb_state, return xkb_state_key_get_syms(keyboard->wlr->xkb_state,
keycode, keysyms); keycode, keysyms);
} }
@ -314,13 +312,11 @@ static size_t keyboard_keysyms_translated(struct sway_keyboard *keyboard,
static size_t keyboard_keysyms_raw(struct sway_keyboard *keyboard, static size_t keyboard_keysyms_raw(struct sway_keyboard *keyboard,
xkb_keycode_t keycode, const xkb_keysym_t **keysyms, xkb_keycode_t keycode, const xkb_keysym_t **keysyms,
uint32_t *modifiers) { uint32_t *modifiers) {
struct wlr_input_device *device = *modifiers = wlr_keyboard_get_modifiers(keyboard->wlr);
keyboard->seat_device->input_device->wlr_device;
*modifiers = wlr_keyboard_get_modifiers(device->keyboard);
xkb_layout_index_t layout_index = xkb_state_key_get_layout( xkb_layout_index_t layout_index = xkb_state_key_get_layout(
device->keyboard->xkb_state, keycode); keyboard->wlr->xkb_state, keycode);
return xkb_keymap_key_get_syms_by_level(device->keyboard->keymap, return xkb_keymap_key_get_syms_by_level(keyboard->wlr->keymap,
keycode, layout_index, 0, keysyms); keycode, layout_index, 0, keysyms);
} }
@ -360,8 +356,7 @@ static void update_keyboard_state(struct sway_keyboard *keyboard,
keyinfo->keycode, &keyinfo->translated_keysyms, keyinfo->keycode, &keyinfo->translated_keysyms,
&keyinfo->translated_modifiers); &keyinfo->translated_modifiers);
keyinfo->code_modifiers = wlr_keyboard_get_modifiers( keyinfo->code_modifiers = wlr_keyboard_get_modifiers(keyboard->wlr);
keyboard->seat_device->input_device->wlr_device->keyboard);
// Update shortcut model keyinfo // Update shortcut model keyinfo
update_shortcut_state(&keyboard->state_keycodes, raw_keycode, keystate, update_shortcut_state(&keyboard->state_keycodes, raw_keycode, keystate,
@ -401,15 +396,16 @@ static struct wlr_input_method_keyboard_grab_v2 *keyboard_get_im_grab(
} }
static void handle_key_event(struct sway_keyboard *keyboard, static void handle_key_event(struct sway_keyboard *keyboard,
struct wlr_event_keyboard_key *event) { struct wlr_keyboard_key_event *event) {
struct sway_seat *seat = keyboard->seat_device->sway_seat; struct sway_seat *seat = keyboard->seat_device->sway_seat;
struct wlr_seat *wlr_seat = seat->wlr_seat; struct wlr_seat *wlr_seat = seat->wlr_seat;
struct wlr_input_device *wlr_device = struct wlr_input_device *wlr_device =
keyboard->seat_device->input_device->wlr_device; keyboard->seat_device->input_device->wlr_device;
char *device_identifier = input_device_get_identifier(wlr_device); char *device_identifier = input_device_get_identifier(wlr_device);
bool exact_identifier = wlr_device->keyboard->group != NULL; bool exact_identifier = keyboard->wlr->group != NULL;
seat_idle_notify_activity(seat, IDLE_SOURCE_KEYBOARD); seat_idle_notify_activity(seat, IDLE_SOURCE_KEYBOARD);
bool input_inhibited = seat->exclusive_client != NULL; bool input_inhibited = seat->exclusive_client != NULL ||
server.session_lock.locked;
struct sway_keyboard_shortcuts_inhibitor *sway_inhibitor = struct sway_keyboard_shortcuts_inhibitor *sway_inhibitor =
keyboard_shortcuts_inhibitor_get_for_focused_surface(seat); keyboard_shortcuts_inhibitor_get_for_focused_surface(seat);
bool shortcuts_inhibited = sway_inhibitor && sway_inhibitor->inhibitor->active; bool shortcuts_inhibited = sway_inhibitor && sway_inhibitor->inhibitor->active;
@ -477,10 +473,10 @@ static void handle_key_event(struct sway_keyboard *keyboard,
// Set up (or clear) keyboard repeat for a pressed binding. Since the // Set up (or clear) keyboard repeat for a pressed binding. Since the
// binding may remove the keyboard, the timer needs to be updated first // binding may remove the keyboard, the timer needs to be updated first
if (binding && !(binding->flags & BINDING_NOREPEAT) && if (binding && !(binding->flags & BINDING_NOREPEAT) &&
wlr_device->keyboard->repeat_info.delay > 0) { keyboard->wlr->repeat_info.delay > 0) {
keyboard->repeat_binding = binding; keyboard->repeat_binding = binding;
if (wl_event_source_timer_update(keyboard->key_repeat_source, if (wl_event_source_timer_update(keyboard->key_repeat_source,
wlr_device->keyboard->repeat_info.delay) < 0) { keyboard->wlr->repeat_info.delay) < 0) {
sway_log(SWAY_DEBUG, "failed to set key repeat timer"); sway_log(SWAY_DEBUG, "failed to set key repeat timer");
} }
} else if (keyboard->repeat_binding) { } else if (keyboard->repeat_binding) {
@ -492,7 +488,7 @@ static void handle_key_event(struct sway_keyboard *keyboard,
handled = true; handled = true;
} }
if (!handled && wlr_device->keyboard->group) { if (!handled && keyboard->wlr->group) {
// Only handle device specific bindings for keyboards in a group // Only handle device specific bindings for keyboards in a group
free(device_identifier); free(device_identifier);
return; return;
@ -517,7 +513,7 @@ static void handle_key_event(struct sway_keyboard *keyboard,
&keyboard->state_pressed_sent, event->keycode, &keyboard->state_pressed_sent, event->keycode,
event->state, keyinfo.keycode, 0); event->state, keyinfo.keycode, 0);
if (pressed_sent) { if (pressed_sent) {
wlr_seat_set_keyboard(wlr_seat, wlr_device); wlr_seat_set_keyboard(wlr_seat, keyboard->wlr);
wlr_seat_keyboard_notify_key(wlr_seat, event->time_msec, wlr_seat_keyboard_notify_key(wlr_seat, event->time_msec,
event->keycode, event->state); event->keycode, event->state);
handled = true; handled = true;
@ -528,8 +524,7 @@ static void handle_key_event(struct sway_keyboard *keyboard,
struct wlr_input_method_keyboard_grab_v2 *kb_grab = keyboard_get_im_grab(keyboard); struct wlr_input_method_keyboard_grab_v2 *kb_grab = keyboard_get_im_grab(keyboard);
if (kb_grab) { if (kb_grab) {
wlr_input_method_keyboard_grab_v2_set_keyboard(kb_grab, wlr_input_method_keyboard_grab_v2_set_keyboard(kb_grab, keyboard->wlr);
wlr_device->keyboard);
wlr_input_method_keyboard_grab_v2_send_key(kb_grab, wlr_input_method_keyboard_grab_v2_send_key(kb_grab,
event->time_msec, event->keycode, event->state); event->time_msec, event->keycode, event->state);
handled = true; handled = true;
@ -542,7 +537,7 @@ static void handle_key_event(struct sway_keyboard *keyboard,
update_shortcut_state( update_shortcut_state(
&keyboard->state_pressed_sent, event->keycode, event->state, &keyboard->state_pressed_sent, event->keycode, event->state,
keyinfo.keycode, 0); keyinfo.keycode, 0);
wlr_seat_set_keyboard(wlr_seat, wlr_device); wlr_seat_set_keyboard(wlr_seat, keyboard->wlr);
wlr_seat_keyboard_notify_key(wlr_seat, event->time_msec, wlr_seat_keyboard_notify_key(wlr_seat, event->time_msec,
event->keycode, event->state); event->keycode, event->state);
} }
@ -618,14 +613,12 @@ static void handle_keyboard_group_leave(struct wl_listener *listener,
} }
static int handle_keyboard_repeat(void *data) { static int handle_keyboard_repeat(void *data) {
struct sway_keyboard *keyboard = (struct sway_keyboard *)data; struct sway_keyboard *keyboard = data;
struct wlr_keyboard *wlr_device =
keyboard->seat_device->input_device->wlr_device->keyboard;
if (keyboard->repeat_binding) { if (keyboard->repeat_binding) {
if (wlr_device->repeat_info.rate > 0) { if (keyboard->wlr->repeat_info.rate > 0) {
// We queue the next event first, as the command might cancel it // We queue the next event first, as the command might cancel it
if (wl_event_source_timer_update(keyboard->key_repeat_source, if (wl_event_source_timer_update(keyboard->key_repeat_source,
1000 / wlr_device->repeat_info.rate) < 0) { 1000 / keyboard->wlr->repeat_info.rate) < 0) {
sway_log(SWAY_DEBUG, "failed to update key repeat timer"); sway_log(SWAY_DEBUG, "failed to update key repeat timer");
} }
} }
@ -658,31 +651,28 @@ static void determine_bar_visibility(uint32_t modifiers) {
} }
static void handle_modifier_event(struct sway_keyboard *keyboard) { static void handle_modifier_event(struct sway_keyboard *keyboard) {
struct wlr_input_device *wlr_device = if (!keyboard->wlr->group) {
keyboard->seat_device->input_device->wlr_device;
if (!wlr_device->keyboard->group) {
struct wlr_input_method_keyboard_grab_v2 *kb_grab = keyboard_get_im_grab(keyboard); struct wlr_input_method_keyboard_grab_v2 *kb_grab = keyboard_get_im_grab(keyboard);
if (kb_grab) { if (kb_grab) {
wlr_input_method_keyboard_grab_v2_set_keyboard(kb_grab, wlr_input_method_keyboard_grab_v2_set_keyboard(kb_grab, keyboard->wlr);
wlr_device->keyboard);
wlr_input_method_keyboard_grab_v2_send_modifiers(kb_grab, wlr_input_method_keyboard_grab_v2_send_modifiers(kb_grab,
&wlr_device->keyboard->modifiers); &keyboard->wlr->modifiers);
} else { } else {
struct wlr_seat *wlr_seat = keyboard->seat_device->sway_seat->wlr_seat; struct wlr_seat *wlr_seat = keyboard->seat_device->sway_seat->wlr_seat;
wlr_seat_set_keyboard(wlr_seat, wlr_device); wlr_seat_set_keyboard(wlr_seat, keyboard->wlr);
wlr_seat_keyboard_notify_modifiers(wlr_seat, wlr_seat_keyboard_notify_modifiers(wlr_seat,
&wlr_device->keyboard->modifiers); &keyboard->wlr->modifiers);
} }
uint32_t modifiers = wlr_keyboard_get_modifiers(wlr_device->keyboard); uint32_t modifiers = wlr_keyboard_get_modifiers(keyboard->wlr);
determine_bar_visibility(modifiers); determine_bar_visibility(modifiers);
} }
if (wlr_device->keyboard->modifiers.group != keyboard->effective_layout) { if (keyboard->wlr->modifiers.group != keyboard->effective_layout) {
keyboard->effective_layout = wlr_device->keyboard->modifiers.group; keyboard->effective_layout = keyboard->wlr->modifiers.group;
if (!wlr_keyboard_group_from_wlr_keyboard(wlr_device->keyboard)) { if (!wlr_keyboard_group_from_wlr_keyboard(keyboard->wlr)) {
ipc_event_input("xkb_layout", keyboard->seat_device->input_device); ipc_event_input("xkb_layout", keyboard->seat_device->input_device);
} }
} }
@ -711,6 +701,7 @@ struct sway_keyboard *sway_keyboard_create(struct sway_seat *seat,
} }
keyboard->seat_device = device; keyboard->seat_device = device;
keyboard->wlr = wlr_keyboard_from_input_device(device->input_device->wlr_device);
device->keyboard = keyboard; device->keyboard = keyboard;
wl_list_init(&keyboard->keyboard_key.link); wl_list_init(&keyboard->keyboard_key.link);
@ -819,13 +810,12 @@ static void destroy_empty_wlr_keyboard_group(void *data) {
static void sway_keyboard_group_remove(struct sway_keyboard *keyboard) { static void sway_keyboard_group_remove(struct sway_keyboard *keyboard) {
struct sway_input_device *device = keyboard->seat_device->input_device; struct sway_input_device *device = keyboard->seat_device->input_device;
struct wlr_keyboard *wlr_keyboard = device->wlr_device->keyboard; struct wlr_keyboard_group *wlr_group = keyboard->wlr->group;
struct wlr_keyboard_group *wlr_group = wlr_keyboard->group;
sway_log(SWAY_DEBUG, "Removing keyboard %s from group %p", sway_log(SWAY_DEBUG, "Removing keyboard %s from group %p",
device->identifier, wlr_group); device->identifier, wlr_group);
wlr_keyboard_group_remove_keyboard(wlr_keyboard->group, wlr_keyboard); wlr_keyboard_group_remove_keyboard(keyboard->wlr->group, keyboard->wlr);
if (wl_list_empty(&wlr_group->devices)) { if (wl_list_empty(&wlr_group->devices)) {
sway_log(SWAY_DEBUG, "Destroying empty keyboard group %p", sway_log(SWAY_DEBUG, "Destroying empty keyboard group %p",
@ -850,9 +840,7 @@ static void sway_keyboard_group_remove(struct sway_keyboard *keyboard) {
} }
static void sway_keyboard_group_remove_invalid(struct sway_keyboard *keyboard) { static void sway_keyboard_group_remove_invalid(struct sway_keyboard *keyboard) {
struct sway_input_device *device = keyboard->seat_device->input_device; if (!keyboard->wlr->group) {
struct wlr_keyboard *wlr_keyboard = device->wlr_device->keyboard;
if (!wlr_keyboard->group) {
return; return;
} }
@ -868,7 +856,7 @@ static void sway_keyboard_group_remove_invalid(struct sway_keyboard *keyboard) {
break; break;
case KEYBOARD_GROUP_DEFAULT: /* fallthrough */ case KEYBOARD_GROUP_DEFAULT: /* fallthrough */
case KEYBOARD_GROUP_SMART:; case KEYBOARD_GROUP_SMART:;
struct wlr_keyboard_group *group = wlr_keyboard->group; struct wlr_keyboard_group *group = keyboard->wlr->group;
if (!wlr_keyboard_keymaps_match(keyboard->keymap, group->keyboard.keymap) || if (!wlr_keyboard_keymaps_match(keyboard->keymap, group->keyboard.keymap) ||
!repeat_info_match(keyboard, &group->keyboard)) { !repeat_info_match(keyboard, &group->keyboard)) {
sway_keyboard_group_remove(keyboard); sway_keyboard_group_remove(keyboard);
@ -879,7 +867,6 @@ static void sway_keyboard_group_remove_invalid(struct sway_keyboard *keyboard) {
static void sway_keyboard_group_add(struct sway_keyboard *keyboard) { static void sway_keyboard_group_add(struct sway_keyboard *keyboard) {
struct sway_input_device *device = keyboard->seat_device->input_device; struct sway_input_device *device = keyboard->seat_device->input_device;
struct wlr_keyboard *wlr_keyboard = device->wlr_device->keyboard;
struct sway_seat *seat = keyboard->seat_device->sway_seat; struct sway_seat *seat = keyboard->seat_device->sway_seat;
struct seat_config *sc = seat_get_config(seat); struct seat_config *sc = seat_get_config(seat);
@ -911,7 +898,7 @@ static void sway_keyboard_group_add(struct sway_keyboard *keyboard) {
repeat_info_match(keyboard, &wlr_group->keyboard)) { repeat_info_match(keyboard, &wlr_group->keyboard)) {
sway_log(SWAY_DEBUG, "Adding keyboard %s to group %p", sway_log(SWAY_DEBUG, "Adding keyboard %s to group %p",
device->identifier, wlr_group); device->identifier, wlr_group);
wlr_keyboard_group_add_keyboard(wlr_group, wlr_keyboard); wlr_keyboard_group_add_keyboard(wlr_group, keyboard->wlr);
return; return;
} }
break; break;
@ -950,7 +937,7 @@ static void sway_keyboard_group_add(struct sway_keyboard *keyboard) {
goto cleanup; goto cleanup;
} }
sway_group->seat_device->input_device->wlr_device = sway_group->seat_device->input_device->wlr_device =
sway_group->wlr_group->input_device; &sway_group->wlr_group->keyboard.base;
if (!sway_keyboard_create(seat, sway_group->seat_device)) { if (!sway_keyboard_create(seat, sway_group->seat_device)) {
sway_log(SWAY_ERROR, "Failed to allocate sway_keyboard for group"); sway_log(SWAY_ERROR, "Failed to allocate sway_keyboard for group");
@ -959,7 +946,7 @@ static void sway_keyboard_group_add(struct sway_keyboard *keyboard) {
sway_log(SWAY_DEBUG, "Adding keyboard %s to group %p", sway_log(SWAY_DEBUG, "Adding keyboard %s to group %p",
device->identifier, sway_group->wlr_group); device->identifier, sway_group->wlr_group);
wlr_keyboard_group_add_keyboard(sway_group->wlr_group, wlr_keyboard); wlr_keyboard_group_add_keyboard(sway_group->wlr_group, keyboard->wlr);
wl_list_insert(&seat->keyboard_groups, &sway_group->link); wl_list_insert(&seat->keyboard_groups, &sway_group->link);
@ -991,10 +978,8 @@ cleanup:
void sway_keyboard_configure(struct sway_keyboard *keyboard) { void sway_keyboard_configure(struct sway_keyboard *keyboard) {
struct input_config *input_config = struct input_config *input_config =
input_device_get_config(keyboard->seat_device->input_device); input_device_get_config(keyboard->seat_device->input_device);
struct wlr_input_device *wlr_device =
keyboard->seat_device->input_device->wlr_device;
if (!sway_assert(!wlr_keyboard_group_from_wlr_keyboard(wlr_device->keyboard), if (!sway_assert(!wlr_keyboard_group_from_wlr_keyboard(keyboard->wlr),
"sway_keyboard_configure should not be called with a " "sway_keyboard_configure should not be called with a "
"keyboard group's keyboard")) { "keyboard group's keyboard")) {
return; return;
@ -1036,11 +1021,11 @@ void sway_keyboard_configure(struct sway_keyboard *keyboard) {
sway_keyboard_group_remove_invalid(keyboard); sway_keyboard_group_remove_invalid(keyboard);
wlr_keyboard_set_keymap(wlr_device->keyboard, keyboard->keymap); wlr_keyboard_set_keymap(keyboard->wlr, keyboard->keymap);
wlr_keyboard_set_repeat_info(wlr_device->keyboard, wlr_keyboard_set_repeat_info(keyboard->wlr,
keyboard->repeat_rate, keyboard->repeat_delay); keyboard->repeat_rate, keyboard->repeat_delay);
if (!wlr_device->keyboard->group) { if (!keyboard->wlr->group) {
sway_keyboard_group_add(keyboard); sway_keyboard_group_add(keyboard);
} }
@ -1060,40 +1045,42 @@ void sway_keyboard_configure(struct sway_keyboard *keyboard) {
} }
} }
if (locked_mods) { if (locked_mods) {
wlr_keyboard_notify_modifiers(wlr_device->keyboard, 0, 0, wlr_keyboard_notify_modifiers(keyboard->wlr, 0, 0,
locked_mods, 0); locked_mods, 0);
uint32_t leds = 0; uint32_t leds = 0;
for (uint32_t i = 0; i < WLR_LED_COUNT; ++i) { for (uint32_t i = 0; i < WLR_LED_COUNT; ++i) {
if (xkb_state_led_index_is_active( if (xkb_state_led_index_is_active(keyboard->wlr->xkb_state,
wlr_device->keyboard->xkb_state, keyboard->wlr->led_indexes[i])) {
wlr_device->keyboard->led_indexes[i])) {
leds |= (1 << i); leds |= (1 << i);
} }
} }
if (wlr_device->keyboard->group) { if (keyboard->wlr->group) {
wlr_keyboard_led_update( wlr_keyboard_led_update(&keyboard->wlr->group->keyboard, leds);
&wlr_device->keyboard->group->keyboard, leds);
} else { } else {
wlr_keyboard_led_update(wlr_device->keyboard, leds); wlr_keyboard_led_update(keyboard->wlr, leds);
} }
} }
} else { } else {
xkb_keymap_unref(keymap); xkb_keymap_unref(keymap);
sway_keyboard_group_remove_invalid(keyboard); sway_keyboard_group_remove_invalid(keyboard);
if (!wlr_device->keyboard->group) { if (!keyboard->wlr->group) {
sway_keyboard_group_add(keyboard); sway_keyboard_group_add(keyboard);
} }
} }
// If the seat has no active keyboard, set this one
struct wlr_seat *seat = keyboard->seat_device->sway_seat->wlr_seat; struct wlr_seat *seat = keyboard->seat_device->sway_seat->wlr_seat;
wlr_seat_set_keyboard(seat, wlr_device); 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_list_remove(&keyboard->keyboard_key.link);
wl_signal_add(&wlr_device->keyboard->events.key, &keyboard->keyboard_key); wl_signal_add(&keyboard->wlr->events.key, &keyboard->keyboard_key);
keyboard->keyboard_key.notify = handle_keyboard_key; keyboard->keyboard_key.notify = handle_keyboard_key;
wl_list_remove(&keyboard->keyboard_modifiers.link); wl_list_remove(&keyboard->keyboard_modifiers.link);
wl_signal_add(&wlr_device->keyboard->events.modifiers, wl_signal_add(&keyboard->wlr->events.modifiers,
&keyboard->keyboard_modifiers); &keyboard->keyboard_modifiers);
keyboard->keyboard_modifiers.notify = handle_keyboard_modifiers; keyboard->keyboard_modifiers.notify = handle_keyboard_modifiers;
@ -1110,12 +1097,11 @@ void sway_keyboard_destroy(struct sway_keyboard *keyboard) {
if (!keyboard) { if (!keyboard) {
return; return;
} }
if (keyboard->seat_device->input_device->wlr_device->keyboard->group) { if (keyboard->wlr->group) {
sway_keyboard_group_remove(keyboard); sway_keyboard_group_remove(keyboard);
} }
struct wlr_seat *wlr_seat = keyboard->seat_device->sway_seat->wlr_seat; struct wlr_seat *wlr_seat = keyboard->seat_device->sway_seat->wlr_seat;
struct sway_input_device *device = keyboard->seat_device->input_device; if (wlr_seat_get_keyboard(wlr_seat) == keyboard->wlr) {
if (wlr_seat_get_keyboard(wlr_seat) == device->wlr_device->keyboard) {
wlr_seat_set_keyboard(wlr_seat, NULL); wlr_seat_set_keyboard(wlr_seat, NULL);
} }
if (keyboard->keymap) { if (keyboard->keymap) {

View file

@ -166,6 +166,16 @@ static bool set_dwt(struct libinput_device *device, bool dwt) {
return true; return true;
} }
static bool set_dwtp(struct libinput_device *device, bool dwtp) {
if (!libinput_device_config_dwtp_is_available(device) ||
libinput_device_config_dwtp_get_enabled(device) == dwtp) {
return false;
}
sway_log(SWAY_DEBUG, "dwtp_set_enabled(%d)", dwtp);
log_status(libinput_device_config_dwtp_set_enabled(device, dwtp));
return true;
}
static bool set_calibration_matrix(struct libinput_device *dev, float mat[6]) { static bool set_calibration_matrix(struct libinput_device *dev, float mat[6]) {
if (!libinput_device_config_calibration_has_matrix(dev)) { if (!libinput_device_config_calibration_has_matrix(dev)) {
return false; return false;
@ -187,10 +197,10 @@ static bool set_calibration_matrix(struct libinput_device *dev, float mat[6]) {
return changed; return changed;
} }
void sway_input_configure_libinput_device(struct sway_input_device *input_device) { bool sway_input_configure_libinput_device(struct sway_input_device *input_device) {
struct input_config *ic = input_device_get_config(input_device); struct input_config *ic = input_device_get_config(input_device);
if (!ic || !wlr_input_device_is_libinput(input_device->wlr_device)) { if (!ic || !wlr_input_device_is_libinput(input_device->wlr_device)) {
return; return false;
} }
struct libinput_device *device = struct libinput_device *device =
@ -255,13 +265,14 @@ void sway_input_configure_libinput_device(struct sway_input_device *input_device
if (ic->dwt != INT_MIN) { if (ic->dwt != INT_MIN) {
changed |= set_dwt(device, ic->dwt); changed |= set_dwt(device, ic->dwt);
} }
if (ic->dwtp != INT_MIN) {
changed |= set_dwtp(device, ic->dwtp);
}
if (ic->calibration_matrix.configured) { if (ic->calibration_matrix.configured) {
changed |= set_calibration_matrix(device, ic->calibration_matrix.matrix); changed |= set_calibration_matrix(device, ic->calibration_matrix.matrix);
} }
if (changed) { return changed;
ipc_event_input("libinput_config", input_device);
}
} }
void sway_input_reset_libinput_device(struct sway_input_device *input_device) { void sway_input_reset_libinput_device(struct sway_input_device *input_device) {
@ -304,6 +315,8 @@ void sway_input_reset_libinput_device(struct sway_input_device *input_device) {
libinput_device_config_scroll_get_default_button(device)); libinput_device_config_scroll_get_default_button(device));
changed |= set_dwt(device, changed |= set_dwt(device,
libinput_device_config_dwt_get_default_enabled(device)); libinput_device_config_dwt_get_default_enabled(device));
changed |= set_dwtp(device,
libinput_device_config_dwtp_get_default_enabled(device));
float matrix[6]; float matrix[6];
libinput_device_config_calibration_get_default_matrix(device, matrix); libinput_device_config_calibration_get_default_matrix(device, matrix);

View file

@ -7,10 +7,12 @@
#include <wlr/types/wlr_cursor.h> #include <wlr/types/wlr_cursor.h>
#include <wlr/types/wlr_data_device.h> #include <wlr/types/wlr_data_device.h>
#include <wlr/types/wlr_idle.h> #include <wlr/types/wlr_idle.h>
#include <wlr/types/wlr_idle_notify_v1.h>
#include <wlr/types/wlr_keyboard_group.h> #include <wlr/types/wlr_keyboard_group.h>
#include <wlr/types/wlr_output_layout.h> #include <wlr/types/wlr_output_layout.h>
#include <wlr/types/wlr_primary_selection.h> #include <wlr/types/wlr_primary_selection.h>
#include <wlr/types/wlr_tablet_v2.h> #include <wlr/types/wlr_tablet_v2.h>
#include <wlr/types/wlr_touch.h>
#include <wlr/types/wlr_xcursor_manager.h> #include <wlr/types/wlr_xcursor_manager.h>
#include "config.h" #include "config.h"
#include "list.h" #include "list.h"
@ -42,6 +44,7 @@ static void seat_device_destroy(struct sway_seat_device *seat_device) {
sway_keyboard_destroy(seat_device->keyboard); sway_keyboard_destroy(seat_device->keyboard);
sway_tablet_destroy(seat_device->tablet); sway_tablet_destroy(seat_device->tablet);
sway_tablet_pad_destroy(seat_device->tablet_pad); sway_tablet_pad_destroy(seat_device->tablet_pad);
sway_switch_destroy(seat_device->switch_device);
wlr_cursor_detach_input_device(seat_device->sway_seat->cursor->cursor, wlr_cursor_detach_input_device(seat_device->sway_seat->cursor->cursor,
seat_device->input_device->wlr_device); seat_device->input_device->wlr_device);
wl_list_remove(&seat_device->link); wl_list_remove(&seat_device->link);
@ -110,6 +113,7 @@ void seat_idle_notify_activity(struct sway_seat *seat,
} }
if ((source & mask) > 0) { if ((source & mask) > 0) {
wlr_idle_notify_activity(server.idle, seat->wlr_seat); wlr_idle_notify_activity(server.idle, seat->wlr_seat);
wlr_idle_notifier_v1_notify_activity(server.idle_notifier_v1, seat->wlr_seat);
} }
} }
@ -140,7 +144,7 @@ static struct sway_keyboard *sway_keyboard_for_wlr_keyboard(
if (input_device->wlr_device->type != WLR_INPUT_DEVICE_KEYBOARD) { if (input_device->wlr_device->type != WLR_INPUT_DEVICE_KEYBOARD) {
continue; continue;
} }
if (input_device->wlr_device->keyboard == wlr_keyboard) { if (input_device->wlr_device == &wlr_keyboard->base) {
return seat_device->keyboard; return seat_device->keyboard;
} }
} }
@ -148,7 +152,7 @@ static struct sway_keyboard *sway_keyboard_for_wlr_keyboard(
wl_list_for_each(group, &seat->keyboard_groups, link) { wl_list_for_each(group, &seat->keyboard_groups, link) {
struct sway_input_device *input_device = struct sway_input_device *input_device =
group->seat_device->input_device; group->seat_device->input_device;
if (input_device->wlr_device->keyboard == wlr_keyboard) { if (input_device->wlr_device == &wlr_keyboard->base) {
return group->seat_device->keyboard; return group->seat_device->keyboard;
} }
} }
@ -245,7 +249,7 @@ static void handle_seat_node_destroy(struct wl_listener *listener, void *data) {
seat_node_destroy(seat_node); seat_node_destroy(seat_node);
// If an unmanaged or layer surface is focused when an output gets // If an unmanaged or layer surface is focused when an output gets
// disabled and an empty workspace on the output was focused by the // disabled and an empty workspace on the output was focused by the
// seat, the seat needs to refocus it's focus inactive to update the // seat, the seat needs to refocus its focus inactive to update the
// value of seat->workspace. // value of seat->workspace.
if (seat->workspace == node->sway_workspace) { if (seat->workspace == node->sway_workspace) {
struct sway_node *node = seat_get_focus_inactive(seat, &root->node); struct sway_node *node = seat_get_focus_inactive(seat, &root->node);
@ -378,8 +382,8 @@ void drag_icon_update_position(struct sway_drag_icon *icon) {
case WLR_DRAG_GRAB_KEYBOARD: case WLR_DRAG_GRAB_KEYBOARD:
return; return;
case WLR_DRAG_GRAB_KEYBOARD_POINTER: case WLR_DRAG_GRAB_KEYBOARD_POINTER:
icon->x = cursor->x; icon->x = cursor->x + wlr_icon->surface->sx;
icon->y = cursor->y; icon->y = cursor->y + wlr_icon->surface->sy;
break; break;
case WLR_DRAG_GRAB_KEYBOARD_TOUCH:; case WLR_DRAG_GRAB_KEYBOARD_TOUCH:;
struct wlr_touch_point *point = struct wlr_touch_point *point =
@ -387,8 +391,8 @@ void drag_icon_update_position(struct sway_drag_icon *icon) {
if (point == NULL) { if (point == NULL) {
return; return;
} }
icon->x = seat->touch_x; icon->x = seat->touch_x + wlr_icon->surface->sx;
icon->y = seat->touch_y; icon->y = seat->touch_y + wlr_icon->surface->sy;
} }
drag_icon_damage_whole(icon); drag_icon_damage_whole(icon);
@ -724,14 +728,25 @@ static void seat_apply_input_config(struct sway_seat *seat,
ic == NULL ? MAPPED_TO_DEFAULT : ic->mapped_to; ic == NULL ? MAPPED_TO_DEFAULT : ic->mapped_to;
switch (mapped_to) { switch (mapped_to) {
case MAPPED_TO_DEFAULT: case MAPPED_TO_DEFAULT:;
/* /*
* If the wlroots backend provides an output name, use that. * If the wlroots backend provides an output name, use that.
* *
* Otherwise, try to map built-in touch and tablet tool devices to the * Otherwise, try to map built-in touch and pointer devices to the
* built-in output. * built-in output.
*/ */
mapped_to_output = sway_device->input_device->wlr_device->output_name; struct wlr_input_device *dev = sway_device->input_device->wlr_device;
switch (dev->type) {
case WLR_INPUT_DEVICE_POINTER:
mapped_to_output = wlr_pointer_from_input_device(dev)->output_name;
break;
case WLR_INPUT_DEVICE_TOUCH:
mapped_to_output = wlr_touch_from_input_device(dev)->output_name;
break;
default:
mapped_to_output = NULL;
break;
}
if (mapped_to_output == NULL && is_touch_or_tablet_tool(sway_device) && if (mapped_to_output == NULL && is_touch_or_tablet_tool(sway_device) &&
sway_libinput_device_is_builtin(sway_device->input_device)) { sway_libinput_device_is_builtin(sway_device->input_device)) {
mapped_to_output = get_builtin_output_name(); mapped_to_output = get_builtin_output_name();
@ -799,13 +814,22 @@ static void seat_configure_keyboard(struct sway_seat *seat,
sway_keyboard_create(seat, seat_device); sway_keyboard_create(seat, seat_device);
} }
sway_keyboard_configure(seat_device->keyboard); sway_keyboard_configure(seat_device->keyboard);
wlr_seat_set_keyboard(seat->wlr_seat,
seat_device->input_device->wlr_device); // We only need to update the current keyboard, as the rest will be updated
struct sway_node *focus = seat_get_focus(seat); // as they are activated.
if (focus && node_is_view(focus)) { struct wlr_keyboard *wlr_keyboard =
// force notify reenter to pick up the new configuration 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.
struct wlr_surface *surface = seat->wlr_seat->keyboard_state.focused_surface;
if (surface) {
wlr_seat_keyboard_notify_clear_focus(seat->wlr_seat); wlr_seat_keyboard_notify_clear_focus(seat->wlr_seat);
seat_keyboard_notify_enter(seat, focus->sway_container->view->surface); seat_keyboard_notify_enter(seat, surface);
} }
} }
@ -1057,7 +1081,8 @@ void seat_configure_xcursor(struct sway_seat *seat) {
bool seat_is_input_allowed(struct sway_seat *seat, bool seat_is_input_allowed(struct sway_seat *seat,
struct wlr_surface *surface) { struct wlr_surface *surface) {
struct wl_client *client = wl_resource_get_client(surface->resource); struct wl_client *client = wl_resource_get_client(surface->resource);
return !seat->exclusive_client || seat->exclusive_client == client; return seat->exclusive_client == client ||
(seat->exclusive_client == NULL && !server.session_lock.locked);
} }
static void send_unfocus(struct sway_container *con, void *data) { static void send_unfocus(struct sway_container *con, void *data) {
@ -1116,15 +1141,7 @@ void seat_set_raw_focus(struct sway_seat *seat, struct sway_node *node) {
} }
} }
void seat_set_focus(struct sway_seat *seat, struct sway_node *node) { static void seat_set_workspace_focus(struct sway_seat *seat, struct sway_node *node) {
if (seat->focused_layer) {
struct wlr_layer_surface_v1 *layer = seat->focused_layer;
seat_set_focus_layer(seat, NULL);
seat_set_focus(seat, node);
seat_set_focus_layer(seat, layer);
return;
}
struct sway_node *last_focus = seat_get_focus(seat); struct sway_node *last_focus = seat_get_focus(seat);
if (last_focus == node) { if (last_focus == node) {
return; return;
@ -1257,6 +1274,20 @@ void seat_set_focus(struct sway_seat *seat, struct sway_node *node) {
} }
} }
void seat_set_focus(struct sway_seat *seat, struct sway_node *node) {
if (seat->focused_layer) {
struct wlr_layer_surface_v1 *layer = seat->focused_layer;
seat_set_focus_layer(seat, NULL);
seat_set_workspace_focus(seat, node);
seat_set_focus_layer(seat, layer);
} else {
seat_set_workspace_focus(seat, node);
}
if (server.session_lock.locked) {
seat_set_focus_surface(seat, server.session_lock.focused, false);
}
}
void seat_set_focus_container(struct sway_seat *seat, void seat_set_focus_container(struct sway_seat *seat,
struct sway_container *con) { struct sway_container *con) {
seat_set_focus(seat, con ? &con->node : NULL); seat_set_focus(seat, con ? &con->node : NULL);
@ -1561,7 +1592,7 @@ void seatop_pointer_motion(struct sway_seat *seat, uint32_t time_msec) {
} }
void seatop_pointer_axis(struct sway_seat *seat, void seatop_pointer_axis(struct sway_seat *seat,
struct wlr_event_pointer_axis *event) { struct wlr_pointer_axis_event *event) {
if (seat->seatop_impl->pointer_axis) { if (seat->seatop_impl->pointer_axis) {
seat->seatop_impl->pointer_axis(seat, event); seat->seatop_impl->pointer_axis(seat, event);
} }
@ -1584,6 +1615,62 @@ void seatop_tablet_tool_motion(struct sway_seat *seat,
} }
} }
void seatop_hold_begin(struct sway_seat *seat,
struct wlr_pointer_hold_begin_event *event) {
if (seat->seatop_impl->hold_begin) {
seat->seatop_impl->hold_begin(seat, event);
}
}
void seatop_hold_end(struct sway_seat *seat,
struct wlr_pointer_hold_end_event *event) {
if (seat->seatop_impl->hold_end) {
seat->seatop_impl->hold_end(seat, event);
}
}
void seatop_pinch_begin(struct sway_seat *seat,
struct wlr_pointer_pinch_begin_event *event) {
if (seat->seatop_impl->pinch_begin) {
seat->seatop_impl->pinch_begin(seat, event);
}
}
void seatop_pinch_update(struct sway_seat *seat,
struct wlr_pointer_pinch_update_event *event) {
if (seat->seatop_impl->pinch_update) {
seat->seatop_impl->pinch_update(seat, event);
}
}
void seatop_pinch_end(struct sway_seat *seat,
struct wlr_pointer_pinch_end_event *event) {
if (seat->seatop_impl->pinch_end) {
seat->seatop_impl->pinch_end(seat, event);
}
}
void seatop_swipe_begin(struct sway_seat *seat,
struct wlr_pointer_swipe_begin_event *event) {
if (seat->seatop_impl->swipe_begin) {
seat->seatop_impl->swipe_begin(seat, event);
}
}
void seatop_swipe_update(struct sway_seat *seat,
struct wlr_pointer_swipe_update_event *event) {
if (seat->seatop_impl->swipe_update) {
seat->seatop_impl->swipe_update(seat, event);
}
}
void seatop_swipe_end(struct sway_seat *seat,
struct wlr_pointer_swipe_end_event *event) {
if (seat->seatop_impl->swipe_end) {
seat->seatop_impl->swipe_end(seat, event);
}
}
void seatop_rebase(struct sway_seat *seat, uint32_t time_msec) { void seatop_rebase(struct sway_seat *seat, uint32_t time_msec) {
if (seat->seatop_impl->rebase) { if (seat->seatop_impl->rebase) {
seat->seatop_impl->rebase(seat, time_msec); seat->seatop_impl->rebase(seat, time_msec);

View file

@ -4,6 +4,7 @@
#include <wlr/types/wlr_cursor.h> #include <wlr/types/wlr_cursor.h>
#include <wlr/types/wlr_tablet_v2.h> #include <wlr/types/wlr_tablet_v2.h>
#include <wlr/types/wlr_xcursor_manager.h> #include <wlr/types/wlr_xcursor_manager.h>
#include "gesture.h"
#include "sway/desktop/transaction.h" #include "sway/desktop/transaction.h"
#include "sway/input/cursor.h" #include "sway/input/cursor.h"
#include "sway/input/seat.h" #include "sway/input/seat.h"
@ -20,6 +21,7 @@ struct seatop_default_event {
struct sway_node *previous_node; struct sway_node *previous_node;
uint32_t pressed_buttons[SWAY_CURSOR_PRESSED_BUTTONS_CAP]; uint32_t pressed_buttons[SWAY_CURSOR_PRESSED_BUTTONS_CAP];
size_t pressed_button_count; size_t pressed_button_count;
struct gesture_tracker gestures;
}; };
/*-----------------------------------------\ /*-----------------------------------------\
@ -427,13 +429,31 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec,
} }
} }
// Handle changing focus when clicking on a container
if (cont && state == WLR_BUTTON_PRESSED) {
// Default case: focus the container that was just clicked.
node = &cont->node;
// If the container is a tab/stacked container and the click happened
// on a tab, switch to the tab. If the tab contents were already
// focused, focus the tab container itself. If the tab container was
// already focused, cycle back to focusing the tab contents.
if (on_titlebar) {
struct sway_container *focus = seat_get_focused_container(seat);
if (focus == cont || !container_has_ancestor(focus, cont)) {
node = seat_get_focus_inactive(seat, &cont->node);
}
}
seat_set_focus(seat, node);
transaction_commit_dirty();
}
// Handle beginning floating move // Handle beginning floating move
if (cont && is_floating_or_child && !is_fullscreen_or_child && if (cont && is_floating_or_child && !is_fullscreen_or_child &&
state == WLR_BUTTON_PRESSED) { state == WLR_BUTTON_PRESSED) {
uint32_t btn_move = config->floating_mod_inverse ? BTN_RIGHT : BTN_LEFT; uint32_t btn_move = config->floating_mod_inverse ? BTN_RIGHT : BTN_LEFT;
if (button == btn_move && (mod_pressed || on_titlebar)) { if (button == btn_move && (mod_pressed || on_titlebar)) {
seat_set_focus_container(seat,
seat_get_focus_inactive_view(seat, &cont->node));
seatop_begin_move_floating(seat, container_toplevel_ancestor(cont)); seatop_begin_move_floating(seat, container_toplevel_ancestor(cont));
return; return;
} }
@ -444,6 +464,7 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec,
state == WLR_BUTTON_PRESSED) { state == WLR_BUTTON_PRESSED) {
// Via border // Via border
if (button == BTN_LEFT && resize_edge != WLR_EDGE_NONE) { if (button == BTN_LEFT && resize_edge != WLR_EDGE_NONE) {
seat_set_focus_container(seat, cont);
seatop_begin_resize_floating(seat, cont, resize_edge); seatop_begin_resize_floating(seat, cont, resize_edge);
return; return;
} }
@ -458,6 +479,7 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec,
WLR_EDGE_RIGHT : WLR_EDGE_LEFT; WLR_EDGE_RIGHT : WLR_EDGE_LEFT;
edge |= cursor->cursor->y > floater->pending.y + floater->pending.height / 2 ? edge |= cursor->cursor->y > floater->pending.y + floater->pending.height / 2 ?
WLR_EDGE_BOTTOM : WLR_EDGE_TOP; WLR_EDGE_BOTTOM : WLR_EDGE_TOP;
seat_set_focus_container(seat, floater);
seatop_begin_resize_floating(seat, floater, edge); seatop_begin_resize_floating(seat, floater, edge);
return; return;
} }
@ -467,25 +489,18 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec,
if (config->tiling_drag && (mod_pressed || on_titlebar) && if (config->tiling_drag && (mod_pressed || on_titlebar) &&
state == WLR_BUTTON_PRESSED && !is_floating_or_child && state == WLR_BUTTON_PRESSED && !is_floating_or_child &&
cont && cont->pending.fullscreen_mode == FULLSCREEN_NONE) { cont && cont->pending.fullscreen_mode == FULLSCREEN_NONE) {
struct sway_container *focus = seat_get_focused_container(seat); // If moving a container by its title bar, use a threshold for the drag
bool focused = focus == cont || container_has_ancestor(focus, cont);
if (on_titlebar && !focused) {
node = seat_get_focus_inactive(seat, &cont->node);
seat_set_focus(seat, node);
}
// If moving a container by it's title bar, use a threshold for the drag
if (!mod_pressed && config->tiling_drag_threshold > 0) { if (!mod_pressed && config->tiling_drag_threshold > 0) {
seatop_begin_move_tiling_threshold(seat, cont); seatop_begin_move_tiling_threshold(seat, cont);
} else { } else {
seatop_begin_move_tiling(seat, cont); seatop_begin_move_tiling(seat, cont);
} }
return; return;
} }
// Handle mousedown on a container surface // Handle mousedown on a container surface
if (surface && cont && state == WLR_BUTTON_PRESSED) { if (surface && cont && state == WLR_BUTTON_PRESSED) {
seat_set_focus_container(seat, cont);
seatop_begin_down(seat, cont, time_msec, sx, sy); seatop_begin_down(seat, cont, time_msec, sx, sy);
seat_pointer_notify_button(seat, time_msec, button, WLR_BUTTON_PRESSED); seat_pointer_notify_button(seat, time_msec, button, WLR_BUTTON_PRESSED);
return; return;
@ -493,9 +508,6 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec,
// Handle clicking a container surface or decorations // Handle clicking a container surface or decorations
if (cont && state == WLR_BUTTON_PRESSED) { if (cont && state == WLR_BUTTON_PRESSED) {
node = seat_get_focus_inactive(seat, &cont->node);
seat_set_focus(seat, node);
transaction_commit_dirty();
seat_pointer_notify_button(seat, time_msec, button, state); seat_pointer_notify_button(seat, time_msec, button, state);
return; return;
} }
@ -645,7 +657,7 @@ static void handle_tablet_tool_motion(struct sway_seat *seat,
* Functions used by handle_pointer_axis / * Functions used by handle_pointer_axis /
*--------------------------------------*/ *--------------------------------------*/
static uint32_t wl_axis_to_button(struct wlr_event_pointer_axis *event) { static uint32_t wl_axis_to_button(struct wlr_pointer_axis_event *event) {
switch (event->orientation) { switch (event->orientation) {
case WLR_AXIS_ORIENTATION_VERTICAL: case WLR_AXIS_ORIENTATION_VERTICAL:
return event->delta < 0 ? SWAY_SCROLL_UP : SWAY_SCROLL_DOWN; return event->delta < 0 ? SWAY_SCROLL_UP : SWAY_SCROLL_DOWN;
@ -658,9 +670,9 @@ static uint32_t wl_axis_to_button(struct wlr_event_pointer_axis *event) {
} }
static void handle_pointer_axis(struct sway_seat *seat, static void handle_pointer_axis(struct sway_seat *seat,
struct wlr_event_pointer_axis *event) { struct wlr_pointer_axis_event *event) {
struct sway_input_device *input_device = struct sway_input_device *input_device =
event->device ? event->device->data : NULL; event->pointer ? event->pointer->base.data : NULL;
struct input_config *ic = struct input_config *ic =
input_device ? input_device_get_config(input_device) : NULL; input_device ? input_device_get_config(input_device) : NULL;
struct sway_cursor *cursor = seat->cursor; struct sway_cursor *cursor = seat->cursor;
@ -706,6 +718,7 @@ static void handle_pointer_axis(struct sway_seat *seat,
// Scrolling on a tabbed or stacked title bar (handled as press event) // Scrolling on a tabbed or stacked title bar (handled as press event)
if (!handled && (on_titlebar || on_titlebar_border)) { if (!handled && (on_titlebar || on_titlebar_border)) {
struct sway_node *new_focus;
enum sway_container_layout layout = container_parent_layout(cont); enum sway_container_layout layout = container_parent_layout(cont);
if (layout == L_TABBED || layout == L_STACKED) { if (layout == L_TABBED || layout == L_STACKED) {
struct sway_node *tabcontainer = node_get_parent(node); struct sway_node *tabcontainer = node_get_parent(node);
@ -713,7 +726,7 @@ static void handle_pointer_axis(struct sway_seat *seat,
seat_get_active_tiling_child(seat, tabcontainer); seat_get_active_tiling_child(seat, tabcontainer);
list_t *siblings = container_get_siblings(cont); list_t *siblings = container_get_siblings(cont);
int desired = list_find(siblings, active->sway_container) + int desired = list_find(siblings, active->sway_container) +
round(scroll_factor * event->delta_discrete); round(scroll_factor * event->delta_discrete / WLR_POINTER_AXIS_DISCRETE_STEP);
if (desired < 0) { if (desired < 0) {
desired = 0; desired = 0;
} else if (desired >= siblings->length) { } else if (desired >= siblings->length) {
@ -722,14 +735,16 @@ static void handle_pointer_axis(struct sway_seat *seat,
struct sway_container *new_sibling_con = siblings->items[desired]; struct sway_container *new_sibling_con = siblings->items[desired];
struct sway_node *new_sibling = &new_sibling_con->node; struct sway_node *new_sibling = &new_sibling_con->node;
struct sway_node *new_focus =
seat_get_focus_inactive(seat, new_sibling);
// Use the focused child of the tabbed/stacked container, not the // Use the focused child of the tabbed/stacked container, not the
// container the user scrolled on. // container the user scrolled on.
seat_set_focus(seat, new_focus); new_focus = seat_get_focus_inactive(seat, new_sibling);
transaction_commit_dirty(); } else {
handled = true; new_focus = seat_get_focus_inactive(seat, &cont->node);
} }
seat_set_focus(seat, new_focus);
transaction_commit_dirty();
handled = true;
} }
// Handle mouse bindings - x11 mouse buttons 4-7 - release event // Handle mouse bindings - x11 mouse buttons 4-7 - release event
@ -750,6 +765,304 @@ static void handle_pointer_axis(struct sway_seat *seat,
} }
} }
/*------------------------------------\
* Functions used by gesture support /
*----------------------------------*/
/**
* Check gesture binding for a specific gesture type and finger count.
* Returns true if binding is present, false otherwise
*/
static bool gesture_binding_check(list_t *bindings, enum gesture_type type,
uint8_t fingers, struct sway_input_device *device) {
char *input =
device ? input_device_get_identifier(device->wlr_device) : strdup("*");
for (int i = 0; i < bindings->length; ++i) {
struct sway_gesture_binding *binding = bindings->items[i];
// Check type and finger count
if (!gesture_check(&binding->gesture, type, fingers)) {
continue;
}
// Check that input matches
if (strcmp(binding->input, "*") != 0 &&
strcmp(binding->input, input) != 0) {
continue;
}
free(input);
return true;
}
free(input);
return false;
}
/**
* Return the gesture binding which matches gesture type, finger count
* and direction, otherwise return null.
*/
static struct sway_gesture_binding* gesture_binding_match(
list_t *bindings, struct gesture *gesture, const char *input) {
struct sway_gesture_binding *current = NULL;
// Find best matching binding
for (int i = 0; i < bindings->length; ++i) {
struct sway_gesture_binding *binding = bindings->items[i];
bool exact = binding->flags & BINDING_EXACT;
// Check gesture matching
if (!gesture_match(&binding->gesture, gesture, exact)) {
continue;
}
// Check input matching
if (strcmp(binding->input, "*") != 0 &&
strcmp(binding->input, input) != 0) {
continue;
}
// If we already have a match ...
if (current) {
// ... check if input matching is equivalent
if (strcmp(current->input, binding->input) == 0) {
// ... - do not override an exact binding
if (!exact && current->flags & BINDING_EXACT) {
continue;
}
// ... - and ensure direction matching is better or equal
if (gesture_compare(&current->gesture, &binding->gesture) > 0) {
continue;
}
} else if (strcmp(binding->input, "*") == 0) {
// ... do not accept worse input match
continue;
}
}
// Accept newer or better match
current = binding;
// If exact binding and input is found, quit search
if (strcmp(current->input, input) == 0 &&
gesture_compare(&current->gesture, gesture) == 0) {
break;
}
} // for all gesture bindings
return current;
}
// Wrapper around gesture_tracker_end to use tracker with sway bindings
static struct sway_gesture_binding* gesture_tracker_end_and_match(
struct gesture_tracker *tracker, struct sway_input_device* device) {
// Determine name of input that received gesture
char *input = device
? input_device_get_identifier(device->wlr_device)
: strdup("*");
// Match tracking result to binding
struct gesture *gesture = gesture_tracker_end(tracker);
struct sway_gesture_binding *binding = gesture_binding_match(
config->current_mode->gesture_bindings, gesture, input);
free(gesture);
free(input);
return binding;
}
// Small wrapper around seat_execute_command to work on gesture bindings
static void gesture_binding_execute(struct sway_seat *seat,
struct sway_gesture_binding *binding) {
struct sway_binding *dummy_binding =
calloc(1, sizeof(struct sway_binding));
dummy_binding->type = BINDING_GESTURE;
dummy_binding->command = binding->command;
char *description = gesture_to_string(&binding->gesture);
sway_log(SWAY_DEBUG, "executing gesture binding: %s", description);
free(description);
seat_execute_command(seat, dummy_binding);
free(dummy_binding);
}
static void handle_hold_begin(struct sway_seat *seat,
struct wlr_pointer_hold_begin_event *event) {
// Start tracking gesture if there is a matching binding ...
struct sway_input_device *device =
event->pointer ? event->pointer->base.data : NULL;
list_t *bindings = config->current_mode->gesture_bindings;
if (gesture_binding_check(bindings, GESTURE_TYPE_HOLD, event->fingers, device)) {
struct seatop_default_event *seatop = seat->seatop_data;
gesture_tracker_begin(&seatop->gestures, GESTURE_TYPE_HOLD, event->fingers);
} else {
// ... otherwise forward to client
struct sway_cursor *cursor = seat->cursor;
wlr_pointer_gestures_v1_send_hold_begin(
cursor->pointer_gestures, cursor->seat->wlr_seat,
event->time_msec, event->fingers);
}
}
static void handle_hold_end(struct sway_seat *seat,
struct wlr_pointer_hold_end_event *event) {
// Ensure that gesture is being tracked and was not cancelled
struct seatop_default_event *seatop = seat->seatop_data;
if (!gesture_tracker_check(&seatop->gestures, GESTURE_TYPE_HOLD)) {
struct sway_cursor *cursor = seat->cursor;
wlr_pointer_gestures_v1_send_hold_end(
cursor->pointer_gestures, cursor->seat->wlr_seat,
event->time_msec, event->cancelled);
return;
}
if (event->cancelled) {
gesture_tracker_cancel(&seatop->gestures);
return;
}
// End gesture tracking and execute matched binding
struct sway_input_device *device =
event->pointer ? event->pointer->base.data : NULL;
struct sway_gesture_binding *binding = gesture_tracker_end_and_match(
&seatop->gestures, device);
if (binding) {
gesture_binding_execute(seat, binding);
}
}
static void handle_pinch_begin(struct sway_seat *seat,
struct wlr_pointer_pinch_begin_event *event) {
// Start tracking gesture if there is a matching binding ...
struct sway_input_device *device =
event->pointer ? event->pointer->base.data : NULL;
list_t *bindings = config->current_mode->gesture_bindings;
if (gesture_binding_check(bindings, GESTURE_TYPE_PINCH, event->fingers, device)) {
struct seatop_default_event *seatop = seat->seatop_data;
gesture_tracker_begin(&seatop->gestures, GESTURE_TYPE_PINCH, event->fingers);
} else {
// ... otherwise forward to client
struct sway_cursor *cursor = seat->cursor;
wlr_pointer_gestures_v1_send_pinch_begin(
cursor->pointer_gestures, cursor->seat->wlr_seat,
event->time_msec, event->fingers);
}
}
static void handle_pinch_update(struct sway_seat *seat,
struct wlr_pointer_pinch_update_event *event) {
// Update any ongoing tracking ...
struct seatop_default_event *seatop = seat->seatop_data;
if (gesture_tracker_check(&seatop->gestures, GESTURE_TYPE_PINCH)) {
gesture_tracker_update(&seatop->gestures, event->dx, event->dy,
event->scale, event->rotation);
} else {
// ... otherwise forward to client
struct sway_cursor *cursor = seat->cursor;
wlr_pointer_gestures_v1_send_pinch_update(
cursor->pointer_gestures,
cursor->seat->wlr_seat,
event->time_msec, event->dx, event->dy,
event->scale, event->rotation);
}
}
static void handle_pinch_end(struct sway_seat *seat,
struct wlr_pointer_pinch_end_event *event) {
// Ensure that gesture is being tracked and was not cancelled
struct seatop_default_event *seatop = seat->seatop_data;
if (!gesture_tracker_check(&seatop->gestures, GESTURE_TYPE_PINCH)) {
struct sway_cursor *cursor = seat->cursor;
wlr_pointer_gestures_v1_send_pinch_end(
cursor->pointer_gestures, cursor->seat->wlr_seat,
event->time_msec, event->cancelled);
return;
}
if (event->cancelled) {
gesture_tracker_cancel(&seatop->gestures);
return;
}
// End gesture tracking and execute matched binding
struct sway_input_device *device =
event->pointer ? event->pointer->base.data : NULL;
struct sway_gesture_binding *binding = gesture_tracker_end_and_match(
&seatop->gestures, device);
if (binding) {
gesture_binding_execute(seat, binding);
}
}
static void handle_swipe_begin(struct sway_seat *seat,
struct wlr_pointer_swipe_begin_event *event) {
// Start tracking gesture if there is a matching binding ...
struct sway_input_device *device =
event->pointer ? event->pointer->base.data : NULL;
list_t *bindings = config->current_mode->gesture_bindings;
if (gesture_binding_check(bindings, GESTURE_TYPE_SWIPE, event->fingers, device)) {
struct seatop_default_event *seatop = seat->seatop_data;
gesture_tracker_begin(&seatop->gestures, GESTURE_TYPE_SWIPE, event->fingers);
} else {
// ... otherwise forward to client
struct sway_cursor *cursor = seat->cursor;
wlr_pointer_gestures_v1_send_swipe_begin(
cursor->pointer_gestures, cursor->seat->wlr_seat,
event->time_msec, event->fingers);
}
}
static void handle_swipe_update(struct sway_seat *seat,
struct wlr_pointer_swipe_update_event *event) {
// Update any ongoing tracking ...
struct seatop_default_event *seatop = seat->seatop_data;
if (gesture_tracker_check(&seatop->gestures, GESTURE_TYPE_SWIPE)) {
gesture_tracker_update(&seatop->gestures,
event->dx, event->dy, NAN, NAN);
} else {
// ... otherwise forward to client
struct sway_cursor *cursor = seat->cursor;
wlr_pointer_gestures_v1_send_swipe_update(
cursor->pointer_gestures, cursor->seat->wlr_seat,
event->time_msec, event->dx, event->dy);
}
}
static void handle_swipe_end(struct sway_seat *seat,
struct wlr_pointer_swipe_end_event *event) {
// Ensure gesture is being tracked and was not cancelled
struct seatop_default_event *seatop = seat->seatop_data;
if (!gesture_tracker_check(&seatop->gestures, GESTURE_TYPE_SWIPE)) {
struct sway_cursor *cursor = seat->cursor;
wlr_pointer_gestures_v1_send_swipe_end(cursor->pointer_gestures,
cursor->seat->wlr_seat, event->time_msec, event->cancelled);
return;
}
if (event->cancelled) {
gesture_tracker_cancel(&seatop->gestures);
return;
}
// End gesture tracking and execute matched binding
struct sway_input_device *device =
event->pointer ? event->pointer->base.data : NULL;
struct sway_gesture_binding *binding = gesture_tracker_end_and_match(
&seatop->gestures, device);
if (binding) {
gesture_binding_execute(seat, binding);
}
}
/*----------------------------------\ /*----------------------------------\
* Functions used by handle_rebase / * Functions used by handle_rebase /
*--------------------------------*/ *--------------------------------*/
@ -779,6 +1092,14 @@ static const struct sway_seatop_impl seatop_impl = {
.pointer_axis = handle_pointer_axis, .pointer_axis = handle_pointer_axis,
.tablet_tool_tip = handle_tablet_tool_tip, .tablet_tool_tip = handle_tablet_tool_tip,
.tablet_tool_motion = handle_tablet_tool_motion, .tablet_tool_motion = handle_tablet_tool_motion,
.hold_begin = handle_hold_begin,
.hold_end = handle_hold_end,
.pinch_begin = handle_pinch_begin,
.pinch_update = handle_pinch_update,
.pinch_end = handle_pinch_end,
.swipe_begin = handle_swipe_begin,
.swipe_update = handle_swipe_update,
.swipe_end = handle_swipe_end,
.rebase = handle_rebase, .rebase = handle_rebase,
.allow_set_cursor = true, .allow_set_cursor = true,
}; };
@ -789,8 +1110,8 @@ void seatop_begin_default(struct sway_seat *seat) {
struct seatop_default_event *e = struct seatop_default_event *e =
calloc(1, sizeof(struct seatop_default_event)); calloc(1, sizeof(struct seatop_default_event));
sway_assert(e, "Unable to allocate seatop_default_event"); sway_assert(e, "Unable to allocate seatop_default_event");
seat->seatop_impl = &seatop_impl; seat->seatop_impl = &seatop_impl;
seat->seatop_data = e; seat->seatop_data = e;
seatop_rebase(seat, 0); seatop_rebase(seat, 0);
} }

View file

@ -18,9 +18,9 @@ struct seatop_down_event {
}; };
static void handle_pointer_axis(struct sway_seat *seat, static void handle_pointer_axis(struct sway_seat *seat,
struct wlr_event_pointer_axis *event) { struct wlr_pointer_axis_event *event) {
struct sway_input_device *input_device = struct sway_input_device *input_device =
event->device ? event->device->data : NULL; event->pointer ? event->pointer->base.data : NULL;
struct input_config *ic = struct input_config *ic =
input_device ? input_device_get_config(input_device) : NULL; input_device ? input_device_get_config(input_device) : NULL;
float scroll_factor = float scroll_factor =

View file

@ -11,6 +11,7 @@ struct sway_switch *sway_switch_create(struct sway_seat *seat,
return NULL; return NULL;
} }
device->switch_device = switch_device; device->switch_device = switch_device;
switch_device->wlr = wlr_switch_from_input_device(device->input_device->wlr_device);
switch_device->seat_device = device; switch_device->seat_device = device;
switch_device->state = WLR_SWITCH_STATE_OFF; switch_device->state = WLR_SWITCH_STATE_OFF;
wl_list_init(&switch_device->switch_toggle.link); wl_list_init(&switch_device->switch_toggle.link);
@ -19,9 +20,23 @@ struct sway_switch *sway_switch_create(struct sway_seat *seat,
return switch_device; return switch_device;
} }
static bool sway_switch_trigger_test(enum sway_switch_trigger trigger,
enum wlr_switch_state state) {
switch (trigger) {
case SWAY_SWITCH_TRIGGER_ON:
return state == WLR_SWITCH_STATE_ON;
case SWAY_SWITCH_TRIGGER_OFF:
return state == WLR_SWITCH_STATE_OFF;
case SWAY_SWITCH_TRIGGER_TOGGLE:
return true;
}
abort(); // unreachable
}
static void execute_binding(struct sway_switch *sway_switch) { static void execute_binding(struct sway_switch *sway_switch) {
struct sway_seat* seat = sway_switch->seat_device->sway_seat; struct sway_seat* seat = sway_switch->seat_device->sway_seat;
bool input_inhibited = seat->exclusive_client != NULL; bool input_inhibited = seat->exclusive_client != NULL ||
server.session_lock.locked;
list_t *bindings = config->current_mode->switch_bindings; list_t *bindings = config->current_mode->switch_bindings;
struct sway_switch_binding *matched_binding = NULL; struct sway_switch_binding *matched_binding = NULL;
@ -30,11 +45,10 @@ static void execute_binding(struct sway_switch *sway_switch) {
if (binding->type != sway_switch->type) { if (binding->type != sway_switch->type) {
continue; continue;
} }
if (binding->state != WLR_SWITCH_STATE_TOGGLE && if (!sway_switch_trigger_test(binding->trigger, sway_switch->state)) {
binding->state != sway_switch->state) {
continue; continue;
} }
if (config->reloading && (binding->state == WLR_SWITCH_STATE_TOGGLE if (config->reloading && (binding->trigger == SWAY_SWITCH_TRIGGER_TOGGLE
|| (binding->flags & BINDING_RELOAD) == 0)) { || (binding->flags & BINDING_RELOAD) == 0)) {
continue; continue;
} }
@ -65,7 +79,7 @@ static void execute_binding(struct sway_switch *sway_switch) {
static void handle_switch_toggle(struct wl_listener *listener, void *data) { static void handle_switch_toggle(struct wl_listener *listener, void *data) {
struct sway_switch *sway_switch = struct sway_switch *sway_switch =
wl_container_of(listener, sway_switch, switch_toggle); wl_container_of(listener, sway_switch, switch_toggle);
struct wlr_event_switch_toggle *event = data; struct wlr_switch_toggle_event *event = data;
struct sway_seat *seat = sway_switch->seat_device->sway_seat; struct sway_seat *seat = sway_switch->seat_device->sway_seat;
seat_idle_notify_activity(seat, IDLE_SOURCE_SWITCH); seat_idle_notify_activity(seat, IDLE_SOURCE_SWITCH);
@ -82,10 +96,8 @@ static void handle_switch_toggle(struct wl_listener *listener, void *data) {
} }
void sway_switch_configure(struct sway_switch *sway_switch) { void sway_switch_configure(struct sway_switch *sway_switch) {
struct wlr_input_device *wlr_device =
sway_switch->seat_device->input_device->wlr_device;
wl_list_remove(&sway_switch->switch_toggle.link); wl_list_remove(&sway_switch->switch_toggle.link);
wl_signal_add(&wlr_device->switch_device->events.toggle, wl_signal_add(&sway_switch->wlr->events.toggle,
&sway_switch->switch_toggle); &sway_switch->switch_toggle);
sway_switch->switch_toggle.notify = handle_switch_toggle; sway_switch->switch_toggle.notify = handle_switch_toggle;
sway_log(SWAY_DEBUG, "Configured switch for device"); sway_log(SWAY_DEBUG, "Configured switch for device");

View file

@ -196,7 +196,7 @@ static void handle_tablet_pad_attach(struct wl_listener *listener,
static void handle_tablet_pad_ring(struct wl_listener *listener, void *data) { static void handle_tablet_pad_ring(struct wl_listener *listener, void *data) {
struct sway_tablet_pad *pad = wl_container_of(listener, pad, ring); struct sway_tablet_pad *pad = wl_container_of(listener, pad, ring);
struct wlr_event_tablet_pad_ring *event = data; struct wlr_tablet_pad_ring_event *event = data;
if (!pad->current_surface) { if (!pad->current_surface) {
return; return;
@ -210,7 +210,7 @@ static void handle_tablet_pad_ring(struct wl_listener *listener, void *data) {
static void handle_tablet_pad_strip(struct wl_listener *listener, void *data) { static void handle_tablet_pad_strip(struct wl_listener *listener, void *data) {
struct sway_tablet_pad *pad = wl_container_of(listener, pad, strip); struct sway_tablet_pad *pad = wl_container_of(listener, pad, strip);
struct wlr_event_tablet_pad_strip *event = data; struct wlr_tablet_pad_strip_event *event = data;
if (!pad->current_surface) { if (!pad->current_surface) {
return; return;
@ -224,7 +224,7 @@ static void handle_tablet_pad_strip(struct wl_listener *listener, void *data) {
static void handle_tablet_pad_button(struct wl_listener *listener, void *data) { static void handle_tablet_pad_button(struct wl_listener *listener, void *data) {
struct sway_tablet_pad *pad = wl_container_of(listener, pad, button); struct sway_tablet_pad *pad = wl_container_of(listener, pad, button);
struct wlr_event_tablet_pad_button *event = data; struct wlr_tablet_pad_button_event *event = data;
if (!pad->current_surface) { if (!pad->current_surface) {
return; return;
@ -246,6 +246,7 @@ struct sway_tablet_pad *sway_tablet_pad_create(struct sway_seat *seat,
return NULL; return NULL;
} }
tablet_pad->wlr = wlr_tablet_pad_from_input_device(device->input_device->wlr_device);
tablet_pad->seat_device = device; tablet_pad->seat_device = device;
wl_list_init(&tablet_pad->attach.link); wl_list_init(&tablet_pad->attach.link);
wl_list_init(&tablet_pad->button.link); wl_list_init(&tablet_pad->button.link);
@ -260,40 +261,40 @@ struct sway_tablet_pad *sway_tablet_pad_create(struct sway_seat *seat,
} }
void sway_configure_tablet_pad(struct sway_tablet_pad *tablet_pad) { void sway_configure_tablet_pad(struct sway_tablet_pad *tablet_pad) {
struct wlr_input_device *device = struct wlr_input_device *wlr_device =
tablet_pad->seat_device->input_device->wlr_device; tablet_pad->seat_device->input_device->wlr_device;
struct sway_seat *seat = tablet_pad->seat_device->sway_seat; struct sway_seat *seat = tablet_pad->seat_device->sway_seat;
if (!tablet_pad->tablet_v2_pad) { if (!tablet_pad->tablet_v2_pad) {
tablet_pad->tablet_v2_pad = tablet_pad->tablet_v2_pad =
wlr_tablet_pad_create(server.tablet_v2, seat->wlr_seat, device); wlr_tablet_pad_create(server.tablet_v2, seat->wlr_seat, wlr_device);
} }
wl_list_remove(&tablet_pad->attach.link); wl_list_remove(&tablet_pad->attach.link);
tablet_pad->attach.notify = handle_tablet_pad_attach; tablet_pad->attach.notify = handle_tablet_pad_attach;
wl_signal_add(&device->tablet_pad->events.attach_tablet, wl_signal_add(&tablet_pad->wlr->events.attach_tablet,
&tablet_pad->attach); &tablet_pad->attach);
wl_list_remove(&tablet_pad->button.link); wl_list_remove(&tablet_pad->button.link);
tablet_pad->button.notify = handle_tablet_pad_button; tablet_pad->button.notify = handle_tablet_pad_button;
wl_signal_add(&device->tablet_pad->events.button, &tablet_pad->button); wl_signal_add(&tablet_pad->wlr->events.button, &tablet_pad->button);
wl_list_remove(&tablet_pad->strip.link); wl_list_remove(&tablet_pad->strip.link);
tablet_pad->strip.notify = handle_tablet_pad_strip; tablet_pad->strip.notify = handle_tablet_pad_strip;
wl_signal_add(&device->tablet_pad->events.strip, &tablet_pad->strip); wl_signal_add(&tablet_pad->wlr->events.strip, &tablet_pad->strip);
wl_list_remove(&tablet_pad->ring.link); wl_list_remove(&tablet_pad->ring.link);
tablet_pad->ring.notify = handle_tablet_pad_ring; tablet_pad->ring.notify = handle_tablet_pad_ring;
wl_signal_add(&device->tablet_pad->events.ring, &tablet_pad->ring); wl_signal_add(&tablet_pad->wlr->events.ring, &tablet_pad->ring);
/* Search for a sibling tablet */ /* Search for a sibling tablet */
if (!wlr_input_device_is_libinput(device)) { if (!wlr_input_device_is_libinput(wlr_device)) {
/* We can only do this on libinput devices */ /* We can only do this on libinput devices */
return; return;
} }
struct libinput_device_group *group = struct libinput_device_group *group =
libinput_device_get_device_group(wlr_libinput_get_device_handle(device)); libinput_device_get_device_group(wlr_libinput_get_device_handle(wlr_device));
struct sway_tablet *tool; struct sway_tablet *tool;
wl_list_for_each(tool, &seat->cursor->tablets, link) { wl_list_for_each(tool, &seat->cursor->tablets, link) {
struct wlr_input_device *tablet = struct wlr_input_device *tablet =

View file

@ -77,8 +77,6 @@ static void handle_im_grab_keyboard(struct wl_listener *listener, void *data) {
struct wlr_keyboard *active_keyboard = wlr_seat_get_keyboard(relay->seat->wlr_seat); struct wlr_keyboard *active_keyboard = wlr_seat_get_keyboard(relay->seat->wlr_seat);
wlr_input_method_keyboard_grab_v2_set_keyboard(keyboard_grab, wlr_input_method_keyboard_grab_v2_set_keyboard(keyboard_grab,
active_keyboard); active_keyboard);
wlr_input_method_keyboard_grab_v2_send_modifiers(keyboard_grab,
&active_keyboard->modifiers);
wl_signal_add(&keyboard_grab->events.destroy, wl_signal_add(&keyboard_grab->events.destroy,
&relay->input_method_keyboard_grab_destroy); &relay->input_method_keyboard_grab_destroy);

View file

@ -112,12 +112,43 @@ static const char *ipc_json_output_adaptive_sync_status_description(
return "disabled"; return "disabled";
case WLR_OUTPUT_ADAPTIVE_SYNC_ENABLED: case WLR_OUTPUT_ADAPTIVE_SYNC_ENABLED:
return "enabled"; return "enabled";
case WLR_OUTPUT_ADAPTIVE_SYNC_UNKNOWN:
return "unknown";
} }
return NULL; return NULL;
} }
static const char *ipc_json_output_mode_aspect_ratio_description(
enum wlr_output_mode_aspect_ratio aspect_ratio) {
switch (aspect_ratio) {
case WLR_OUTPUT_MODE_ASPECT_RATIO_NONE:
return "none";
case WLR_OUTPUT_MODE_ASPECT_RATIO_4_3:
return "4:3";
case WLR_OUTPUT_MODE_ASPECT_RATIO_16_9:
return "16:9";
case WLR_OUTPUT_MODE_ASPECT_RATIO_64_27:
return "64:27";
case WLR_OUTPUT_MODE_ASPECT_RATIO_256_135:
return "256:135";
}
return NULL;
}
static json_object *ipc_json_output_mode_description(
const struct wlr_output_mode *mode) {
const char *pic_ar =
ipc_json_output_mode_aspect_ratio_description(mode->picture_aspect_ratio);
json_object *mode_object = json_object_new_object();
json_object_object_add(mode_object, "width",
json_object_new_int(mode->width));
json_object_object_add(mode_object, "height",
json_object_new_int(mode->height));
json_object_object_add(mode_object, "refresh",
json_object_new_int(mode->refresh));
json_object_object_add(mode_object, "picture_aspect_ratio",
json_object_new_string(pic_ar));
return mode_object;
}
#if HAVE_XWAYLAND #if HAVE_XWAYLAND
static const char *ipc_json_xwindow_type_description(struct sway_view *view) { static const char *ipc_json_xwindow_type_description(struct sway_view *view) {
struct wlr_xwayland_surface *surface = view->wlr_xwayland_surface; struct wlr_xwayland_surface *surface = view->wlr_xwayland_surface;
@ -242,23 +273,50 @@ static json_object *ipc_json_create_node(int id, const char* type, char *name,
return object; return object;
} }
static void ipc_json_describe_wlr_output(struct wlr_output *wlr_output, json_object *object) {
json_object_object_add(object, "primary", json_object_new_boolean(false));
json_object_object_add(object, "make",
json_object_new_string(wlr_output->make ? wlr_output->make : "Unknown"));
json_object_object_add(object, "model",
json_object_new_string(wlr_output->model ? wlr_output->model : "Unknown"));
json_object_object_add(object, "serial",
json_object_new_string(wlr_output->serial ? wlr_output->serial : "Unknown"));
json_object *modes_array = json_object_new_array();
struct wlr_output_mode *mode;
wl_list_for_each(mode, &wlr_output->modes, link) {
json_object *mode_object = json_object_new_object();
json_object_object_add(mode_object, "width",
json_object_new_int(mode->width));
json_object_object_add(mode_object, "height",
json_object_new_int(mode->height));
json_object_object_add(mode_object, "refresh",
json_object_new_int(mode->refresh));
json_object_array_add(modes_array, mode_object);
}
json_object_object_add(object, "modes", modes_array);
}
static void ipc_json_describe_output(struct sway_output *output, static void ipc_json_describe_output(struct sway_output *output,
json_object *object) { json_object *object) {
ipc_json_describe_wlr_output(output->wlr_output, object);
}
static void ipc_json_describe_enabled_output(struct sway_output *output,
json_object *object) {
ipc_json_describe_output(output, object);
struct wlr_output *wlr_output = output->wlr_output; struct wlr_output *wlr_output = output->wlr_output;
json_object_object_add(object, "non_desktop", json_object_new_boolean(false));
json_object_object_add(object, "active", json_object_new_boolean(true)); json_object_object_add(object, "active", json_object_new_boolean(true));
json_object_object_add(object, "dpms", json_object_object_add(object, "dpms",
json_object_new_boolean(wlr_output->enabled)); json_object_new_boolean(wlr_output->enabled));
json_object_object_add(object, "primary", json_object_new_boolean(false)); json_object_object_add(object, "power",
json_object_new_boolean(wlr_output->enabled));
json_object_object_add(object, "layout", json_object_new_string("output")); json_object_object_add(object, "layout", json_object_new_string("output"));
json_object_object_add(object, "orientation", json_object_object_add(object, "orientation",
json_object_new_string( json_object_new_string(
ipc_json_orientation_description(L_NONE))); ipc_json_orientation_description(L_NONE)));
json_object_object_add(object, "make",
json_object_new_string(wlr_output->make));
json_object_object_add(object, "model",
json_object_new_string(wlr_output->model));
json_object_object_add(object, "serial",
json_object_new_string(wlr_output->serial));
json_object_object_add(object, "scale", json_object_object_add(object, "scale",
json_object_new_double(wlr_output->scale)); json_object_new_double(wlr_output->scale));
json_object_object_add(object, "scale_filter", json_object_object_add(object, "scale_filter",
@ -283,25 +341,26 @@ static void ipc_json_describe_output(struct sway_output *output,
json_object *modes_array = json_object_new_array(); json_object *modes_array = json_object_new_array();
struct wlr_output_mode *mode; struct wlr_output_mode *mode;
wl_list_for_each(mode, &wlr_output->modes, link) { wl_list_for_each(mode, &wlr_output->modes, link) {
json_object *mode_object = json_object_new_object(); json_object *mode_object =
json_object_object_add(mode_object, "width", ipc_json_output_mode_description(mode);
json_object_new_int(mode->width));
json_object_object_add(mode_object, "height",
json_object_new_int(mode->height));
json_object_object_add(mode_object, "refresh",
json_object_new_int(mode->refresh));
json_object_array_add(modes_array, mode_object); json_object_array_add(modes_array, mode_object);
} }
json_object_object_add(object, "modes", modes_array); json_object_object_add(object, "modes", modes_array);
json_object *current_mode_object = json_object_new_object(); json_object *current_mode_object;
json_object_object_add(current_mode_object, "width", if (wlr_output->current_mode != NULL) {
json_object_new_int(wlr_output->width)); current_mode_object =
json_object_object_add(current_mode_object, "height", ipc_json_output_mode_description(wlr_output->current_mode);
json_object_new_int(wlr_output->height)); } else {
json_object_object_add(current_mode_object, "refresh", current_mode_object = json_object_new_object();
json_object_new_int(wlr_output->refresh)); json_object_object_add(current_mode_object, "width",
json_object_new_int(wlr_output->width));
json_object_object_add(current_mode_object, "height",
json_object_new_int(wlr_output->height));
json_object_object_add(current_mode_object, "refresh",
json_object_new_int(wlr_output->refresh));
}
json_object_object_add(object, "current_mode", current_mode_object); json_object_object_add(object, "current_mode", current_mode_object);
struct sway_node *parent = node_get_parent(&output->node); struct sway_node *parent = node_get_parent(&output->node);
@ -325,33 +384,15 @@ json_object *ipc_json_describe_disabled_output(struct sway_output *output) {
json_object *object = json_object_new_object(); json_object *object = json_object_new_object();
ipc_json_describe_output(output, object);
json_object_object_add(object, "non_desktop", json_object_new_boolean(false));
json_object_object_add(object, "type", json_object_new_string("output")); json_object_object_add(object, "type", json_object_new_string("output"));
json_object_object_add(object, "name", json_object_object_add(object, "name",
json_object_new_string(wlr_output->name)); json_object_new_string(wlr_output->name));
json_object_object_add(object, "active", json_object_new_boolean(false)); json_object_object_add(object, "active", json_object_new_boolean(false));
json_object_object_add(object, "dpms", json_object_new_boolean(false)); json_object_object_add(object, "dpms", json_object_new_boolean(false));
json_object_object_add(object, "primary", json_object_new_boolean(false)); json_object_object_add(object, "power", json_object_new_boolean(false));
json_object_object_add(object, "make",
json_object_new_string(wlr_output->make));
json_object_object_add(object, "model",
json_object_new_string(wlr_output->model));
json_object_object_add(object, "serial",
json_object_new_string(wlr_output->serial));
json_object *modes_array = json_object_new_array();
struct wlr_output_mode *mode;
wl_list_for_each(mode, &wlr_output->modes, link) {
json_object *mode_object = json_object_new_object();
json_object_object_add(mode_object, "width",
json_object_new_int(mode->width));
json_object_object_add(mode_object, "height",
json_object_new_int(mode->height));
json_object_object_add(mode_object, "refresh",
json_object_new_int(mode->refresh));
json_object_array_add(modes_array, mode_object);
}
json_object_object_add(object, "modes", modes_array);
json_object_object_add(object, "current_workspace", NULL); json_object_object_add(object, "current_workspace", NULL);
@ -367,6 +408,21 @@ json_object *ipc_json_describe_disabled_output(struct sway_output *output) {
return object; return object;
} }
json_object *ipc_json_describe_non_desktop_output(struct sway_output_non_desktop *output) {
struct wlr_output *wlr_output = output->wlr_output;
json_object *object = json_object_new_object();
ipc_json_describe_wlr_output(wlr_output, object);
json_object_object_add(object, "non_desktop", json_object_new_boolean(true));
json_object_object_add(object, "type", json_object_new_string("output"));
json_object_object_add(object, "name",
json_object_new_string(wlr_output->name));
return object;
}
static json_object *ipc_json_describe_scratchpad_output(void) { static json_object *ipc_json_describe_scratchpad_output(void) {
struct wlr_box box; struct wlr_box box;
root_get_box(root, &box); root_get_box(root, &box);
@ -453,7 +509,9 @@ static void ipc_json_describe_workspace(struct sway_workspace *workspace,
static void get_deco_rect(struct sway_container *c, struct wlr_box *deco_rect) { static void get_deco_rect(struct sway_container *c, struct wlr_box *deco_rect) {
enum sway_container_layout parent_layout = container_parent_layout(c); enum sway_container_layout parent_layout = container_parent_layout(c);
bool tab_or_stack = parent_layout == L_TABBED || parent_layout == L_STACKED; list_t *siblings = container_get_siblings(c);
bool tab_or_stack = (parent_layout == L_TABBED || parent_layout == L_STACKED)
&& ((siblings && siblings->length > 1) || !config->hide_lone_tab);
if (((!tab_or_stack || container_is_floating(c)) && if (((!tab_or_stack || container_is_floating(c)) &&
c->current.border != B_NORMAL) || c->current.border != B_NORMAL) ||
c->pending.fullscreen_mode != FULLSCREEN_NONE || c->pending.fullscreen_mode != FULLSCREEN_NONE ||
@ -706,7 +764,7 @@ json_object *ipc_json_describe_node(struct sway_node *node) {
case N_ROOT: case N_ROOT:
break; break;
case N_OUTPUT: case N_OUTPUT:
ipc_json_describe_output(node->sway_output, object); ipc_json_describe_enabled_output(node->sway_output, object);
break; break;
case N_CONTAINER: case N_CONTAINER:
ipc_json_describe_container(node->sway_container, object); ipc_json_describe_container(node->sway_container, object);
@ -942,6 +1000,19 @@ static json_object *describe_libinput_device(struct libinput_device *device) {
json_object_object_add(object, "dwt", json_object_new_string(dwt)); json_object_object_add(object, "dwt", json_object_new_string(dwt));
} }
if (libinput_device_config_dwtp_is_available(device)) {
const char *dwtp = "unknown";
switch (libinput_device_config_dwtp_get_enabled(device)) {
case LIBINPUT_CONFIG_DWTP_ENABLED:
dwtp = "enabled";
break;
case LIBINPUT_CONFIG_DWTP_DISABLED:
dwtp = "disabled";
break;
}
json_object_object_add(object, "dwtp", json_object_new_string(dwtp));
}
if (libinput_device_config_calibration_has_matrix(device)) { if (libinput_device_config_calibration_has_matrix(device)) {
float matrix[6]; float matrix[6];
libinput_device_config_calibration_get_matrix(device, matrix); libinput_device_config_calibration_get_matrix(device, matrix);
@ -977,10 +1048,16 @@ json_object *ipc_json_describe_input(struct sway_input_device *device) {
input_device_get_type(device))); input_device_get_type(device)));
if (device->wlr_device->type == WLR_INPUT_DEVICE_KEYBOARD) { if (device->wlr_device->type == WLR_INPUT_DEVICE_KEYBOARD) {
struct wlr_keyboard *keyboard = device->wlr_device->keyboard; struct wlr_keyboard *keyboard =
wlr_keyboard_from_input_device(device->wlr_device);
struct xkb_keymap *keymap = keyboard->keymap; struct xkb_keymap *keymap = keyboard->keymap;
struct xkb_state *state = keyboard->xkb_state; struct xkb_state *state = keyboard->xkb_state;
json_object_object_add(object, "repeat_delay",
json_object_new_int(keyboard->repeat_info.delay));
json_object_object_add(object, "repeat_rate",
json_object_new_int(keyboard->repeat_info.rate));
json_object *layouts_arr = json_object_new_array(); json_object *layouts_arr = json_object_new_array();
json_object_object_add(object, "xkb_layout_names", layouts_arr); json_object_object_add(object, "xkb_layout_names", layouts_arr);
@ -1005,11 +1082,11 @@ json_object *ipc_json_describe_input(struct sway_input_device *device) {
if (device->wlr_device->type == WLR_INPUT_DEVICE_POINTER) { if (device->wlr_device->type == WLR_INPUT_DEVICE_POINTER) {
struct input_config *ic = input_device_get_config(device); struct input_config *ic = input_device_get_config(device);
float scroll_factor = 1.0f; float scroll_factor = 1.0f;
if (ic != NULL && !isnan(ic->scroll_factor) && if (ic != NULL && !isnan(ic->scroll_factor) &&
ic->scroll_factor != FLT_MIN) { ic->scroll_factor != FLT_MIN) {
scroll_factor = ic->scroll_factor; scroll_factor = ic->scroll_factor;
} }
json_object_object_add(object, "scroll_factor", json_object_object_add(object, "scroll_factor",
json_object_new_double(scroll_factor)); json_object_new_double(scroll_factor));
} }

View file

@ -150,7 +150,6 @@ struct sockaddr_un *ipc_user_sockaddr(void) {
int ipc_handle_connection(int fd, uint32_t mask, void *data) { int ipc_handle_connection(int fd, uint32_t mask, void *data) {
(void) fd; (void) fd;
struct sway_server *server = data; struct sway_server *server = data;
sway_log(SWAY_DEBUG, "Event on IPC listening socket");
assert(mask == WL_EVENT_READABLE); assert(mask == WL_EVENT_READABLE);
int client_fd = accept(ipc_socket, NULL, NULL); int client_fd = accept(ipc_socket, NULL, NULL);
@ -211,13 +210,10 @@ int ipc_client_handle_readable(int client_fd, uint32_t mask, void *data) {
} }
if (mask & WL_EVENT_HANGUP) { if (mask & WL_EVENT_HANGUP) {
sway_log(SWAY_DEBUG, "Client %d hung up", client->fd);
ipc_client_disconnect(client); ipc_client_disconnect(client);
return 0; return 0;
} }
sway_log(SWAY_DEBUG, "Client %d readable", client->fd);
int read_available; int read_available;
if (ioctl(client_fd, FIONREAD, &read_available) == -1) { if (ioctl(client_fd, FIONREAD, &read_available) == -1) {
sway_log_errno(SWAY_INFO, "Unable to read IPC socket buffer size"); sway_log_errno(SWAY_INFO, "Unable to read IPC socket buffer size");
@ -523,7 +519,6 @@ int ipc_client_handle_writable(int client_fd, uint32_t mask, void *data) {
} }
if (mask & WL_EVENT_HANGUP) { if (mask & WL_EVENT_HANGUP) {
sway_log(SWAY_DEBUG, "Client %d hung up", client->fd);
ipc_client_disconnect(client); ipc_client_disconnect(client);
return 0; return 0;
} }
@ -532,8 +527,6 @@ int ipc_client_handle_writable(int client_fd, uint32_t mask, void *data) {
return 0; return 0;
} }
sway_log(SWAY_DEBUG, "Client %d writable", client->fd);
ssize_t written = write(client->fd, client->write_buffer, client->write_buffer_len); ssize_t written = write(client->fd, client->write_buffer, client->write_buffer_len);
if (written == -1 && errno == EAGAIN) { if (written == -1 && errno == EAGAIN) {
@ -692,6 +685,12 @@ void ipc_client_handle_command(struct ipc_client *client, uint32_t payload_lengt
ipc_json_describe_disabled_output(output)); ipc_json_describe_disabled_output(output));
} }
} }
for (int i = 0; i < root->non_desktop_outputs->length; i++) {
struct sway_output_non_desktop *non_desktop_output = root->non_desktop_outputs->items[i];
json_object_array_add(outputs, ipc_json_describe_non_desktop_output(non_desktop_output));
}
const char *json_string = json_object_to_json_string(outputs); const char *json_string = json_object_to_json_string(outputs);
ipc_send_reply(client, payload_type, json_string, ipc_send_reply(client, payload_type, json_string,
(uint32_t)strlen(json_string)); (uint32_t)strlen(json_string));
@ -955,7 +954,5 @@ bool ipc_send_reply(struct ipc_client *client, enum ipc_command_type payload_typ
ipc_client_handle_writable, client); ipc_client_handle_writable, client);
} }
sway_log(SWAY_DEBUG, "Added IPC reply of type 0x%x to client %d queue: %s",
payload_type, client->fd, payload);
return true; return true;
} }

225
sway/lock.c Normal file
View file

@ -0,0 +1,225 @@
#define _POSIX_C_SOURCE 200809L
#include <assert.h>
#include "log.h"
#include "sway/input/keyboard.h"
#include "sway/input/seat.h"
#include "sway/output.h"
#include "sway/server.h"
struct sway_session_lock_surface {
struct wlr_session_lock_surface_v1 *lock_surface;
struct sway_output *output;
struct wlr_surface *surface;
struct wl_listener map;
struct wl_listener destroy;
struct wl_listener surface_commit;
struct wl_listener output_mode;
struct wl_listener output_commit;
struct wl_listener output_destroy;
};
static void set_lock_focused_surface(struct wlr_surface *focused) {
server.session_lock.focused = focused;
struct sway_seat *seat;
wl_list_for_each(seat, &server.input->seats, link) {
seat_set_focus_surface(seat, focused, false);
}
}
static void handle_surface_map(struct wl_listener *listener, void *data) {
struct sway_session_lock_surface *surf = wl_container_of(listener, surf, map);
if (server.session_lock.focused == NULL) {
set_lock_focused_surface(surf->surface);
}
output_damage_whole(surf->output);
}
static void handle_surface_commit(struct wl_listener *listener, void *data) {
struct sway_session_lock_surface *surf = wl_container_of(listener, surf, surface_commit);
output_damage_surface(surf->output, 0, 0, surf->surface, false);
}
static void handle_output_mode(struct wl_listener *listener, void *data) {
struct sway_session_lock_surface *surf = wl_container_of(listener, surf, output_mode);
wlr_session_lock_surface_v1_configure(surf->lock_surface,
surf->output->width, surf->output->height);
}
static void handle_output_commit(struct wl_listener *listener, void *data) {
struct wlr_output_event_commit *event = data;
struct sway_session_lock_surface *surf = wl_container_of(listener, surf, output_commit);
if (event->committed & (
WLR_OUTPUT_STATE_MODE |
WLR_OUTPUT_STATE_SCALE |
WLR_OUTPUT_STATE_TRANSFORM)) {
wlr_session_lock_surface_v1_configure(surf->lock_surface,
surf->output->width, surf->output->height);
}
}
static void destroy_lock_surface(struct sway_session_lock_surface *surf) {
// Move the seat focus to another surface if one is available
if (server.session_lock.focused == surf->surface) {
struct wlr_surface *next_focus = NULL;
struct wlr_session_lock_surface_v1 *other;
wl_list_for_each(other, &server.session_lock.lock->surfaces, link) {
if (other != surf->lock_surface && other->mapped) {
next_focus = other->surface;
break;
}
}
set_lock_focused_surface(next_focus);
}
wl_list_remove(&surf->map.link);
wl_list_remove(&surf->destroy.link);
wl_list_remove(&surf->surface_commit.link);
wl_list_remove(&surf->output_mode.link);
wl_list_remove(&surf->output_commit.link);
wl_list_remove(&surf->output_destroy.link);
output_damage_whole(surf->output);
free(surf);
}
static void handle_surface_destroy(struct wl_listener *listener, void *data) {
struct sway_session_lock_surface *surf = wl_container_of(listener, surf, destroy);
destroy_lock_surface(surf);
}
static void handle_output_destroy(struct wl_listener *listener, void *data) {
struct sway_session_lock_surface *surf =
wl_container_of(listener, surf, output_destroy);
destroy_lock_surface(surf);
}
static void handle_new_surface(struct wl_listener *listener, void *data) {
struct wlr_session_lock_surface_v1 *lock_surface = data;
struct sway_session_lock_surface *surf = calloc(1, sizeof(*surf));
if (surf == NULL) {
return;
}
sway_log(SWAY_DEBUG, "new lock layer surface");
struct sway_output *output = lock_surface->output->data;
wlr_session_lock_surface_v1_configure(lock_surface, output->width, output->height);
surf->lock_surface = lock_surface;
surf->surface = lock_surface->surface;
surf->output = output;
surf->map.notify = handle_surface_map;
wl_signal_add(&lock_surface->events.map, &surf->map);
surf->destroy.notify = handle_surface_destroy;
wl_signal_add(&lock_surface->events.destroy, &surf->destroy);
surf->surface_commit.notify = handle_surface_commit;
wl_signal_add(&surf->surface->events.commit, &surf->surface_commit);
surf->output_mode.notify = handle_output_mode;
wl_signal_add(&output->wlr_output->events.mode, &surf->output_mode);
surf->output_commit.notify = handle_output_commit;
wl_signal_add(&output->wlr_output->events.commit, &surf->output_commit);
surf->output_destroy.notify = handle_output_destroy;
wl_signal_add(&output->node.events.destroy, &surf->output_destroy);
}
static void handle_unlock(struct wl_listener *listener, void *data) {
sway_log(SWAY_DEBUG, "session unlocked");
server.session_lock.locked = false;
server.session_lock.lock = NULL;
server.session_lock.focused = NULL;
wl_list_remove(&server.session_lock.lock_new_surface.link);
wl_list_remove(&server.session_lock.lock_unlock.link);
wl_list_remove(&server.session_lock.lock_destroy.link);
struct sway_seat *seat;
wl_list_for_each(seat, &server.input->seats, link) {
seat_set_exclusive_client(seat, NULL);
// copied from seat_set_focus_layer -- deduplicate?
struct sway_node *previous = seat_get_focus_inactive(seat, &root->node);
if (previous) {
// Hack to get seat to re-focus the return value of get_focus
seat_set_focus(seat, NULL);
seat_set_focus(seat, previous);
}
}
// redraw everything
for (int i = 0; i < root->outputs->length; ++i) {
struct sway_output *output = root->outputs->items[i];
output_damage_whole(output);
}
}
static void handle_abandon(struct wl_listener *listener, void *data) {
sway_log(SWAY_INFO, "session lock abandoned");
server.session_lock.lock = NULL;
server.session_lock.focused = NULL;
wl_list_remove(&server.session_lock.lock_new_surface.link);
wl_list_remove(&server.session_lock.lock_unlock.link);
wl_list_remove(&server.session_lock.lock_destroy.link);
struct sway_seat *seat;
wl_list_for_each(seat, &server.input->seats, link) {
seat->exclusive_client = NULL;
}
// redraw everything
for (int i = 0; i < root->outputs->length; ++i) {
struct sway_output *output = root->outputs->items[i];
output_damage_whole(output);
}
}
static void handle_session_lock(struct wl_listener *listener, void *data) {
struct wlr_session_lock_v1 *lock = data;
struct wl_client *client = wl_resource_get_client(lock->resource);
if (server.session_lock.lock) {
wlr_session_lock_v1_destroy(lock);
return;
}
sway_log(SWAY_DEBUG, "session locked");
server.session_lock.locked = true;
server.session_lock.lock = lock;
struct sway_seat *seat;
wl_list_for_each(seat, &server.input->seats, link) {
seat_set_exclusive_client(seat, client);
}
wl_signal_add(&lock->events.new_surface, &server.session_lock.lock_new_surface);
wl_signal_add(&lock->events.unlock, &server.session_lock.lock_unlock);
wl_signal_add(&lock->events.destroy, &server.session_lock.lock_destroy);
wlr_session_lock_v1_send_locked(lock);
// redraw everything
for (int i = 0; i < root->outputs->length; ++i) {
struct sway_output *output = root->outputs->items[i];
output_damage_whole(output);
}
}
static void handle_session_lock_destroy(struct wl_listener *listener, void *data) {
assert(server.session_lock.lock == NULL);
wl_list_remove(&server.session_lock.new_lock.link);
wl_list_remove(&server.session_lock.manager_destroy.link);
}
void sway_session_lock_init(void) {
server.session_lock.manager = wlr_session_lock_manager_v1_create(server.wl_display);
server.session_lock.lock_new_surface.notify = handle_new_surface;
server.session_lock.lock_unlock.notify = handle_unlock;
server.session_lock.lock_destroy.notify = handle_abandon;
server.session_lock.new_lock.notify = handle_session_lock;
server.session_lock.manager_destroy.notify = handle_session_lock_destroy;
wl_signal_add(&server.session_lock.manager->events.new_lock,
&server.session_lock.new_lock);
wl_signal_add(&server.session_lock.manager->events.destroy,
&server.session_lock.manager_destroy);
}

View file

@ -150,27 +150,17 @@ static void log_kernel(void) {
pclose(f); pclose(f);
} }
static bool detect_suid(void) {
static bool drop_permissions(void) { if (geteuid() != 0 && getegid() != 0) {
if (getuid() != geteuid() || getgid() != getegid()) {
sway_log(SWAY_ERROR, "!!! DEPRECATION WARNING: "
"SUID privilege drop will be removed in a future release, please migrate to seatd-launch");
// Set the gid and uid in the correct order.
if (setgid(getgid()) != 0) {
sway_log(SWAY_ERROR, "Unable to drop root group, refusing to start");
return false;
}
if (setuid(getuid()) != 0) {
sway_log(SWAY_ERROR, "Unable to drop root user, refusing to start");
return false;
}
}
if (setgid(0) != -1 || setuid(0) != -1) {
sway_log(SWAY_ERROR, "Unable to drop root (we shouldn't be able to "
"restore it after setuid), refusing to start");
return false; return false;
} }
if (getuid() == geteuid() && getgid() == getegid()) {
return false;
}
sway_log(SWAY_ERROR, "SUID operation is no longer supported, refusing to start. "
"This check will be removed in a future release.");
return true; return true;
} }
@ -240,35 +230,35 @@ static void handle_wlr_log(enum wlr_log_importance importance,
_sway_vlog(convert_wlr_log_importance(importance), sway_fmt, args); _sway_vlog(convert_wlr_log_importance(importance), sway_fmt, args);
} }
int main(int argc, char **argv) { static const struct option long_options[] = {
static int verbose = 0, debug = 0, validate = 0, allow_unsupported_gpu = 0; {"help", no_argument, NULL, 'h'},
{"config", required_argument, NULL, 'c'},
{"validate", no_argument, NULL, 'C'},
{"debug", no_argument, NULL, 'd'},
{"version", no_argument, NULL, 'v'},
{"verbose", no_argument, NULL, 'V'},
{"get-socketpath", no_argument, NULL, 'p'},
{"unsupported-gpu", no_argument, NULL, 'u'},
{0, 0, 0, 0}
};
static const struct option long_options[] = { static const char usage[] =
{"help", no_argument, NULL, 'h'}, "Usage: sway [options] [command]\n"
{"config", required_argument, NULL, 'c'}, "\n"
{"validate", no_argument, NULL, 'C'}, " -h, --help Show help message and quit.\n"
{"debug", no_argument, NULL, 'd'}, " -c, --config <config> Specify a config file.\n"
{"version", no_argument, NULL, 'v'}, " -C, --validate Check the validity of the config file, then exit.\n"
{"verbose", no_argument, NULL, 'V'}, " -d, --debug Enables full logging, including debug information.\n"
{"get-socketpath", no_argument, NULL, 'p'}, " -v, --version Show the version number and quit.\n"
{"unsupported-gpu", no_argument, NULL, 'u'}, " -V, --verbose Enables more verbose logging.\n"
{0, 0, 0, 0} " --get-socketpath Gets the IPC socket path and prints it, then exits.\n"
}; "\n";
int main(int argc, char **argv) {
static bool verbose = false, debug = false, validate = false, allow_unsupported_gpu = false;
char *config_path = NULL; char *config_path = NULL;
const char* usage =
"Usage: sway [options] [command]\n"
"\n"
" -h, --help Show help message and quit.\n"
" -c, --config <config> Specify a config file.\n"
" -C, --validate Check the validity of the config file, then exit.\n"
" -d, --debug Enables full logging, including debug information.\n"
" -v, --version Show the version number and quit.\n"
" -V, --verbose Enables more verbose logging.\n"
" --get-socketpath Gets the IPC socket path and prints it, then exits.\n"
"\n";
int c; int c;
while (1) { while (1) {
int option_index = 0; int option_index = 0;
@ -286,25 +276,25 @@ int main(int argc, char **argv) {
config_path = strdup(optarg); config_path = strdup(optarg);
break; break;
case 'C': // validate case 'C': // validate
validate = 1; validate = true;
break; break;
case 'd': // debug case 'd': // debug
debug = 1; debug = true;
break; break;
case 'D': // extended debug options case 'D': // extended debug options
enable_debug_flag(optarg); enable_debug_flag(optarg);
break; break;
case 'u': case 'u':
allow_unsupported_gpu = 1; allow_unsupported_gpu = true;
break; break;
case 'v': // version case 'v': // version
printf("sway version " SWAY_VERSION "\n"); printf("sway version " SWAY_VERSION "\n");
exit(EXIT_SUCCESS); exit(EXIT_SUCCESS);
break; break;
case 'V': // verbose case 'V': // verbose
verbose = 1; verbose = true;
break; break;
case 'p': ; // --get-socketpath case 'p': // --get-socketpath
if (getenv("SWAYSOCK")) { if (getenv("SWAYSOCK")) {
printf("%s\n", getenv("SWAYSOCK")); printf("%s\n", getenv("SWAYSOCK"));
exit(EXIT_SUCCESS); exit(EXIT_SUCCESS);
@ -319,6 +309,11 @@ int main(int argc, char **argv) {
} }
} }
// SUID operation is deprecated, so block it for now.
if (detect_suid()) {
exit(EXIT_FAILURE);
}
// Since wayland requires XDG_RUNTIME_DIR to be set, abort with just the // Since wayland requires XDG_RUNTIME_DIR to be set, abort with just the
// clear error message (when not running as an IPC client). // clear error message (when not running as an IPC client).
if (!getenv("XDG_RUNTIME_DIR") && optind == argc) { if (!getenv("XDG_RUNTIME_DIR") && optind == argc) {
@ -357,9 +352,6 @@ int main(int argc, char **argv) {
"`sway -d 2>sway.log`."); "`sway -d 2>sway.log`.");
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
if (!drop_permissions()) {
exit(EXIT_FAILURE);
}
char *socket_path = getenv("SWAYSOCK"); char *socket_path = getenv("SWAYSOCK");
if (!socket_path) { if (!socket_path) {
sway_log(SWAY_ERROR, "Unable to retrieve socket path"); sway_log(SWAY_ERROR, "Unable to retrieve socket path");
@ -372,16 +364,6 @@ int main(int argc, char **argv) {
} }
detect_proprietary(allow_unsupported_gpu); detect_proprietary(allow_unsupported_gpu);
if (!server_privileged_prepare(&server)) {
return 1;
}
if (!drop_permissions()) {
server_fini(&server);
exit(EXIT_FAILURE);
}
increase_nofile_limit(); increase_nofile_limit();
// handle SIGTERM signals // handle SIGTERM signals
@ -413,6 +395,8 @@ int main(int argc, char **argv) {
goto shutdown; goto shutdown;
} }
set_rr_scheduling();
if (!server_start(&server)) { if (!server_start(&server)) {
sway_terminate(EXIT_FAILURE); sway_terminate(EXIT_FAILURE);
goto shutdown; goto shutdown;

View file

@ -5,7 +5,9 @@ sway_sources = files(
'decoration.c', 'decoration.c',
'ipc-json.c', 'ipc-json.c',
'ipc-server.c', 'ipc-server.c',
'lock.c',
'main.c', 'main.c',
'realtime.c',
'server.c', 'server.c',
'swaynag.c', 'swaynag.c',
'xdg_activation_v1.c', 'xdg_activation_v1.c',
@ -20,7 +22,7 @@ sway_sources = files(
'desktop/surface.c', 'desktop/surface.c',
'desktop/transaction.c', 'desktop/transaction.c',
'desktop/xdg_shell.c', 'desktop/xdg_shell.c',
'desktop/launcher.c',
'input/input-manager.c', 'input/input-manager.c',
'input/cursor.c', 'input/cursor.c',
'input/keyboard.c', 'input/keyboard.c',
@ -69,13 +71,13 @@ sway_sources = files(
'commands/force_focus_wrapping.c', 'commands/force_focus_wrapping.c',
'commands/fullscreen.c', 'commands/fullscreen.c',
'commands/gaps.c', 'commands/gaps.c',
'commands/gesture.c',
'commands/hide_edge_borders.c', 'commands/hide_edge_borders.c',
'commands/inhibit_idle.c', 'commands/inhibit_idle.c',
'commands/kill.c', 'commands/kill.c',
'commands/mark.c', 'commands/mark.c',
'commands/max_render_time.c', 'commands/max_render_time.c',
'commands/opacity.c', 'commands/opacity.c',
'commands/saturation.c',
'commands/include.c', 'commands/include.c',
'commands/input.c', 'commands/input.c',
'commands/layout.c', 'commands/layout.c',
@ -91,6 +93,7 @@ sway_sources = files(
'commands/reload.c', 'commands/reload.c',
'commands/rename.c', 'commands/rename.c',
'commands/resize.c', 'commands/resize.c',
'commands/saturation.c',
'commands/scratchpad.c', 'commands/scratchpad.c',
'commands/seat.c', 'commands/seat.c',
'commands/seat/attach.c', 'commands/seat/attach.c',
@ -162,6 +165,7 @@ sway_sources = files(
'commands/input/drag.c', 'commands/input/drag.c',
'commands/input/drag_lock.c', 'commands/input/drag_lock.c',
'commands/input/dwt.c', 'commands/input/dwt.c',
'commands/input/dwtp.c',
'commands/input/events.c', 'commands/input/events.c',
'commands/input/left_handed.c', 'commands/input/left_handed.c',
'commands/input/map_from_region.c', 'commands/input/map_from_region.c',
@ -196,12 +200,14 @@ sway_sources = files(
'commands/output/max_render_time.c', 'commands/output/max_render_time.c',
'commands/output/mode.c', 'commands/output/mode.c',
'commands/output/position.c', 'commands/output/position.c',
'commands/output/power.c',
'commands/output/render_bit_depth.c', 'commands/output/render_bit_depth.c',
'commands/output/scale.c', 'commands/output/scale.c',
'commands/output/scale_filter.c', 'commands/output/scale_filter.c',
'commands/output/subpixel.c', 'commands/output/subpixel.c',
'commands/output/toggle.c', 'commands/output/toggle.c',
'commands/output/transform.c', 'commands/output/transform.c',
'commands/output/unplug.c',
'tree/arrange.c', 'tree/arrange.c',
'tree/container.c', 'tree/container.c',
@ -223,13 +229,14 @@ sway_deps = [
libudev, libudev,
math, math,
pango, pango,
pcre, pcre2,
glesv2, glesv2,
pixman, pixman,
server_protos, threads,
wayland_server, wayland_server,
wlroots, wlroots,
xkbcommon, xkbcommon,
xcb_icccm,
egl, egl,
] ]
@ -240,7 +247,7 @@ endif
executable( executable(
'sway', 'sway',
sway_sources, sway_sources + wl_protos_src,
include_directories: [sway_inc], include_directories: [sway_inc],
dependencies: sway_deps, dependencies: sway_deps,
link_with: [lib_sway_common], link_with: [lib_sway_common],

40
sway/realtime.c Normal file
View file

@ -0,0 +1,40 @@
#include <sys/resource.h>
#include <sched.h>
#include <unistd.h>
#include <pthread.h>
#include "sway/server.h"
#include "log.h"
static void child_fork_callback(void) {
struct sched_param param;
param.sched_priority = 0;
int ret = pthread_setschedparam(pthread_self(), SCHED_OTHER, &param);
if (ret != 0) {
sway_log(SWAY_ERROR, "Failed to reset scheduler policy on fork");
}
}
void set_rr_scheduling(void) {
int prio = sched_get_priority_min(SCHED_RR);
int old_policy;
int ret;
struct sched_param param;
ret = pthread_getschedparam(pthread_self(), &old_policy, &param);
if (ret != 0) {
sway_log(SWAY_DEBUG, "Failed to get old scheduling priority");
return;
}
param.sched_priority = prio;
ret = pthread_setschedparam(pthread_self(), SCHED_RR, &param);
if (ret != 0) {
sway_log(SWAY_INFO, "Failed to set scheduling priority to %d", prio);
return;
}
pthread_atfork(NULL, NULL, child_fork_callback);
}

View file

@ -18,13 +18,16 @@
#include <wlr/types/wlr_export_dmabuf_v1.h> #include <wlr/types/wlr_export_dmabuf_v1.h>
#include <wlr/types/wlr_gamma_control_v1.h> #include <wlr/types/wlr_gamma_control_v1.h>
#include <wlr/types/wlr_idle.h> #include <wlr/types/wlr_idle.h>
#include <wlr/types/wlr_idle_notify_v1.h>
#include <wlr/types/wlr_layer_shell_v1.h> #include <wlr/types/wlr_layer_shell_v1.h>
#include <wlr/types/wlr_linux_dmabuf_v1.h> #include <wlr/types/wlr_linux_dmabuf_v1.h>
#include <wlr/types/wlr_pointer_constraints_v1.h> #include <wlr/types/wlr_pointer_constraints_v1.h>
#include <wlr/types/wlr_primary_selection_v1.h> #include <wlr/types/wlr_primary_selection_v1.h>
#include <wlr/types/wlr_relative_pointer_v1.h> #include <wlr/types/wlr_relative_pointer_v1.h>
#include <wlr/types/wlr_screencopy_v1.h> #include <wlr/types/wlr_screencopy_v1.h>
#include <wlr/types/wlr_single_pixel_buffer_v1.h>
#include <wlr/types/wlr_server_decoration.h> #include <wlr/types/wlr_server_decoration.h>
#include <wlr/types/wlr_subcompositor.h>
#include <wlr/types/wlr_tablet_v2.h> #include <wlr/types/wlr_tablet_v2.h>
#include <wlr/types/wlr_viewporter.h> #include <wlr/types/wlr_viewporter.h>
#include <wlr/types/wlr_xcursor_manager.h> #include <wlr/types/wlr_xcursor_manager.h>
@ -48,19 +51,6 @@
#include "sway/xwayland.h" #include "sway/xwayland.h"
#endif #endif
bool server_privileged_prepare(struct sway_server *server) {
sway_log(SWAY_DEBUG, "Preparing Wayland server initialization");
server->wl_display = wl_display_create();
server->wl_event_loop = wl_display_get_event_loop(server->wl_display);
server->backend = wlr_backend_autocreate(server->wl_display);
if (!server->backend) {
sway_log(SWAY_ERROR, "Unable to create backend");
return false;
}
return true;
}
static void handle_drm_lease_request(struct wl_listener *listener, void *data) { static void handle_drm_lease_request(struct wl_listener *listener, void *data) {
/* We only offer non-desktop outputs, but in the future we might want to do /* We only offer non-desktop outputs, but in the future we might want to do
* more logic here. */ * more logic here. */
@ -73,8 +63,18 @@ static void handle_drm_lease_request(struct wl_listener *listener, void *data) {
} }
} }
#define SWAY_XDG_SHELL_VERSION 2
bool server_init(struct sway_server *server) { bool server_init(struct sway_server *server) {
sway_log(SWAY_DEBUG, "Initializing Wayland server"); sway_log(SWAY_DEBUG, "Initializing Wayland server");
server->wl_display = wl_display_create();
server->wl_event_loop = wl_display_get_event_loop(server->wl_display);
server->backend = wlr_backend_autocreate(server->wl_display);
if (!server->backend) {
sway_log(SWAY_ERROR, "Unable to create backend");
return false;
}
server->wlr_renderer = wlr_renderer_autocreate(server->backend); server->wlr_renderer = wlr_renderer_autocreate(server->backend);
if (!server->wlr_renderer) { if (!server->wlr_renderer) {
@ -109,6 +109,8 @@ bool server_init(struct sway_server *server) {
wl_signal_add(&server->compositor->events.new_surface, wl_signal_add(&server->compositor->events.new_surface,
&server->compositor_new_surface); &server->compositor_new_surface);
wlr_subcompositor_create(server->wl_display);
server->data_device_manager = server->data_device_manager =
wlr_data_device_manager_create(server->wl_display); wlr_data_device_manager_create(server->wl_display);
@ -123,6 +125,7 @@ bool server_init(struct sway_server *server) {
wlr_xdg_output_manager_v1_create(server->wl_display, root->output_layout); wlr_xdg_output_manager_v1_create(server->wl_display, root->output_layout);
server->idle = wlr_idle_create(server->wl_display); server->idle = wlr_idle_create(server->wl_display);
server->idle_notifier_v1 = wlr_idle_notifier_v1_create(server->wl_display);
server->idle_inhibit_manager_v1 = server->idle_inhibit_manager_v1 =
sway_idle_inhibit_manager_v1_create(server->wl_display, server->idle); sway_idle_inhibit_manager_v1_create(server->wl_display, server->idle);
@ -131,7 +134,8 @@ bool server_init(struct sway_server *server) {
&server->layer_shell_surface); &server->layer_shell_surface);
server->layer_shell_surface.notify = handle_layer_shell_surface; server->layer_shell_surface.notify = handle_layer_shell_surface;
server->xdg_shell = wlr_xdg_shell_create(server->wl_display); server->xdg_shell = wlr_xdg_shell_create(server->wl_display,
SWAY_XDG_SHELL_VERSION);
wl_signal_add(&server->xdg_shell->events.new_surface, wl_signal_add(&server->xdg_shell->events.new_surface,
&server->xdg_shell_surface); &server->xdg_shell_surface);
server->xdg_shell_surface.notify = handle_xdg_shell_surface; server->xdg_shell_surface.notify = handle_xdg_shell_surface;
@ -188,6 +192,8 @@ bool server_init(struct sway_server *server) {
server->foreign_toplevel_manager = server->foreign_toplevel_manager =
wlr_foreign_toplevel_manager_v1_create(server->wl_display); wlr_foreign_toplevel_manager_v1_create(server->wl_display);
sway_session_lock_init();
server->drm_lease_manager= server->drm_lease_manager=
wlr_drm_lease_v1_manager_create(server->wl_display, server->backend); wlr_drm_lease_v1_manager_create(server->wl_display, server->backend);
if (server->drm_lease_manager) { if (server->drm_lease_manager) {
@ -204,6 +210,7 @@ bool server_init(struct sway_server *server) {
wlr_data_control_manager_v1_create(server->wl_display); wlr_data_control_manager_v1_create(server->wl_display);
wlr_primary_selection_v1_device_manager_create(server->wl_display); wlr_primary_selection_v1_device_manager_create(server->wl_display);
wlr_viewporter_create(server->wl_display); wlr_viewporter_create(server->wl_display);
wlr_single_pixel_buffer_manager_v1_create(server->wl_display);
struct wlr_xdg_foreign_registry *foreign_registry = struct wlr_xdg_foreign_registry *foreign_registry =
wlr_xdg_foreign_registry_create(server->wl_display); wlr_xdg_foreign_registry_create(server->wl_display);
@ -216,10 +223,12 @@ bool server_init(struct sway_server *server) {
wl_signal_add(&server->xdg_activation_v1->events.request_activate, wl_signal_add(&server->xdg_activation_v1->events.request_activate,
&server->xdg_activation_v1_request_activate); &server->xdg_activation_v1_request_activate);
wl_list_init(&server->pending_launcher_ctxs);
// Avoid using "wayland-0" as display socket // Avoid using "wayland-0" as display socket
char name_candidate[16]; char name_candidate[16];
for (int i = 1; i <= 32; ++i) { for (unsigned int i = 1; i <= 32; ++i) {
sprintf(name_candidate, "wayland-%d", i); snprintf(name_candidate, sizeof(name_candidate), "wayland-%u", i);
if (wl_display_add_socket(server->wl_display, name_candidate) >= 0) { if (wl_display_add_socket(server->wl_display, name_candidate) >= 0) {
server->socket = strdup(name_candidate); server->socket = strdup(name_candidate);
break; break;

View file

@ -147,6 +147,10 @@ The following commands may only be used in the configuration file.
*input* <identifier> dwt enabled|disabled *input* <identifier> dwt enabled|disabled
Enables or disables disable-while-typing for the specified input device. Enables or disables disable-while-typing for the specified input device.
*input* <identifier> dwtp enabled|disabled
Enables or disables disable-while-trackpointing for the specified input
device.
*input* <identifier> events enabled|disabled|disabled_on_external_mouse|toggle [<toggle-modes>] *input* <identifier> events enabled|disabled|disabled_on_external_mouse|toggle [<toggle-modes>]
Enables or disables send_events for specified input device. Disabling Enables or disables send_events for specified input device. Disabling
send_events disables the input device. send_events disables the input device.

View file

@ -213,7 +213,10 @@ following properties:
: Whether this output is active/enabled : Whether this output is active/enabled
|- dpms |- dpms
: boolean : boolean
: Whether this output is on/off (via DPMS) : (Deprecated, use _power_ instead) Whether this output is on/off (via DPMS)
|- power
: boolean
: Whether this output is on/off
|- primary |- primary
: boolean : boolean
: For i3 compatibility, this will be false. It does not make sense in Wayland : For i3 compatibility, this will be false. It does not make sense in Wayland
@ -370,7 +373,7 @@ node and will have the following properties:
that can be used as an aid in submitting reproduction steps for bug reports that can be used as an aid in submitting reproduction steps for bug reports
|- fullscreen_mode |- fullscreen_mode
: integer : integer
: (Only containers and views) The fullscreen mode of the node. 0 means none, 1 means : (Only containers and views) The fullscreen mode of the node. 0 means none, 1 means
full workspace, and 2 means global fullscreen full workspace, and 2 means global fullscreen
|- app_id |- app_id
: string : string
@ -1194,6 +1197,10 @@ following properties will be included for devices that support them:
|- dwt |- dwt
: string : string
: Whether disable-while-typing is enabled. It can be _enabled_ or _disabled_ : Whether disable-while-typing is enabled. It can be _enabled_ or _disabled_
|- dwtp
: string
: Whether disable-while-trackpointing is enabled. It can be _enabled_ or
_disabled_
|- calibration_matrix |- calibration_matrix
: array : array
: An array of 6 floats representing the calibration matrix for absolute : An array of 6 floats representing the calibration matrix for absolute
@ -1233,7 +1240,8 @@ following properties will be included for devices that support them:
"click_method": "button_areas", "click_method": "button_areas",
"middle_emulation": "disabled", "middle_emulation": "disabled",
"scroll_method": "edge", "scroll_method": "edge",
"dwt": "enabled" "dwt": "enabled",
"dwtp": "enabled"
} }
}, },
{ {
@ -1360,7 +1368,8 @@ one seat. Each object has the following properties:
"click_method": "button_areas", "click_method": "button_areas",
"middle_emulation": "disabled", "middle_emulation": "disabled",
"scroll_method": "edge", "scroll_method": "edge",
"dwt": "enabled" "dwt": "enabled",
"dwtp": "enabled"
} }
}, },
{ {

View file

@ -24,7 +24,7 @@ must be separated by one space. For example:
# COMMANDS # COMMANDS
*output* <name> mode|resolution|res [--custom] <WIDTHxHEIGHT>[@<RATE>Hz] *output* <name> mode|resolution|res [--custom] <width>x<height>[@<rate>Hz]
Configures the specified output to use the given mode. Modes are a Configures the specified output to use the given mode. Modes are a
combination of width and height (in pixels) and a refresh rate that your combination of width and height (in pixels) and a refresh rate that your
display can be configured to use. For a list of available modes for each display can be configured to use. For a list of available modes for each
@ -119,12 +119,20 @@ must be separated by one space. For example:
Enables or disables the specified output (all outputs are enabled by Enables or disables the specified output (all outputs are enabled by
default). default).
As opposed to the _power_ command, the output will loose its current
workspace and windows.
*output* <name> toggle *output* <name> toggle
Toggle the specified output. Toggle the specified output.
*output* <name> power on|off|toggle
Turns on or off the specified output.
As opposed to the _enable_ and _disable_ commands, the output keeps its
current workspaces and windows.
*output* <name> dpms on|off|toggle *output* <name> dpms on|off|toggle
Enables or disables the specified output via DPMS. To turn an output off Deprecated. Alias for _power_.
(ie. blank the screen but keep workspaces as-is), one can set DPMS to off.
*output* <name> max_render_time off|<msec> *output* <name> max_render_time off|<msec>
Controls when sway composites the output, as a positive number of Controls when sway composites the output, as a positive number of

View file

@ -176,6 +176,12 @@ set|plus|minus|toggle <amount>
*layout* default|splith|splitv|stacking|tabbed *layout* default|splith|splitv|stacking|tabbed
Sets the layout mode of the focused container. Sets the layout mode of the focused container.
When using the _stacking_ layout, only the focused window in the container is
displayed, with the opened windows' list on the top of the container.
The _tabbed_ layout is similar to _stacking_, but the windows list is vertically
split.
*layout* toggle [split|all] *layout* toggle [split|all]
Cycles the layout mode of the focused container though a preset list of Cycles the layout mode of the focused container though a preset list of
layouts. If no argument is given, then it cycles through stacking, tabbed layouts. If no argument is given, then it cycles through stacking, tabbed
@ -210,15 +216,14 @@ set|plus|minus|toggle <amount>
further details. further details.
*move* left|right|up|down [<px> px] *move* left|right|up|down [<px> px]
Moves the focused container in the direction specified. If the container, Moves the focused container in the direction specified. The optional _px_
the optional _px_ argument specifies how many pixels to move the container. argument specifies how many pixels to move the container. If unspecified,
If unspecified, the default is 10 pixels. Pixels are ignored when moving the default is 10 pixels. Pixels are ignored when moving tiled containers.
tiled containers.
*move* [absolute] position <pos_x> [px|ppt] <pos_y> [px|ppt] *move* [absolute] position <pos_x> [px|ppt] <pos_y> [px|ppt]
Moves the focused container to the specified position in the workspace. Moves the focused container to the specified position in the workspace.
The position can be specified in pixels or percentage points, omitting The position can be specified in pixels or percentage points, omitting
the unit defaults to pixels. If _absolute_ is used, the position is the unit defaults to pixels. If _absolute_ is used, the position is
relative to all outputs. _absolute_ can not be used with percentage points. relative to all outputs. _absolute_ can not be used with percentage points.
*move* [absolute] position center *move* [absolute] position center
@ -482,6 +487,62 @@ runtime.
bindswitch lid:toggle exec echo "Lid moved" bindswitch lid:toggle exec echo "Lid moved"
``` ```
*bindgesture* [--exact] [--input-device=<device>] [--no-warn] \
<gesture>[:<fingers>][:directions] <command>
Binds _gesture_ to execute the sway command _command_ when detected.
Currently supports the _hold_, _pinch_ or _swipe_ gesture. Optionally
can be limited to bind to a certain number of _fingers_ or, for a
_pinch_ or _swipe_ gesture, to certain _directions_.
[[ *type*
:[ *fingers*
:< *direction*
| hold
:- 1 - 5
: none
| swipe
: 3 - 5
: up, down, left, right
| pinch
: 2 - 5
: all above + inward, outward, clockwise, counterclockwise
The _fingers_ can be limited to any sensible number or left empty to accept
any finger counts.
Valid directions are _up_, _down_, _left_ and _right_, as well as _inward_,
_outward_, _clockwise_, _counterclockwise_ for the _pinch_ gesture.
Multiple directions can be combined by a plus.
If a _input-device_ is given, the binding will only be executed for
that input device and will be executed instead of any binding that is
generic to all devices. By default, if you overwrite a binding,
swaynag will give you a warning. To silence this, use the _--no-warn_ flag.
The _--exact_ flag can be used to ensure a binding only matches when exactly
all specified directions are matched and nothing more. If there is matching
binding with _--exact_, it will be preferred.
The priority for matching bindings is as follows: input device, then
exact matches followed by matches with the highest number of matching
directions.
Gestures executed while the pointer is above a bar are not handled by sway.
See the respective documentation, e.g. *bindgesture* in *sway-bar*(5).
Example:
```
# Allow switching between workspaces with left and right swipes
bindgesture swipe:right workspace prev
bindgesture swipe:left workspace next
# Allow container movements by pinching them
bindgesture pinch:inward+up move up
bindgesture pinch:inward+down move down
bindgesture pinch:inward+left move left
bindgesture pinch:inward+right move right
```
*client.background* <color> *client.background* <color>
This command is ignored and is only present for i3 compatibility. This command is ignored and is only present for i3 compatibility.
@ -643,11 +704,11 @@ The default colors are:
after switching between workspaces. after switching between workspaces.
*focus_on_window_activation* smart|urgent|focus|none *focus_on_window_activation* smart|urgent|focus|none
This option determines what to do when an xwayland client requests This option determines what to do when a client requests window activation.
window activation. If set to _urgent_, the urgent state will be set If set to _urgent_, the urgent state will be set for that window. If set to
for that window. If set to _focus_, the window will become focused. _focus_, the window will become focused. If set to _smart_, the window will
If set to _smart_, the window will become focused only if it is already become focused only if it is already visible, otherwise the urgent state
visible, otherwise the urgent state will be set. Default is _urgent_. will be set. Default is _urgent_.
*focus_wrapping* yes|no|force|workspace *focus_wrapping* yes|no|force|workspace
This option determines what to do when attempting to focus over the edge This option determines what to do when attempting to focus over the edge
@ -816,6 +877,11 @@ The default colors are:
*unbindswitch* <switch>:<state> *unbindswitch* <switch>:<state>
Removes a binding for when <switch> changes to <state>. Removes a binding for when <switch> changes to <state>.
*unbindgesture* [--exact] [--input-device=<device>] \
<gesture>[:<fingers>][:directions]
Removes a binding for the specified _gesture_, _fingers_
and _directions_ combination.
*unbindsym* [--whole-window] [--border] [--exclude-titlebar] [--release] [--locked] \ *unbindsym* [--whole-window] [--border] [--exclude-titlebar] [--release] [--locked] \
[--to-code] [--input-device=<device>] <key combo> [--to-code] [--input-device=<device>] <key combo>
Removes the binding for _key combo_ that was previously bound with the Removes the binding for _key combo_ that was previously bound with the
@ -929,7 +995,8 @@ The following attributes may be matched with:
*class* *class*
Compare value against the window class. Can be a regular expression. If Compare value against the window class. Can be a regular expression. If
value is \_\_focused\_\_, then the window class must be the same as that of value is \_\_focused\_\_, then the window class must be the same as that of
the currently focused window. _class_ are specific to X11 applications. the currently focused window. _class_ are specific to X11 applications and
require XWayland.
*con_id* *con_id*
Compare against the internal container ID, which you can find via IPC. If Compare against the internal container ID, which you can find via IPC. If
@ -943,12 +1010,14 @@ The following attributes may be matched with:
Matches floating windows. Matches floating windows.
*id* *id*
Compare value against the X11 window ID. Must be numeric. Compare value against the X11 window ID. Must be numeric. id is specific to
X11 applications and requires XWayland.
*instance* *instance*
Compare value against the window instance. Can be a regular expression. If Compare value against the window instance. Can be a regular expression. If
value is \_\_focused\_\_, then the window instance must be the same as that value is \_\_focused\_\_, then the window instance must be the same as that
of the currently focused window. of the currently focused window. instance is specific to X11 applications and
requires XWayland.
*pid* *pid*
Compare value against the window's process ID. Must be numeric. Compare value against the window's process ID. Must be numeric.
@ -973,12 +1042,14 @@ The following attributes may be matched with:
*window_role* *window_role*
Compare against the window role (WM_WINDOW_ROLE). Can be a regular Compare against the window role (WM_WINDOW_ROLE). Can be a regular
expression. If value is \_\_focused\_\_, then the window role must be the expression. If value is \_\_focused\_\_, then the window role must be the
same as that of the currently focused window. same as that of the currently focused window. window_role is specific to X11
applications and requires XWayland.
*window_type* *window_type*
Compare against the window type (\_NET_WM_WINDOW_TYPE). Possible values Compare against the window type (\_NET_WM_WINDOW_TYPE). Possible values
are normal, dialog, utility, toolbar, splash, menu, dropdown_menu, are normal, dialog, utility, toolbar, splash, menu, dropdown_menu,
popup_menu, tooltip and notification. popup_menu, tooltip and notification. window_type is specific to X11
applications and requires XWayland.
*workspace* *workspace*
Compare against the workspace name for this view. Can be a regular Compare against the workspace name for this view. Can be a regular

Some files were not shown because too many files have changed in this diff Show more