diff --git a/README.md b/README.md index cf082ccf..5e9016e6 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,6 @@ # 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: + **Anti-aliased rounded corners, borders, and titlebars** @@ -51,7 +50,7 @@ Install dependencies: * wlroots * wayland * wayland-protocols \* -* pcre +* pcre2 * json-c * pango * cairo @@ -78,9 +77,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. 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/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 Please join our (for the time being very small) Discord server for development chatter! https://discord.gg/jVjjFdcN - diff --git a/build-scripts/aur-git/PKGBUILD b/build-scripts/aur-git/PKGBUILD index 127d5271..0bcb28ab 100644 --- a/build-scripts/aur-git/PKGBUILD +++ b/build-scripts/aur-git/PKGBUILD @@ -23,9 +23,9 @@ depends=( "libxcb" "libxkbcommon.so" "pango" - "pcre" + "pcre2" "ttf-font" - "wlroots<0.16" + "wlroots<0.17" ) optdepends=( "alacritty: Terminal emulator used by the default config" diff --git a/build-scripts/aur/PKGBUILD b/build-scripts/aur/PKGBUILD index fcc4638b..32ab9635 100644 --- a/build-scripts/aur/PKGBUILD +++ b/build-scripts/aur/PKGBUILD @@ -23,9 +23,9 @@ depends=( "libxcb" "libxkbcommon.so" "pango" - "pcre" + "pcre2" "ttf-font" - "wlroots<0.16" + "wlroots<0.17" ) optdepends=( "alacritty: Terminal emulator used by the default config" diff --git a/build-scripts/swayfx.rpkg.spec b/build-scripts/swayfx.rpkg.spec index 61dd6e2d..4f57193a 100644 --- a/build-scripts/swayfx.rpkg.spec +++ b/build-scripts/swayfx.rpkg.spec @@ -2,7 +2,7 @@ ### CHANGE THESE VARIABLES BEFORE RELEASE: # Change to current Sway base version! -%global SwayBaseVersion 1.7 +%global SwayBaseVersion 1.8 # Change to current SwayFX tag! %global Tag 0.1.1 @@ -23,8 +23,8 @@ BuildRequires: pkgconfig(gdk-pixbuf-2.0) BuildRequires: pkgconfig(json-c) >= 0.13 BuildRequires: pkgconfig(libdrm) BuildRequires: pkgconfig(libevdev) -BuildRequires: pkgconfig(libinput) >= 1.6.0 -BuildRequires: pkgconfig(libpcre) +BuildRequires: pkgconfig(libinput) >= 1.21.0 +BuildRequires: pkgconfig(libpcre2) BuildRequires: pkgconfig(libsystemd) >= 239 BuildRequires: pkgconfig(libudev) BuildRequires: pkgconfig(pango) @@ -35,7 +35,7 @@ BuildRequires: pkgconfig(wayland-cursor) BuildRequires: pkgconfig(wayland-egl) BuildRequires: pkgconfig(wayland-server) >= 1.20.0 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(xkbcommon) # Dmenu is the default launcher in sway diff --git a/client/pool-buffer.c b/client/pool-buffer.c index ea31edd3..3546b897 100644 --- a/client/pool-buffer.c +++ b/client/pool-buffer.c @@ -1,50 +1,43 @@ #define _POSIX_C_SOURCE 200809 #include #include +#include #include #include #include #include #include #include +#include #include #include #include "config.h" #include "pool-buffer.h" #include "util.h" -static int create_pool_file(size_t size, char **name) { - static const char template[] = "sway-client-XXXXXX"; - const char *path = getenv("XDG_RUNTIME_DIR"); - if (path == NULL) { - fprintf(stderr, "XDG_RUNTIME_DIR is not set\n"); - return -1; - } +static int anonymous_shm_open(void) { + int retries = 100; - size_t name_size = strlen(template) + 1 + strlen(path) + 1; - *name = malloc(name_size); - if (*name == NULL) { - fprintf(stderr, "allocation failed\n"); - return -1; - } - snprintf(*name, name_size, "%s/%s", path, template); + do { + // try a probably-unique name + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + pid_t pid = getpid(); + char name[50]; + snprintf(name, sizeof(name), "/sway-%x-%x", + (unsigned int)pid, (unsigned int)ts.tv_nsec); - int fd = mkstemp(*name); - if (fd < 0) { - return -1; - } + // shm_open guarantees that O_CLOEXEC is set + int fd = shm_open(name, O_RDWR | O_CREAT | O_EXCL, 0600); + if (fd >= 0) { + shm_unlink(name); + return fd; + } - if (!sway_set_cloexec(fd, true)) { - close(fd); - return -1; - } + --retries; + } while (retries > 0 && errno == EEXIST); - if (ftruncate(fd, size) < 0) { - close(fd); - return -1; - } - - return fd; + return -1; } static void buffer_release(void *data, struct wl_buffer *wl_buffer) { @@ -62,17 +55,20 @@ static struct pool_buffer *create_buffer(struct wl_shm *shm, uint32_t stride = width * 4; size_t size = stride * height; - char *name; - int fd = create_pool_file(size, &name); - assert(fd != -1); + int fd = anonymous_shm_open(); + if (fd == -1) { + return NULL; + } + if (ftruncate(fd, size) < 0) { + close(fd); + return NULL; + } void *data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); struct wl_shm_pool *pool = wl_shm_create_pool(shm, fd, size); buf->buffer = wl_shm_pool_create_buffer(pool, 0, width, height, stride, format); wl_shm_pool_destroy(pool); close(fd); - unlink(name); - free(name); buf->size = size; buf->width = width; diff --git a/common/gesture.c b/common/gesture.c new file mode 100644 index 00000000..8c2efe99 --- /dev/null +++ b/common/gesture.c @@ -0,0 +1,350 @@ +#define _POSIX_C_SOURCE 200809L +#include "gesture.h" + +#include +#include +#include +#include +#include +#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 [:][: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; +} diff --git a/common/meson.build b/common/meson.build index c653dd72..3756075a 100644 --- a/common/meson.build +++ b/common/meson.build @@ -3,6 +3,7 @@ lib_sway_common = static_library( files( 'background-image.c', 'cairo.c', + 'gesture.c', 'ipc-client.c', 'log.c', 'loop.c', diff --git a/common/pango.c b/common/pango.c index abc18281..e04bf80f 100644 --- a/common/pango.c +++ b/common/pango.c @@ -50,7 +50,7 @@ size_t escape_markup_text(const char *src, char *dest) { 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) { PangoLayout *layout = pango_cairo_create_layout(cairo); 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)); - PangoFontDescription *desc = pango_font_description_from_string(font); pango_layout_set_font_description(layout, desc); pango_layout_set_single_paragraph_mode(layout, 1); pango_layout_set_attributes(layout, attrs); pango_attr_list_unref(attrs); - pango_font_description_free(desc); 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, ...) { va_list args; 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); 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_layout_get_pixel_size(layout, width, height); if (baseline) { @@ -109,10 +107,9 @@ void get_text_size(cairo_t *cairo, const char *font, int *width, int *height, 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); 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. 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; pango_font_metrics_unref(metrics); - pango_font_description_free(description); g_object_unref(pango); 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, ...) { va_list args; va_start(args, fmt); @@ -142,7 +138,7 @@ void render_text(cairo_t *cairo, const char *font, vsnprintf(buf, length, fmt, 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_get_font_options(cairo, fo); pango_cairo_context_set_font_options(pango_layout_get_context(layout), fo); diff --git a/completions/bash/sway b/completions/bash/sway index edd752cd..01b20073 100644 --- a/completions/bash/sway +++ b/completions/bash/sway @@ -2,7 +2,7 @@ _sway() { - local cur prev + local cur prev short long _get_comp_words_by_ref cur prev short=( diff --git a/completions/bash/swaybar b/completions/bash/swaybar index 1e085c65..3709d4f9 100644 --- a/completions/bash/swaybar +++ b/completions/bash/swaybar @@ -2,7 +2,7 @@ _swaybar() { - local cur prev + local cur prev short long _get_comp_words_by_ref cur prev short=( diff --git a/completions/bash/swaymsg b/completions/bash/swaymsg index f865e4e1..30457751 100644 --- a/completions/bash/swaymsg +++ b/completions/bash/swaymsg @@ -2,7 +2,7 @@ _swaymsg() { - local cur prev + local cur prev types short long _get_comp_words_by_ref cur prev types=( diff --git a/config.in b/config.in index f1a23036..bcc89de2 100644 --- a/config.in +++ b/config.in @@ -46,7 +46,7 @@ output * bg @datadir@/backgrounds/sway/Sway_Wallpaper_Blue_1920x1080.png fill # # exec swayidle -w \ # 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' # # This will lock your screen after 300 seconds of inactivity, then turn off diff --git a/contrib/grimshot b/contrib/grimshot index 4ce31f29..1ec19def 100755 --- a/contrib/grimshot +++ b/contrib/grimshot @@ -13,18 +13,32 @@ ## See `man 1 grimshot` or `grimshot usage` for further details. getTargetDirectory() { - test -f ${XDG_CONFIG_HOME:-~/.config}/user-dirs.dirs && \ - . ${XDG_CONFIG_HOME:-~/.config}/user-dirs.dirs + test -f "${XDG_CONFIG_HOME:-$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=yes - shift 1 -else - NOTIFY=no -fi +NOTIFY=no +CURSOR= + +while [ $# -gt 0 ]; do + key="$1" + + 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} SUBJECT=${2:-screen} @@ -32,7 +46,7 @@ FILE=${3:-$(getTargetDirectory)/$(date -Ins).png} if [ "$ACTION" != "save" ] && [ "$ACTION" != "copy" ] && [ "$ACTION" != "check" ]; then 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 usage" echo "" @@ -67,7 +81,7 @@ notifyError() { MESSAGE=${1:-"Error taking screenshot with grim"} notify -u critical "$TITLE" "$MESSAGE" else - echo $1 + echo "$1" fi } @@ -91,12 +105,12 @@ takeScreenshot() { FILE=$1 GEOM=$2 OUTPUT=$3 - if [ ! -z "$OUTPUT" ]; then - grim -o "$OUTPUT" "$FILE" || die "Unable to invoke grim" + if [ -n "$OUTPUT" ]; then + grim ${CURSOR:+-c} -o "$OUTPUT" "$FILE" || die "Unable to invoke grim" elif [ -z "$GEOM" ]; then - grim "$FILE" || die "Unable to invoke grim" + grim ${CURSOR:+-c} "$FILE" || die "Unable to invoke grim" else - grim -g "$GEOM" "$FILE" || die "Unable to invoke grim" + grim ${CURSOR:+-c} -g "$GEOM" "$FILE" || die "Unable to invoke grim" fi } @@ -147,7 +161,7 @@ else TITLE="Screenshot of $SUBJECT" MESSAGE=$(basename "$FILE") notifyOk "$MESSAGE" "$TITLE" - echo $FILE + echo "$FILE" else notifyError "Error taking screenshot with grim" fi diff --git a/contrib/grimshot.1 b/contrib/grimshot.1 index e4baccfd..2c4c6a95 100644 --- a/contrib/grimshot.1 +++ b/contrib/grimshot.1 @@ -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 .ie \n(.g .ds Aq \(aq .el .ds Aq ' .nh .ad l .\" Begin generated content: -.TH "grimshot" "1" "2021-02-23" +.TH "grimshot" "1" "2022-03-31" .P .SH NAME .P @@ -13,7 +13,7 @@ grimshot - a helper for screenshots within sway .P .SH SYNOPSIS .P -\fBgrimshot\fR [--notify] (copy|save) [TARGET] [FILE] +\fBgrimshot\fR [--notify] [--cursor] (copy|save) [TARGET] [FILE] .br \fBgrimshot\fR check .br @@ -26,12 +26,17 @@ grimshot - a helper for screenshots within sway Show notifications to the user that a screenshot has been taken.\& .P .RE +\fB--cursor\fR +.RS 4 +Include cursors in the screenshot.\& +.P +.RE \fBsave\fR .RS 4 Save the screenshot into a regular file.\& Grimshot will write images 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.\& -Set FILE to '-' to pipe the output to STDOUT.\& +Set FILE to '\&-'\& to pipe the output to STDOUT.\& .P .RE \fBcopy\fR diff --git a/contrib/grimshot.1.scd b/contrib/grimshot.1.scd index d2a57759..e356f99d 100644 --- a/contrib/grimshot.1.scd +++ b/contrib/grimshot.1.scd @@ -6,7 +6,7 @@ grimshot - a helper for screenshots within sway # SYNOPSIS -*grimshot* [--notify] (copy|save) [TARGET] [FILE]++ +*grimshot* [--notify] [--cursor] (copy|save) [TARGET] [FILE]++ *grimshot* check++ *grimshot* usage @@ -15,8 +15,11 @@ grimshot - a helper for screenshots within sway *--notify* Show notifications to the user that a screenshot has been taken. +*--cursor* + Include cursors in the screenshot. + *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 in *user-dirs.dir*), or otherwise fall back to *XDG_PICTURES_DIR*. Set FILE to '-' to pipe the output to STDOUT. diff --git a/include/gesture.h b/include/gesture.h new file mode 100644 index 00000000..9c6b0f91 --- /dev/null +++ b/include/gesture.h @@ -0,0 +1,104 @@ +#ifndef _SWAY_GESTURE_H +#define _SWAY_GESTURE_H + +#include +#include + +/** + * 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 [:][:] 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 diff --git a/include/pango.h b/include/pango.h index 93affc23..1db113c2 100644 --- a/include/pango.h +++ b/include/pango.h @@ -13,12 +13,12 @@ * escaped string to dest if provided. */ 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); -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, ...); -void get_text_metrics(const char *font, int *height, int *baseline); -void render_text(cairo_t *cairo, const char *font, +void get_text_metrics(const PangoFontDescription *desc, int *height, int *baseline); +void render_text(cairo_t *cairo, PangoFontDescription *desc, double scale, bool markup, const char *fmt, ...); #endif diff --git a/include/stringop.h b/include/stringop.h index 8d7089e9..b29f59b2 100644 --- a/include/stringop.h +++ b/include/stringop.h @@ -2,6 +2,7 @@ #define _SWAY_STRINGOP_H #include +#include #include "list.h" void strip_whitespace(char *str); diff --git a/include/sway/commands.h b/include/sway/commands.h index 86856fcf..98b232e1 100644 --- a/include/sway/commands.h +++ b/include/sway/commands.h @@ -106,6 +106,7 @@ sway_cmd cmd_exec_process; sway_cmd cmd_assign; sway_cmd cmd_bar; sway_cmd cmd_bindcode; +sway_cmd cmd_bindgesture; sway_cmd cmd_bindswitch; sway_cmd cmd_bindsym; sway_cmd cmd_border; @@ -196,6 +197,7 @@ sway_cmd cmd_titlebar_border_thickness; sway_cmd cmd_titlebar_padding; sway_cmd cmd_unbindcode; sway_cmd cmd_unbindswitch; +sway_cmd cmd_unbindgesture; sway_cmd cmd_unbindsym; sway_cmd cmd_unmark; sway_cmd cmd_urgent; @@ -255,6 +257,7 @@ sway_cmd input_cmd_click_method; sway_cmd input_cmd_drag; sway_cmd input_cmd_drag_lock; sway_cmd input_cmd_dwt; +sway_cmd input_cmd_dwtp; sway_cmd input_cmd_events; sway_cmd input_cmd_left_handed; sway_cmd input_cmd_map_from_region; @@ -290,12 +293,14 @@ sway_cmd output_cmd_max_render_time; sway_cmd output_cmd_mode; sway_cmd output_cmd_modeline; sway_cmd output_cmd_position; +sway_cmd output_cmd_power; sway_cmd output_cmd_render_bit_depth; sway_cmd output_cmd_scale; sway_cmd output_cmd_scale_filter; sway_cmd output_cmd_subpixel; sway_cmd output_cmd_toggle; sway_cmd output_cmd_transform; +sway_cmd output_cmd_unplug; sway_cmd seat_cmd_attach; sway_cmd seat_cmd_cursor; diff --git a/include/sway/config.h b/include/sway/config.h index 4ec035c2..55aa8fbe 100644 --- a/include/sway/config.h +++ b/include/sway/config.h @@ -10,12 +10,14 @@ #include #include #include "../include/config.h" +#include "gesture.h" #include "list.h" #include "swaynag.h" #include "tree/container.h" #include "sway/input/tablet.h" #include "sway/tree/root.h" #include "wlr-layer-shell-unstable-v1-protocol.h" +#include // TODO: Refactor this shit @@ -32,7 +34,8 @@ enum binding_input_type { BINDING_KEYSYM, BINDING_MOUSECODE, 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 { @@ -45,10 +48,11 @@ enum binding_flags { BINDING_RELOAD = 1 << 6, // switch only; (re)trigger binding on reload BINDING_INHIBITED = 1 << 7, // keyboard only: ignore shortcut inhibitor 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 { enum binding_input_type type; @@ -62,12 +66,10 @@ struct sway_binding { char *command; }; -/** - * A mouse binding and an associated command. - */ -struct sway_mouse_binding { - uint32_t button; - char *command; +enum sway_switch_trigger { + SWAY_SWITCH_TRIGGER_OFF, + SWAY_SWITCH_TRIGGER_ON, + SWAY_SWITCH_TRIGGER_TOGGLE, }; /** @@ -75,11 +77,21 @@ struct sway_mouse_binding { */ struct sway_switch_binding { enum wlr_switch_type type; - enum wlr_switch_state state; + enum sway_switch_trigger trigger; uint32_t flags; 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. */ @@ -99,6 +111,7 @@ struct sway_mode { list_t *keycode_bindings; list_t *mouse_bindings; list_t *switch_bindings; + list_t *gesture_bindings; bool pango; }; @@ -137,6 +150,7 @@ struct input_config { int drag; int drag_lock; int dwt; + int dwtp; int left_handed; int middle_emulation; int natural_scroll; @@ -234,12 +248,6 @@ struct seat_config { } xcursor_theme; }; -enum config_dpms { - DPMS_IGNORE, - DPMS_ON, - DPMS_OFF, -}; - enum scale_filter_mode { SCALE_FILTER_DEFAULT, // the default is currently smart SCALE_FILTER_LINEAR, @@ -261,6 +269,7 @@ enum render_bit_depth { struct output_config { char *name; int enabled; + int power; int width, height; float refresh_rate; int custom_mode; @@ -277,7 +286,6 @@ struct output_config { char *background; char *background_option; char *background_fallback; - enum config_dpms dpms_state; }; /** @@ -501,7 +509,8 @@ struct sway_config { char *floating_scroll_right_cmd; enum sway_container_layout default_orientation; 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_baseline; bool pango_markup; @@ -700,6 +709,8 @@ void free_sway_binding(struct sway_binding *sb); 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 load_swaybar(struct bar_config *bar); diff --git a/include/sway/criteria.h b/include/sway/criteria.h index ad8610cd..59f57f94 100644 --- a/include/sway/criteria.h +++ b/include/sway/criteria.h @@ -1,7 +1,8 @@ #ifndef _SWAY_CRITERIA_H #define _SWAY_CRITERIA_H -#include +#define PCRE2_CODE_UNIT_WIDTH 8 +#include #include "config.h" #include "list.h" #include "tree/view.h" @@ -15,13 +16,13 @@ enum criteria_type { }; enum pattern_type { - PATTERN_PCRE, + PATTERN_PCRE2, PATTERN_FOCUSED, }; struct pattern { enum pattern_type match_type; - pcre *regex; + pcre2_code *regex; }; struct criteria { diff --git a/include/sway/desktop.h b/include/sway/desktop.h index c969a76b..7f2f5b3e 100644 --- a/include/sway/desktop.h +++ b/include/sway/desktop.h @@ -1,4 +1,4 @@ -#include +#include struct sway_container; struct sway_view; diff --git a/include/sway/desktop/launcher.h b/include/sway/desktop/launcher.h new file mode 100644 index 00000000..3b577f74 --- /dev/null +++ b/include/sway/desktop/launcher.h @@ -0,0 +1,32 @@ +#ifndef _SWAY_LAUNCHER_H +#define _SWAY_LAUNCHER_H + +#include + +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 diff --git a/include/sway/input/cursor.h b/include/sway/input/cursor.h index 7d66e699..8a2898dd 100644 --- a/include/sway/input/cursor.h +++ b/include/sway/input/cursor.h @@ -4,7 +4,7 @@ #include #include #include -#include +#include #include "sway/input/seat.h" #include "config.h" @@ -36,6 +36,8 @@ struct sway_cursor { bool active_confine_requires_warp; 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_update; struct wl_listener pinch_end; @@ -110,7 +112,7 @@ void dispatch_cursor_button(struct sway_cursor *cursor, enum wlr_button_state state); 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, struct wl_client *client); diff --git a/include/sway/input/keyboard.h b/include/sway/input/keyboard.h index 2c61e5a7..571d9e6f 100644 --- a/include/sway/input/keyboard.h +++ b/include/sway/input/keyboard.h @@ -50,6 +50,7 @@ struct sway_shortcut_state { struct sway_keyboard { struct sway_seat_device *seat_device; + struct wlr_keyboard *wlr; struct xkb_keymap *keymap; xkb_layout_index_t effective_layout; diff --git a/include/sway/input/libinput.h b/include/sway/input/libinput.h index 890d632e..e4b1acc3 100644 --- a/include/sway/input/libinput.h +++ b/include/sway/input/libinput.h @@ -2,7 +2,7 @@ #define _SWAY_INPUT_LIBINPUT_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); diff --git a/include/sway/input/seat.h b/include/sway/input/seat.h index 77c2278d..e3a46872 100644 --- a/include/sway/input/seat.h +++ b/include/sway/input/seat.h @@ -18,7 +18,23 @@ struct sway_seatop_impl { enum wlr_button_state state); void (*pointer_motion)(struct sway_seat *seat, uint32_t time_msec); 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 (*tablet_tool_motion)(struct sway_seat *seat, 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_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, 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, 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); /** diff --git a/include/sway/input/switch.h b/include/sway/input/switch.h index 213b471d..de6787b7 100644 --- a/include/sway/input/switch.h +++ b/include/sway/input/switch.h @@ -5,6 +5,7 @@ struct sway_switch { struct sway_seat_device *seat_device; + struct wlr_switch *wlr; enum wlr_switch_state state; enum wlr_switch_type type; diff --git a/include/sway/input/tablet.h b/include/sway/input/tablet.h index d7e4c242..c0a5aff7 100644 --- a/include/sway/input/tablet.h +++ b/include/sway/input/tablet.h @@ -32,6 +32,7 @@ struct sway_tablet_pad { struct wl_list link; struct sway_seat_device *seat_device; struct sway_tablet *tablet; + struct wlr_tablet_pad *wlr; struct wlr_tablet_v2_tablet_pad *tablet_v2_pad; struct wl_listener attach; diff --git a/include/sway/input/text_input.h b/include/sway/input/text_input.h index 37744266..c70fd935 100644 --- a/include/sway/input/text_input.h +++ b/include/sway/input/text_input.h @@ -3,7 +3,7 @@ #include #include -#include +#include #include "sway/input/seat.h" /** diff --git a/include/sway/ipc-json.h b/include/sway/ipc-json.h index 6f4ade1a..bc9f4985 100644 --- a/include/sway/ipc-json.h +++ b/include/sway/ipc-json.h @@ -1,6 +1,7 @@ #ifndef _SWAY_IPC_JSON_H #define _SWAY_IPC_JSON_H #include +#include "sway/output.h" #include "sway/tree/container.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_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_recursive(struct sway_node *node); json_object *ipc_json_describe_input(struct sway_input_device *device); diff --git a/include/sway/layers.h b/include/sway/layers.h index 14816861..f8508493 100644 --- a/include/sway/layers.h +++ b/include/sway/layers.h @@ -1,7 +1,7 @@ #ifndef _SWAY_LAYERS_H #define _SWAY_LAYERS_H #include -#include +#include #include enum layer_parent { diff --git a/include/sway/output.h b/include/sway/output.h index dc34686a..2d702741 100644 --- a/include/sway/output.h +++ b/include/sway/output.h @@ -3,6 +3,7 @@ #include #include #include +#include #include #include "config.h" #include "sway/desktop/fx_renderer.h" @@ -27,13 +28,13 @@ struct sway_output { struct wlr_box usable_area; struct timespec last_frame; - struct wlr_output_damage *damage; + struct wlr_damage_ring damage_ring; int lx, ly; // layout coords int width, height; // transformed buffer size enum wl_output_subpixel detected_subpixel; 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; bool enabling, enabled; @@ -45,8 +46,9 @@ struct sway_output { struct wl_listener commit; struct wl_listener mode; struct wl_listener present; - struct wl_listener damage_destroy; - struct wl_listener damage_frame; + struct wl_listener damage; + struct wl_listener frame; + struct wl_listener needs_frame; struct { struct wl_signal disable; @@ -58,6 +60,12 @@ struct sway_output { 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); 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 *data); +struct sway_output_non_desktop *output_non_desktop_create(struct wlr_output *wlr_output); + #endif diff --git a/include/sway/server.h b/include/sway/server.h index 4496c23f..0c2eccf3 100644 --- a/include/sway/server.h +++ b/include/sway/server.h @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -52,6 +53,7 @@ struct sway_server { struct wl_listener output_layout_change; struct wlr_idle *idle; + struct wlr_idle_notifier_v1 *idle_notifier_v1; struct sway_idle_inhibit_manager_v1 *idle_inhibit_manager_v1; 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_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 wl_listener output_power_manager_set_mode; struct wlr_input_method_manager_v2 *input_method; @@ -99,6 +115,8 @@ struct sway_server { struct wlr_xdg_activation_v1 *xdg_activation_v1; struct wl_listener xdg_activation_v1_request_activate; + struct wl_list pending_launcher_ctxs; // launcher_ctx::link + // The timeout for transactions, after which a transaction is applied // regardless of readiness. size_t txn_timeout_ms; @@ -135,8 +153,6 @@ struct sway_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); void server_fini(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_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); #if HAVE_XWAYLAND 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 *data); +void set_rr_scheduling(void); + #endif diff --git a/include/sway/surface.h b/include/sway/surface.h index 4da96c02..fb1cd775 100644 --- a/include/sway/surface.h +++ b/include/sway/surface.h @@ -1,6 +1,6 @@ #ifndef _SWAY_SURFACE_H #define _SWAY_SURFACE_H -#include +#include struct sway_surface { struct wlr_surface *wlr_surface; diff --git a/include/sway/tree/container.h b/include/sway/tree/container.h index ebe9568c..75a8113a 100644 --- a/include/sway/tree/container.h +++ b/include/sway/tree/container.h @@ -2,7 +2,7 @@ #define _SWAY_CONTAINER_H #include #include -#include +#include #include "list.h" #include "sway/tree/node.h" @@ -368,7 +368,7 @@ bool container_is_sticky_or_child(struct sway_container *con); * This will destroy pairs of redundant H/V splits * e.g. H[V[H[app app]] app] -> H[app app app] * 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: * https://github.com/i3/i3/blob/1f0c628cde40cf87371481041b7197344e0417c6/src/tree.c#L651 diff --git a/include/sway/tree/root.h b/include/sway/tree/root.h index 5d4a2f2d..a2c088e7 100644 --- a/include/sway/tree/root.h +++ b/include/sway/tree/root.h @@ -28,6 +28,7 @@ struct sway_root { double width, height; list_t *outputs; // struct sway_output + list_t *non_desktop_outputs; // struct sway_output_non_desktop list_t *scratchpad; // struct sway_container // 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); -struct sway_workspace *root_workspace_for_pid(pid_t pid); - -void root_record_workspace_pid(pid_t pid); - -void root_remove_workspace_pid(pid_t pid); - void root_for_each_workspace(void (*f)(struct sway_workspace *ws, void *data), void *data); @@ -91,6 +86,4 @@ struct sway_container *root_find_container( void root_get_box(struct sway_root *root, struct wlr_box *box); -void root_rename_pid_workspaces(const char *old_name, const char *new_name); - #endif diff --git a/include/sway/tree/view.h b/include/sway/tree/view.h index ee34af48..684d13a0 100644 --- a/include/sway/tree/view.h +++ b/include/sway/tree/view.h @@ -1,7 +1,7 @@ #ifndef _SWAY_VIEW_H #define _SWAY_VIEW_H #include -#include +#include #include "config.h" #if HAVE_XWAYLAND #include @@ -74,6 +74,7 @@ struct sway_view { struct sway_xdg_decoration *xdg_decoration; pid_t pid; + struct launcher_ctx *ctx; // The size the view would want to be if it weren't tiled. // Used when changing a view from tiled to floating. @@ -109,7 +110,7 @@ struct sway_view { list_t *executed_criteria; // struct criteria * union { - struct wlr_xdg_surface *wlr_xdg_surface; + struct wlr_xdg_toplevel *wlr_xdg_toplevel; #if HAVE_XWAYLAND struct wlr_xwayland_surface *wlr_xwayland_surface; #endif @@ -132,6 +133,7 @@ struct sway_xdg_shell_view { struct wl_listener commit; struct wl_listener request_move; struct wl_listener request_resize; + struct wl_listener request_maximize; struct wl_listener request_fullscreen; struct wl_listener set_title; struct wl_listener set_app_id; @@ -155,6 +157,7 @@ struct sway_xwayland_view { struct wl_listener set_title; struct wl_listener set_class; struct wl_listener set_role; + struct wl_listener set_startup_id; struct wl_listener set_window_type; struct wl_listener set_hints; struct wl_listener set_decorations; @@ -170,6 +173,7 @@ struct sway_xwayland_unmanaged { int lx, ly; + struct wl_listener request_activate; struct wl_listener request_configure; struct wl_listener request_fullscreen; struct wl_listener commit; @@ -217,7 +221,7 @@ struct sway_subsurface { struct sway_xdg_popup { 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 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); +void view_assign_ctx(struct sway_view *view, struct launcher_ctx *ctx); + #endif diff --git a/include/swaybar/bar.h b/include/swaybar/bar.h index 545a66a8..3ad0bdf3 100644 --- a/include/swaybar/bar.h +++ b/include/swaybar/bar.h @@ -58,7 +58,6 @@ struct swaybar_output { struct zxdg_output_v1 *xdg_output; struct wl_surface *surface; struct zwlr_layer_surface_v1 *layer_surface; - struct wl_region *input_region; uint32_t wl_name; struct wl_list workspaces; // swaybar_workspace::link diff --git a/include/swaybar/config.h b/include/swaybar/config.h index 4cacd21a..361acd99 100644 --- a/include/swaybar/config.h +++ b/include/swaybar/config.h @@ -6,6 +6,7 @@ #include "../include/config.h" #include "list.h" #include "util.h" +#include struct box_colors { uint32_t border; @@ -28,7 +29,7 @@ struct swaybar_config { char *status_command; bool pango_markup; uint32_t position; // zwlr_layer_surface_v1_anchor - char *font; + PangoFontDescription *font_description; char *sep_symbol; char *mode; char *hidden_state; diff --git a/include/swaybar/i3bar.h b/include/swaybar/i3bar.h index 1aec6d6c..dced2a6c 100644 --- a/include/swaybar/i3bar.h +++ b/include/swaybar/i3bar.h @@ -30,6 +30,6 @@ void i3bar_block_unref(struct i3bar_block *block); bool i3bar_handle_readable(struct status_line *status); enum hotspot_event_handling i3bar_block_send_click(struct status_line *status, struct i3bar_block *block, double x, double y, double rx, double ry, - double w, double h, int scale, uint32_t button); + double w, double h, int scale, uint32_t button, bool released); #endif diff --git a/include/swaybar/input.h b/include/swaybar/input.h index e8735d88..8ea88a69 100644 --- a/include/swaybar/input.h +++ b/include/swaybar/input.h @@ -49,7 +49,7 @@ struct swaybar_hotspot { int x, y, width, height; enum hotspot_event_handling (*callback)(struct swaybar_output *output, struct swaybar_hotspot *hotspot, double x, double y, uint32_t button, - void *data); + bool released, void *data); void (*destroy)(void *data); void *data; }; diff --git a/include/swaynag/swaynag.h b/include/swaynag/swaynag.h index baa6ee8b..2d68b6c9 100644 --- a/include/swaynag/swaynag.h +++ b/include/swaynag/swaynag.h @@ -67,7 +67,7 @@ struct swaynag_details { int offset; int visible_lines; int total_lines; - struct swaynag_button *button_details; + struct swaynag_button button_details; struct swaynag_button button_up; struct swaynag_button button_down; }; diff --git a/include/swaynag/types.h b/include/swaynag/types.h index 3c3b2754..18f218e0 100644 --- a/include/swaynag/types.h +++ b/include/swaynag/types.h @@ -4,7 +4,8 @@ struct swaynag_type { char *name; - char *font; + char *font; // Used for debugging. + PangoFontDescription *font_description; char *output; uint32_t anchors; int32_t layer; // enum zwlr_layer_shell_v1_layer or -1 if unset diff --git a/meson.build b/meson.build index 2a667125..a90f9ba1 100644 --- a/meson.build +++ b/meson.build @@ -35,14 +35,24 @@ if is_freebsd add_project_arguments('-D_C11_SOURCE', language: 'c') 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') -pcre = dependency('libpcre') -wayland_server = dependency('wayland-server', version: '>=1.20.0') +pcre2 = dependency('libpcre2-8') +wayland_server = dependency('wayland-server', version: '>=1.21.0') wayland_client = dependency('wayland-client') wayland_cursor = dependency('wayland-cursor') wayland_egl = dependency('wayland-egl') egl = dependency('egl') wayland_protos = dependency('wayland-protocols', version: '>=1.24') +wlroots = dependency('wlroots', version: wlroots_version) xkbcommon = dependency('xkbcommon') cairo = dependency('cairo') pango = dependency('pango') @@ -51,7 +61,7 @@ gdk_pixbuf = dependency('gdk-pixbuf-2.0', required: get_option('gdk-pixbuf')) pixman = dependency('pixman-1') glesv2 = dependency('glesv2') libevdev = dependency('libevdev') -libinput = dependency('libinput', version: '>=1.6.0') +libinput = dependency('libinput', version: '>=1.21.0') xcb = dependency('xcb', required: get_option('xwayland')) drm_full = dependency('libdrm') # only needed for drm_fourcc.h 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) math = cc.find_library('m') rt = cc.find_library('rt') - -# Try first to find wlroots as a subproject, then as a system dependency -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 +xcb_icccm = dependency('xcb-icccm', required: get_option('xwayland')) +threads = dependency('threads') # for pthread_setschedparam wlroots_features = { 'xwayland': false, @@ -87,7 +85,7 @@ endforeach if get_option('xwayland').enabled() and not wlroots_features['xwayland'] error('Cannot enable Xwayland in sway: wlroots has been built without Xwayland support') endif -have_xwayland = xcb.found() and wlroots_features['xwayland'] +have_xwayland = xcb.found() and xcb_icccm.found() and wlroots_features['xwayland'] if get_option('sd-bus-provider') == 'auto' if not get_option('tray').disabled() diff --git a/protocols/meson.build b/protocols/meson.build index df24a4e5..904aead4 100644 --- a/protocols/meson.build +++ b/protocols/meson.build @@ -1,81 +1,42 @@ wl_protocol_dir = wayland_protos.get_variable('pkgdatadir') -wayland_scanner_dep = dependency('wayland-scanner', required: false, native: true) -if wayland_scanner_dep.found() - wayland_scanner = find_program( - wayland_scanner_dep.get_variable(pkgconfig: 'wayland_scanner'), - native: true, - ) -else - wayland_scanner = find_program('wayland-scanner', native: true) -endif +wayland_scanner_dep = dependency('wayland-scanner', native: true) +wayland_scanner = find_program( + wayland_scanner_dep.get_variable(pkgconfig: 'wayland_scanner'), + native: true, +) protocols = [ - [wl_protocol_dir, 'stable/xdg-shell/xdg-shell.xml'], - [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/tablet/tablet-unstable-v2.xml'], - [wl_protocol_dir, 'unstable/linux-dmabuf/linux-dmabuf-unstable-v1.xml'], - ['wlr-layer-shell-unstable-v1.xml'], - ['idle.xml'], - ['wlr-input-inhibitor-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_protocol_dir / 'stable/xdg-shell/xdg-shell.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/tablet/tablet-unstable-v2.xml', + wl_protocol_dir / 'unstable/linux-dmabuf/linux-dmabuf-unstable-v1.xml', + 'wlr-layer-shell-unstable-v1.xml', + 'idle.xml', + 'wlr-input-inhibitor-unstable-v1.xml', + 'wlr-output-power-management-unstable-v1.xml', ] wl_protos_src = [] -wl_protos_headers = [] -foreach p : protocols - xml = join_paths(p) +foreach xml : protocols wl_protos_src += custom_target( - xml.underscorify() + '_server_c', + xml.underscorify() + '_c', input: xml, output: '@BASENAME@-protocol.c', command: [wayland_scanner, 'private-code', '@INPUT@', '@OUTPUT@'], ) - wl_protos_headers += custom_target( + wl_protos_src += custom_target( xml.underscorify() + '_server_h', input: xml, output: '@BASENAME@-protocol.h', command: [wayland_scanner, 'server-header', '@INPUT@', '@OUTPUT@'], ) -endforeach - -foreach p : client_protocols - xml = join_paths(p) - wl_protos_headers += custom_target( + wl_protos_src += custom_target( xml.underscorify() + '_client_h', input: xml, output: '@BASENAME@-client-protocol.h', command: [wayland_scanner, 'client-header', '@INPUT@', '@OUTPUT@'], ) endforeach - -lib_client_protos = static_library( - 'client_protos', - wl_protos_src + wl_protos_headers, - dependencies: wayland_client.partial_dependency(compile_args: true), -) - -client_protos = declare_dependency( - link_with: lib_client_protos, - sources: wl_protos_headers, -) - -lib_server_protos = static_library( - 'server_protos', - wl_protos_src + wl_protos_headers, - dependencies: wayland_server.partial_dependency(compile_args: true), -) - -server_protos = declare_dependency( - link_with: lib_server_protos, - sources: wl_protos_headers, -) diff --git a/sway/commands.c b/sway/commands.c index 91e559a1..6e473b97 100644 --- a/sway/commands.c +++ b/sway/commands.c @@ -46,6 +46,7 @@ static const struct cmd_handler handlers[] = { { "assign", cmd_assign }, { "bar", cmd_bar }, { "bindcode", cmd_bindcode }, + { "bindgesture", cmd_bindgesture }, { "bindswitch", cmd_bindswitch }, { "bindsym", cmd_bindsym }, { "client.background", cmd_client_noop }, @@ -96,6 +97,7 @@ static const struct cmd_handler handlers[] = { { "titlebar_border_thickness", cmd_titlebar_border_thickness }, { "titlebar_padding", cmd_titlebar_padding }, { "unbindcode", cmd_unbindcode }, + { "unbindgesture", cmd_unbindgesture }, { "unbindswitch", cmd_unbindswitch }, { "unbindsym", cmd_unbindsym }, { "workspace", cmd_workspace }, @@ -412,6 +414,7 @@ struct cmd_results *config_command(char *exec, char **new_block) { && handler->handle != cmd_bindsym && handler->handle != cmd_bindcode && handler->handle != cmd_bindswitch + && handler->handle != cmd_bindgesture && handler->handle != cmd_set && handler->handle != cmd_for_window && (*argv[i] == '\"' || *argv[i] == '\'')) { @@ -487,13 +490,19 @@ struct cmd_results *cmd_results_new(enum cmd_status status, } results->status = status; if (format) { - char *error = malloc(256); + char *error = NULL; va_list args; va_start(args, format); - if (error) { - vsnprintf(error, 256, format, args); - } + int slen = vsnprintf(NULL, 0, format, 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; } else { results->error = NULL; diff --git a/sway/commands/bind.c b/sway/commands/bind.c index 25be415e..c0b383db 100644 --- a/sway/commands/bind.c +++ b/sway/commands/bind.c @@ -47,7 +47,7 @@ static bool binding_switch_compare(struct sway_switch_binding *binding_a, if (binding_a->type != binding_b->type) { return false; } - if (binding_a->state != binding_b->state) { + if (binding_a->trigger != binding_b->trigger) { return false; } 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) { free(binding->input); binding->input = strdup(argv[0] + strlen("--input-device=")); + strip_quotes(binding->input); } else if (strcmp("--no-warn", argv[0]) == 0) { warn = false; } 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]); } 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) { - binding->state = WLR_SWITCH_STATE_OFF; + binding->trigger = SWAY_SWITCH_TRIGGER_OFF; } else if (strcmp(split->items[1], "toggle") == 0) { - binding->state = WLR_SWITCH_STATE_TOGGLE; + binding->trigger = SWAY_SWITCH_TRIGGER_TOGGLE; } else { free_switch_binding(binding); return cmd_results_new(CMD_FAILURE, diff --git a/sway/commands/exec_always.c b/sway/commands/exec_always.c index b35065c1..e6b09e64 100644 --- a/sway/commands/exec_always.c +++ b/sway/commands/exec_always.c @@ -8,6 +8,8 @@ #include "sway/commands.h" #include "sway/config.h" #include "sway/server.h" +#include "sway/desktop/launcher.h" +#include "sway/server.h" #include "sway/tree/container.h" #include "sway/tree/root.h" #include "sway/tree/workspace.h" @@ -25,11 +27,22 @@ struct cmd_results *cmd_exec_validate(int argc, char **argv) { return error; } +static void export_xdga_token(struct launcher_ctx *ctx) { + const char *token = launcher_ctx_get_token_name(ctx); + setenv("XDG_ACTIVATION_TOKEN", token, 1); +} + +static void export_startup_id(struct launcher_ctx *ctx) { + const char *token = launcher_ctx_get_token_name(ctx); + setenv("DESKTOP_STARTUP_ID", token, 1); +} + struct cmd_results *cmd_exec_process(int argc, char **argv) { struct cmd_results *error = NULL; char *cmd = NULL; + bool no_startup_id = false; if (strcmp(argv[0], "--no-startup-id") == 0) { - sway_log(SWAY_INFO, "exec switch '--no-startup-id' not supported, ignored."); + no_startup_id = true; --argc; ++argv; if ((error = checkarg(argc, argv[-1], EXPECTED_AT_LEAST, 1))) { return error; @@ -51,6 +64,7 @@ struct cmd_results *cmd_exec_process(int argc, char **argv) { } pid_t pid, child; + struct launcher_ctx *ctx = launcher_ctx_create(); // Fork process if ((pid = fork()) == 0) { // Fork child process again @@ -63,6 +77,12 @@ struct cmd_results *cmd_exec_process(int argc, char **argv) { close(fd[0]); if ((child = fork()) == 0) { close(fd[1]); + if (ctx) { + export_xdga_token(ctx); + } + if (ctx && !no_startup_id) { + export_startup_id(ctx); + } execlp("sh", "sh", "-c", cmd, (void *)NULL); sway_log_errno(SWAY_ERROR, "execlp failed"); _exit(1); @@ -90,8 +110,12 @@ struct cmd_results *cmd_exec_process(int argc, char **argv) { waitpid(pid, NULL, 0); if (child > 0) { sway_log(SWAY_DEBUG, "Child process created with pid %d", child); - root_record_workspace_pid(child); + if (ctx != NULL) { + sway_log(SWAY_DEBUG, "Recording workspace for process %d", child); + ctx->pid = child; + } } else { + launcher_ctx_destroy(ctx); return cmd_results_new(CMD_FAILURE, "Second fork() failed"); } diff --git a/sway/commands/focus.c b/sway/commands/focus.c index b8d28480..facd82de 100644 --- a/sway/commands/focus.c +++ b/sway/commands/focus.c @@ -54,7 +54,7 @@ static bool get_direction_from_next_prev(struct sway_container *container, } else { return false; } - + return true; } @@ -285,7 +285,7 @@ static struct cmd_results *focus_mode(struct sway_workspace *ws, } } else { 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"); } 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) { if (!argc) { return cmd_results_new(CMD_INVALID, - "Expected 'focus output '"); + "Expected 'focus output '."); } char *identifier = join_args(argv, argc); 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)) { free(identifier); 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); if (!ws) { free(identifier); 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); @@ -375,10 +375,14 @@ struct cmd_results *cmd_focus(int argc, char **argv) { struct sway_seat *seat = config->handler_context.seat; if (node->type < N_WORKSPACE) { 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)) { root_scratchpad_show(container); } diff --git a/sway/commands/font.c b/sway/commands/font.c index cea720f5..74bb6b9f 100644 --- a/sway/commands/font.c +++ b/sway/commands/font.c @@ -4,6 +4,7 @@ #include "sway/config.h" #include "log.h" #include "stringop.h" +#include struct cmd_results *cmd_font(int argc, char **argv) { struct cmd_results *error = NULL; @@ -16,12 +17,34 @@ struct cmd_results *cmd_font(int argc, char **argv) { if (strncmp(font, "pango:", 6) == 0) { config->pango_markup = true; config->font = strdup(font + 6); + free(font); } else { 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(); + return cmd_results_new(CMD_SUCCESS, NULL); } diff --git a/sway/commands/gesture.c b/sway/commands/gesture.c new file mode 100644 index 00000000..d4442cc3 --- /dev/null +++ b/sway/commands/gesture.c @@ -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); +} diff --git a/sway/commands/input.c b/sway/commands/input.c index 77acb671..ea531659 100644 --- a/sway/commands/input.c +++ b/sway/commands/input.c @@ -14,6 +14,7 @@ static const struct cmd_handler input_handlers[] = { { "drag", input_cmd_drag }, { "drag_lock", input_cmd_drag_lock }, { "dwt", input_cmd_dwt }, + { "dwtp", input_cmd_dwtp }, { "events", input_cmd_events }, { "left_handed", input_cmd_left_handed }, { "map_from_region", input_cmd_map_from_region }, diff --git a/sway/commands/input/dwtp.c b/sway/commands/input/dwtp.c new file mode 100644 index 00000000..232e2b26 --- /dev/null +++ b/sway/commands/input/dwtp.c @@ -0,0 +1,25 @@ +#include +#include +#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); +} diff --git a/sway/commands/input/xkb_switch_layout.c b/sway/commands/input/xkb_switch_layout.c index d6548a68..3cce4ec8 100644 --- a/sway/commands/input/xkb_switch_layout.c +++ b/sway/commands/input/xkb_switch_layout.c @@ -1,10 +1,16 @@ #define _POSIX_C_SOURCE 200809L #include +#include #include "sway/config.h" #include "sway/commands.h" #include "sway/input/input-manager.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) { xkb_layout_index_t num_layouts = xkb_keymap_num_layouts(kbd->keymap); if (idx >= num_layouts) { @@ -28,10 +34,10 @@ static xkb_layout_index_t get_current_layout_index(struct wlr_keyboard *kbd) { 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 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) { @@ -66,6 +72,18 @@ struct cmd_results *input_cmd_xkb_switch_layout(int argc, char **argv) { 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; wl_list_for_each(dev, &server.input->devices, link) { 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) { continue; } + + struct xkb_switch_layout_action *action = + &actions[actions_len++]; + + action->keyboard = wlr_keyboard_from_input_device(dev->wlr_device); if (relative) { - switch_layout_relative(dev->wlr_device->keyboard, relative); + action->layout = get_layout_relative(action->keyboard, relative); } 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); } diff --git a/sway/commands/mode.c b/sway/commands/mode.c index e23e4ee4..7263efcb 100644 --- a/sway/commands/mode.c +++ b/sway/commands/mode.c @@ -11,10 +11,12 @@ // Must be in order for the bsearch static const struct cmd_handler mode_handlers[] = { { "bindcode", cmd_bindcode }, + { "bindgesture", cmd_bindgesture }, { "bindswitch", cmd_bindswitch }, { "bindsym", cmd_bindsym }, { "set", cmd_set }, { "unbindcode", cmd_unbindcode }, + { "unbindgesture", cmd_unbindgesture }, { "unbindswitch", cmd_unbindswitch }, { "unbindsym", cmd_unbindsym }, }; @@ -59,6 +61,7 @@ struct cmd_results *cmd_mode(int argc, char **argv) { mode->keycode_bindings = create_list(); mode->mouse_bindings = create_list(); mode->switch_bindings = create_list(); + mode->gesture_bindings = create_list(); mode->pango = pango; list_add(config->modes, mode); } diff --git a/sway/commands/move.c b/sway/commands/move.c index 1a05a7a6..7bd1fe3e 100644 --- a/sway/commands/move.c +++ b/sway/commands/move.c @@ -686,6 +686,9 @@ static struct cmd_results *cmd_move_workspace(int argc, char **argv) { arrange_output(old_output); arrange_output(new_output); + struct sway_seat *seat = config->handler_context.seat; + seat_consider_warp_to_focus(seat); + return cmd_results_new(CMD_SUCCESS, NULL); } @@ -788,15 +791,15 @@ static struct cmd_results *cmd_move_to_position_pointer( struct wlr_output *output = wlr_output_layout_output_at( root->output_layout, cursor->x, cursor->y); if (output) { - struct wlr_box *box = - wlr_output_layout_get_box(root->output_layout, output); - lx = fmax(lx, box->x); - ly = fmax(ly, box->y); - if (lx + container->pending.width > box->x + box->width) { - lx = box->x + box->width - container->pending.width; + struct wlr_box box; + wlr_output_layout_get_box(root->output_layout, output, &box); + lx = fmax(lx, box.x); + ly = fmax(ly, box.y); + if (lx + container->pending.width > box.x + box.width) { + lx = box.x + box.width - container->pending.width; } - if (ly + container->pending.height > box->y + box->height) { - ly = box->y + box->height - container->pending.height; + if (ly + container->pending.height > box.y + box.height) { + ly = box.y + box.height - container->pending.height; } } diff --git a/sway/commands/output.c b/sway/commands/output.c index 125df5a7..df32c673 100644 --- a/sway/commands/output.c +++ b/sway/commands/output.c @@ -18,6 +18,7 @@ static const struct cmd_handler output_handlers[] = { { "modeline", output_cmd_modeline }, { "pos", output_cmd_position }, { "position", output_cmd_position }, + { "power", output_cmd_power }, { "render_bit_depth", output_cmd_render_bit_depth }, { "res", output_cmd_mode }, { "resolution", output_cmd_mode }, @@ -26,6 +27,7 @@ static const struct cmd_handler output_handlers[] = { { "subpixel", output_cmd_subpixel }, { "toggle", output_cmd_toggle }, { "transform", output_cmd_transform }, + { "unplug", output_cmd_unplug }, }; struct cmd_results *cmd_output(int argc, char **argv) { diff --git a/sway/commands/output/background.c b/sway/commands/output/background.c index 68ee9fe1..67f212ff 100644 --- a/sway/commands/output/background.c +++ b/sway/commands/output/background.c @@ -102,19 +102,19 @@ struct cmd_results *output_cmd_background(int argc, char **argv) { } char *conf_path = dirname(conf); - char *rel_path = src; - src = malloc(strlen(conf_path) + strlen(src) + 2); - if (!src) { - free(rel_path); + char *real_src = malloc(strlen(conf_path) + strlen(src) + 2); + if (!real_src) { + free(src); free(conf); sway_log(SWAY_ERROR, "Unable to allocate memory"); return cmd_results_new(CMD_FAILURE, "Unable to allocate resources"); } - sprintf(src, "%s/%s", conf_path, rel_path); - free(rel_path); + snprintf(real_src, strlen(conf_path) + strlen(src) + 2, "%s/%s", conf_path, src); + free(src); free(conf); + src = real_src; } bool can_access = access(src, F_OK) != -1; diff --git a/sway/commands/output/dpms.c b/sway/commands/output/dpms.c index 638c0ade..c7adbd58 100644 --- a/sway/commands/output/dpms.c +++ b/sway/commands/output/dpms.c @@ -1,45 +1,8 @@ +#include "log.h" #include "sway/commands.h" -#include "sway/config.h" -#include "sway/output.h" -#include "util.h" -#include struct cmd_results *output_cmd_dpms(int argc, char **argv) { - if (!config->handler_context.output_config) { - return cmd_results_new(CMD_FAILURE, "Missing output config"); - } - 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; + sway_log(SWAY_INFO, "The \"output dpms\" command is deprecated, " + "use \"output power\" instead"); + return output_cmd_power(argc, argv); } diff --git a/sway/commands/output/power.c b/sway/commands/output/power.c new file mode 100644 index 00000000..e6ae2852 --- /dev/null +++ b/sway/commands/output/power.c @@ -0,0 +1,43 @@ +#include +#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; +} diff --git a/sway/commands/output/unplug.c b/sway/commands/output/unplug.c new file mode 100644 index 00000000..dfef626f --- /dev/null +++ b/sway/commands/output/unplug.c @@ -0,0 +1,54 @@ +#include +#include +#include +#include +#if WLR_HAS_X11_BACKEND +#include +#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); +} diff --git a/sway/commands/rename.c b/sway/commands/rename.c index 3b855fdf..60a66d58 100644 --- a/sway/commands/rename.c +++ b/sway/commands/rename.c @@ -7,6 +7,7 @@ #include "sway/config.h" #include "sway/ipc-server.h" #include "sway/output.h" +#include "sway/desktop/launcher.h" #include "sway/tree/container.h" #include "sway/tree/workspace.h" #include "sway/tree/root.h" @@ -91,8 +92,6 @@ struct cmd_results *cmd_rename(int argc, char **argv) { sway_log(SWAY_DEBUG, "renaming workspace '%s' to '%s'", workspace->name, new_name); - root_rename_pid_workspaces(workspace->name, new_name); - free(workspace->name); workspace->name = new_name; diff --git a/sway/commands/seat/cursor.c b/sway/commands/seat/cursor.c index 749235eb..504a9f5e 100644 --- a/sway/commands/seat/cursor.c +++ b/sway/commands/seat/cursor.c @@ -111,8 +111,8 @@ static struct cmd_results *press_or_release(struct sway_cursor *cursor, : WLR_AXIS_ORIENTATION_HORIZONTAL; double delta = (button == SWAY_SCROLL_UP || button == SWAY_SCROLL_LEFT) ? -1 : 1; - struct wlr_event_pointer_axis event = { - .device = NULL, + struct wlr_pointer_axis_event event = { + .pointer = NULL, .time_msec = 0, .source = WLR_AXIS_SOURCE_WHEEL, .orientation = orientation, diff --git a/sway/config.c b/sway/config.c index f7ff49b3..e99a3458 100644 --- a/sway/config.c +++ b/sway/config.c @@ -82,6 +82,12 @@ static void free_mode(struct sway_mode *mode) { } 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); } @@ -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->mouse_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); config->floating_mod = 0; @@ -236,6 +243,7 @@ static void config_defaults(struct sway_config *config) { config->default_layout = L_NONE; config->default_orientation = L_NONE; if (!(config->font = strdup("monospace 10"))) goto cleanup; + config->font_description = pango_font_description_from_string(config->font); config->urgent_timeout = 500; config->focus_on_window_activation = FOWA_URGENT; config->popup_during_fullscreen = POPUP_SMART; @@ -1005,7 +1013,7 @@ int workspace_output_cmp_workspace(const void *a, const void *b) { void config_update_font_height(void) { 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) { arrange_root(); diff --git a/sway/config/input.c b/sway/config/input.c index a998e170..a98204df 100644 --- a/sway/config/input.c +++ b/sway/config/input.c @@ -25,6 +25,7 @@ struct input_config *new_input_config(const char* identifier) { input->drag = INT_MIN; input->drag_lock = INT_MIN; input->dwt = INT_MIN; + input->dwtp = INT_MIN; input->send_events = INT_MIN; input->click_method = 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) { dst->dwt = src->dwt; } + if (src->dwtp != INT_MIN) { + dst->dwtp = src->dwtp; + } if (src->left_handed != INT_MIN) { dst->left_handed = src->left_handed; } diff --git a/sway/config/output.c b/sway/config/output.c index fa509252..9c7082d0 100644 --- a/sway/config/output.c +++ b/sway/config/output.c @@ -27,8 +27,10 @@ int output_name_cmp(const void *item, const void *data) { void output_get_identifier(char *identifier, size_t len, struct sway_output *output) { struct wlr_output *wlr_output = output->wlr_output; - snprintf(identifier, len, "%s %s %s", wlr_output->make, wlr_output->model, - wlr_output->serial); + snprintf(identifier, len, "%s %s %s", + 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) { @@ -69,6 +71,7 @@ struct output_config *new_output_config(const char *name) { oc->max_render_time = -1; oc->adaptive_sync = -1; oc->render_bit_depth = RENDER_BIT_DEPTH_DEFAULT; + oc->power = -1; return oc; } @@ -130,8 +133,8 @@ void merge_output_config(struct output_config *dst, struct output_config *src) { free(dst->background_fallback); dst->background_fallback = strdup(src->background_fallback); } - if (src->dpms_state != 0) { - dst->dpms_state = src->dpms_state; + if (src->power != -1) { + 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); sway_log(SWAY_DEBUG, "Generated id on name output config \"%s\"" " (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->refresh_rate, ion_oc->x, ion_oc->y, ion_oc->scale, 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); } } @@ -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 " - "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)", 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->transform, oc->background, oc->background_option, oc->dpms_state, + oc->transform, oc->background, oc->background_option, oc->power, oc->max_render_time); return oc; } -static void set_mode(struct wlr_output *output, int width, int height, - float refresh_rate, bool custom) { +static void set_mode(struct wlr_output *output, struct wlr_output_state *pending, + int width, int height, float refresh_rate, bool custom) { // Not all floating point integers can be represented exactly // as (int)(1000 * mHz / 1000.f) // 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) { 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); return; } @@ -278,10 +281,11 @@ static void set_mode(struct wlr_output *output, int width, int height, } else { 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)) { sway_log(SWAY_ERROR, "Modeline can only be set to DRM output"); 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); struct wlr_output_mode *mode = wlr_drm_connector_add_mode(output, drm_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 #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 }; - if (output->pending.committed & WLR_OUTPUT_STATE_MODE) { - switch (output->pending.mode_type) { + if (pending->committed & WLR_OUTPUT_STATE_MODE) { + switch (pending->mode_type) { case WLR_OUTPUT_STATE_MODE_FIXED: - box.width = output->pending.mode->width; - box.height = output->pending.mode->height; + box.width = pending->mode->width; + box.height = pending->mode->height; break; case WLR_OUTPUT_STATE_MODE_CUSTOM: - box.width = output->pending.custom_mode.width; - box.height = output->pending.custom_mode.height; + box.width = pending->custom_mode.width; + box.height = pending->custom_mode.height; break; } } enum wl_output_transform transform = output->transform; - if (output->pending.committed & WLR_OUTPUT_STATE_TRANSFORM) { - transform = output->pending.transform; + if (pending->committed & WLR_OUTPUT_STATE_TRANSFORM) { + transform = pending->transform; } 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, - struct sway_output *output) { + struct sway_output *output, struct wlr_output_state *pending) { if (output == root->fallback_output) { return; } 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); - wlr_output_enable(wlr_output, false); + wlr_output_state_set_enabled(pending, false); return; } 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) { sway_log(SWAY_DEBUG, "Set %s modeline", 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) { sway_log(SWAY_DEBUG, "Set %s mode to %dx%d (%f Hz)", 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); } else if (!wl_list_empty(&wlr_output->modes)) { sway_log(SWAY_DEBUG, "Set preferred mode"); struct wlr_output_mode *preferred_mode = 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, " "falling back to another mode"); struct wlr_output_mode *mode; @@ -414,8 +419,8 @@ static void queue_output_config(struct output_config *oc, continue; } - wlr_output_set_mode(wlr_output, mode); - if (wlr_output_test(wlr_output)) { + wlr_output_state_set_mode(pending, mode); + if (wlr_output_test_state(wlr_output, pending)) { break; } } @@ -425,7 +430,7 @@ static void queue_output_config(struct output_config *oc, if (oc && (oc->subpixel != WL_OUTPUT_SUBPIXEL_UNKNOWN || config->reloading)) { sway_log(SWAY_DEBUG, "Set %s subpixel to %s", oc->name, 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; @@ -437,7 +442,7 @@ static void queue_output_config(struct output_config *oc, } if (wlr_output->transform != 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 @@ -446,18 +451,22 @@ static void queue_output_config(struct output_config *oc, if (oc && oc->scale > 0) { scale = oc->scale; } else { - scale = compute_default_scale(wlr_output); + scale = compute_default_scale(wlr_output, pending); sway_log(SWAY_DEBUG, "Auto-detected output scale: %f", scale); } if (scale != wlr_output->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) { sway_log(SWAY_DEBUG, "Set %s adaptive sync to %d", wlr_output->name, 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) { @@ -465,8 +474,8 @@ static void queue_output_config(struct output_config *oc, assert(fmts); for (size_t i = 0; fmts[i] != DRM_FORMAT_INVALID; i++) { - wlr_output_set_render_format(wlr_output, fmts[i]); - if (wlr_output_test(wlr_output)) { + wlr_output_state_set_render_format(pending, fmts[i]); + if (wlr_output_test_state(wlr_output, pending)) { 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 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) { - output->current_mode = wlr_output->pending.mode; + if (!oc || oc->power != 0) { + output->current_mode = pending.mode; } 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. // Leave the output disabled for now and try again when the output gets // 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} - struct wlr_box *output_box = - wlr_output_layout_get_box(root->output_layout, wlr_output); - output->lx = output_box->x; - output->ly = output_box->y; - output->width = output_box->width; - output->height = output_box->height; + struct wlr_box output_box; + wlr_output_layout_get_box(root->output_layout, wlr_output, &output_box); + output->lx = output_box.x; + output->ly = output_box.y; + output->width = output_box.width; + output->height = output_box.height; if (!output->enabled) { output_enable(output); @@ -577,15 +587,15 @@ bool test_output_config(struct output_config *oc, struct sway_output *output) { return false; } - queue_output_config(oc, output); - bool ok = wlr_output_test(output->wlr_output); - wlr_output_rollback(output->wlr_output); - return ok; + struct wlr_output_state pending = {0}; + queue_output_config(oc, output, &pending); + return wlr_output_test_state(output->wlr_output, &pending); } static void default_output_config(struct output_config *oc, struct wlr_output *wlr_output) { oc->enabled = 1; + oc->power = 1; struct wlr_output_mode *mode = wlr_output_preferred_mode(wlr_output); if (mode != NULL) { oc->width = mode->width; @@ -598,7 +608,6 @@ static void default_output_config(struct output_config *oc, struct sway_output *output = wlr_output->data; oc->subpixel = output->detected_subpixel; oc->transform = WL_OUTPUT_TRANSFORM_NORMAL; - oc->dpms_state = DPMS_ON; 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)" " (%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->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); } else if (oc_name) { // No identifier config, just return a copy of the name config diff --git a/sway/criteria.c b/sway/criteria.c index d2a5566f..d7326bea 100644 --- a/sway/criteria.c +++ b/sway/criteria.c @@ -3,7 +3,8 @@ #include #include #include -#include +#define PCRE2_CODE_UNIT_WIDTH 8 +#include #include "sway/criteria.h" #include "sway/tree/container.h" #include "sway/config.h" @@ -40,17 +41,19 @@ bool criteria_is_empty(struct criteria *criteria) { char *error = NULL; // Returns error string on failure or NULL otherwise. -static bool generate_regex(pcre **regex, char *value) { - const char *reg_err; - int offset; - - *regex = pcre_compile(value, PCRE_UTF8 | PCRE_UCP, ®_err, &offset, NULL); +static bool generate_regex(pcre2_code **regex, char *value) { + int errorcode; + PCRE2_SIZE offset; + *regex = pcre2_compile((PCRE2_SPTR)value, PCRE2_ZERO_TERMINATED, PCRE2_UTF | PCRE2_UCP, &errorcode, &offset, NULL); if (!*regex) { + PCRE2_UCHAR buffer[256]; + pcre2_get_error_message(errorcode, buffer, sizeof(buffer)); + 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); - snprintf(error, len, fmt, value, reg_err); + snprintf(error, len, fmt, value, buffer); return false; } @@ -66,7 +69,7 @@ static bool pattern_create(struct pattern **pattern, char *value) { if (strcmp(value, "__focused__") == 0) { (*pattern)->match_type = PATTERN_FOCUSED; } else { - (*pattern)->match_type = PATTERN_PCRE; + (*pattern)->match_type = PATTERN_PCRE2; if (!generate_regex(&(*pattern)->regex, value)) { return false; }; @@ -77,7 +80,7 @@ static bool pattern_create(struct pattern **pattern, char *value) { static void pattern_destroy(struct pattern *pattern) { if (pattern) { if (pattern->regex) { - pcre_free(pattern->regex); + pcre2_code_free(pattern->regex); } free(pattern); } @@ -93,14 +96,18 @@ void criteria_destroy(struct criteria *criteria) { pattern_destroy(criteria->window_role); #endif pattern_destroy(criteria->con_mark); - free(criteria->workspace); + pattern_destroy(criteria->workspace); + free(criteria->target); free(criteria->cmdlist); free(criteria->raw); free(criteria); } -static int regex_cmp(const char *item, const pcre *regex) { - return pcre_exec(regex, NULL, item, strlen(item), 0, 0, NULL, 0); +static int regex_cmp(const char *item, const pcre2_code *regex) { + 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 @@ -155,7 +162,7 @@ static bool criteria_matches_container(struct criteria *criteria, bool exists = false; struct sway_container *con = container; 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; break; } @@ -183,7 +190,7 @@ static bool criteria_matches_view(struct criteria *criteria, if (criteria->title) { const char *title = view_get_title(view); if (!title) { - return false; + title = ""; } switch (criteria->title->match_type) { @@ -192,8 +199,8 @@ static bool criteria_matches_view(struct criteria *criteria, return false; } break; - case PATTERN_PCRE: - if (regex_cmp(title, criteria->title->regex) != 0) { + case PATTERN_PCRE2: + if (regex_cmp(title, criteria->title->regex) < 0) { return false; } break; @@ -203,7 +210,7 @@ static bool criteria_matches_view(struct criteria *criteria, if (criteria->shell) { const char *shell = view_get_shell(view); if (!shell) { - return false; + shell = ""; } switch (criteria->shell->match_type) { @@ -212,8 +219,8 @@ static bool criteria_matches_view(struct criteria *criteria, return false; } break; - case PATTERN_PCRE: - if (regex_cmp(shell, criteria->shell->regex) != 0) { + case PATTERN_PCRE2: + if (regex_cmp(shell, criteria->shell->regex) < 0) { return false; } break; @@ -223,7 +230,7 @@ static bool criteria_matches_view(struct criteria *criteria, if (criteria->app_id) { const char *app_id = view_get_app_id(view); if (!app_id) { - return false; + app_id = ""; } switch (criteria->app_id->match_type) { @@ -232,8 +239,8 @@ static bool criteria_matches_view(struct criteria *criteria, return false; } break; - case PATTERN_PCRE: - if (regex_cmp(app_id, criteria->app_id->regex) != 0) { + case PATTERN_PCRE2: + if (regex_cmp(app_id, criteria->app_id->regex) < 0) { return false; } break; @@ -255,7 +262,7 @@ static bool criteria_matches_view(struct criteria *criteria, if (criteria->class) { const char *class = view_get_class(view); if (!class) { - return false; + class = ""; } switch (criteria->class->match_type) { @@ -264,8 +271,8 @@ static bool criteria_matches_view(struct criteria *criteria, return false; } break; - case PATTERN_PCRE: - if (regex_cmp(class, criteria->class->regex) != 0) { + case PATTERN_PCRE2: + if (regex_cmp(class, criteria->class->regex) < 0) { return false; } break; @@ -275,17 +282,17 @@ static bool criteria_matches_view(struct criteria *criteria, if (criteria->instance) { const char *instance = view_get_instance(view); if (!instance) { - return false; + instance = ""; } switch (criteria->instance->match_type) { case PATTERN_FOCUSED: - if (focused && strcmp(instance, view_get_instance(focused))) { + if (focused && lenient_strcmp(instance, view_get_instance(focused))) { return false; } break; - case PATTERN_PCRE: - if (regex_cmp(instance, criteria->instance->regex) != 0) { + case PATTERN_PCRE2: + if (regex_cmp(instance, criteria->instance->regex) < 0) { return false; } break; @@ -295,17 +302,17 @@ static bool criteria_matches_view(struct criteria *criteria, if (criteria->window_role) { const char *window_role = view_get_window_role(view); if (!window_role) { - return false; + window_role = ""; } switch (criteria->window_role->match_type) { case PATTERN_FOCUSED: - if (focused && strcmp(window_role, view_get_window_role(focused))) { + if (focused && lenient_strcmp(window_role, view_get_window_role(focused))) { return false; } break; - case PATTERN_PCRE: - if (regex_cmp(window_role, criteria->window_role->regex) != 0) { + case PATTERN_PCRE2: + if (regex_cmp(window_role, criteria->window_role->regex) < 0) { return false; } break; @@ -363,8 +370,8 @@ static bool criteria_matches_view(struct criteria *criteria, return false; } break; - case PATTERN_PCRE: - if (regex_cmp(ws->name, criteria->workspace->regex) != 0) { + case PATTERN_PCRE2: + if (regex_cmp(ws->name, criteria->workspace->regex) < 0) { return false; } break; @@ -676,7 +683,7 @@ struct criteria *criteria_parse(char *raw, char **error_arg) { } name = calloc(head - namestart + 1, 1); if (head != namestart) { - strncpy(name, namestart, head - namestart); + memcpy(name, namestart, head - namestart); } // Parse token value skip_spaces(&head); @@ -703,7 +710,7 @@ struct criteria *criteria_parse(char *raw, char **error_arg) { } } value = calloc(head - valuestart + 1, 1); - strncpy(value, valuestart, head - valuestart); + memcpy(value, valuestart, head - valuestart); if (in_quotes) { ++head; in_quotes = false; @@ -734,7 +741,7 @@ struct criteria *criteria_parse(char *raw, char **error_arg) { ++head; int len = head - raw; criteria->raw = calloc(len + 1, 1); - strncpy(criteria->raw, raw, len); + memcpy(criteria->raw, raw, len); return criteria; cleanup: diff --git a/sway/desktop/desktop.c b/sway/desktop/desktop.c index ec45d80a..c8d4502c 100644 --- a/sway/desktop/desktop.c +++ b/sway/desktop/desktop.c @@ -6,10 +6,11 @@ void desktop_damage_surface(struct wlr_surface *surface, double lx, double ly, bool whole) { for (int i = 0; i < root->outputs->length; ++i) { struct sway_output *output = root->outputs->items[i]; - struct wlr_box *output_box = wlr_output_layout_get_box( - root->output_layout, output->wlr_output); - output_damage_surface(output, lx - output_box->x, - ly - output_box->y, surface, whole); + struct wlr_box output_box; + wlr_output_layout_get_box(root->output_layout, + output->wlr_output, &output_box); + output_damage_surface(output, lx - output_box.x, + ly - output_box.y, surface, whole); } } diff --git a/sway/desktop/fx_renderer.c b/sway/desktop/fx_renderer.c index 3b4279fe..782ceb88 100644 --- a/sway/desktop/fx_renderer.c +++ b/sway/desktop/fx_renderer.c @@ -35,6 +35,71 @@ static const GLfloat verts[] = { 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) { GLuint shader = glCreateShader(type); glShaderSource(shader, 1, &src, NULL); @@ -156,7 +221,8 @@ struct fx_renderer *fx_renderer_create(struct wlr_egl *egl) { // TODO: wlr_egl_make_current or eglMakeCurrent? // 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"); return NULL; } @@ -242,7 +308,11 @@ struct fx_renderer *fx_renderer_create(struct wlr_egl *egl) { 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"); return renderer; @@ -257,7 +327,10 @@ error: glDeleteProgram(renderer->shaders.tex_rgbx.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? free(renderer); @@ -270,7 +343,7 @@ void fx_renderer_begin(struct fx_renderer *renderer, uint32_t width, uint32_t he glViewport(0, 0, width, height); // refresh projection matrix - wlr_matrix_projection(renderer->projection, width, height, + matrix_projection(renderer->projection, width, height, WL_OUTPUT_TRANSFORM_FLIPPED_180); glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); diff --git a/sway/desktop/idle_inhibit_v1.c b/sway/desktop/idle_inhibit_v1.c index 82353038..3a4d0b87 100644 --- a/sway/desktop/idle_inhibit_v1.c +++ b/sway/desktop/idle_inhibit_v1.c @@ -1,5 +1,6 @@ #include #include +#include #include "log.h" #include "sway/desktop/idle_inhibit_v1.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_notifier_v1_set_inhibited(server.idle_notifier_v1, inhibited); } struct sway_idle_inhibit_manager_v1 *sway_idle_inhibit_manager_v1_create( diff --git a/sway/desktop/launcher.c b/sway/desktop/launcher.c new file mode 100644 index 00000000..48e5d24c --- /dev/null +++ b/sway/desktop/launcher.c @@ -0,0 +1,211 @@ +#define _POSIX_C_SOURCE 200809L +#include +#include +#include +#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; +} diff --git a/sway/desktop/layer_shell.c b/sway/desktop/layer_shell.c index 27e457f1..6e3cc0e2 100644 --- a/sway/desktop/layer_shell.c +++ b/sway/desktop/layer_shell.c @@ -3,8 +3,8 @@ #include #include #include -#include #include +#include #include "log.h" #include "sway/desktop/transaction.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); 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) { struct sway_layer_surface *layer = 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); } @@ -291,10 +286,7 @@ static void handle_surface_commit(struct wl_listener *listener, void *data) { wl_container_of(listener, layer, surface_commit); struct wlr_layer_surface_v1 *layer_surface = layer->layer_surface; struct wlr_output *wlr_output = layer_surface->output; - if (wlr_output == NULL) { - return; - } - + sway_assert(wlr_output, "wlr_layer_surface_v1 has null output"); struct sway_output *output = wlr_output->data; struct wlr_box old_extent = layer->extent; @@ -341,13 +333,8 @@ static void unmap(struct sway_layer_surface *sway_layer) { cursor_rebase_all(); struct wlr_output *wlr_output = sway_layer->layer_surface->output; - if (wlr_output == NULL) { - return; - } + sway_assert(wlr_output, "wlr_layer_surface_v1 has null output"); struct sway_output *output = wlr_output->data; - if (output == NULL) { - return; - } output_damage_surface(output, sway_layer->geo.x, sway_layer->geo.y, 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->new_popup.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; - if (output != NULL) { - arrange_layers(output); - transaction_commit_dirty(); - } - wl_list_remove(&sway_layer->output_destroy.link); - sway_layer->layer_surface->output = NULL; - } + + 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; + arrange_layers(output); + transaction_commit_dirty(); + wl_list_remove(&sway_layer->output_destroy.link); + sway_layer->layer_surface->output = NULL; + free(sway_layer); } static void handle_map(struct wl_listener *listener, void *data) { struct sway_layer_surface *sway_layer = wl_container_of(listener, 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, sway_layer->layer_surface->surface, true); wlr_surface_send_enter(sway_layer->layer_surface->surface, @@ -408,9 +397,7 @@ static void subsurface_damage(struct sway_layer_subsurface *subsurface, bool whole) { struct sway_layer_surface *layer = subsurface->layer_surface; struct wlr_output *wlr_output = layer->layer_surface->output; - if (!wlr_output) { - return; - } + sway_assert(wlr_output, "wlr_layer_surface_v1 has null output"); struct sway_output *output = wlr_output->data; int ox = subsurface->wlr_subsurface->current.x + layer->geo.x; 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) { struct wlr_xdg_popup *popup = layer_popup->wlr_popup; struct wlr_surface *surface = popup->base->surface; - int popup_sx = popup->geometry.x - popup->base->current.geometry.x; - int popup_sy = popup->geometry.y - popup->base->current.geometry.y; + int popup_sx = popup->current.geometry.x - popup->base->current.geometry.x; + int popup_sy = popup->current.geometry.y - popup->base->current.geometry.y; int ox = popup_sx, oy = popup_sy; struct sway_layer_surface *layer; while (true) { if (layer_popup->parent_type == LAYER_PARENT_POPUP) { layer_popup = layer_popup->parent_popup; - ox += layer_popup->wlr_popup->geometry.x; - oy += layer_popup->wlr_popup->geometry.y; + ox += layer_popup->wlr_popup->current.geometry.x; + oy += layer_popup->wlr_popup->current.geometry.y; } else { layer = layer_popup->parent_layer; 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; + sway_assert(wlr_output, "wlr_layer_surface_v1 has null output"); struct sway_output *output = wlr_output->data; 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_surface *layer = popup_get_layer(popup); 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); 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 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 // of the popup @@ -642,6 +633,10 @@ void handle_layer_shell_surface(struct wl_listener *listener, void *data) { sway_log(SWAY_ERROR, "no output to auto-assign layer surface '%s' to", 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); return; } diff --git a/sway/desktop/output.c b/sway/desktop/output.c index b6d19dd6..182ca428 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -10,11 +10,10 @@ #include #include #include -#include #include #include #include -#include +#include #include #include "config.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, 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)) { goto overlay; } @@ -435,6 +453,10 @@ static bool scan_out_fullscreen_view(struct sway_output *output, return false; } + if (server.session_lock.locked) { + return false; + } + if (!wl_list_empty(&view->saved_buffers)) { return false; } @@ -533,31 +555,43 @@ static int output_repaint_timer_handler(void *data) { } } - bool needs_frame; - pixman_region32_t damage; - pixman_region32_init(&damage); - if (!wlr_output_damage_attach_render(output->damage, - &needs_frame, &damage)) { + int buffer_age; + if (!wlr_output_attach_render(output->wlr_output, &buffer_age)) { return 0; } - if (needs_frame) { - struct timespec now; - clock_gettime(CLOCK_MONOTONIC, &now); - - output_render(output, &now, &damage); - } else { + pixman_region32_t damage; + pixman_region32_init(&damage); + wlr_damage_ring_get_buffer_damage(&output->damage_ring, buffer_age, &damage); + if (!output->wlr_output->needs_frame && + !pixman_region32_not_empty(&output->damage_ring.current)) { + pixman_region32_fini(&damage); 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); 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 = - 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) { return; } @@ -620,11 +654,18 @@ static void damage_handle_frame(struct wl_listener *listener, void *user_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) { // The output can exist with no wlr_output if it's just been disconnected // and the transaction to evacuate it has't completed yet. - if (output && output->wlr_output && output->damage) { - wlr_output_damage_add_whole(output->damage); + if (output != NULL && output->wlr_output != NULL) { + 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); } 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); 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)) { @@ -682,7 +727,9 @@ void output_damage_box(struct sway_output *output, struct wlr_box *_box) { box.x -= output->lx; box.y -= output->ly; 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, @@ -706,7 +753,9 @@ void output_damage_whole_container(struct sway_output *output, .height = con->current.height + 2, }; 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 if (con->view) { damage_child_views_iterator(con, output); @@ -715,20 +764,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) { struct wlr_output_configuration_v1 *config = wlr_output_configuration_v1_create(); @@ -740,14 +775,15 @@ static void update_output_manager_config(struct sway_server *server) { } struct wlr_output_configuration_head_v1 *config_head = wlr_output_configuration_head_v1_create(config, output->wlr_output); - struct wlr_box *output_box = wlr_output_layout_get_box( - root->output_layout, output->wlr_output); - // We mark the output enabled even if it is switched off by DPMS + struct wlr_box output_box; + wlr_output_layout_get_box(root->output_layout, + 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.mode = output->current_mode; - if (output_box) { - config_head->state.x = output_box->x; - config_head->state.y = output_box->y; + if (!wlr_box_empty(&output_box)) { + config_head->state.x = output_box.x; + config_head->state.y = output_box.y; } } @@ -757,18 +793,24 @@ static void update_output_manager_config(struct sway_server *server) { static void handle_destroy(struct wl_listener *listener, void *data) { struct sway_output *output = wl_container_of(listener, output, destroy); struct sway_server *server = output->server; - output_begin_destroy(output); if (output->enabled) { output_disable(output); } + output_begin_destroy(output); + wl_list_remove(&output->link); wl_list_remove(&output->destroy.link); wl_list_remove(&output->commit.link); wl_list_remove(&output->mode.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 = NULL; @@ -796,10 +838,16 @@ static void handle_mode(struct wl_listener *listener, void *data) { if (!output->enabled) { return; } + arrange_layers(output); arrange_output(output); 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); } @@ -827,6 +875,13 @@ static void handle_commit(struct wl_listener *listener, void *data) { update_output_manager_config(output->server); } + + if (event->committed & (WLR_OUTPUT_STATE_MODE | WLR_OUTPUT_STATE_TRANSFORM)) { + 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) { @@ -862,10 +917,12 @@ void handle_new_output(struct wl_listener *listener, void *data) { if (wlr_output->non_desktop) { 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) { wlr_drm_lease_v1_manager_offer_output(server->drm_lease_manager, wlr_output); } + list_add(root->non_desktop_outputs, non_desktop); return; } @@ -880,7 +937,7 @@ void handle_new_output(struct wl_listener *listener, void *data) { return; } 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); output->destroy.notify = handle_destroy; @@ -890,10 +947,12 @@ void handle_new_output(struct wl_listener *listener, void *data) { output->mode.notify = handle_mode; wl_signal_add(&wlr_output->events.present, &output->present); output->present.notify = handle_present; - wl_signal_add(&output->damage->events.frame, &output->damage_frame); - output->damage_frame.notify = damage_handle_frame; - wl_signal_add(&output->damage->events.destroy, &output->damage_destroy); - output->damage_destroy.notify = damage_handle_destroy; + wl_signal_add(&wlr_output->events.damage, &output->damage); + output->damage.notify = handle_damage; + wl_signal_add(&wlr_output->events.frame, &output->frame); + 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_handler, output); @@ -1007,10 +1066,10 @@ void handle_output_power_manager_set_mode(struct wl_listener *listener, struct output_config *oc = new_output_config(output->wlr_output->name); switch (event->mode) { case ZWLR_OUTPUT_POWER_V1_MODE_OFF: - oc->dpms_state = DPMS_OFF; + oc->power = 0; break; case ZWLR_OUTPUT_POWER_V1_MODE_ON: - oc->dpms_state = DPMS_ON; + oc->power = 1; break; } oc = store_output_config(oc); diff --git a/sway/desktop/render.c b/sway/desktop/render.c index 6247d937..35e2150e 100644 --- a/sway/desktop/render.c +++ b/sway/desktop/render.c @@ -9,11 +9,11 @@ #include #include #include +#include #include -#include #include #include -#include +#include #include #include "log.h" #include "config.h" @@ -1301,6 +1301,41 @@ void output_render(struct sway_output *output, struct timespec *when, 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)) { goto render_overlay; } @@ -1409,7 +1444,7 @@ renderer_end: enum wl_output_transform 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); if (debug.damage != DAMAGE_DEFAULT) { @@ -1423,5 +1458,7 @@ renderer_end: if (!wlr_output_commit(wlr_output)) { return; } + + wlr_damage_ring_rotate(&output->damage_ring); output->last_frame = *when; } diff --git a/sway/desktop/surface.c b/sway/desktop/surface.c index 767b2045..1d7b536d 100644 --- a/sway/desktop/surface.c +++ b/sway/desktop/surface.c @@ -1,7 +1,7 @@ #define _POSIX_C_SOURCE 200112L #include #include -#include +#include #include "sway/server.h" #include "sway/surface.h" diff --git a/sway/desktop/xdg_shell.c b/sway/desktop/xdg_shell.c index 5fae8296..8da922d5 100644 --- a/sway/desktop/xdg_shell.c +++ b/sway/desktop/xdg_shell.c @@ -24,11 +24,11 @@ static const struct sway_view_child_impl popup_impl; static void popup_get_view_coords(struct sway_view_child *child, int *sx, int *sy) { 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, - surface->popup->geometry.x - surface->current.geometry.x, - surface->popup->geometry.y - surface->current.geometry.y, + wlr_xdg_popup_get_toplevel_coords(wlr_popup, + wlr_popup->current.geometry.x - wlr_popup->base->current.geometry.x, + wlr_popup->current.geometry.y - wlr_popup->base->current.geometry.y, 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) { 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; @@ -91,7 +91,7 @@ static struct sway_xdg_popup *popup_create( return NULL; } 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); 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, double *max_width, double *min_height, double *max_height) { 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; *max_width = state->max_width > 0 ? state->max_width : DBL_MAX; *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) { case VIEW_PROP_TITLE: - return view->wlr_xdg_surface->toplevel->title; + return view->wlr_xdg_toplevel->title; case VIEW_PROP_APP_ID: - return view->wlr_xdg_surface->toplevel->app_id; + return view->wlr_xdg_toplevel->app_id; default: return NULL; } @@ -148,50 +148,45 @@ static uint32_t configure(struct sway_view *view, double lx, double ly, if (xdg_shell_view == NULL) { 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) { if (xdg_shell_view_from_view(view) == NULL) { return; } - struct wlr_xdg_surface *surface = view->wlr_xdg_surface; - if (surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL) { - wlr_xdg_toplevel_set_activated(surface, activated); - } + wlr_xdg_toplevel_set_activated(view->wlr_xdg_toplevel, activated); } static void set_tiled(struct sway_view *view, bool tiled) { if (xdg_shell_view_from_view(view) == NULL) { return; } - struct wlr_xdg_surface *surface = view->wlr_xdg_surface; enum wlr_edges edges = WLR_EDGE_NONE; if (tiled) { edges = WLR_EDGE_LEFT | WLR_EDGE_RIGHT | WLR_EDGE_TOP | 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) { if (xdg_shell_view_from_view(view) == NULL) { return; } - struct wlr_xdg_surface *surface = view->wlr_xdg_surface; - wlr_xdg_toplevel_set_fullscreen(surface, fullscreen); + wlr_xdg_toplevel_set_fullscreen(view->wlr_xdg_toplevel, fullscreen); } static void set_resizing(struct sway_view *view, bool resizing) { if (xdg_shell_view_from_view(view) == NULL) { return; } - struct wlr_xdg_surface *surface = view->wlr_xdg_surface; - wlr_xdg_toplevel_set_resizing(surface, resizing); + wlr_xdg_toplevel_set_resizing(view->wlr_xdg_toplevel, resizing); } 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; return (state->min_width != 0 && state->min_height != 0 && (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) { 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); } @@ -213,8 +208,8 @@ static void for_each_popup_surface(struct sway_view *view, if (xdg_shell_view_from_view(view) == NULL) { return; } - wlr_xdg_surface_for_each_popup_surface(view->wlr_xdg_surface, iterator, - user_data); + wlr_xdg_surface_for_each_popup_surface(view->wlr_xdg_toplevel->base, + iterator, user_data); } 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) { return false; } - struct wlr_xdg_surface *surface = child->wlr_xdg_surface; - while (surface && surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL) { - if (surface->toplevel->parent == ancestor->wlr_xdg_surface) { + struct wlr_xdg_toplevel *toplevel = child->wlr_xdg_toplevel; + while (toplevel) { + if (toplevel->parent == ancestor->wlr_xdg_toplevel) { return true; } - surface = surface->toplevel->parent; + toplevel = toplevel->parent; } return false; } @@ -236,17 +231,13 @@ static void _close(struct sway_view *view) { if (xdg_shell_view_from_view(view) == NULL) { return; } - struct wlr_xdg_surface *surface = view->wlr_xdg_surface; - if (surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL - && surface->toplevel) { - wlr_xdg_toplevel_send_close(surface); - } + wlr_xdg_toplevel_send_close(view->wlr_xdg_toplevel); } static void close_popups(struct sway_view *view) { struct wlr_xdg_popup *popup, *tmp; - wl_list_for_each_safe(popup, tmp, &view->wlr_xdg_surface->popups, link) { - wlr_xdg_popup_destroy(popup->base); + wl_list_for_each_safe(popup, tmp, &view->wlr_xdg_toplevel->base->popups, link) { + 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 = wl_container_of(listener, xdg_shell_view, commit); 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; 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); } +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) { struct sway_xdg_shell_view *xdg_shell_view = wl_container_of(listener, xdg_shell_view, request_fullscreen); - struct wlr_xdg_toplevel_set_fullscreen_event *e = data; - struct wlr_xdg_surface *xdg_surface = - xdg_shell_view->view.wlr_xdg_surface; + struct wlr_xdg_toplevel *toplevel = xdg_shell_view->view.wlr_xdg_toplevel; struct sway_view *view = &xdg_shell_view->view; - if (!sway_assert(xdg_surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL, - "xdg_shell requested fullscreen of surface with role %i", - xdg_surface->role)) { - return; - } - if (!xdg_surface->mapped) { + if (!toplevel->base->mapped) { return; } struct sway_container *container = view->container; - if (e->fullscreen && e->output && e->output->data) { - struct sway_output *output = e->output->data; + struct wlr_xdg_toplevel_requested *req = &toplevel->requested; + 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); if (ws && !container_is_scratchpad_hidden(container) && 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(); 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 = wl_container_of(listener, xdg_shell_view, request_move); 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; } 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->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_move.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 = wl_container_of(listener, xdg_shell_view, map); 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_height = view->wlr_xdg_surface->current.geometry.height; + view->natural_width = toplevel->base->current.geometry.width; + view->natural_height = toplevel->base->current.geometry.height; if (!view->natural_width && !view->natural_height) { - view->natural_width = view->wlr_xdg_surface->surface->current.width; - view->natural_height = view->wlr_xdg_surface->surface->current.height; + view->natural_width = toplevel->base->surface->current.width; + view->natural_height = toplevel->base->surface->current.height; } 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; } else { struct sway_server_decoration *deco = - decoration_from_surface(xdg_surface->surface); + decoration_from_surface(toplevel->base->surface); csd = !deco || deco->wlr_server_decoration->mode == WLR_SERVER_DECORATION_MANAGER_MODE_CLIENT; } - view_map(view, view->wlr_xdg_surface->surface, - xdg_surface->toplevel->requested.fullscreen, - xdg_surface->toplevel->requested.fullscreen_output, + view_map(view, toplevel->base->surface, + toplevel->requested.fullscreen, + toplevel->requested.fullscreen_output, csd); transaction_commit_dirty(); 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->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->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; - 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_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_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->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_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); } @@ -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->map.link); wl_list_remove(&xdg_shell_view->unmap.link); - view->wlr_xdg_surface = NULL; + view->wlr_xdg_toplevel = NULL; if (view->xdg_decoration) { 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); - 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; wl_signal_add(&xdg_surface->events.map, &xdg_shell_view->map); diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c index 40288f97..e15a3341 100644 --- a/sway/desktop/xwayland.c +++ b/sway/desktop/xwayland.c @@ -5,7 +5,9 @@ #include #include #include +#include #include +#include #include "log.h" #include "sway/desktop.h" #include "sway/desktop/transaction.h" @@ -15,6 +17,7 @@ #include "sway/output.h" #include "sway/tree/arrange.h" #include "sway/tree/container.h" +#include "sway/server.h" #include "sway/tree/view.h" #include "sway/tree/workspace.h" @@ -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) { struct sway_xwayland_unmanaged *surface = 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->destroy.link); wl_list_remove(&surface->override_redirect.link); + wl_list_remove(&surface->request_activate.link); free(surface); } @@ -176,6 +194,8 @@ static struct sway_xwayland_unmanaged *create_unmanaged( surface->destroy.notify = unmanaged_handle_destroy; wl_signal_add(&xsurface->events.set_override_redirect, &surface->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; } @@ -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 && size_hints->min_width > 0 && size_hints->min_height > 0 && (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, double *max_width, double *min_height, double *max_height) { 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) { *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_class.link); wl_list_remove(&xwayland_view->set_role.link); + wl_list_remove(&xwayland_view->set_startup_id.link); wl_list_remove(&xwayland_view->set_window_type.link); wl_list_remove(&xwayland_view->set_hints.link); wl_list_remove(&xwayland_view->set_decorations.link); @@ -577,7 +598,8 @@ static void handle_request_move(struct wl_listener *listener, void *data) { if (!xsurface->mapped) { return; } - if (!container_is_floating(view->container)) { + if (!container_is_floating(view->container) || + view->container->pending.fullscreen_mode) { return; } 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); } +static void handle_set_startup_id(struct wl_listener *listener, void *data) { + struct sway_xwayland_view *xwayland_view = + wl_container_of(listener, xwayland_view, set_startup_id); + struct sway_view *view = &xwayland_view->view; + struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; + if (xsurface->startup_id == NULL) { + return; + } + + struct wlr_xdg_activation_token_v1 *token = + wlr_xdg_activation_v1_find_token( + server.xdg_activation_v1, xsurface->startup_id); + if (token == NULL) { + // Tried to activate with an unknown or expired token + return; + } + + struct launcher_ctx *ctx = token->data; + if (token->data == NULL) { + // TODO: support external launchers in X + return; + } + view_assign_ctx(view, ctx); +} + static void handle_set_window_type(struct wl_listener *listener, void *data) { struct sway_xwayland_view *xwayland_view = wl_container_of(listener, xwayland_view, set_window_type); @@ -666,14 +713,15 @@ static void handle_set_hints(struct wl_listener *listener, void *data) { if (!xsurface->mapped) { 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 // unset urgency so that the view remains urgent until the timer clears // it. return; } 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); xwayland_view->set_role.notify = handle_set_role; + wl_signal_add(&xsurface->events.set_startup_id, + &xwayland_view->set_startup_id); + xwayland_view->set_startup_id.notify = handle_set_startup_id; + wl_signal_add(&xsurface->events.set_window_type, &xwayland_view->set_window_type); xwayland_view->set_window_type.notify = handle_set_window_type; diff --git a/sway/input/cursor.c b/sway/input/cursor.c index 6fddee90..449aa430 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c @@ -386,28 +386,29 @@ static void pointer_motion(struct sway_cursor *cursor, uint32_t time_msec, static void handle_pointer_motion_relative( struct wl_listener *listener, void *data) { struct sway_cursor *cursor = wl_container_of(listener, cursor, motion); - struct wlr_event_pointer_motion *e = data; - cursor_handle_activity_from_device(cursor, e->device); + struct wlr_pointer_motion_event *e = data; + cursor_handle_activity_from_device(cursor, &e->pointer->base); - pointer_motion(cursor, e->time_msec, e->device, e->delta_x, e->delta_y, - e->unaccel_dx, e->unaccel_dy); + pointer_motion(cursor, e->time_msec, &e->pointer->base, e->delta_x, + e->delta_y, e->unaccel_dx, e->unaccel_dy); } static void handle_pointer_motion_absolute( struct wl_listener *listener, void *data) { struct sway_cursor *cursor = wl_container_of(listener, cursor, motion_absolute); - struct wlr_event_pointer_motion_absolute *event = data; - cursor_handle_activity_from_device(cursor, event->device); + struct wlr_pointer_motion_absolute_event *event = data; + cursor_handle_activity_from_device(cursor, &event->pointer->base); 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); double dx = lx - cursor->cursor->x; 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, @@ -422,7 +423,7 @@ void dispatch_cursor_button(struct sway_cursor *cursor, static void handle_pointer_button(struct wl_listener *listener, void *data) { 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) { 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); - dispatch_cursor_button(cursor, event->device, + cursor_handle_activity_from_device(cursor, &event->pointer->base); + dispatch_cursor_button(cursor, &event->pointer->base, event->time_msec, event->button, event->state); } 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); } static void handle_pointer_axis(struct wl_listener *listener, void *data) { struct sway_cursor *cursor = wl_container_of(listener, cursor, axis); - struct wlr_event_pointer_axis *event = data; - cursor_handle_activity_from_device(cursor, event->device); + struct wlr_pointer_axis_event *event = data; + cursor_handle_activity_from_device(cursor, &event->pointer->base); 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) { struct sway_cursor *cursor = wl_container_of(listener, cursor, touch_down); - struct wlr_event_touch_down *event = data; - cursor_handle_activity_from_device(cursor, event->device); + struct wlr_touch_down_event *event = data; + cursor_handle_activity_from_device(cursor, &event->touch->base); cursor_hide(cursor); 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; 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); double 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; dx = lx - cursor->cursor->x; dy = ly - cursor->cursor->y; - pointer_motion(cursor, event->time_msec, event->device, dx, dy, dx, dy); - dispatch_cursor_button(cursor, event->device, event->time_msec, + pointer_motion(cursor, event->time_msec, &event->touch->base, dx, dy, + dx, dy); + dispatch_cursor_button(cursor, &event->touch->base, event->time_msec, BTN_LEFT, WLR_BUTTON_PRESSED); } } static void handle_touch_up(struct wl_listener *listener, void *data) { struct sway_cursor *cursor = wl_container_of(listener, cursor, touch_up); - struct wlr_event_touch_up *event = data; - cursor_handle_activity_from_device(cursor, event->device); + struct wlr_touch_up_event *event = data; + cursor_handle_activity_from_device(cursor, &event->touch->base); struct wlr_seat *wlr_seat = cursor->seat->wlr_seat; if (cursor->simulating_pointer_from_touch) { if (cursor->pointer_touch_id == cursor->seat->touch_id) { cursor->pointer_touch_up = true; - dispatch_cursor_button(cursor, event->device, event->time_msec, - BTN_LEFT, WLR_BUTTON_RELEASED); + dispatch_cursor_button(cursor, &event->touch->base, + event->time_msec, BTN_LEFT, WLR_BUTTON_RELEASED); } } else { 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) { struct sway_cursor *cursor = wl_container_of(listener, cursor, touch_motion); - struct wlr_event_touch_motion *event = data; - cursor_handle_activity_from_device(cursor, event->device); + struct wlr_touch_motion_event *event = data; + cursor_handle_activity_from_device(cursor, &event->touch->base); struct sway_seat *seat = cursor->seat; struct wlr_seat *wlr_seat = seat->wlr_seat; struct wlr_surface *surface = NULL; 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); double 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; dx = lx - cursor->cursor->x; 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) { 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 y1 = region->y1, y2 = region->y2; - if (region->mm) { - if (device->width_mm == 0 || device->height_mm == 0) { + if (region->mm && device->type == WLR_INPUT_DEVICE_TABLET_TOOL) { + struct wlr_tablet *tablet = wlr_tablet_from_input_device(device); + if (tablet->width_mm == 0 || tablet->height_mm == 0) { return; } - x1 /= device->width_mm; - x2 /= device->width_mm; - y1 /= device->height_mm; - y2 /= device->height_mm; + x1 /= tablet->width_mm; + x2 /= tablet->width_mm; + y1 /= tablet->height_mm; + y2 /= tablet->height_mm; } *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) { struct sway_cursor *cursor = wl_container_of(listener, cursor, tool_axis); - struct wlr_event_tablet_tool_axis *event = data; - cursor_handle_activity_from_device(cursor, event->device); + struct wlr_tablet_tool_axis_event *event = data; + cursor_handle_activity_from_device(cursor, &event->tablet->base); struct sway_tablet_tool *sway_tool = event->tool->data; 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) { struct sway_cursor *cursor = wl_container_of(listener, cursor, tool_tip); - struct wlr_event_tablet_tool_tip *event = data; - cursor_handle_activity_from_device(cursor, event->device); + struct wlr_tablet_tool_tip_event *event = data; + cursor_handle_activity_from_device(cursor, &event->tablet->base); struct sway_tablet_tool *sway_tool = event->tool->data; 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 && event->state == WLR_TABLET_TOOL_TIP_UP) { 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); wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat); } 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); } else { cursor->simulating_pointer_from_tool_tip = true; - dispatch_cursor_button(cursor, event->device, event->time_msec, - BTN_LEFT, WLR_BUTTON_PRESSED); + dispatch_cursor_button(cursor, &event->tablet->base, + event->time_msec, BTN_LEFT, WLR_BUTTON_PRESSED); wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat); } } 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) { struct sway_cursor *cursor = wl_container_of(listener, cursor, tool_proximity); - struct wlr_event_tablet_tool_proximity *event = data; - cursor_handle_activity_from_device(cursor, event->device); + struct wlr_tablet_tool_proximity_event *event = data; + cursor_handle_activity_from_device(cursor, &event->tablet->base); struct wlr_tablet_tool *tool = event->tool; 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) { sway_log(SWAY_ERROR, "no tablet for tablet tool"); 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) { struct sway_cursor *cursor = wl_container_of(listener, cursor, tool_button); - struct wlr_event_tablet_tool_button *event = data; - cursor_handle_activity_from_device(cursor, event->device); + struct wlr_tablet_tool_button_event *event = data; + cursor_handle_activity_from_device(cursor, &event->tablet->base); struct sway_tablet_tool *sway_tool = event->tool->data; if (!sway_tool) { @@ -819,14 +824,14 @@ static void handle_tool_button(struct wl_listener *listener, void *data) { switch (event->state) { case WLR_BUTTON_PRESSED: 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); } cursor->tool_buttons++; break; case WLR_BUTTON_RELEASED: 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); } cursor->tool_buttons--; @@ -923,65 +928,68 @@ static void handle_request_pointer_set_cursor(struct wl_listener *listener, 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) { struct sway_cursor *cursor = wl_container_of( listener, cursor, pinch_begin); - struct wlr_event_pointer_pinch_begin *event = data; - cursor_handle_activity_from_device(cursor, event->device); - wlr_pointer_gestures_v1_send_pinch_begin( - cursor->pointer_gestures, cursor->seat->wlr_seat, - event->time_msec, event->fingers); + struct wlr_pointer_pinch_begin_event *event = data; + cursor_handle_activity_from_device(cursor, &event->pointer->base); + seatop_pinch_begin(cursor->seat, event); } static void handle_pointer_pinch_update(struct wl_listener *listener, void *data) { struct sway_cursor *cursor = wl_container_of( listener, cursor, pinch_update); - struct wlr_event_pointer_pinch_update *event = data; - cursor_handle_activity_from_device(cursor, event->device); - 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); + struct wlr_pointer_pinch_update_event *event = data; + cursor_handle_activity_from_device(cursor, &event->pointer->base); + seatop_pinch_update(cursor->seat, event); } static void handle_pointer_pinch_end(struct wl_listener *listener, void *data) { struct sway_cursor *cursor = wl_container_of( listener, cursor, pinch_end); - struct wlr_event_pointer_pinch_end *event = data; - cursor_handle_activity_from_device(cursor, event->device); - wlr_pointer_gestures_v1_send_pinch_end( - cursor->pointer_gestures, cursor->seat->wlr_seat, - event->time_msec, event->cancelled); + struct wlr_pointer_pinch_end_event *event = data; + cursor_handle_activity_from_device(cursor, &event->pointer->base); + seatop_pinch_end(cursor->seat, event); } static void handle_pointer_swipe_begin(struct wl_listener *listener, void *data) { struct sway_cursor *cursor = wl_container_of( listener, cursor, swipe_begin); - struct wlr_event_pointer_swipe_begin *event = data; - cursor_handle_activity_from_device(cursor, event->device); - wlr_pointer_gestures_v1_send_swipe_begin( - cursor->pointer_gestures, cursor->seat->wlr_seat, - event->time_msec, event->fingers); + struct wlr_pointer_swipe_begin_event *event = data; + cursor_handle_activity_from_device(cursor, &event->pointer->base); + seatop_swipe_begin(cursor->seat, event); } static void handle_pointer_swipe_update(struct wl_listener *listener, void *data) { struct sway_cursor *cursor = wl_container_of( listener, cursor, swipe_update); - struct wlr_event_pointer_swipe_update *event = data; - cursor_handle_activity_from_device(cursor, event->device); - wlr_pointer_gestures_v1_send_swipe_update( - cursor->pointer_gestures, cursor->seat->wlr_seat, - event->time_msec, event->dx, event->dy); + struct wlr_pointer_swipe_update_event *event = data; + cursor_handle_activity_from_device(cursor, &event->pointer->base); + seatop_swipe_update(cursor->seat, event); } static void handle_pointer_swipe_end(struct wl_listener *listener, void *data) { struct sway_cursor *cursor = wl_container_of( listener, cursor, swipe_end); - struct wlr_event_pointer_swipe_end *event = data; - cursor_handle_activity_from_device(cursor, event->device); - wlr_pointer_gestures_v1_send_swipe_end( - cursor->pointer_gestures, cursor->seat->wlr_seat, - event->time_msec, event->cancelled); + struct wlr_pointer_swipe_end_event *event = data; + cursor_handle_activity_from_device(cursor, &event->pointer->base); + seatop_swipe_end(cursor->seat, event); } 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_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_update.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); cursor->image_surface_destroy.notify = handle_image_surface_destroy; + // gesture events 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); - 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); - 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); - 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); - 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); - 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); + cursor->swipe_end.notify = handle_pointer_swipe_end; // input events 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; wl_signal_add(&constraint->events.destroy, &sway_constraint->destroy); - struct sway_node *focus = seat_get_focus(seat); - if (focus && node_is_view(focus)) { - struct wlr_surface *surface = focus->sway_container->view->surface; - if (surface == constraint->surface) { - sway_cursor_constrain(seat->cursor, constraint); - } + struct wlr_surface *surface = seat->wlr_seat->keyboard_state.focused_surface; + if (surface && surface == constraint->surface) { + sway_cursor_constrain(seat->cursor, constraint); } } diff --git a/sway/input/input-manager.c b/sway/input/input-manager.c index f04a8ce0..39f4b795 100644 --- a/sway/input/input-manager.c +++ b/sway/input/input-manager.c @@ -236,7 +236,7 @@ static void handle_new_input(struct wl_listener *listener, void *data) { 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); 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); + + if (config_changed) { + ipc_event_input("libinput_config", input_device); + } } 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( listener, input_manager, inhibit_deactivate); 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) { seat_set_exclusive_client(seat, NULL); 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 = wl_container_of(listener, input_manager, virtual_keyboard_new); 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 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); struct wlr_virtual_pointer_v1_new_pointer_event *event = data; 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 ? 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( 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; wl_list_for_each(seat, &server.input->seats, link) { seat_configure_device(seat, input_device); } + if (config_changed) { + ipc_event_input("libinput_config", input_device); + } } 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) { + // Set the active keyboard to NULL to avoid spamming configuration updates + // for all keyboard devices. + struct sway_seat *seat; + wl_list_for_each(seat, &server.input->seats, link) { + wlr_seat_set_keyboard(seat->wlr_seat, NULL); + } + struct sway_input_device *input_device = NULL; wl_list_for_each(input_device, &server.input->devices, link) { input_manager_reset_input(input_device); @@ -572,7 +590,6 @@ void input_manager_reset_all_inputs(void) { // If there is at least one keyboard using the default keymap, repeat delay, // and repeat rate, then it is possible that there is a keyboard group that // need their keyboard disarmed. - struct sway_seat *seat; wl_list_for_each(seat, &server.input->seats, link) { struct sway_keyboard_group *group; wl_list_for_each(group, &seat->keyboard_groups, link) { diff --git a/sway/input/keyboard.c b/sway/input/keyboard.c index f258ac7d..c5a646c4 100644 --- a/sway/input/keyboard.c +++ b/sway/input/keyboard.c @@ -291,14 +291,12 @@ static bool keyboard_execute_compositor_binding(struct sway_keyboard *keyboard, static size_t keyboard_keysyms_translated(struct sway_keyboard *keyboard, xkb_keycode_t keycode, const xkb_keysym_t **keysyms, uint32_t *modifiers) { - struct wlr_input_device *device = - keyboard->seat_device->input_device->wlr_device; - *modifiers = wlr_keyboard_get_modifiers(device->keyboard); + *modifiers = wlr_keyboard_get_modifiers(keyboard->wlr); 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; - return xkb_state_key_get_syms(device->keyboard->xkb_state, + return xkb_state_key_get_syms(keyboard->wlr->xkb_state, 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, xkb_keycode_t keycode, const xkb_keysym_t **keysyms, uint32_t *modifiers) { - struct wlr_input_device *device = - keyboard->seat_device->input_device->wlr_device; - *modifiers = wlr_keyboard_get_modifiers(device->keyboard); + *modifiers = wlr_keyboard_get_modifiers(keyboard->wlr); xkb_layout_index_t layout_index = xkb_state_key_get_layout( - device->keyboard->xkb_state, keycode); - return xkb_keymap_key_get_syms_by_level(device->keyboard->keymap, + keyboard->wlr->xkb_state, keycode); + return xkb_keymap_key_get_syms_by_level(keyboard->wlr->keymap, keycode, layout_index, 0, keysyms); } @@ -360,8 +356,7 @@ static void update_keyboard_state(struct sway_keyboard *keyboard, keyinfo->keycode, &keyinfo->translated_keysyms, &keyinfo->translated_modifiers); - keyinfo->code_modifiers = wlr_keyboard_get_modifiers( - keyboard->seat_device->input_device->wlr_device->keyboard); + keyinfo->code_modifiers = wlr_keyboard_get_modifiers(keyboard->wlr); // Update shortcut model keyinfo 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, - struct wlr_event_keyboard_key *event) { + struct wlr_keyboard_key_event *event) { struct sway_seat *seat = keyboard->seat_device->sway_seat; struct wlr_seat *wlr_seat = seat->wlr_seat; struct wlr_input_device *wlr_device = keyboard->seat_device->input_device->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); - 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 = keyboard_shortcuts_inhibitor_get_for_focused_surface(seat); 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 // binding may remove the keyboard, the timer needs to be updated first if (binding && !(binding->flags & BINDING_NOREPEAT) && - wlr_device->keyboard->repeat_info.delay > 0) { + keyboard->wlr->repeat_info.delay > 0) { keyboard->repeat_binding = binding; 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"); } } else if (keyboard->repeat_binding) { @@ -492,7 +488,7 @@ static void handle_key_event(struct sway_keyboard *keyboard, handled = true; } - if (!handled && wlr_device->keyboard->group) { + if (!handled && keyboard->wlr->group) { // Only handle device specific bindings for keyboards in a group free(device_identifier); return; @@ -517,7 +513,7 @@ static void handle_key_event(struct sway_keyboard *keyboard, &keyboard->state_pressed_sent, event->keycode, event->state, keyinfo.keycode, 0); 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, event->keycode, event->state); 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); if (kb_grab) { - wlr_input_method_keyboard_grab_v2_set_keyboard(kb_grab, - wlr_device->keyboard); + wlr_input_method_keyboard_grab_v2_set_keyboard(kb_grab, keyboard->wlr); wlr_input_method_keyboard_grab_v2_send_key(kb_grab, event->time_msec, event->keycode, event->state); handled = true; @@ -542,7 +537,7 @@ static void handle_key_event(struct sway_keyboard *keyboard, update_shortcut_state( &keyboard->state_pressed_sent, event->keycode, event->state, 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, 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) { - struct sway_keyboard *keyboard = (struct sway_keyboard *)data; - struct wlr_keyboard *wlr_device = - keyboard->seat_device->input_device->wlr_device->keyboard; + struct sway_keyboard *keyboard = data; 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 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"); } } @@ -658,31 +651,28 @@ static void determine_bar_visibility(uint32_t modifiers) { } static void handle_modifier_event(struct sway_keyboard *keyboard) { - struct wlr_input_device *wlr_device = - keyboard->seat_device->input_device->wlr_device; - if (!wlr_device->keyboard->group) { + if (!keyboard->wlr->group) { struct wlr_input_method_keyboard_grab_v2 *kb_grab = keyboard_get_im_grab(keyboard); if (kb_grab) { - wlr_input_method_keyboard_grab_v2_set_keyboard(kb_grab, - wlr_device->keyboard); + wlr_input_method_keyboard_grab_v2_set_keyboard(kb_grab, keyboard->wlr); wlr_input_method_keyboard_grab_v2_send_modifiers(kb_grab, - &wlr_device->keyboard->modifiers); + &keyboard->wlr->modifiers); } else { 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_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); } - if (wlr_device->keyboard->modifiers.group != keyboard->effective_layout) { - keyboard->effective_layout = wlr_device->keyboard->modifiers.group; + if (keyboard->wlr->modifiers.group != keyboard->effective_layout) { + 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); } } @@ -711,6 +701,7 @@ struct sway_keyboard *sway_keyboard_create(struct sway_seat *seat, } keyboard->seat_device = device; + keyboard->wlr = wlr_keyboard_from_input_device(device->input_device->wlr_device); device->keyboard = keyboard; 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) { 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 = wlr_keyboard->group; + struct wlr_keyboard_group *wlr_group = keyboard->wlr->group; sway_log(SWAY_DEBUG, "Removing keyboard %s from group %p", 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)) { 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) { - struct sway_input_device *device = keyboard->seat_device->input_device; - struct wlr_keyboard *wlr_keyboard = device->wlr_device->keyboard; - if (!wlr_keyboard->group) { + if (!keyboard->wlr->group) { return; } @@ -868,7 +856,7 @@ static void sway_keyboard_group_remove_invalid(struct sway_keyboard *keyboard) { break; case KEYBOARD_GROUP_DEFAULT: /* fallthrough */ 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) || !repeat_info_match(keyboard, &group->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) { 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 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)) { sway_log(SWAY_DEBUG, "Adding keyboard %s to group %p", device->identifier, wlr_group); - wlr_keyboard_group_add_keyboard(wlr_group, wlr_keyboard); + wlr_keyboard_group_add_keyboard(wlr_group, keyboard->wlr); return; } break; @@ -950,7 +937,7 @@ static void sway_keyboard_group_add(struct sway_keyboard *keyboard) { goto cleanup; } 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)) { 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", 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); @@ -991,10 +978,8 @@ cleanup: void sway_keyboard_configure(struct sway_keyboard *keyboard) { struct input_config *input_config = 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 " "keyboard group's keyboard")) { return; @@ -1036,11 +1021,11 @@ void sway_keyboard_configure(struct sway_keyboard *keyboard) { sway_keyboard_group_remove_invalid(keyboard); - wlr_keyboard_set_keymap(wlr_device->keyboard, keyboard->keymap); - wlr_keyboard_set_repeat_info(wlr_device->keyboard, + wlr_keyboard_set_keymap(keyboard->wlr, keyboard->keymap); + wlr_keyboard_set_repeat_info(keyboard->wlr, keyboard->repeat_rate, keyboard->repeat_delay); - if (!wlr_device->keyboard->group) { + if (!keyboard->wlr->group) { sway_keyboard_group_add(keyboard); } @@ -1060,40 +1045,42 @@ void sway_keyboard_configure(struct sway_keyboard *keyboard) { } } if (locked_mods) { - wlr_keyboard_notify_modifiers(wlr_device->keyboard, 0, 0, + wlr_keyboard_notify_modifiers(keyboard->wlr, 0, 0, locked_mods, 0); uint32_t leds = 0; for (uint32_t i = 0; i < WLR_LED_COUNT; ++i) { - if (xkb_state_led_index_is_active( - wlr_device->keyboard->xkb_state, - wlr_device->keyboard->led_indexes[i])) { + if (xkb_state_led_index_is_active(keyboard->wlr->xkb_state, + keyboard->wlr->led_indexes[i])) { leds |= (1 << i); } } - if (wlr_device->keyboard->group) { - wlr_keyboard_led_update( - &wlr_device->keyboard->group->keyboard, leds); + if (keyboard->wlr->group) { + wlr_keyboard_led_update(&keyboard->wlr->group->keyboard, leds); } else { - wlr_keyboard_led_update(wlr_device->keyboard, leds); + wlr_keyboard_led_update(keyboard->wlr, leds); } } } else { xkb_keymap_unref(keymap); sway_keyboard_group_remove_invalid(keyboard); - if (!wlr_device->keyboard->group) { + if (!keyboard->wlr->group) { 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; - 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_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; 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.notify = handle_keyboard_modifiers; @@ -1110,12 +1097,11 @@ void sway_keyboard_destroy(struct sway_keyboard *keyboard) { if (!keyboard) { return; } - if (keyboard->seat_device->input_device->wlr_device->keyboard->group) { + if (keyboard->wlr->group) { sway_keyboard_group_remove(keyboard); } 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) == device->wlr_device->keyboard) { + if (wlr_seat_get_keyboard(wlr_seat) == keyboard->wlr) { wlr_seat_set_keyboard(wlr_seat, NULL); } if (keyboard->keymap) { diff --git a/sway/input/libinput.c b/sway/input/libinput.c index 3c0f359d..53019301 100644 --- a/sway/input/libinput.c +++ b/sway/input/libinput.c @@ -166,6 +166,16 @@ static bool set_dwt(struct libinput_device *device, bool dwt) { 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]) { if (!libinput_device_config_calibration_has_matrix(dev)) { return false; @@ -187,10 +197,10 @@ static bool set_calibration_matrix(struct libinput_device *dev, float mat[6]) { 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); if (!ic || !wlr_input_device_is_libinput(input_device->wlr_device)) { - return; + return false; } 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) { changed |= set_dwt(device, ic->dwt); } + if (ic->dwtp != INT_MIN) { + changed |= set_dwtp(device, ic->dwtp); + } if (ic->calibration_matrix.configured) { changed |= set_calibration_matrix(device, ic->calibration_matrix.matrix); } - if (changed) { - ipc_event_input("libinput_config", input_device); - } + return changed; } 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)); changed |= set_dwt(device, libinput_device_config_dwt_get_default_enabled(device)); + changed |= set_dwtp(device, + libinput_device_config_dwtp_get_default_enabled(device)); float matrix[6]; libinput_device_config_calibration_get_default_matrix(device, matrix); diff --git a/sway/input/seat.c b/sway/input/seat.c index ce933b66..646f3866 100644 --- a/sway/input/seat.c +++ b/sway/input/seat.c @@ -7,10 +7,12 @@ #include #include #include +#include #include #include #include #include +#include #include #include "config.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_tablet_destroy(seat_device->tablet); 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, seat_device->input_device->wlr_device); wl_list_remove(&seat_device->link); @@ -110,6 +113,7 @@ void seat_idle_notify_activity(struct sway_seat *seat, } if ((source & mask) > 0) { 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) { continue; } - if (input_device->wlr_device->keyboard == wlr_keyboard) { + if (input_device->wlr_device == &wlr_keyboard->base) { 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) { struct sway_input_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; } } @@ -245,7 +249,7 @@ static void handle_seat_node_destroy(struct wl_listener *listener, void *data) { seat_node_destroy(seat_node); // If an unmanaged or layer surface is focused when an output gets // 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. if (seat->workspace == node->sway_workspace) { 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: return; case WLR_DRAG_GRAB_KEYBOARD_POINTER: - icon->x = cursor->x; - icon->y = cursor->y; + icon->x = cursor->x + wlr_icon->surface->sx; + icon->y = cursor->y + wlr_icon->surface->sy; break; case WLR_DRAG_GRAB_KEYBOARD_TOUCH:; struct wlr_touch_point *point = @@ -387,8 +391,8 @@ void drag_icon_update_position(struct sway_drag_icon *icon) { if (point == NULL) { return; } - icon->x = seat->touch_x; - icon->y = seat->touch_y; + icon->x = seat->touch_x + wlr_icon->surface->sx; + icon->y = seat->touch_y + wlr_icon->surface->sy; } 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; switch (mapped_to) { - case MAPPED_TO_DEFAULT: + case MAPPED_TO_DEFAULT:; /* * 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. */ - 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) && sway_libinput_device_is_builtin(sway_device->input_device)) { 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_configure(seat_device->keyboard); - wlr_seat_set_keyboard(seat->wlr_seat, - seat_device->input_device->wlr_device); - struct sway_node *focus = seat_get_focus(seat); - if (focus && node_is_view(focus)) { - // force notify reenter to pick up the new configuration + + // We only need to update the current keyboard, as the rest will be updated + // as they are activated. + struct wlr_keyboard *wlr_keyboard = + wlr_keyboard_from_input_device(seat_device->input_device->wlr_device); + struct wlr_keyboard *current_keyboard = seat->wlr_seat->keyboard_state.keyboard; + if (wlr_keyboard != current_keyboard) { + return; + } + + // force notify reenter to pick up the new configuration. This reuses + // the current focused surface to avoid breaking input grabs. + struct wlr_surface *surface = seat->wlr_seat->keyboard_state.focused_surface; + if (surface) { 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, struct wlr_surface *surface) { 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) { @@ -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) { - 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; - } - +static void seat_set_workspace_focus(struct sway_seat *seat, struct sway_node *node) { struct sway_node *last_focus = seat_get_focus(seat); if (last_focus == node) { 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, struct sway_container *con) { 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, - struct wlr_event_pointer_axis *event) { + struct wlr_pointer_axis_event *event) { if (seat->seatop_impl->pointer_axis) { 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) { if (seat->seatop_impl->rebase) { seat->seatop_impl->rebase(seat, time_msec); diff --git a/sway/input/seatop_default.c b/sway/input/seatop_default.c index 4320a3b4..84acefdf 100644 --- a/sway/input/seatop_default.c +++ b/sway/input/seatop_default.c @@ -4,6 +4,7 @@ #include #include #include +#include "gesture.h" #include "sway/desktop/transaction.h" #include "sway/input/cursor.h" #include "sway/input/seat.h" @@ -20,6 +21,7 @@ struct seatop_default_event { struct sway_node *previous_node; uint32_t pressed_buttons[SWAY_CURSOR_PRESSED_BUTTONS_CAP]; 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 if (cont && is_floating_or_child && !is_fullscreen_or_child && state == WLR_BUTTON_PRESSED) { uint32_t btn_move = config->floating_mod_inverse ? BTN_RIGHT : BTN_LEFT; 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)); return; } @@ -444,6 +464,7 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec, state == WLR_BUTTON_PRESSED) { // Via border if (button == BTN_LEFT && resize_edge != WLR_EDGE_NONE) { + seat_set_focus_container(seat, cont); seatop_begin_resize_floating(seat, cont, resize_edge); return; } @@ -458,6 +479,7 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec, WLR_EDGE_RIGHT : WLR_EDGE_LEFT; edge |= cursor->cursor->y > floater->pending.y + floater->pending.height / 2 ? WLR_EDGE_BOTTOM : WLR_EDGE_TOP; + seat_set_focus_container(seat, floater); seatop_begin_resize_floating(seat, floater, edge); 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) && state == WLR_BUTTON_PRESSED && !is_floating_or_child && cont && cont->pending.fullscreen_mode == FULLSCREEN_NONE) { - struct sway_container *focus = seat_get_focused_container(seat); - 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 moving a container by its title bar, use a threshold for the drag if (!mod_pressed && config->tiling_drag_threshold > 0) { seatop_begin_move_tiling_threshold(seat, cont); } else { seatop_begin_move_tiling(seat, cont); } + return; } // Handle mousedown on a container surface if (surface && cont && state == WLR_BUTTON_PRESSED) { - seat_set_focus_container(seat, cont); seatop_begin_down(seat, cont, time_msec, sx, sy); seat_pointer_notify_button(seat, time_msec, button, WLR_BUTTON_PRESSED); return; @@ -493,9 +508,6 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec, // Handle clicking a container surface or decorations 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); return; } @@ -645,7 +657,7 @@ static void handle_tablet_tool_motion(struct sway_seat *seat, * 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) { case WLR_AXIS_ORIENTATION_VERTICAL: 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, - struct wlr_event_pointer_axis *event) { + struct wlr_pointer_axis_event *event) { struct sway_input_device *input_device = - event->device ? event->device->data : NULL; + event->pointer ? event->pointer->base.data : NULL; struct input_config *ic = input_device ? input_device_get_config(input_device) : NULL; 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) if (!handled && (on_titlebar || on_titlebar_border)) { + struct sway_node *new_focus; enum sway_container_layout layout = container_parent_layout(cont); if (layout == L_TABBED || layout == L_STACKED) { 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); list_t *siblings = container_get_siblings(cont); 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) { desired = 0; } 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_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 // container the user scrolled on. - seat_set_focus(seat, new_focus); - transaction_commit_dirty(); - handled = true; + new_focus = seat_get_focus_inactive(seat, new_sibling); + } else { + 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 @@ -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(¤t->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(¤t->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 / *--------------------------------*/ @@ -779,6 +1092,14 @@ static const struct sway_seatop_impl seatop_impl = { .pointer_axis = handle_pointer_axis, .tablet_tool_tip = handle_tablet_tool_tip, .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, .allow_set_cursor = true, }; @@ -789,8 +1110,8 @@ void seatop_begin_default(struct sway_seat *seat) { struct seatop_default_event *e = calloc(1, sizeof(struct seatop_default_event)); sway_assert(e, "Unable to allocate seatop_default_event"); + seat->seatop_impl = &seatop_impl; seat->seatop_data = e; - seatop_rebase(seat, 0); } diff --git a/sway/input/seatop_down.c b/sway/input/seatop_down.c index ecc34fea..b40773d0 100644 --- a/sway/input/seatop_down.c +++ b/sway/input/seatop_down.c @@ -18,9 +18,9 @@ struct seatop_down_event { }; 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 = - event->device ? event->device->data : NULL; + event->pointer ? event->pointer->base.data : NULL; struct input_config *ic = input_device ? input_device_get_config(input_device) : NULL; float scroll_factor = diff --git a/sway/input/switch.c b/sway/input/switch.c index 9ea87a1a..fc7dfaff 100644 --- a/sway/input/switch.c +++ b/sway/input/switch.c @@ -11,6 +11,7 @@ struct sway_switch *sway_switch_create(struct sway_seat *seat, return NULL; } 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->state = WLR_SWITCH_STATE_OFF; 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; } +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) { 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; 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) { continue; } - if (binding->state != WLR_SWITCH_STATE_TOGGLE && - binding->state != sway_switch->state) { + if (!sway_switch_trigger_test(binding->trigger, sway_switch->state)) { continue; } - if (config->reloading && (binding->state == WLR_SWITCH_STATE_TOGGLE + if (config->reloading && (binding->trigger == SWAY_SWITCH_TRIGGER_TOGGLE || (binding->flags & BINDING_RELOAD) == 0)) { 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) { struct sway_switch *sway_switch = 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; 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) { - struct wlr_input_device *wlr_device = - sway_switch->seat_device->input_device->wlr_device; 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.notify = handle_switch_toggle; sway_log(SWAY_DEBUG, "Configured switch for device"); diff --git a/sway/input/tablet.c b/sway/input/tablet.c index 26e86e36..92ede3fa 100644 --- a/sway/input/tablet.c +++ b/sway/input/tablet.c @@ -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) { 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) { 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) { 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) { 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) { 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) { return; @@ -246,6 +246,7 @@ struct sway_tablet_pad *sway_tablet_pad_create(struct sway_seat *seat, return NULL; } + tablet_pad->wlr = wlr_tablet_pad_from_input_device(device->input_device->wlr_device); tablet_pad->seat_device = device; wl_list_init(&tablet_pad->attach.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) { - struct wlr_input_device *device = + struct wlr_input_device *wlr_device = tablet_pad->seat_device->input_device->wlr_device; struct sway_seat *seat = tablet_pad->seat_device->sway_seat; if (!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); 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); wl_list_remove(&tablet_pad->button.link); 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); 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); 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 */ - if (!wlr_input_device_is_libinput(device)) { + if (!wlr_input_device_is_libinput(wlr_device)) { /* We can only do this on libinput devices */ return; } 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; wl_list_for_each(tool, &seat->cursor->tablets, link) { struct wlr_input_device *tablet = diff --git a/sway/input/text_input.c b/sway/input/text_input.c index b8c19c17..58911c2d 100644 --- a/sway/input/text_input.c +++ b/sway/input/text_input.c @@ -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); wlr_input_method_keyboard_grab_v2_set_keyboard(keyboard_grab, active_keyboard); - wlr_input_method_keyboard_grab_v2_send_modifiers(keyboard_grab, - &active_keyboard->modifiers); wl_signal_add(&keyboard_grab->events.destroy, &relay->input_method_keyboard_grab_destroy); diff --git a/sway/ipc-json.c b/sway/ipc-json.c index 1b64f86e..d757f21f 100644 --- a/sway/ipc-json.c +++ b/sway/ipc-json.c @@ -112,12 +112,43 @@ static const char *ipc_json_output_adaptive_sync_status_description( return "disabled"; case WLR_OUTPUT_ADAPTIVE_SYNC_ENABLED: return "enabled"; - case WLR_OUTPUT_ADAPTIVE_SYNC_UNKNOWN: - return "unknown"; } 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 static const char *ipc_json_xwindow_type_description(struct sway_view *view) { 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; } +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, 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; + 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, "dpms", 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, "orientation", json_object_new_string( 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_new_double(wlr_output->scale)); 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(); 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 *mode_object = + ipc_json_output_mode_description(mode); json_object_array_add(modes_array, mode_object); } json_object_object_add(object, "modes", modes_array); - json_object *current_mode_object = json_object_new_object(); - 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 *current_mode_object; + if (wlr_output->current_mode != NULL) { + current_mode_object = + ipc_json_output_mode_description(wlr_output->current_mode); + } else { + current_mode_object = json_object_new_object(); + 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); 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(); + 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, "name", json_object_new_string(wlr_output->name)); 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, "primary", 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, "power", json_object_new_boolean(false)); 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; } +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) { struct wlr_box 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) { 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)) && c->current.border != B_NORMAL) || c->pending.fullscreen_mode != FULLSCREEN_NONE || @@ -706,7 +764,7 @@ json_object *ipc_json_describe_node(struct sway_node *node) { case N_ROOT: break; case N_OUTPUT: - ipc_json_describe_output(node->sway_output, object); + ipc_json_describe_enabled_output(node->sway_output, object); break; case N_CONTAINER: 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)); } + 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)) { float matrix[6]; 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))); 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_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_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) { struct input_config *ic = input_device_get_config(device); float scroll_factor = 1.0f; - if (ic != NULL && !isnan(ic->scroll_factor) && + if (ic != NULL && !isnan(ic->scroll_factor) && ic->scroll_factor != FLT_MIN) { 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)); } diff --git a/sway/ipc-server.c b/sway/ipc-server.c index 1bf5a05f..00b01d7d 100644 --- a/sway/ipc-server.c +++ b/sway/ipc-server.c @@ -150,7 +150,6 @@ struct sockaddr_un *ipc_user_sockaddr(void) { int ipc_handle_connection(int fd, uint32_t mask, void *data) { (void) fd; struct sway_server *server = data; - sway_log(SWAY_DEBUG, "Event on IPC listening socket"); assert(mask == WL_EVENT_READABLE); 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) { - sway_log(SWAY_DEBUG, "Client %d hung up", client->fd); ipc_client_disconnect(client); return 0; } - sway_log(SWAY_DEBUG, "Client %d readable", client->fd); - int read_available; if (ioctl(client_fd, FIONREAD, &read_available) == -1) { 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) { - sway_log(SWAY_DEBUG, "Client %d hung up", client->fd); ipc_client_disconnect(client); return 0; } @@ -532,8 +527,6 @@ int ipc_client_handle_writable(int client_fd, uint32_t mask, void *data) { return 0; } - sway_log(SWAY_DEBUG, "Client %d writable", client->fd); - ssize_t written = write(client->fd, client->write_buffer, client->write_buffer_len); 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)); } } + + 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); ipc_send_reply(client, payload_type, 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); } - sway_log(SWAY_DEBUG, "Added IPC reply of type 0x%x to client %d queue: %s", - payload_type, client->fd, payload); return true; } diff --git a/sway/lock.c b/sway/lock.c new file mode 100644 index 00000000..bf7d5de8 --- /dev/null +++ b/sway/lock.c @@ -0,0 +1,225 @@ +#define _POSIX_C_SOURCE 200809L +#include +#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); +} diff --git a/sway/main.c b/sway/main.c index b6f8a8bf..85bc2f1c 100644 --- a/sway/main.c +++ b/sway/main.c @@ -150,27 +150,17 @@ static void log_kernel(void) { pclose(f); } - -static bool drop_permissions(void) { - 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"); +static bool detect_suid(void) { + if (geteuid() != 0 && getegid() != 0) { 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; } @@ -240,35 +230,35 @@ static void handle_wlr_log(enum wlr_log_importance importance, _sway_vlog(convert_wlr_log_importance(importance), sway_fmt, args); } -int main(int argc, char **argv) { - static int verbose = 0, debug = 0, validate = 0, allow_unsupported_gpu = 0; +static const struct option long_options[] = { + {"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[] = { - {"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 char usage[] = + "Usage: sway [options] [command]\n" + "\n" + " -h, --help Show help message and quit.\n" + " -c, --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 main(int argc, char **argv) { + static bool verbose = false, debug = false, validate = false, allow_unsupported_gpu = false; char *config_path = NULL; - const char* usage = - "Usage: sway [options] [command]\n" - "\n" - " -h, --help Show help message and quit.\n" - " -c, --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; while (1) { int option_index = 0; @@ -286,25 +276,25 @@ int main(int argc, char **argv) { config_path = strdup(optarg); break; case 'C': // validate - validate = 1; + validate = true; break; case 'd': // debug - debug = 1; + debug = true; break; case 'D': // extended debug options enable_debug_flag(optarg); break; case 'u': - allow_unsupported_gpu = 1; + allow_unsupported_gpu = true; break; case 'v': // version printf("sway version " SWAY_VERSION "\n"); exit(EXIT_SUCCESS); break; case 'V': // verbose - verbose = 1; + verbose = true; break; - case 'p': ; // --get-socketpath + case 'p': // --get-socketpath if (getenv("SWAYSOCK")) { printf("%s\n", getenv("SWAYSOCK")); 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 // clear error message (when not running as an IPC client). if (!getenv("XDG_RUNTIME_DIR") && optind == argc) { @@ -357,9 +352,6 @@ int main(int argc, char **argv) { "`sway -d 2>sway.log`."); exit(EXIT_FAILURE); } - if (!drop_permissions()) { - exit(EXIT_FAILURE); - } char *socket_path = getenv("SWAYSOCK"); if (!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); - - if (!server_privileged_prepare(&server)) { - return 1; - } - - if (!drop_permissions()) { - server_fini(&server); - exit(EXIT_FAILURE); - } - increase_nofile_limit(); // handle SIGTERM signals @@ -413,6 +395,8 @@ int main(int argc, char **argv) { goto shutdown; } + set_rr_scheduling(); + if (!server_start(&server)) { sway_terminate(EXIT_FAILURE); goto shutdown; diff --git a/sway/meson.build b/sway/meson.build index ac3d408d..12362fcd 100644 --- a/sway/meson.build +++ b/sway/meson.build @@ -1,245 +1,252 @@ sway_sources = files( - 'commands.c', - 'config.c', - 'criteria.c', - 'decoration.c', - 'ipc-json.c', - 'ipc-server.c', - 'main.c', - 'server.c', - 'swaynag.c', - 'xdg_activation_v1.c', - 'xdg_decoration.c', + 'commands.c', + 'config.c', + 'criteria.c', + 'decoration.c', + 'ipc-json.c', + 'ipc-server.c', + 'lock.c', + 'main.c', + 'realtime.c', + 'server.c', + 'swaynag.c', + 'xdg_activation_v1.c', + 'xdg_decoration.c', - 'desktop/desktop.c', - 'desktop/fx_renderer.c', - 'desktop/idle_inhibit_v1.c', - 'desktop/layer_shell.c', - 'desktop/output.c', - 'desktop/render.c', - 'desktop/surface.c', - 'desktop/transaction.c', - 'desktop/xdg_shell.c', + 'desktop/desktop.c', + 'desktop/fx_renderer.c', + 'desktop/idle_inhibit_v1.c', + 'desktop/layer_shell.c', + 'desktop/output.c', + 'desktop/render.c', + 'desktop/surface.c', + 'desktop/transaction.c', + 'desktop/xdg_shell.c', + 'desktop/launcher.c', + 'input/input-manager.c', + 'input/cursor.c', + 'input/keyboard.c', + 'input/libinput.c', + 'input/seat.c', + 'input/seatop_default.c', + 'input/seatop_down.c', + 'input/seatop_move_floating.c', + 'input/seatop_move_tiling.c', + 'input/seatop_resize_floating.c', + 'input/seatop_resize_tiling.c', + 'input/switch.c', + 'input/tablet.c', + 'input/text_input.c', - 'input/input-manager.c', - 'input/cursor.c', - 'input/keyboard.c', - 'input/libinput.c', - 'input/seat.c', - 'input/seatop_default.c', - 'input/seatop_down.c', - 'input/seatop_move_floating.c', - 'input/seatop_move_tiling.c', - 'input/seatop_resize_floating.c', - 'input/seatop_resize_tiling.c', - 'input/switch.c', - 'input/tablet.c', - 'input/text_input.c', + 'config/bar.c', + 'config/output.c', + 'config/seat.c', + 'config/input.c', - 'config/bar.c', - 'config/output.c', - 'config/seat.c', - 'config/input.c', + 'commands/assign.c', + 'commands/bar.c', + 'commands/bind.c', + 'commands/border.c', + 'commands/client.c', + 'commands/corner_radius.c', + 'commands/create_output.c', + 'commands/default_border.c', + 'commands/default_floating_border.c', + 'commands/default_orientation.c', + 'commands/dim_inactive.c', + 'commands/dim_inactive_colors.c', + 'commands/exit.c', + 'commands/exec.c', + 'commands/exec_always.c', + 'commands/floating.c', + 'commands/floating_minmax_size.c', + 'commands/floating_modifier.c', + 'commands/focus.c', + 'commands/focus_follows_mouse.c', + 'commands/focus_on_window_activation.c', + 'commands/focus_wrapping.c', + 'commands/font.c', + 'commands/for_window.c', + 'commands/force_display_urgency_hint.c', + 'commands/force_focus_wrapping.c', + 'commands/fullscreen.c', + 'commands/gaps.c', + 'commands/gesture.c', + 'commands/hide_edge_borders.c', + 'commands/inhibit_idle.c', + 'commands/kill.c', + 'commands/mark.c', + 'commands/max_render_time.c', + 'commands/opacity.c', + 'commands/include.c', + 'commands/input.c', + 'commands/layout.c', + 'commands/mode.c', + 'commands/mouse_warping.c', + 'commands/move.c', + 'commands/new_float.c', + 'commands/new_window.c', + 'commands/no_focus.c', + 'commands/nop.c', + 'commands/output.c', + 'commands/popup_during_fullscreen.c', + 'commands/reload.c', + 'commands/rename.c', + 'commands/resize.c', + 'commands/saturation.c', + 'commands/scratchpad.c', + 'commands/seat.c', + 'commands/seat/attach.c', + 'commands/seat/cursor.c', + 'commands/seat/fallback.c', + 'commands/seat/hide_cursor.c', + 'commands/seat/idle.c', + 'commands/seat/keyboard_grouping.c', + 'commands/seat/pointer_constraint.c', + 'commands/seat/shortcuts_inhibitor.c', + 'commands/seat/xcursor_theme.c', + 'commands/set.c', + 'commands/show_marks.c', + 'commands/shortcuts_inhibitor.c', + 'commands/smart_borders.c', + 'commands/smart_gaps.c', + 'commands/split.c', + 'commands/sticky.c', + 'commands/swaybg_command.c', + 'commands/swaynag_command.c', + 'commands/swap.c', + 'commands/tiling_drag.c', + 'commands/tiling_drag_threshold.c', + 'commands/title_align.c', + 'commands/title_format.c', + 'commands/titlebar_border_thickness.c', + 'commands/titlebar_padding.c', + 'commands/unmark.c', + 'commands/urgent.c', + 'commands/workspace.c', + 'commands/workspace_layout.c', + 'commands/ws_auto_back_and_forth.c', + 'commands/xwayland.c', - 'commands/assign.c', - 'commands/bar.c', - 'commands/bind.c', - 'commands/border.c', - 'commands/client.c', - 'commands/corner_radius.c', - 'commands/create_output.c', - 'commands/default_border.c', - 'commands/default_floating_border.c', - 'commands/default_orientation.c', - 'commands/dim_inactive.c', - 'commands/dim_inactive_colors.c', - 'commands/exit.c', - 'commands/exec.c', - 'commands/exec_always.c', - 'commands/floating.c', - 'commands/floating_minmax_size.c', - 'commands/floating_modifier.c', - 'commands/focus.c', - 'commands/focus_follows_mouse.c', - 'commands/focus_on_window_activation.c', - 'commands/focus_wrapping.c', - 'commands/font.c', - 'commands/for_window.c', - 'commands/force_display_urgency_hint.c', - 'commands/force_focus_wrapping.c', - 'commands/fullscreen.c', - 'commands/gaps.c', - 'commands/hide_edge_borders.c', - 'commands/inhibit_idle.c', - 'commands/kill.c', - 'commands/mark.c', - 'commands/max_render_time.c', - 'commands/opacity.c', - 'commands/saturation.c', - 'commands/include.c', - 'commands/input.c', - 'commands/layout.c', - 'commands/mode.c', - 'commands/mouse_warping.c', - 'commands/move.c', - 'commands/new_float.c', - 'commands/new_window.c', - 'commands/no_focus.c', - 'commands/nop.c', - 'commands/output.c', - 'commands/popup_during_fullscreen.c', - 'commands/reload.c', - 'commands/rename.c', - 'commands/resize.c', - 'commands/scratchpad.c', - 'commands/seat.c', - 'commands/seat/attach.c', - 'commands/seat/cursor.c', - 'commands/seat/fallback.c', - 'commands/seat/hide_cursor.c', - 'commands/seat/idle.c', - 'commands/seat/keyboard_grouping.c', - 'commands/seat/pointer_constraint.c', - 'commands/seat/shortcuts_inhibitor.c', - 'commands/seat/xcursor_theme.c', - 'commands/set.c', - 'commands/show_marks.c', - 'commands/shortcuts_inhibitor.c', - 'commands/smart_borders.c', - 'commands/smart_gaps.c', - 'commands/split.c', - 'commands/sticky.c', - 'commands/swaybg_command.c', - 'commands/swaynag_command.c', - 'commands/swap.c', - 'commands/tiling_drag.c', - 'commands/tiling_drag_threshold.c', - 'commands/title_align.c', - 'commands/title_format.c', - 'commands/titlebar_border_thickness.c', - 'commands/titlebar_padding.c', - 'commands/unmark.c', - 'commands/urgent.c', - 'commands/workspace.c', - 'commands/workspace_layout.c', - 'commands/ws_auto_back_and_forth.c', - 'commands/xwayland.c', + 'commands/bar/bind.c', + 'commands/bar/binding_mode_indicator.c', + 'commands/bar/colors.c', + 'commands/bar/font.c', + 'commands/bar/gaps.c', + 'commands/bar/height.c', + 'commands/bar/hidden_state.c', + 'commands/bar/icon_theme.c', + 'commands/bar/id.c', + 'commands/bar/mode.c', + 'commands/bar/modifier.c', + 'commands/bar/output.c', + 'commands/bar/pango_markup.c', + 'commands/bar/position.c', + 'commands/bar/separator_symbol.c', + 'commands/bar/status_command.c', + 'commands/bar/status_edge_padding.c', + 'commands/bar/status_padding.c', + 'commands/bar/strip_workspace_numbers.c', + 'commands/bar/strip_workspace_name.c', + 'commands/bar/swaybar_command.c', + 'commands/bar/tray_bind.c', + 'commands/bar/tray_output.c', + 'commands/bar/tray_padding.c', + 'commands/bar/workspace_buttons.c', + 'commands/bar/workspace_min_width.c', + 'commands/bar/wrap_scroll.c', - 'commands/bar/bind.c', - 'commands/bar/binding_mode_indicator.c', - 'commands/bar/colors.c', - 'commands/bar/font.c', - 'commands/bar/gaps.c', - 'commands/bar/height.c', - 'commands/bar/hidden_state.c', - 'commands/bar/icon_theme.c', - 'commands/bar/id.c', - 'commands/bar/mode.c', - 'commands/bar/modifier.c', - 'commands/bar/output.c', - 'commands/bar/pango_markup.c', - 'commands/bar/position.c', - 'commands/bar/separator_symbol.c', - 'commands/bar/status_command.c', - 'commands/bar/status_edge_padding.c', - 'commands/bar/status_padding.c', - 'commands/bar/strip_workspace_numbers.c', - 'commands/bar/strip_workspace_name.c', - 'commands/bar/swaybar_command.c', - 'commands/bar/tray_bind.c', - 'commands/bar/tray_output.c', - 'commands/bar/tray_padding.c', - 'commands/bar/workspace_buttons.c', - 'commands/bar/workspace_min_width.c', - 'commands/bar/wrap_scroll.c', + 'commands/input/accel_profile.c', + 'commands/input/calibration_matrix.c', + 'commands/input/click_method.c', + 'commands/input/drag.c', + 'commands/input/drag_lock.c', + 'commands/input/dwt.c', + 'commands/input/dwtp.c', + 'commands/input/events.c', + 'commands/input/left_handed.c', + 'commands/input/map_from_region.c', + 'commands/input/map_to_output.c', + 'commands/input/map_to_region.c', + 'commands/input/middle_emulation.c', + 'commands/input/natural_scroll.c', + 'commands/input/pointer_accel.c', + 'commands/input/repeat_delay.c', + 'commands/input/repeat_rate.c', + 'commands/input/scroll_button.c', + 'commands/input/scroll_factor.c', + 'commands/input/scroll_method.c', + 'commands/input/tap.c', + 'commands/input/tap_button_map.c', + 'commands/input/tool_mode.c', + 'commands/input/xkb_capslock.c', + 'commands/input/xkb_file.c', + 'commands/input/xkb_layout.c', + 'commands/input/xkb_model.c', + 'commands/input/xkb_numlock.c', + 'commands/input/xkb_options.c', + 'commands/input/xkb_rules.c', + 'commands/input/xkb_switch_layout.c', + 'commands/input/xkb_variant.c', - 'commands/input/accel_profile.c', - 'commands/input/calibration_matrix.c', - 'commands/input/click_method.c', - 'commands/input/drag.c', - 'commands/input/drag_lock.c', - 'commands/input/dwt.c', - 'commands/input/events.c', - 'commands/input/left_handed.c', - 'commands/input/map_from_region.c', - 'commands/input/map_to_output.c', - 'commands/input/map_to_region.c', - 'commands/input/middle_emulation.c', - 'commands/input/natural_scroll.c', - 'commands/input/pointer_accel.c', - 'commands/input/repeat_delay.c', - 'commands/input/repeat_rate.c', - 'commands/input/scroll_button.c', - 'commands/input/scroll_factor.c', - 'commands/input/scroll_method.c', - 'commands/input/tap.c', - 'commands/input/tap_button_map.c', - 'commands/input/tool_mode.c', - 'commands/input/xkb_capslock.c', - 'commands/input/xkb_file.c', - 'commands/input/xkb_layout.c', - 'commands/input/xkb_model.c', - 'commands/input/xkb_numlock.c', - 'commands/input/xkb_options.c', - 'commands/input/xkb_rules.c', - 'commands/input/xkb_switch_layout.c', - 'commands/input/xkb_variant.c', + 'commands/output/adaptive_sync.c', + 'commands/output/background.c', + 'commands/output/disable.c', + 'commands/output/dpms.c', + 'commands/output/enable.c', + 'commands/output/max_render_time.c', + 'commands/output/mode.c', + 'commands/output/position.c', + 'commands/output/power.c', + 'commands/output/render_bit_depth.c', + 'commands/output/scale.c', + 'commands/output/scale_filter.c', + 'commands/output/subpixel.c', + 'commands/output/toggle.c', + 'commands/output/transform.c', + 'commands/output/unplug.c', - 'commands/output/adaptive_sync.c', - 'commands/output/background.c', - 'commands/output/disable.c', - 'commands/output/dpms.c', - 'commands/output/enable.c', - 'commands/output/max_render_time.c', - 'commands/output/mode.c', - 'commands/output/position.c', - 'commands/output/render_bit_depth.c', - 'commands/output/scale.c', - 'commands/output/scale_filter.c', - 'commands/output/subpixel.c', - 'commands/output/toggle.c', - 'commands/output/transform.c', - - 'tree/arrange.c', - 'tree/container.c', - 'tree/node.c', - 'tree/root.c', - 'tree/view.c', - 'tree/workspace.c', - 'tree/output.c', + 'tree/arrange.c', + 'tree/container.c', + 'tree/node.c', + 'tree/root.c', + 'tree/view.c', + 'tree/workspace.c', + 'tree/output.c', ) subdir('desktop/shaders') sway_deps = [ - cairo, - drm, - jsonc, - libevdev, - libinput, - libudev, - math, - pango, - pcre, - glesv2, - pixman, - server_protos, - wayland_server, - wlroots, - xkbcommon, - egl, + cairo, + drm, + jsonc, + libevdev, + libinput, + libudev, + math, + pango, + pcre2, + glesv2, + pixman, + threads, + wayland_server, + wlroots, + xkbcommon, + xcb_icccm, + egl, ] if have_xwayland - sway_sources += 'desktop/xwayland.c' - sway_deps += xcb + sway_sources += 'desktop/xwayland.c' + sway_deps += xcb endif executable( - 'sway', - sway_sources, - include_directories: [sway_inc], - dependencies: sway_deps, - link_with: [lib_sway_common], - install: true + 'sway', + sway_sources + wl_protos_src, + include_directories: [sway_inc], + dependencies: sway_deps, + link_with: [lib_sway_common], + install: true ) diff --git a/sway/realtime.c b/sway/realtime.c new file mode 100644 index 00000000..11154af0 --- /dev/null +++ b/sway/realtime.c @@ -0,0 +1,40 @@ +#include +#include +#include +#include +#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, ¶m); + 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, ¶m); + 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, ¶m); + if (ret != 0) { + sway_log(SWAY_INFO, "Failed to set scheduling priority to %d", prio); + return; + } + + pthread_atfork(NULL, NULL, child_fork_callback); +} diff --git a/sway/server.c b/sway/server.c index 3fab3431..90b2e042 100644 --- a/sway/server.c +++ b/sway/server.c @@ -18,13 +18,16 @@ #include #include #include +#include #include #include #include #include #include #include +#include #include +#include #include #include #include @@ -48,19 +51,6 @@ #include "sway/xwayland.h" #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) { /* We only offer non-desktop outputs, but in the future we might want to do * 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) { 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); if (!server->wlr_renderer) { @@ -109,6 +109,8 @@ bool server_init(struct sway_server *server) { wl_signal_add(&server->compositor->events.new_surface, &server->compositor_new_surface); + wlr_subcompositor_create(server->wl_display); + server->data_device_manager = 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); 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 = 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.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, &server->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 = wlr_foreign_toplevel_manager_v1_create(server->wl_display); + sway_session_lock_init(); + server->drm_lease_manager= wlr_drm_lease_v1_manager_create(server->wl_display, server->backend); 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_primary_selection_v1_device_manager_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 = 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, &server->xdg_activation_v1_request_activate); + wl_list_init(&server->pending_launcher_ctxs); + // Avoid using "wayland-0" as display socket char name_candidate[16]; - for (int i = 1; i <= 32; ++i) { - sprintf(name_candidate, "wayland-%d", i); + for (unsigned int i = 1; i <= 32; ++i) { + snprintf(name_candidate, sizeof(name_candidate), "wayland-%u", i); if (wl_display_add_socket(server->wl_display, name_candidate) >= 0) { server->socket = strdup(name_candidate); break; diff --git a/sway/sway-input.5.scd b/sway/sway-input.5.scd index 8b702b77..e073c45d 100644 --- a/sway/sway-input.5.scd +++ b/sway/sway-input.5.scd @@ -147,6 +147,10 @@ The following commands may only be used in the configuration file. *input* dwt enabled|disabled Enables or disables disable-while-typing for the specified input device. +*input* dwtp enabled|disabled + Enables or disables disable-while-trackpointing for the specified input + device. + *input* events enabled|disabled|disabled_on_external_mouse|toggle [] Enables or disables send_events for specified input device. Disabling send_events disables the input device. diff --git a/sway/sway-ipc.7.scd b/sway/sway-ipc.7.scd index d90fe97a..9121f679 100644 --- a/sway/sway-ipc.7.scd +++ b/sway/sway-ipc.7.scd @@ -213,7 +213,10 @@ following properties: : Whether this output is active/enabled |- dpms : 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 : boolean : 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 |- fullscreen_mode : 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 |- app_id : string @@ -1194,6 +1197,10 @@ following properties will be included for devices that support them: |- dwt : string : 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 : array : 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", "middle_emulation": "disabled", "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", "middle_emulation": "disabled", "scroll_method": "edge", - "dwt": "enabled" + "dwt": "enabled", + "dwtp": "enabled" } }, { diff --git a/sway/sway-output.5.scd b/sway/sway-output.5.scd index 4159a851..b7d5e577 100644 --- a/sway/sway-output.5.scd +++ b/sway/sway-output.5.scd @@ -24,7 +24,7 @@ must be separated by one space. For example: # COMMANDS -*output* mode|resolution|res [--custom] [@Hz] +*output* mode|resolution|res [--custom] x[@Hz] 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 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 default). + As opposed to the _power_ command, the output will loose its current + workspace and windows. + *output* toggle Toggle the specified output. +*output* 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* dpms on|off|toggle - Enables or disables the specified output via DPMS. To turn an output off - (ie. blank the screen but keep workspaces as-is), one can set DPMS to off. + Deprecated. Alias for _power_. *output* max_render_time off| Controls when sway composites the output, as a positive number of diff --git a/sway/sway.5.scd b/sway/sway.5.scd index bf936a27..15e60844 100644 --- a/sway/sway.5.scd +++ b/sway/sway.5.scd @@ -176,6 +176,12 @@ set|plus|minus|toggle *layout* default|splith|splitv|stacking|tabbed 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] 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 @@ -210,15 +216,14 @@ set|plus|minus|toggle further details. *move* left|right|up|down [ px] - Moves the focused container in the direction specified. If the container, - the optional _px_ argument specifies how many pixels to move the container. - If unspecified, the default is 10 pixels. Pixels are ignored when moving - tiled containers. + Moves the focused container in the direction specified. The optional _px_ + argument specifies how many pixels to move the container. If unspecified, + the default is 10 pixels. Pixels are ignored when moving tiled containers. *move* [absolute] position [px|ppt] [px|ppt] Moves the focused container to the specified position in the workspace. 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. *move* [absolute] position center @@ -482,6 +487,62 @@ runtime. bindswitch lid:toggle exec echo "Lid moved" ``` +*bindgesture* [--exact] [--input-device=] [--no-warn] \ +[:][:directions] + 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* This command is ignored and is only present for i3 compatibility. @@ -632,11 +693,11 @@ The default colors are: after switching between workspaces. *focus_on_window_activation* smart|urgent|focus|none - This option determines what to do when an xwayland client requests - window activation. If set to _urgent_, the urgent state will be set - for that window. If set to _focus_, the window will become focused. - If set to _smart_, the window will become focused only if it is already - visible, otherwise the urgent state will be set. Default is _urgent_. + This option determines what to do when a client requests window activation. + If set to _urgent_, the urgent state will be set for that window. If set to + _focus_, the window will become focused. If set to _smart_, the window will + become focused only if it is already visible, otherwise the urgent state + will be set. Default is _urgent_. *focus_wrapping* yes|no|force|workspace This option determines what to do when attempting to focus over the edge @@ -805,6 +866,11 @@ The default colors are: *unbindswitch* : Removes a binding for when changes to . +*unbindgesture* [--exact] [--input-device=] \ +[:][:directions] + Removes a binding for the specified _gesture_, _fingers_ + and _directions_ combination. + *unbindsym* [--whole-window] [--border] [--exclude-titlebar] [--release] [--locked] \ [--to-code] [--input-device=] Removes the binding for _key combo_ that was previously bound with the @@ -918,7 +984,8 @@ The following attributes may be matched with: *class* 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 - 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* Compare against the internal container ID, which you can find via IPC. If @@ -932,12 +999,14 @@ The following attributes may be matched with: Matches floating windows. *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* 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 - of the currently focused window. + of the currently focused window. instance is specific to X11 applications and + requires XWayland. *pid* Compare value against the window's process ID. Must be numeric. @@ -962,12 +1031,14 @@ The following attributes may be matched with: *window_role* Compare against the window role (WM_WINDOW_ROLE). Can be a regular 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* Compare against the window type (\_NET_WM_WINDOW_TYPE). Possible values 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* Compare against the workspace name for this view. Can be a regular diff --git a/sway/tree/arrange.c b/sway/tree/arrange.c index 4aa82c35..9c1a11e5 100644 --- a/sway/tree/arrange.c +++ b/sway/tree/arrange.c @@ -311,12 +311,13 @@ void arrange_output(struct sway_output *output) { if (config->reloading) { return; } - const struct wlr_box *output_box = wlr_output_layout_get_box( - root->output_layout, output->wlr_output); - output->lx = output_box->x; - output->ly = output_box->y; - output->width = output_box->width; - output->height = output_box->height; + struct wlr_box output_box; + wlr_output_layout_get_box(root->output_layout, + output->wlr_output, &output_box); + output->lx = output_box.x; + output->ly = output_box.y; + output->width = output_box.width; + output->height = output_box.height; for (int i = 0; i < output->workspaces->length; ++i) { struct sway_workspace *workspace = output->workspaces->items[i]; @@ -328,12 +329,12 @@ void arrange_root(void) { if (config->reloading) { return; } - const struct wlr_box *layout_box = - wlr_output_layout_get_box(root->output_layout, NULL); - root->x = layout_box->x; - root->y = layout_box->y; - root->width = layout_box->width; - root->height = layout_box->height; + struct wlr_box layout_box; + wlr_output_layout_get_box(root->output_layout, NULL, &layout_box); + root->x = layout_box.x; + root->y = layout_box.y; + root->width = layout_box.width; + root->height = layout_box.height; if (root->fullscreen_global) { struct sway_container *fs = root->fullscreen_global; diff --git a/sway/tree/container.c b/sway/tree/container.c index 73bb865c..29a1e226 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include "linux-dmabuf-unstable-v1-protocol.h" #include "cairo_util.h" @@ -51,7 +52,7 @@ struct sway_container *container_create(struct sway_view *view) { c->outputs = create_list(); wl_signal_init(&c->events.destroy); - wl_signal_emit(&root->events.new_node, &c->node); + wl_signal_emit_mutable(&root->events.new_node, &c->node); return c; } @@ -106,7 +107,7 @@ void container_begin_destroy(struct sway_container *con) { container_fullscreen_disable(con); } - wl_signal_emit(&con->node.events.destroy, &con->node); + wl_signal_emit_mutable(&con->node.events.destroy, &con->node); container_end_mouse_operation(con); @@ -196,7 +197,7 @@ static struct sway_container *surface_at_view(struct sway_container *con, double #endif case SWAY_VIEW_XDG_SHELL: _surface = wlr_xdg_surface_surface_at( - view->wlr_xdg_surface, + view->wlr_xdg_toplevel->base, view_sx, view_sy, &_sx, &_sy); break; } @@ -522,7 +523,7 @@ static void render_titlebar_text_texture(struct sway_output *output, to_cairo_subpixel_order(output->wlr_output->subpixel)); } cairo_set_font_options(c, fo); - get_text_size(c, config->font, &width, NULL, &baseline, scale, + get_text_size(c, config->font_description, &width, NULL, &baseline, scale, config->pango_markup, "%s", text); cairo_surface_destroy(dummy_surface); cairo_destroy(c); @@ -556,7 +557,7 @@ static void render_titlebar_text_texture(struct sway_output *output, class->text[2], class->text[3]); cairo_move_to(cairo, 0, config->font_baseline * scale - baseline); - render_text(cairo, config->font, scale, pango_markup, "%s", text); + render_text(cairo, config->font_description, scale, pango_markup, "%s", text); cairo_surface_flush(surface); unsigned char *data = cairo_image_surface_get_data(surface); @@ -697,12 +698,13 @@ void floating_calculate_constraints(int *min_width, int *max_width, *min_height = config->floating_minimum_height; } - struct wlr_box *box = wlr_output_layout_get_box(root->output_layout, NULL); + struct wlr_box box; + wlr_output_layout_get_box(root->output_layout, NULL, &box); if (config->floating_maximum_width == -1) { // no maximum *max_width = INT_MAX; } else if (config->floating_maximum_width == 0) { // automatic - *max_width = box->width; + *max_width = box.width; } else { *max_width = config->floating_maximum_width; } @@ -710,7 +712,7 @@ void floating_calculate_constraints(int *min_width, int *max_width, if (config->floating_maximum_height == -1) { // no maximum *max_height = INT_MAX; } else if (config->floating_maximum_height == 0) { // automatic - *max_height = box->height; + *max_height = box.height; } else { *max_height = config->floating_maximum_height; } @@ -742,9 +744,9 @@ void container_floating_resize_and_center(struct sway_container *con) { return; } - struct wlr_box *ob = wlr_output_layout_get_box(root->output_layout, - ws->output->wlr_output); - if (!ob) { + struct wlr_box ob; + wlr_output_layout_get_box(root->output_layout, ws->output->wlr_output, &ob); + if (wlr_box_empty(&ob)) { // On NOOP output. Will be called again when moved to an output con->pending.x = 0; con->pending.y = 0; @@ -756,8 +758,8 @@ void container_floating_resize_and_center(struct sway_container *con) { floating_natural_resize(con); if (!con->view) { if (con->pending.width > ws->width || con->pending.height > ws->height) { - con->pending.x = ob->x + (ob->width - con->pending.width) / 2; - con->pending.y = ob->y + (ob->height - con->pending.height) / 2; + con->pending.x = ob.x + (ob.width - con->pending.width) / 2; + con->pending.y = ob.y + (ob.height - con->pending.height) / 2; } else { con->pending.x = ws->x + (ws->width - con->pending.width) / 2; con->pending.y = ws->y + (ws->height - con->pending.height) / 2; @@ -765,8 +767,8 @@ void container_floating_resize_and_center(struct sway_container *con) { } else { if (con->pending.content_width > ws->width || con->pending.content_height > ws->height) { - con->pending.content_x = ob->x + (ob->width - con->pending.content_width) / 2; - con->pending.content_y = ob->y + (ob->height - con->pending.content_height) / 2; + con->pending.content_x = ob.x + (ob.width - con->pending.content_width) / 2; + con->pending.content_y = ob.y + (ob.height - con->pending.content_height) / 2; } else { con->pending.content_x = ws->x + (ws->width - con->pending.content_width) / 2; con->pending.content_y = ws->y + (ws->height - con->pending.content_height) / 2; @@ -788,11 +790,11 @@ void container_floating_set_default_size(struct sway_container *con) { int min_width, max_width, min_height, max_height; floating_calculate_constraints(&min_width, &max_width, &min_height, &max_height); - struct wlr_box *box = calloc(1, sizeof(struct wlr_box)); - workspace_get_box(con->pending.workspace, box); + struct wlr_box box; + workspace_get_box(con->pending.workspace, &box); - double width = fmax(min_width, fmin(box->width * 0.5, max_width)); - double height = fmax(min_height, fmin(box->height * 0.75, max_height)); + double width = fmax(min_width, fmin(box.width * 0.5, max_width)); + double height = fmax(min_height, fmin(box.height * 0.75, max_height)); if (!con->view) { con->pending.width = width; con->pending.height = height; @@ -801,8 +803,6 @@ void container_floating_set_default_size(struct sway_container *con) { con->pending.content_height = height; container_set_geometry_from_content(con); } - - free(box); } @@ -1413,6 +1413,9 @@ list_t *container_get_siblings(struct sway_container *container) { if (container_is_scratchpad_hidden(container)) { return NULL; } + if (!container->pending.workspace) { + return NULL; + } if (list_find(container->pending.workspace->tiling, container) != -1) { return container->pending.workspace->tiling; } @@ -1707,7 +1710,7 @@ static void update_marks_texture(struct sway_container *con, for (int i = 0; i < con->marks->length; ++i) { char *mark = con->marks->items[i]; if (mark[0] != '_') { - sprintf(part, "[%s]", mark); + snprintf(part, len + 1, "[%s]", mark); strcat(buffer, part); } } diff --git a/sway/tree/node.c b/sway/tree/node.c index bc7e2aa5..12361c75 100644 --- a/sway/tree/node.c +++ b/sway/tree/node.c @@ -18,13 +18,13 @@ void node_init(struct sway_node *node, enum sway_node_type type, void *thing) { const char *node_type_to_str(enum sway_node_type type) { switch (type) { case N_ROOT: - return "N_ROOT"; + return "root"; case N_OUTPUT: - return "N_OUTPUT"; + return "output"; case N_WORKSPACE: - return "N_WORKSPACE"; + return "workspace"; case N_CONTAINER: - return "N_CONTAINER"; + return "container"; } return ""; } diff --git a/sway/tree/output.c b/sway/tree/output.c index ad8d2482..eccab2f7 100644 --- a/sway/tree/output.c +++ b/sway/tree/output.c @@ -3,12 +3,12 @@ #include #include #include -#include #include "sway/ipc-server.h" #include "sway/layers.h" #include "sway/output.h" #include "sway/tree/arrange.h" #include "sway/tree/workspace.h" +#include "sway/server.h" #include "log.h" #include "util.h" @@ -146,7 +146,7 @@ void output_enable(struct sway_output *output) { input_manager_configure_xcursor(); - wl_signal_emit(&root->events.new_node, &output->node); + wl_signal_emit_mutable(&root->events.new_node, &output->node); arrange_layers(output); arrange_root(); @@ -262,7 +262,7 @@ void output_disable(struct sway_output *output) { } sway_log(SWAY_DEBUG, "Disabling output '%s'", output->wlr_output->name); - wl_signal_emit(&output->events.disable, output); + wl_signal_emit_mutable(&output->events.disable, output); output_evacuate(output); @@ -286,7 +286,7 @@ void output_begin_destroy(struct sway_output *output) { return; } sway_log(SWAY_DEBUG, "Destroying output '%s'", output->wlr_output->name); - wl_signal_emit(&output->node.events.destroy, &output->node); + wl_signal_emit_mutable(&output->node.events.destroy, &output->node); output->node.destroying = true; node_set_dirty(&output->node); @@ -301,10 +301,10 @@ struct sway_output *output_get_in_direction(struct sway_output *reference, if (!sway_assert(direction, "got invalid direction: %d", direction)) { return NULL; } - struct wlr_box *output_box = - wlr_output_layout_get_box(root->output_layout, reference->wlr_output); - int lx = output_box->x + output_box->width / 2; - int ly = output_box->y + output_box->height / 2; + struct wlr_box output_box; + wlr_output_layout_get_box(root->output_layout, reference->wlr_output, &output_box); + int lx = output_box.x + output_box.width / 2; + int ly = output_box.y + output_box.height / 2; struct wlr_output *wlr_adjacent = wlr_output_layout_adjacent_output( root->output_layout, direction, reference->wlr_output, lx, ly); if (!wlr_adjacent) { @@ -390,6 +390,33 @@ void output_get_box(struct sway_output *output, struct wlr_box *box) { box->height = output->height; } +static void handle_destroy_non_desktop(struct wl_listener *listener, void *data) { + struct sway_output_non_desktop *output = + wl_container_of(listener, output, destroy); + + sway_log(SWAY_DEBUG, "Destroying non-desktop output '%s'", output->wlr_output->name); + + int index = list_find(root->non_desktop_outputs, output); + list_del(root->non_desktop_outputs, index); + + wl_list_remove(&output->destroy.link); + + free(output); +} + +struct sway_output_non_desktop *output_non_desktop_create( + struct wlr_output *wlr_output) { + struct sway_output_non_desktop *output = + calloc(1, sizeof(struct sway_output_non_desktop)); + + output->wlr_output = wlr_output; + + wl_signal_add(&wlr_output->events.destroy, &output->destroy); + output->destroy.notify = handle_destroy_non_desktop; + + return output; +} + enum sway_container_layout output_get_default_layout( struct sway_output *output) { if (config->default_orientation != L_NONE) { diff --git a/sway/tree/root.c b/sway/tree/root.c index 73f3993c..8934721f 100644 --- a/sway/tree/root.c +++ b/sway/tree/root.c @@ -38,6 +38,7 @@ struct sway_root *root_create(void) { wl_list_init(&root->drag_icons); wl_signal_init(&root->events.new_node); root->outputs = create_list(); + root->non_desktop_outputs = create_list(); root->scratchpad = create_list(); root->output_layout_change.notify = output_layout_handle_change; @@ -183,172 +184,6 @@ void root_scratchpad_hide(struct sway_container *con) { ipc_event_window(con, "move"); } -struct pid_workspace { - pid_t pid; - char *workspace; - struct timespec time_added; - - struct sway_output *output; - struct wl_listener output_destroy; - - struct wl_list link; -}; - -static struct wl_list pid_workspaces; - -/** - * Get the pid of a parent process given the pid of a child process. - * - * Returns the parent pid or NULL if the parent pid cannot be determined. - */ -static pid_t get_parent_pid(pid_t child) { - pid_t parent = -1; - char file_name[100]; - char *buffer = NULL; - const char *sep = " "; - FILE *stat = NULL; - size_t buf_size = 0; - - sprintf(file_name, "/proc/%d/stat", child); - - if ((stat = fopen(file_name, "r"))) { - if (getline(&buffer, &buf_size, stat) != -1) { - strtok(buffer, sep); // pid - strtok(NULL, sep); // executable name - strtok(NULL, sep); // state - char *token = strtok(NULL, sep); // parent pid - parent = strtol(token, NULL, 10); - } - free(buffer); - fclose(stat); - } - - if (parent) { - return (parent == child) ? -1 : parent; - } - - return -1; -} - -static void pid_workspace_destroy(struct pid_workspace *pw) { - wl_list_remove(&pw->output_destroy.link); - wl_list_remove(&pw->link); - free(pw->workspace); - free(pw); -} - -struct sway_workspace *root_workspace_for_pid(pid_t pid) { - if (!pid_workspaces.prev && !pid_workspaces.next) { - wl_list_init(&pid_workspaces); - return NULL; - } - - struct sway_workspace *ws = NULL; - struct pid_workspace *pw = NULL; - - sway_log(SWAY_DEBUG, "Looking up workspace for pid %d", pid); - - do { - struct pid_workspace *_pw = NULL; - wl_list_for_each(_pw, &pid_workspaces, link) { - if (pid == _pw->pid) { - pw = _pw; - sway_log(SWAY_DEBUG, - "found pid_workspace for pid %d, workspace %s", - pid, pw->workspace); - goto found; - } - } - pid = get_parent_pid(pid); - } while (pid > 1); -found: - - if (pw && pw->workspace) { - ws = workspace_by_name(pw->workspace); - - if (!ws) { - sway_log(SWAY_DEBUG, - "Creating workspace %s for pid %d because it disappeared", - pw->workspace, pid); - - struct sway_output *output = pw->output; - if (pw->output && !pw->output->enabled) { - sway_log(SWAY_DEBUG, - "Workspace output %s is disabled, trying another one", - pw->output->wlr_output->name); - output = NULL; - } - - ws = workspace_create(output, pw->workspace); - } - - pid_workspace_destroy(pw); - } - - return ws; -} - -static void pw_handle_output_destroy(struct wl_listener *listener, void *data) { - struct pid_workspace *pw = wl_container_of(listener, pw, output_destroy); - pw->output = NULL; - wl_list_remove(&pw->output_destroy.link); - wl_list_init(&pw->output_destroy.link); -} - -void root_record_workspace_pid(pid_t pid) { - sway_log(SWAY_DEBUG, "Recording workspace for process %d", pid); - if (!pid_workspaces.prev && !pid_workspaces.next) { - wl_list_init(&pid_workspaces); - } - - struct sway_seat *seat = input_manager_current_seat(); - struct sway_workspace *ws = seat_get_focused_workspace(seat); - if (!ws) { - sway_log(SWAY_DEBUG, "Bailing out, no workspace"); - return; - } - struct sway_output *output = ws->output; - if (!output) { - sway_log(SWAY_DEBUG, "Bailing out, no output"); - return; - } - - struct timespec now; - clock_gettime(CLOCK_MONOTONIC, &now); - - // Remove expired entries - static const int timeout = 60; - struct pid_workspace *old, *_old; - wl_list_for_each_safe(old, _old, &pid_workspaces, link) { - if (now.tv_sec - old->time_added.tv_sec >= timeout) { - pid_workspace_destroy(old); - } - } - - struct pid_workspace *pw = calloc(1, sizeof(struct pid_workspace)); - pw->workspace = strdup(ws->name); - pw->output = output; - pw->pid = pid; - memcpy(&pw->time_added, &now, sizeof(struct timespec)); - pw->output_destroy.notify = pw_handle_output_destroy; - wl_signal_add(&output->wlr_output->events.destroy, &pw->output_destroy); - wl_list_insert(&pid_workspaces, &pw->link); -} - -void root_remove_workspace_pid(pid_t pid) { - if (!pid_workspaces.prev || !pid_workspaces.next) { - return; - } - - struct pid_workspace *pw, *tmp; - wl_list_for_each_safe(pw, tmp, &pid_workspaces, link) { - if (pid == pw->pid) { - pid_workspace_destroy(pw); - return; - } - } -} - void root_for_each_workspace(void (*f)(struct sway_workspace *ws, void *data), void *data) { for (int i = 0; i < root->outputs->length; ++i) { @@ -443,17 +278,3 @@ void root_get_box(struct sway_root *root, struct wlr_box *box) { box->width = root->width; box->height = root->height; } - -void root_rename_pid_workspaces(const char *old_name, const char *new_name) { - if (!pid_workspaces.prev && !pid_workspaces.next) { - wl_list_init(&pid_workspaces); - } - - struct pid_workspace *pw = NULL; - wl_list_for_each(pw, &pid_workspaces, link) { - if (strcmp(pw->workspace, old_name) == 0) { - free(pw->workspace); - pw->workspace = strdup(new_name); - } - } -} diff --git a/sway/tree/view.c b/sway/tree/view.c index 8b7061ba..9d7b68d1 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -6,6 +6,7 @@ #include #include #include +#include #include #include "config.h" #if HAVE_XWAYLAND @@ -18,6 +19,7 @@ #include "sway/desktop.h" #include "sway/desktop/transaction.h" #include "sway/desktop/idle_inhibit_v1.h" +#include "sway/desktop/launcher.h" #include "sway/input/cursor.h" #include "sway/ipc-server.h" #include "sway/output.h" @@ -62,6 +64,8 @@ void view_destroy(struct sway_view *view) { } list_free(view->executed_criteria); + view_assign_ctx(view, NULL); + free(view->title_format); if (view->impl->destroy) { @@ -532,6 +536,20 @@ static void view_populate_pid(struct sway_view *view) { view->pid = pid; } +void view_assign_ctx(struct sway_view *view, struct launcher_ctx *ctx) { + if (view->ctx) { + // This ctx has been replaced + launcher_ctx_destroy(view->ctx); + view->ctx = NULL; + } + if (ctx == NULL) { + return; + } + launcher_ctx_consume(ctx); + + view->ctx = ctx; +} + static struct sway_workspace *select_workspace(struct sway_view *view) { struct sway_seat *seat = input_manager_current_seat(); @@ -567,13 +585,14 @@ static struct sway_workspace *select_workspace(struct sway_view *view) { } list_free(criterias); if (ws) { - root_remove_workspace_pid(view->pid); + view_assign_ctx(view, NULL); return ws; } // Check if there's a PID mapping - ws = root_workspace_for_pid(view->pid); + ws = view->ctx ? launcher_ctx_get_workspace(view->ctx) : NULL; if (ws) { + view_assign_ctx(view, NULL); return ws; } @@ -739,6 +758,13 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface, view_populate_pid(view); view->container = container_create(view); + if (view->ctx == NULL) { + struct launcher_ctx *ctx = launcher_ctx_find_pid(view->pid); + if (ctx != NULL) { + view_assign_ctx(view, ctx); + } + } + // If there is a request to be opened fullscreen on a specific output, try // to honor that request. Otherwise, fallback to assigns, pid mappings, // focused workspace, etc @@ -873,7 +899,7 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface, } void view_unmap(struct sway_view *view) { - wl_signal_emit(&view->events.unmap, view); + wl_signal_emit_mutable(&view->events.unmap, view); wl_list_remove(&view->surface_new_subsurface.link); @@ -1308,21 +1334,23 @@ void view_update_title(struct sway_view *view, bool force) { free(view->container->title); free(view->container->formatted_title); - if (title) { - size_t len = parse_title_format(view, NULL); + + size_t len = parse_title_format(view, NULL); + + if (len) { char *buffer = calloc(len + 1, sizeof(char)); if (!sway_assert(buffer, "Unable to allocate title string")) { return; } - parse_title_format(view, buffer); - view->container->title = strdup(title); + parse_title_format(view, buffer); view->container->formatted_title = buffer; } else { - view->container->title = NULL; view->container->formatted_title = NULL; } + view->container->title = title ? strdup(title) : NULL; + // Update title after the global font height is updated container_update_title_textures(view->container); diff --git a/sway/tree/workspace.c b/sway/tree/workspace.c index c84320bd..ee940466 100644 --- a/sway/tree/workspace.c +++ b/sway/tree/workspace.c @@ -56,6 +56,8 @@ struct sway_output *workspace_get_initial_output(const char *name) { struct sway_workspace *workspace_create(struct sway_output *output, const char *name) { + sway_assert(name, "NULL name given to workspace_create"); + if (output == NULL) { output = workspace_get_initial_output(name); } @@ -69,7 +71,7 @@ struct sway_workspace *workspace_create(struct sway_output *output, return NULL; } node_init(&ws->node, N_WORKSPACE, ws); - ws->name = name ? strdup(name) : NULL; + ws->name = strdup(name); ws->prev_split_layout = L_NONE; ws->layout = output_get_default_layout(output); ws->floating = create_list(); @@ -114,7 +116,7 @@ struct sway_workspace *workspace_create(struct sway_output *output, output_sort_workspaces(output); ipc_event_workspace(NULL, ws, "init"); - wl_signal_emit(&root->events.new_node, &ws->node); + wl_signal_emit_mutable(&root->events.new_node, &ws->node); return ws; } @@ -142,7 +144,7 @@ void workspace_destroy(struct sway_workspace *workspace) { void workspace_begin_destroy(struct sway_workspace *workspace) { sway_log(SWAY_DEBUG, "Destroying workspace '%s'", workspace->name); ipc_event_workspace(NULL, workspace, "empty"); // intentional - wl_signal_emit(&workspace->node.events.destroy, &workspace->node); + wl_signal_emit_mutable(&workspace->node.events.destroy, &workspace->node); if (workspace->output) { workspace_detach(workspace); diff --git a/sway/xdg_activation_v1.c b/sway/xdg_activation_v1.c index 6c70c785..2b94136c 100644 --- a/sway/xdg_activation_v1.c +++ b/sway/xdg_activation_v1.c @@ -1,4 +1,5 @@ #include +#include "sway/desktop/launcher.h" #include "sway/tree/view.h" void xdg_activation_v1_handle_request_activate(struct wl_listener *listener, @@ -11,8 +12,26 @@ void xdg_activation_v1_handle_request_activate(struct wl_listener *listener, struct wlr_xdg_surface *xdg_surface = wlr_xdg_surface_from_wlr_surface(event->surface); + if (xdg_surface == NULL) { + return; + } struct sway_view *view = xdg_surface->data; - if (!xdg_surface->mapped || view == NULL) { + if (view == NULL) { + return; + } + + if (!xdg_surface->mapped) { + // This is a startup notification. If we are tracking it, the data + // field is a launcher_ctx. + struct launcher_ctx *ctx = event->token->data; + if (!ctx || ctx->activated) { + // This ctx has already been activated and cannot be used again + // for a startup notification. It will be destroyed + return; + } else { + ctx->activated = true; + view_assign_ctx(view, ctx); + } return; } diff --git a/swaybar/bar.c b/swaybar/bar.c index 6ffdc9b4..5e4ebd97 100644 --- a/swaybar/bar.c +++ b/swaybar/bar.c @@ -51,9 +51,6 @@ static void swaybar_output_free(struct swaybar_output *output) { if (output->surface != NULL) { wl_surface_destroy(output->surface); } - if (output->input_region != NULL) { - wl_region_destroy(output->input_region); - } wl_output_destroy(output->output); destroy_buffer(&output->buffers[0]); destroy_buffer(&output->buffers[1]); @@ -113,10 +110,9 @@ static void add_layer_surface(struct swaybar_output *output) { if (overlay) { // Empty input region - output->input_region = wl_compositor_create_region(bar->compositor); - assert(output->input_region); - - wl_surface_set_input_region(output->surface, output->input_region); + struct wl_region *region = wl_compositor_create_region(bar->compositor); + wl_surface_set_input_region(output->surface, region); + wl_region_destroy(region); } zwlr_layer_surface_v1_set_anchor(output->layer_surface, config->position); diff --git a/swaybar/config.c b/swaybar/config.c index abedaec0..5e828773 100644 --- a/swaybar/config.c +++ b/swaybar/config.c @@ -26,7 +26,7 @@ struct swaybar_config *init_config(void) { config->status_command = NULL; config->pango_markup = false; config->position = parse_position("bottom"); - config->font = strdup("monospace 10"); + config->font_description = pango_font_description_from_string("monospace 10"); config->mode = strdup("dock"); config->hidden_state = strdup("hide"); config->sep_symbol = NULL; @@ -105,7 +105,7 @@ void free_tray_binding(struct tray_binding *binding) { void free_config(struct swaybar_config *config) { free(config->status_command); - free(config->font); + pango_font_description_free(config->font_description); free(config->mode); free(config->hidden_state); free(config->sep_symbol); diff --git a/swaybar/i3bar.c b/swaybar/i3bar.c index 6d00befb..ccd5a076 100644 --- a/swaybar/i3bar.c +++ b/swaybar/i3bar.c @@ -269,11 +269,16 @@ bool i3bar_handle_readable(struct status_line *status) { enum hotspot_event_handling i3bar_block_send_click(struct status_line *status, struct i3bar_block *block, double x, double y, double rx, double ry, - double w, double h, int scale, uint32_t button) { + double w, double h, int scale, uint32_t button, bool released) { sway_log(SWAY_DEBUG, "block %s clicked", block->name); if (!block->name || !status->click_events) { return HOTSPOT_PROCESS; } + if (released) { + // Since we handle the pressed event, also handle the released event + // to block it from falling through to a binding in the bar + return HOTSPOT_IGNORE; + } struct json_object *event_json = json_object_new_object(); json_object_object_add(event_json, "name", diff --git a/swaybar/input.c b/swaybar/input.c index c8c8f0d4..8eccf542 100644 --- a/swaybar/input.c +++ b/swaybar/input.c @@ -141,14 +141,15 @@ static bool check_bindings(struct swaybar *bar, uint32_t button, } static bool process_hotspots(struct swaybar_output *output, - double x, double y, uint32_t button) { + double x, double y, uint32_t button, uint32_t state) { + bool released = state == WL_POINTER_BUTTON_STATE_RELEASED; struct swaybar_hotspot *hotspot; wl_list_for_each(hotspot, &output->hotspots, link) { if (x >= hotspot->x && y >= hotspot->y && x < hotspot->x + hotspot->width && y < hotspot->y + hotspot->height) { if (HOTSPOT_IGNORE == hotspot->callback(output, hotspot, x, y, - button, hotspot->data)) { + button, released, hotspot->data)) { return true; } } @@ -166,14 +167,11 @@ static void wl_pointer_button(void *data, struct wl_pointer *wl_pointer, return; } - if (check_bindings(seat->bar, button, state)) { + if (process_hotspots(output, pointer->x, pointer->y, button, state)) { return; } - if (state != WL_POINTER_BUTTON_STATE_PRESSED) { - return; - } - process_hotspots(output, pointer->x, pointer->y, button); + check_bindings(seat->bar, button, state); } static void workspace_next(struct swaybar *bar, struct swaybar_output *output, @@ -222,15 +220,15 @@ static void workspace_next(struct swaybar *bar, struct swaybar_output *output, static void process_discrete_scroll(struct swaybar_seat *seat, struct swaybar_output *output, struct swaybar_pointer *pointer, uint32_t axis, wl_fixed_t value) { - // If there is a button press binding, execute it, skip default behavior, - // and check button release bindings uint32_t button = wl_axis_to_button(axis, value); - if (check_bindings(seat->bar, button, WL_POINTER_BUTTON_STATE_PRESSED)) { - check_bindings(seat->bar, button, WL_POINTER_BUTTON_STATE_RELEASED); + if (process_hotspots(output, pointer->x, pointer->y, button, WL_POINTER_BUTTON_STATE_PRESSED)) { + // (Currently hotspots don't do anything on release events, so no need to emit one) return; } - if (process_hotspots(output, pointer->x, pointer->y, button)) { + // If there is a button press binding, execute it, and check button release bindings + if (check_bindings(seat->bar, button, WL_POINTER_BUTTON_STATE_PRESSED)) { + check_bindings(seat->bar, button, WL_POINTER_BUTTON_STATE_RELEASED); return; } @@ -403,7 +401,8 @@ static void wl_touch_up(void *data, struct wl_touch *wl_touch, } if (time - slot->time < 500) { // Tap, treat it like a pointer click - process_hotspots(slot->output, slot->x, slot->y, BTN_LEFT); + process_hotspots(slot->output, slot->x, slot->y, BTN_LEFT, WL_POINTER_BUTTON_STATE_PRESSED); + // (Currently hotspots don't do anything on release events, so no need to emit one) } slot->output = NULL; } diff --git a/swaybar/ipc.c b/swaybar/ipc.c index 2cb235bf..9d81a9fb 100644 --- a/swaybar/ipc.c +++ b/swaybar/ipc.c @@ -147,8 +147,10 @@ static bool ipc_parse_config( json_object *font = json_object_object_get(bar_config, "font"); if (font) { - free(config->font); - config->font = parse_font(json_object_get_string(font)); + pango_font_description_free(config->font_description); + char *font_value = parse_font(json_object_get_string(font)); + config->font_description = pango_font_description_from_string(font_value); + free(font_value); } json_object *gaps = json_object_object_get(bar_config, "gaps"); @@ -485,8 +487,7 @@ static bool handle_barconfig_update(struct swaybar *bar, const char *payload, destroy_layer_surface(output); wl_list_remove(&output->link); wl_list_insert(&bar->unused_outputs, &output->link); - } else if (!oldcfg->font || !newcfg->font || - strcmp(oldcfg->font, newcfg->font) != 0) { + } else if (!pango_font_description_equal(oldcfg->font_description, newcfg->font_description)) { output->height = 0; // force update height } } diff --git a/swaybar/meson.build b/swaybar/meson.build index 9feb3cd2..e5f1811e 100644 --- a/swaybar/meson.build +++ b/swaybar/meson.build @@ -8,7 +8,6 @@ tray_files = have_tray ? [ swaybar_deps = [ cairo, - client_protos, gdk_pixbuf, jsonc, math, @@ -32,7 +31,8 @@ executable( 'main.c', 'render.c', 'status_line.c', - tray_files + tray_files, + wl_protos_src, ], include_directories: [sway_inc], dependencies: swaybar_deps, diff --git a/swaybar/render.c b/swaybar/render.c index dcde6b9e..95f6e5be 100644 --- a/swaybar/render.c +++ b/swaybar/render.c @@ -62,7 +62,7 @@ static uint32_t render_status_line_error(struct render_context *ctx, double *x) int margin = 3; double ws_vertical_padding = output->bar->config->status_padding; - char *font = output->bar->config->font; + PangoFontDescription *font = output->bar->config->font_description; int text_width, text_height; get_text_size(cairo, font, &text_width, &text_height, NULL, 1, false, "%s", error); @@ -97,7 +97,7 @@ static uint32_t render_status_line_text(struct render_context *ctx, double *x) { cairo_set_source_u32(cairo, fontcolor); int text_width, text_height; - get_text_size(cairo, config->font, &text_width, &text_height, NULL, + get_text_size(cairo, config->font_description, &text_width, &text_height, NULL, 1, config->pango_markup, "%s", text); double ws_vertical_padding = config->status_padding; @@ -115,7 +115,7 @@ static uint32_t render_status_line_text(struct render_context *ctx, double *x) { double text_y = height / 2.0 - text_height / 2.0; cairo_move_to(cairo, *x, (int)floor(text_y)); choose_text_aa_mode(ctx, fontcolor); - render_text(cairo, config->font, 1, config->pango_markup, "%s", text); + render_text(cairo, config->font_description, 1, config->pango_markup, "%s", text); *x -= margin; return output->height; } @@ -160,7 +160,7 @@ static void render_sharp_line(cairo_t *cairo, uint32_t color, static enum hotspot_event_handling block_hotspot_callback( struct swaybar_output *output, struct swaybar_hotspot *hotspot, - double x, double y, uint32_t button, void *data) { + double x, double y, uint32_t button, bool released, void *data) { struct i3bar_block *block = data; struct status_line *status = output->bar->status; return i3bar_block_send_click(status, block, x, y, @@ -168,7 +168,7 @@ static enum hotspot_event_handling block_hotspot_callback( y - (double)hotspot->y, (double)hotspot->width, (double)hotspot->height, - output->scale, button); + output->scale, button, released); } static void i3bar_block_unref_callback(void *data) { @@ -190,7 +190,7 @@ static uint32_t render_status_block(struct render_context *ctx, struct swaybar_output *output = ctx->output; struct swaybar_config *config = output->bar->config; int text_width, text_height; - get_text_size(cairo, config->font, &text_width, &text_height, NULL, 1, + get_text_size(cairo, config->font_description, &text_width, &text_height, NULL, 1, block->markup, "%s", text); int margin = 3; @@ -199,7 +199,7 @@ static uint32_t render_status_block(struct render_context *ctx, int width = text_width; if (block->min_width_str) { int w; - get_text_size(cairo, config->font, &w, NULL, NULL, 1, block->markup, + get_text_size(cairo, config->font_description, &w, NULL, NULL, 1, block->markup, "%s", block->min_width_str); block->min_width = w; } @@ -229,7 +229,7 @@ static uint32_t render_status_block(struct render_context *ctx, int sep_block_width = block->separator_block_width; if (!edge) { if (config->sep_symbol) { - get_text_size(cairo, config->font, &sep_width, &sep_height, NULL, + get_text_size(cairo, config->font_description, &sep_width, &sep_height, NULL, 1, false, "%s", config->sep_symbol); uint32_t _ideal_height = sep_height + ws_vertical_padding * 2; uint32_t _ideal_surface_height = _ideal_height; @@ -307,7 +307,7 @@ static uint32_t render_status_block(struct render_context *ctx, color = block->urgent ? config->colors.urgent_workspace.text : color; cairo_set_source_u32(cairo, color); choose_text_aa_mode(ctx, color); - render_text(cairo, config->font, 1, block->markup, "%s", text); + render_text(cairo, config->font_description, 1, block->markup, "%s", text); x_pos += width; if (block->border_set || block->urgent) { @@ -331,7 +331,7 @@ static uint32_t render_status_block(struct render_context *ctx, double sep_y = height / 2.0 - sep_height / 2.0; cairo_move_to(cairo, offset, (int)floor(sep_y)); choose_text_aa_mode(ctx, color); - render_text(cairo, config->font, 1, false, + render_text(cairo, config->font_description, 1, false, "%s", config->sep_symbol); } else { cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE); @@ -354,7 +354,7 @@ static void predict_status_block_pos(cairo_t *cairo, struct swaybar_config *config = output->bar->config; int text_width, text_height; - get_text_size(cairo, config->font, &text_width, &text_height, NULL, 1, + get_text_size(cairo, config->font_description, &text_width, &text_height, NULL, 1, block->markup, "%s", block->full_text); int margin = 3; @@ -364,7 +364,7 @@ static void predict_status_block_pos(cairo_t *cairo, if (block->min_width_str) { int w; - get_text_size(cairo, config->font, &w, NULL, NULL, + get_text_size(cairo, config->font_description, &w, NULL, NULL, 1, block->markup, "%s", block->min_width_str); block->min_width = w; } @@ -391,7 +391,7 @@ static void predict_status_block_pos(cairo_t *cairo, int sep_block_width = block->separator_block_width; if (!edge) { if (config->sep_symbol) { - get_text_size(cairo, config->font, &sep_width, &sep_height, NULL, + get_text_size(cairo, config->font_description, &sep_width, &sep_height, NULL, 1, false, "%s", config->sep_symbol); uint32_t _ideal_height = sep_height + ws_vertical_padding * 2; uint32_t _ideal_surface_height = _ideal_height; @@ -426,7 +426,7 @@ static uint32_t predict_workspace_button_length(cairo_t *cairo, struct swaybar_config *config = output->bar->config; int text_width, text_height; - get_text_size(cairo, config->font, &text_width, &text_height, NULL, 1, + get_text_size(cairo, config->font_description, &text_width, &text_height, NULL, 1, config->pango_markup, "%s", ws->label); int ws_vertical_padding = WS_VERTICAL_PADDING; @@ -474,7 +474,7 @@ static uint32_t predict_binding_mode_indicator_length(cairo_t *cairo, } int text_width, text_height; - get_text_size(cairo, config->font, &text_width, &text_height, NULL, + get_text_size(cairo, config->font_description, &text_width, &text_height, NULL, 1, output->bar->mode_pango_markup, "%s", mode); @@ -551,7 +551,7 @@ static uint32_t render_binding_mode_indicator(struct render_context *ctx, cairo_t *cairo = ctx->cairo; struct swaybar_config *config = output->bar->config; int text_width, text_height; - get_text_size(cairo, config->font, &text_width, &text_height, NULL, + get_text_size(cairo, config->font_description, &text_width, &text_height, NULL, 1, output->bar->mode_pango_markup, "%s", mode); @@ -592,17 +592,22 @@ static uint32_t render_binding_mode_indicator(struct render_context *ctx, cairo_set_source_u32(cairo, config->colors.binding_mode.text); cairo_move_to(cairo, x + width / 2 - text_width / 2, (int)floor(text_y)); choose_text_aa_mode(ctx, config->colors.binding_mode.text); - render_text(cairo, config->font, 1, output->bar->mode_pango_markup, + render_text(cairo, config->font_description, 1, output->bar->mode_pango_markup, "%s", mode); return output->height; } static enum hotspot_event_handling workspace_hotspot_callback( struct swaybar_output *output, struct swaybar_hotspot *hotspot, - double x, double y, uint32_t button, void *data) { + double x, double y, uint32_t button, bool released, void *data) { if (button != BTN_LEFT) { return HOTSPOT_PROCESS; } + if (released) { + // Since we handle the pressed event, also handle the released event + // to block it from falling through to a binding in the bar + return HOTSPOT_IGNORE; + } ipc_send_workspace_command(output->bar, (const char *)data); return HOTSPOT_IGNORE; } @@ -626,7 +631,7 @@ static uint32_t render_workspace_button(struct render_context *ctx, cairo_t *cairo = ctx->cairo; int text_width, text_height; - get_text_size(cairo, config->font, &text_width, &text_height, NULL, + get_text_size(cairo, config->font_description, &text_width, &text_height, NULL, 1, config->pango_markup, "%s", ws->label); int ws_vertical_padding = WS_VERTICAL_PADDING; @@ -666,7 +671,7 @@ static uint32_t render_workspace_button(struct render_context *ctx, cairo_set_source_u32(cairo, box_colors.text); cairo_move_to(cairo, *x + width / 2 - text_width / 2, (int)floor(text_y)); choose_text_aa_mode(ctx, box_colors.text); - render_text(cairo, config->font, 1, config->pango_markup, + render_text(cairo, config->font_description, 1, config->pango_markup, "%s", ws->label); struct swaybar_hotspot *hotspot = calloc(1, sizeof(struct swaybar_hotspot)); @@ -699,7 +704,7 @@ static uint32_t render_to_cairo(struct render_context *ctx) { cairo_paint(cairo); int th; - get_text_size(cairo, config->font, NULL, &th, NULL, 1, false, ""); + get_text_size(cairo, config->font_description, NULL, &th, NULL, 1, false, ""); uint32_t max_height = (th + WS_VERTICAL_PADDING * 4); /* * Each render_* function takes the actual height of the bar, and returns @@ -831,6 +836,15 @@ void render_frame(struct swaybar_output *output) { wl_surface_damage(output->surface, 0, 0, output->width, output->height); + uint32_t bg_alpha = ctx.background_color & 0xFF; + if (bg_alpha == 0xFF) { + struct wl_region *region = + wl_compositor_create_region(output->bar->compositor); + wl_region_add(region, 0, 0, INT32_MAX, INT32_MAX); + wl_surface_set_opaque_region(output->surface, region); + wl_region_destroy(region); + } + struct wl_callback *frame_callback = wl_surface_frame(output->surface); wl_callback_add_listener(frame_callback, &output_frame_listener, output); output->frame_scheduled = true; diff --git a/swaybar/tray/item.c b/swaybar/tray/item.c index 6d4b17bf..1f18b8bb 100644 --- a/swaybar/tray/item.c +++ b/swaybar/tray/item.c @@ -385,13 +385,18 @@ static int cmp_sni_id(const void *item, const void *cmp_to) { static enum hotspot_event_handling icon_hotspot_callback( struct swaybar_output *output, struct swaybar_hotspot *hotspot, - double x, double y, uint32_t button, void *data) { + double x, double y, uint32_t button, bool released, void *data) { sway_log(SWAY_DEBUG, "Clicked on %s", (char *)data); struct swaybar_tray *tray = output->bar->tray; int idx = list_seq_find(tray->items, cmp_sni_id, data); if (idx != -1) { + if (released) { + // Since we handle the pressed event, also handle the released event + // to block it from falling through to a binding in the bar + return HOTSPOT_IGNORE; + } struct swaybar_sni *sni = tray->items->items[idx]; // guess global position since wayland doesn't expose it struct swaybar_config *config = tray->bar->config; @@ -466,6 +471,11 @@ uint32_t render_sni(cairo_t *cairo, struct swaybar_output *output, double *x, sni->target_size = target_size; } + // Passive + if (sni->status && sni->status[0] == 'P') { + return 0; + } + int icon_size; cairo_surface_t *icon; if (sni->icon) { diff --git a/swaymsg/main.c b/swaymsg/main.c index 0d9dc5a0..c0b5809e 100644 --- a/swaymsg/main.c +++ b/swaymsg/main.c @@ -185,12 +185,13 @@ static void pretty_print_seat(json_object *i) { } static void pretty_print_output(json_object *o) { - json_object *name, *rect, *focused, *active, *ws, *current_mode; + json_object *name, *rect, *focused, *active, *ws, *current_mode, *non_desktop; json_object_object_get_ex(o, "name", &name); json_object_object_get_ex(o, "rect", &rect); json_object_object_get_ex(o, "focused", &focused); json_object_object_get_ex(o, "active", &active); json_object_object_get_ex(o, "current_workspace", &ws); + json_object_object_get_ex(o, "non_desktop", &non_desktop); json_object *make, *model, *serial, *scale, *scale_filter, *subpixel, *transform, *max_render_time, *adaptive_sync_status; json_object_object_get_ex(o, "make", &make); @@ -213,7 +214,15 @@ static void pretty_print_output(json_object *o) { json_object_object_get_ex(current_mode, "height", &height); json_object_object_get_ex(current_mode, "refresh", &refresh); - if (json_object_get_boolean(active)) { + if (json_object_get_boolean(non_desktop)) { + printf( + "Output %s '%s %s %s' (non-desktop)\n", + json_object_get_string(name), + json_object_get_string(make), + json_object_get_string(model), + json_object_get_string(serial) + ); + } else if (json_object_get_boolean(active)) { printf( "Output %s '%s %s %s'%s\n" " Current mode: %dx%d @ %.3f Hz\n" @@ -262,14 +271,22 @@ static void pretty_print_output(json_object *o) { for (size_t i = 0; i < modes_len; ++i) { json_object *mode = json_object_array_get_idx(modes, i); - json_object *mode_width, *mode_height, *mode_refresh; + json_object *mode_width, *mode_height, *mode_refresh, + *mode_picture_aspect_ratio; json_object_object_get_ex(mode, "width", &mode_width); json_object_object_get_ex(mode, "height", &mode_height); json_object_object_get_ex(mode, "refresh", &mode_refresh); + json_object_object_get_ex(mode, "picture_aspect_ratio", + &mode_picture_aspect_ratio); - printf(" %dx%d @ %.3f Hz\n", json_object_get_int(mode_width), + printf(" %dx%d @ %.3f Hz", json_object_get_int(mode_width), json_object_get_int(mode_height), (double)json_object_get_int(mode_refresh) / 1000); + if (mode_picture_aspect_ratio && + strcmp("none", json_object_get_string(mode_picture_aspect_ratio)) != 0) { + printf(" (%s)", json_object_get_string(mode_picture_aspect_ratio)); + } + printf("\n"); } } @@ -288,28 +305,83 @@ static void pretty_print_config(json_object *c) { printf("%s\n", json_object_get_string(config)); } +static void pretty_print_tree(json_object *obj, int indent) { + for (int i = 0; i < indent; i++) { + printf(" "); + } + + int id = json_object_get_int(json_object_object_get(obj, "id")); + const char *name = json_object_get_string(json_object_object_get(obj, "name")); + const char *type = json_object_get_string(json_object_object_get(obj, "type")); + const char *shell = json_object_get_string(json_object_object_get(obj, "shell")); + + printf("#%d: %s \"%s\"", id, type, name); + + if (shell != NULL) { + int pid = json_object_get_int(json_object_object_get(obj, "pid")); + const char *app_id = json_object_get_string(json_object_object_get(obj, "app_id")); + json_object *window_props_obj = json_object_object_get(obj, "window_properties"); + const char *instance = json_object_get_string(json_object_object_get(window_props_obj, "instance")); + const char *class = json_object_get_string(json_object_object_get(window_props_obj, "class")); + int x11_id = json_object_get_int(json_object_object_get(obj, "window")); + + printf(" (%s, pid: %d", shell, pid); + if (app_id != NULL) { + printf(", app_id: \"%s\"", app_id); + } + if (instance != NULL) { + printf(", instance: \"%s\"", instance); + } + if (class != NULL) { + printf(", class: \"%s\"", class); + } + if (x11_id != 0) { + printf(", X11 window: 0x%X", x11_id); + } + printf(")"); + } + + printf("\n"); + + json_object *nodes_obj = json_object_object_get(obj, "nodes"); + size_t len = json_object_array_length(nodes_obj); + for (size_t i = 0; i < len; i++) { + pretty_print_tree(json_object_array_get_idx(nodes_obj, i), indent + 1); + } + + json_object *floating_nodes_obj; + json_bool floating_nodes = json_object_object_get_ex(obj, "floating_nodes", &floating_nodes_obj); + if (floating_nodes) { + size_t len = json_object_array_length(floating_nodes_obj); + for (size_t i = 0; i < len; i++) { + pretty_print_tree(json_object_array_get_idx(floating_nodes_obj, i), indent + 1); + } + } +} + static void pretty_print(int type, json_object *resp) { - if (type != IPC_COMMAND && type != IPC_GET_WORKSPACES && - type != IPC_GET_INPUTS && type != IPC_GET_OUTPUTS && - type != IPC_GET_VERSION && type != IPC_GET_SEATS && - type != IPC_GET_CONFIG && type != IPC_SEND_TICK) { - printf("%s\n", json_object_to_json_string_ext(resp, - JSON_C_TO_STRING_PRETTY | JSON_C_TO_STRING_SPACED)); + switch (type) { + case IPC_SEND_TICK: return; - } - - if (type == IPC_SEND_TICK) { - return; - } - - if (type == IPC_GET_VERSION) { + case IPC_GET_VERSION: pretty_print_version(resp); return; - } - - if (type == IPC_GET_CONFIG) { + case IPC_GET_CONFIG: pretty_print_config(resp); return; + case IPC_GET_TREE: + pretty_print_tree(resp, 0); + return; + case IPC_COMMAND: + case IPC_GET_WORKSPACES: + case IPC_GET_INPUTS: + case IPC_GET_OUTPUTS: + case IPC_GET_SEATS: + break; + default: + printf("%s\n", json_object_to_json_string_ext(resp, + JSON_C_TO_STRING_PRETTY | JSON_C_TO_STRING_SPACED)); + return; } json_object *obj; diff --git a/swaynag/config.c b/swaynag/config.c index 6db7cce5..a0bf3197 100644 --- a/swaynag/config.c +++ b/swaynag/config.c @@ -11,24 +11,40 @@ #include "util.h" #include "wlr-layer-shell-unstable-v1-client-protocol.h" -static char *read_from_stdin(void) { - char *buffer = NULL; - size_t buffer_len = 0; - char *line = NULL; - size_t line_size = 0; - ssize_t nread; - while ((nread = getline(&line, &line_size, stdin)) != -1) { +static char *read_and_trim_stdin(void) { + char *buffer = NULL, *line = NULL; + size_t buffer_len = 0, line_size = 0; + while (1) { + ssize_t nread = getline(&line, &line_size, stdin); + if (nread == -1) { + if (feof(stdin)) { + break; + } else { + perror("getline"); + goto freeline; + } + } buffer = realloc(buffer, buffer_len + nread + 1); - snprintf(&buffer[buffer_len], nread + 1, "%s", line); + if (!buffer) { + perror("realloc"); + goto freebuf; + } + memcpy(&buffer[buffer_len], line, nread + 1); buffer_len += nread; } free(line); - while (buffer && buffer[buffer_len - 1] == '\n') { + while (buffer_len && buffer[buffer_len - 1] == '\n') { buffer[--buffer_len] = '\0'; } return buffer; + +freeline: + free(line); +freebuf: + free(buffer); + return NULL; } int swaynag_parse_options(int argc, char **argv, struct swaynag *swaynag, @@ -150,8 +166,11 @@ int swaynag_parse_options(int argc, char **argv, struct swaynag *swaynag, fprintf(stderr, "Missing action for button %s\n", optarg); return EXIT_FAILURE; } - struct swaynag_button *button; - button = calloc(sizeof(struct swaynag_button), 1); + struct swaynag_button *button = calloc(sizeof(struct swaynag_button), 1); + if (!button) { + perror("calloc"); + return EXIT_FAILURE; + } button->text = strdup(optarg); button->type = SWAYNAG_ACTION_COMMAND; button->action = strdup(argv[optind]); @@ -208,21 +227,26 @@ int swaynag_parse_options(int argc, char **argv, struct swaynag *swaynag, case 'f': // Font if (type) { free(type->font); + pango_font_description_free(type->font_description); type->font = strdup(optarg); + type->font_description = pango_font_description_from_string(type->font); } break; case 'l': // Detailed Message if (swaynag) { free(swaynag->details.message); - swaynag->details.message = read_from_stdin(); + swaynag->details.message = read_and_trim_stdin(); + if (!swaynag->details.message) { + return EXIT_FAILURE; + } swaynag->details.button_up.text = strdup("▲"); swaynag->details.button_down.text = strdup("▼"); } break; case 'L': // Detailed Button Text if (swaynag) { - free(swaynag->details.button_details->text); - swaynag->details.button_details->text = strdup(optarg); + free(swaynag->details.button_details.text); + swaynag->details.button_details.text = strdup(optarg); } break; case 'm': // Message @@ -239,8 +263,7 @@ int swaynag_parse_options(int argc, char **argv, struct swaynag *swaynag, break; case 's': // Dismiss Button Text if (swaynag) { - struct swaynag_button *button_close; - button_close = swaynag->buttons->items[0]; + struct swaynag_button *button_close = swaynag->buttons->items[0]; free(button_close->text); button_close->text = strdup(optarg); } @@ -399,23 +422,24 @@ int swaynag_load_config(char *path, struct swaynag *swaynag, list_t *types) { if (line[0] == '[') { char *close = strchr(line, ']'); - if (!close) { - fprintf(stderr, "Closing bracket not found on line %d\n", - line_number); + if (!close || close != &line[nread - 2] || nread <= 3) { + fprintf(stderr, "Line %d is malformed\n", line_number); result = 1; break; } - char *name = calloc(1, close - line); - strncat(name, line + 1, close - line - 1); - type = swaynag_type_get(types, name); + *close = '\0'; + type = swaynag_type_get(types, &line[1]); if (!type) { - type = swaynag_type_new(name); + type = swaynag_type_new(&line[1]); list_add(types, type); } - free(name); } else { - char *flag = malloc(sizeof(char) * (nread + 3)); - sprintf(flag, "--%s", line); + char *flag = malloc(nread + 3); + if (!flag) { + perror("calloc"); + return EXIT_FAILURE; + } + snprintf(flag, nread + 3, "--%s", line); char *argv[] = {"swaynag", flag}; result = swaynag_parse_options(2, argv, swaynag, types, type, NULL, NULL); diff --git a/swaynag/main.c b/swaynag/main.c index 88007818..2ce37831 100644 --- a/swaynag/main.c +++ b/swaynag/main.c @@ -20,33 +20,25 @@ void sway_terminate(int code) { } int main(int argc, char **argv) { - int exit_code = EXIT_SUCCESS; + int status = EXIT_SUCCESS; list_t *types = create_list(); swaynag_types_add_default(types); - memset(&swaynag, 0, sizeof(swaynag)); swaynag.buttons = create_list(); wl_list_init(&swaynag.outputs); wl_list_init(&swaynag.seats); - struct swaynag_button *button_close = - calloc(sizeof(struct swaynag_button), 1); - button_close->text = strdup("X"); - button_close->type = SWAYNAG_ACTION_DISMISS; - list_add(swaynag.buttons, button_close); - - swaynag.details.button_details = - calloc(sizeof(struct swaynag_button), 1); - swaynag.details.button_details->text = strdup("Toggle details"); - swaynag.details.button_details->type = SWAYNAG_ACTION_EXPAND; + struct swaynag_button button_close = { 0 }; + button_close.text = strdup("X"); + button_close.type = SWAYNAG_ACTION_DISMISS; + list_add(swaynag.buttons, &button_close); char *config_path = NULL; bool debug = false; - int launch_status = swaynag_parse_options(argc, argv, NULL, NULL, NULL, + status = swaynag_parse_options(argc, argv, NULL, NULL, NULL, &config_path, &debug); - if (launch_status != 0) { - exit_code = launch_status; + if (status != 0) { goto cleanup; } sway_log_init(debug ? SWAY_DEBUG : SWAY_ERROR, NULL); @@ -56,29 +48,29 @@ int main(int argc, char **argv) { } if (config_path) { sway_log(SWAY_DEBUG, "Loading config file: %s", config_path); - int config_status = swaynag_load_config(config_path, &swaynag, types); - free(config_path); - if (config_status != 0) { - exit_code = config_status; + status = swaynag_load_config(config_path, &swaynag, types); + if (status != 0) { goto cleanup; } } + swaynag.details.button_details.text = strdup("Toggle details"); + swaynag.details.button_details.type = SWAYNAG_ACTION_EXPAND; + if (argc > 1) { struct swaynag_type *type_args = swaynag_type_new(""); list_add(types, type_args); - int result = swaynag_parse_options(argc, argv, &swaynag, types, + status = swaynag_parse_options(argc, argv, &swaynag, types, type_args, NULL, NULL); - if (result != 0) { - exit_code = result; + if (status != 0) { goto cleanup; } } if (!swaynag.message) { sway_log(SWAY_ERROR, "No message passed. Please provide --message/-m"); - exit_code = EXIT_FAILURE; + status = EXIT_FAILURE; goto cleanup; } @@ -99,10 +91,7 @@ int main(int argc, char **argv) { swaynag_types_free(types); if (swaynag.details.message) { - list_add(swaynag.buttons, swaynag.details.button_details); - } else { - free(swaynag.details.button_details->text); - free(swaynag.details.button_details); + list_add(swaynag.buttons, &swaynag.details.button_details); } sway_log(SWAY_DEBUG, "Output: %s", swaynag.type->output); @@ -120,12 +109,11 @@ int main(int argc, char **argv) { swaynag_setup(&swaynag); swaynag_run(&swaynag); - return exit_code; + return status; cleanup: swaynag_types_free(types); - free(swaynag.details.button_details->text); - free(swaynag.details.button_details); + free(swaynag.details.button_details.text); swaynag_destroy(&swaynag); - return exit_code; + return status; } diff --git a/swaynag/meson.build b/swaynag/meson.build index 71f2fc2d..aef21483 100644 --- a/swaynag/meson.build +++ b/swaynag/meson.build @@ -5,13 +5,14 @@ executable( 'render.c', 'swaynag.c', 'types.c', + wl_protos_src, ], include_directories: [sway_inc], dependencies: [ cairo, - client_protos, pango, pangocairo, + rt, wayland_client, wayland_cursor, ], diff --git a/swaynag/render.c b/swaynag/render.c index d72f42c2..21b03289 100644 --- a/swaynag/render.c +++ b/swaynag/render.c @@ -9,7 +9,7 @@ static uint32_t render_message(cairo_t *cairo, struct swaynag *swaynag) { int text_width, text_height; - get_text_size(cairo, swaynag->type->font, &text_width, &text_height, NULL, + get_text_size(cairo, swaynag->type->font_description, &text_width, &text_height, NULL, 1, true, "%s", swaynag->message); int padding = swaynag->type->message_padding; @@ -22,7 +22,7 @@ static uint32_t render_message(cairo_t *cairo, struct swaynag *swaynag) { cairo_set_source_u32(cairo, swaynag->type->text); cairo_move_to(cairo, padding, (int)(ideal_height - text_height) / 2); - render_text(cairo, swaynag->type->font, 1, false, + render_text(cairo, swaynag->type->font_description, 1, false, "%s", swaynag->message); return ideal_surface_height; @@ -31,7 +31,7 @@ static uint32_t render_message(cairo_t *cairo, struct swaynag *swaynag) { static void render_details_scroll_button(cairo_t *cairo, struct swaynag *swaynag, struct swaynag_button *button) { int text_width, text_height; - get_text_size(cairo, swaynag->type->font, &text_width, &text_height, NULL, + get_text_size(cairo, swaynag->type->font_description, &text_width, &text_height, NULL, 1, true, "%s", button->text); int border = swaynag->type->button_border_thickness; @@ -50,17 +50,17 @@ static void render_details_scroll_button(cairo_t *cairo, cairo_set_source_u32(cairo, swaynag->type->button_text); cairo_move_to(cairo, button->x + border + padding, button->y + border + (button->height - text_height) / 2); - render_text(cairo, swaynag->type->font, 1, true, + render_text(cairo, swaynag->type->font_description, 1, true, "%s", button->text); } static int get_detailed_scroll_button_width(cairo_t *cairo, struct swaynag *swaynag) { int up_width, down_width, temp_height; - get_text_size(cairo, swaynag->type->font, &up_width, &temp_height, NULL, + get_text_size(cairo, swaynag->type->font_description, &up_width, &temp_height, NULL, 1, true, "%s", swaynag->details.button_up.text); - get_text_size(cairo, swaynag->type->font, &down_width, &temp_height, NULL, + get_text_size(cairo, swaynag->type->font_description, &down_width, &temp_height, NULL, 1, true, "%s", swaynag->details.button_down.text); @@ -83,7 +83,7 @@ static uint32_t render_detailed(cairo_t *cairo, struct swaynag *swaynag, swaynag->details.y = y + decor; swaynag->details.width = width - decor * 2; - PangoLayout *layout = get_pango_layout(cairo, swaynag->type->font, + PangoLayout *layout = get_pango_layout(cairo, swaynag->type->font_description, swaynag->details.message, 1, false); pango_layout_set_width(layout, (swaynag->details.width - padding * 2) * PANGO_SCALE); @@ -172,7 +172,7 @@ static uint32_t render_button(cairo_t *cairo, struct swaynag *swaynag, struct swaynag_button *button = swaynag->buttons->items[button_index]; int text_width, text_height; - get_text_size(cairo, swaynag->type->font, &text_width, &text_height, NULL, + get_text_size(cairo, swaynag->type->font_description, &text_width, &text_height, NULL, 1, true, "%s", button->text); int border = swaynag->type->button_border_thickness; @@ -201,7 +201,7 @@ static uint32_t render_button(cairo_t *cairo, struct swaynag *swaynag, cairo_set_source_u32(cairo, swaynag->type->button_text); cairo_move_to(cairo, button->x + padding, button->y + padding); - render_text(cairo, swaynag->type->font, 1, true, + render_text(cairo, swaynag->type->font_description, 1, true, "%s", button->text); *x = button->x - border; diff --git a/swaynag/swaynag.c b/swaynag/swaynag.c index 9b57d578..5620155d 100644 --- a/swaynag/swaynag.c +++ b/swaynag/swaynag.c @@ -28,8 +28,13 @@ static bool terminal_execute(char *terminal, char *command) { fprintf(tmp, "#!/bin/sh\nrm %s\n%s", fname, command); fclose(tmp); chmod(fname, S_IRUSR | S_IWUSR | S_IXUSR); - char *cmd = malloc(sizeof(char) * (strlen(terminal) + strlen(" -e ") + strlen(fname) + 1)); - sprintf(cmd, "%s -e %s", terminal, fname); + size_t cmd_size = strlen(terminal) + strlen(" -e ") + strlen(fname) + 1; + char *cmd = malloc(cmd_size); + if (!cmd) { + perror("malloc"); + return false; + } + snprintf(cmd, cmd_size, "%s -e %s", terminal, fname); execlp("sh", "sh", "-c", cmd, NULL); sway_log_errno(SWAY_ERROR, "Failed to run command, execlp() returned."); free(cmd); @@ -58,7 +63,7 @@ static void swaynag_button_execute(struct swaynag *swaynag, } else if (pid == 0) { // Child of the child. Will be reparented to the init process char *terminal = getenv("TERMINAL"); - if (button->terminal && terminal && strlen(terminal)) { + if (button->terminal && terminal && *terminal) { sway_log(SWAY_DEBUG, "Found $TERMINAL: %s", terminal); if (!terminal_execute(terminal, button->action)) { swaynag_destroy(swaynag); @@ -138,7 +143,7 @@ static void update_cursor(struct swaynag_seat *seat) { const char *cursor_theme = getenv("XCURSOR_THEME"); unsigned cursor_size = 24; const char *env_cursor_size = getenv("XCURSOR_SIZE"); - if (env_cursor_size && strlen(env_cursor_size) > 0) { + if (env_cursor_size && *env_cursor_size) { errno = 0; char *end; unsigned size = strtoul(env_cursor_size, &end, 10); @@ -339,6 +344,7 @@ static void handle_global(void *data, struct wl_registry *registry, struct swaynag_seat *seat = calloc(1, sizeof(struct swaynag_seat)); if (!seat) { + perror("calloc"); return; } @@ -356,6 +362,10 @@ static void handle_global(void *data, struct wl_registry *registry, if (!swaynag->output) { struct swaynag_output *output = calloc(1, sizeof(struct swaynag_output)); + if (!output) { + perror("calloc"); + return; + } output->wl_output = wl_registry_bind(registry, name, &wl_output_interface, 4); output->wl_name = name; @@ -511,13 +521,8 @@ void swaynag_destroy(struct swaynag *swaynag) { swaynag_seat_destroy(seat); } - if (&swaynag->buffers[0]) { - destroy_buffer(&swaynag->buffers[0]); - } - - if (&swaynag->buffers[1]) { - destroy_buffer(&swaynag->buffers[1]); - } + destroy_buffer(&swaynag->buffers[0]); + destroy_buffer(&swaynag->buffers[1]); if (swaynag->outputs.prev || swaynag->outputs.next) { struct swaynag_output *output, *temp; diff --git a/swaynag/types.c b/swaynag/types.c index 7bef0f87..a46aacd5 100644 --- a/swaynag/types.c +++ b/swaynag/types.c @@ -33,6 +33,8 @@ struct swaynag_type *swaynag_type_new(const char *name) { void swaynag_types_add_default(list_t *types) { struct swaynag_type *type_defaults = swaynag_type_new(""); type_defaults->font = strdup("pango:Monospace 10"); + type_defaults->font_description = + pango_font_description_from_string(type_defaults->font); type_defaults->anchors = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT; @@ -94,6 +96,10 @@ void swaynag_type_merge(struct swaynag_type *dest, struct swaynag_type *src) { dest->font = strdup(src->font); } + if (src->font_description) { + dest->font_description = pango_font_description_copy(src->font_description); + } + if (src->output) { dest->output = strdup(src->output); } @@ -173,6 +179,7 @@ void swaynag_type_merge(struct swaynag_type *dest, struct swaynag_type *src) { void swaynag_type_free(struct swaynag_type *type) { free(type->name); free(type->font); + pango_font_description_free(type->font_description); free(type->output); free(type); }