diff --git a/.gitignore b/.gitignore index ba02e504..160e0867 100644 --- a/.gitignore +++ b/.gitignore @@ -2,11 +2,11 @@ install_manifest.txt *.swp *.o *.a -.cache/ bin/ test/ build/ build-*/ +.cache/ !build-scripts !build-scripts/* .lvimrc diff --git a/README.md b/README.md index e5b467ef..f14e31aa 100644 --- a/README.md +++ b/README.md @@ -99,7 +99,8 @@ Install dependencies: * json-c * pango * cairo -* gdk-pixbuf2 (optional: system tray) +* gdk-pixbuf2 (optional: additional image formats for system tray) +* [swaybg] (optional: wallpaper) * [scdoc] (optional: man pages) \* * git (optional: version info) \* diff --git a/common/gesture.c b/common/gesture.c index 8c2efe99..58170443 100644 --- a/common/gesture.c +++ b/common/gesture.c @@ -12,23 +12,6 @@ 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; @@ -38,7 +21,7 @@ char *gesture_parse(const char *input, struct gesture *output) { // Split input type, fingers and directions list_t *split = split_string(input, ":"); if (split->length < 1 || split->length > 3) { - return strformat( + return format_str( "expected [:][:direction], got %s", input); } @@ -51,8 +34,8 @@ char *gesture_parse(const char *input, struct gesture *output) { } 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]); + return format_str("expected hold|pinch|swipe, got %s", + (const char *)split->items[0]); } // Parse optional arguments @@ -67,7 +50,7 @@ char *gesture_parse(const char *input, struct gesture *output) { 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); + return format_str("expected 1-9, got %s", next); } // If there is an argument left, try to parse as direction @@ -95,7 +78,7 @@ char *gesture_parse(const char *input, struct gesture *output) { } else if (strcmp(item, "counterclockwise") == 0) { output->directions |= GESTURE_DIRECTION_COUNTERCLOCKWISE; } else { - return strformat("expected direction, got %s", item); + return format_str("expected direction, got %s", item); } } list_free_items_and_destroy(directions); @@ -163,7 +146,7 @@ static char *gesture_directions_to_string(uint32_t directions) { if (!result) { result = strdup(name); } else { - char *new = strformat("%s+%s", result, name); + char *new = format_str("%s+%s", result, name); free(result); result = new; } @@ -179,7 +162,7 @@ static char *gesture_directions_to_string(uint32_t directions) { char *gesture_to_string(struct gesture *gesture) { char *directions = gesture_directions_to_string(gesture->directions); - char *result = strformat("%s:%u:%s", + char *result = format_str("%s:%u:%s", gesture_type_string(gesture->type), gesture->fingers, directions); free(directions); diff --git a/common/pango.c b/common/pango.c index e04bf80f..288569b3 100644 --- a/common/pango.c +++ b/common/pango.c @@ -84,18 +84,11 @@ void get_text_size(cairo_t *cairo, const PangoFontDescription *desc, int *width, int *baseline, double scale, bool markup, const char *fmt, ...) { va_list args; va_start(args, fmt); - // Add one since vsnprintf excludes null terminator. - int length = vsnprintf(NULL, 0, fmt, args) + 1; + char *buf = vformat_str(fmt, args); va_end(args); - - char *buf = malloc(length); if (buf == NULL) { - sway_log(SWAY_ERROR, "Failed to allocate memory"); return; } - va_start(args, fmt); - vsnprintf(buf, length, fmt, args); - va_end(args); PangoLayout *layout = get_pango_layout(cairo, desc, buf, scale, markup); pango_cairo_update_layout(cairo, layout); @@ -104,6 +97,7 @@ void get_text_size(cairo_t *cairo, const PangoFontDescription *desc, int *width, *baseline = pango_layout_get_baseline(layout) / PANGO_SCALE; } g_object_unref(layout); + free(buf); } @@ -125,18 +119,11 @@ void render_text(cairo_t *cairo, const PangoFontDescription *desc, double scale, bool markup, const char *fmt, ...) { va_list args; va_start(args, fmt); - // Add one since vsnprintf excludes null terminator. - int length = vsnprintf(NULL, 0, fmt, args) + 1; + char *buf = vformat_str(fmt, args); va_end(args); - - char *buf = malloc(length); if (buf == NULL) { - sway_log(SWAY_ERROR, "Failed to allocate memory"); return; } - va_start(args, fmt); - vsnprintf(buf, length, fmt, args); - va_end(args); PangoLayout *layout = get_pango_layout(cairo, desc, buf, scale, markup); cairo_font_options_t *fo = cairo_font_options_create(); @@ -146,5 +133,6 @@ void render_text(cairo_t *cairo, const PangoFontDescription *desc, pango_cairo_update_layout(cairo, layout); pango_cairo_show_layout(cairo, layout); g_object_unref(layout); + free(buf); } diff --git a/common/stringop.c b/common/stringop.c index 7fb3fe12..c503143a 100644 --- a/common/stringop.c +++ b/common/stringop.c @@ -1,5 +1,6 @@ #define _POSIX_C_SOURCE 200809L #include +#include #include #include #include @@ -328,3 +329,35 @@ bool expand_path(char **path) { wordfree(&p); return true; } + +char *vformat_str(const char *fmt, va_list args) { + char *str = NULL; + va_list args_copy; + va_copy(args_copy, args); + + int len = vsnprintf(NULL, 0, fmt, args); + if (len < 0) { + sway_log_errno(SWAY_ERROR, "vsnprintf(\"%s\") failed", fmt); + goto out; + } + + str = malloc(len + 1); + if (str == NULL) { + sway_log_errno(SWAY_ERROR, "malloc() failed"); + goto out; + } + + vsnprintf(str, len + 1, fmt, args_copy); + +out: + va_end(args_copy); + return str; +} + +char *format_str(const char *fmt, ...) { + va_list args; + va_start(args, fmt); + char *str = vformat_str(fmt, args); + va_end(args); + return str; +} diff --git a/completions/meson.build b/completions/meson.build new file mode 100644 index 00000000..6bca9391 --- /dev/null +++ b/completions/meson.build @@ -0,0 +1,57 @@ +if get_option('zsh-completions') + zsh_files = files( + 'zsh/_sway', + 'zsh/_swaymsg', + ) + zsh_install_dir = join_paths(datadir, 'zsh', 'site-functions') + + install_data(zsh_files, install_dir: zsh_install_dir) +endif + +if get_option('bash-completions') + bash_comp = dependency('bash-completion', required: false) + + bash_files = files( + 'bash/sway', + 'bash/swaymsg', + ) + + if get_option('swaybar') + bash_files += files('bash/swaybar') + endif + + if bash_comp.found() + bash_install_dir = bash_comp.get_variable( + pkgconfig: 'completionsdir', + pkgconfig_define: ['datadir', datadir] + ) + else + bash_install_dir = join_paths(datadir, 'bash-completion', 'completions') + endif + + install_data(bash_files, install_dir: bash_install_dir) +endif + +if get_option('fish-completions') + fish_comp = dependency('fish', required: false) + + fish_files = files( + 'fish/sway.fish', + 'fish/swaymsg.fish', + ) + + if get_option('swaynag') + fish_files += files('fish/swaynag.fish') + endif + + if fish_comp.found() + fish_install_dir = fish_comp.get_variable( + pkgconfig: 'completionsdir', + pkgconfig_define: ['datadir', datadir] + ) + else + fish_install_dir = join_paths(datadir, 'fish', 'vendor_completions.d') + endif + + install_data(fish_files, install_dir: fish_install_dir) +endif diff --git a/config.in b/config.in index 05b461db..d478178e 100644 --- a/config.in +++ b/config.in @@ -18,7 +18,7 @@ set $term foot # Your preferred application launcher # Note: pass the final command to swaymsg so that the resulting window can be opened # on the original workspace that the command was run on. -set $menu dmenu_path | dmenu | xargs swaymsg exec -- +set $menu dmenu_path | wmenu | xargs swaymsg exec -- ### Appearance # window corner radius in px @@ -228,7 +228,7 @@ bar { # When the status_command prints a new line to stdout, swaybar updates. # The default just shows the current date and time. - status_command while date +'%Y-%m-%d %I:%M:%S %p'; do sleep 1; done + status_command while date +'%Y-%m-%d %X'; do sleep 1; done colors { statusline #ffffff diff --git a/contrib/autoname-workspaces.py b/contrib/autoname-workspaces.py deleted file mode 100755 index 3ec39928..00000000 --- a/contrib/autoname-workspaces.py +++ /dev/null @@ -1,124 +0,0 @@ -#!/usr/bin/python - -# This script requires i3ipc-python package (install it from a system package manager -# or pip). -# It adds icons to the workspace name for each open window. -# Set your keybindings like this: set $workspace1 workspace number 1 -# Add your icons to WINDOW_ICONS. -# Based on https://github.com/maximbaz/dotfiles/blob/master/bin/i3-autoname-workspaces - -import argparse -import i3ipc -import logging -import re -import signal -import sys - -WINDOW_ICONS = { - "firefox": "", -} - -DEFAULT_ICON = "󰀏" - - -def icon_for_window(window): - name = None - if window.app_id is not None and len(window.app_id) > 0: - name = window.app_id.lower() - elif window.window_class is not None and len(window.window_class) > 0: - name = window.window_class.lower() - - if name in WINDOW_ICONS: - return WINDOW_ICONS[name] - - logging.info("No icon available for window with name: %s" % str(name)) - return DEFAULT_ICON - -def rename_workspaces(ipc): - for workspace in ipc.get_tree().workspaces(): - name_parts = parse_workspace_name(workspace.name) - icon_tuple = () - for w in workspace: - if w.app_id is not None or w.window_class is not None: - icon = icon_for_window(w) - if not ARGUMENTS.duplicates and icon in icon_tuple: - continue - icon_tuple += (icon,) - name_parts["icons"] = " ".join(icon_tuple) + " " - new_name = construct_workspace_name(name_parts) - ipc.command('rename workspace "%s" to "%s"' % (workspace.name, new_name)) - - -def undo_window_renaming(ipc): - for workspace in ipc.get_tree().workspaces(): - name_parts = parse_workspace_name(workspace.name) - name_parts["icons"] = None - new_name = construct_workspace_name(name_parts) - ipc.command('rename workspace "%s" to "%s"' % (workspace.name, new_name)) - ipc.main_quit() - sys.exit(0) - - -def parse_workspace_name(name): - return re.match( - "(?P[0-9]+):?(?P\w+)? ?(?P.+)?", name - ).groupdict() - - -def construct_workspace_name(parts): - new_name = str(parts["num"]) - if parts["shortname"] or parts["icons"]: - new_name += ":" - - if parts["shortname"]: - new_name += parts["shortname"] - - if parts["icons"]: - new_name += " " + parts["icons"] - - return new_name - - -if __name__ == "__main__": - parser = argparse.ArgumentParser( - description="This script automatically changes the workspace name in sway depending on your open applications." - ) - parser.add_argument( - "--duplicates", - "-d", - action="store_true", - help="Set it when you want an icon for each instance of the same application per workspace.", - ) - parser.add_argument( - "--logfile", - "-l", - type=str, - default="/tmp/sway-autoname-workspaces.log", - help="Path for the logfile.", - ) - args = parser.parse_args() - global ARGUMENTS - ARGUMENTS = args - - logging.basicConfig( - level=logging.INFO, - filename=ARGUMENTS.logfile, - filemode="w", - format="%(message)s", - ) - - ipc = i3ipc.Connection() - - for sig in [signal.SIGINT, signal.SIGTERM]: - signal.signal(sig, lambda signal, frame: undo_window_renaming(ipc)) - - def window_event_handler(ipc, e): - if e.change in ["new", "close", "move"]: - rename_workspaces(ipc) - - ipc.on("window", window_event_handler) - - rename_workspaces(ipc) - - ipc.main() - diff --git a/contrib/grimshot b/contrib/grimshot deleted file mode 100755 index 1ec19def..00000000 --- a/contrib/grimshot +++ /dev/null @@ -1,168 +0,0 @@ -#!/bin/sh - -## Grimshot: a helper for screenshots within sway -## Requirements: -## - `grim`: screenshot utility for wayland -## - `slurp`: to select an area -## - `swaymsg`: to read properties of current window -## - `wl-copy`: clipboard utility -## - `jq`: json utility to parse swaymsg output -## - `notify-send`: to show notifications -## Those are needed to be installed, if unsure, run `grimshot check` -## -## See `man 1 grimshot` or `grimshot usage` for further details. - -getTargetDirectory() { - 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}}" -} - -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} -FILE=${3:-$(getTargetDirectory)/$(date -Ins).png} - -if [ "$ACTION" != "save" ] && [ "$ACTION" != "copy" ] && [ "$ACTION" != "check" ]; then - echo "Usage:" - echo " grimshot [--notify] [--cursor] (copy|save) [active|screen|output|area|window] [FILE|-]" - echo " grimshot check" - echo " grimshot usage" - echo "" - echo "Commands:" - echo " copy: Copy the screenshot data into the clipboard." - echo " save: Save the screenshot to a regular file or '-' to pipe to STDOUT." - echo " check: Verify if required tools are installed and exit." - echo " usage: Show this message and exit." - echo "" - echo "Targets:" - echo " active: Currently active window." - echo " screen: All visible outputs." - echo " output: Currently active output." - echo " area: Manually select a region." - echo " window: Manually select a window." - exit -fi - -notify() { - notify-send -t 3000 -a grimshot "$@" -} -notifyOk() { - [ "$NOTIFY" = "no" ] && return - - TITLE=${2:-"Screenshot"} - MESSAGE=${1:-"OK"} - notify "$TITLE" "$MESSAGE" -} -notifyError() { - if [ $NOTIFY = "yes" ]; then - TITLE=${2:-"Screenshot"} - MESSAGE=${1:-"Error taking screenshot with grim"} - notify -u critical "$TITLE" "$MESSAGE" - else - echo "$1" - fi -} - -die() { - MSG=${1:-Bye} - notifyError "Error: $MSG" - exit 2 -} - -check() { - COMMAND=$1 - if command -v "$COMMAND" > /dev/null 2>&1; then - RESULT="OK" - else - RESULT="NOT FOUND" - fi - echo " $COMMAND: $RESULT" -} - -takeScreenshot() { - FILE=$1 - GEOM=$2 - OUTPUT=$3 - if [ -n "$OUTPUT" ]; then - grim ${CURSOR:+-c} -o "$OUTPUT" "$FILE" || die "Unable to invoke grim" - elif [ -z "$GEOM" ]; then - grim ${CURSOR:+-c} "$FILE" || die "Unable to invoke grim" - else - grim ${CURSOR:+-c} -g "$GEOM" "$FILE" || die "Unable to invoke grim" - fi -} - -if [ "$ACTION" = "check" ] ; then - echo "Checking if required tools are installed. If something is missing, install it to your system and make it available in PATH..." - check grim - check slurp - check swaymsg - check wl-copy - check jq - check notify-send - exit -elif [ "$SUBJECT" = "area" ] ; then - GEOM=$(slurp -d) - # Check if user exited slurp without selecting the area - if [ -z "$GEOM" ]; then - exit 1 - fi - WHAT="Area" -elif [ "$SUBJECT" = "active" ] ; then - FOCUSED=$(swaymsg -t get_tree | jq -r 'recurse(.nodes[]?, .floating_nodes[]?) | select(.focused)') - GEOM=$(echo "$FOCUSED" | jq -r '.rect | "\(.x),\(.y) \(.width)x\(.height)"') - APP_ID=$(echo "$FOCUSED" | jq -r '.app_id') - WHAT="$APP_ID window" -elif [ "$SUBJECT" = "screen" ] ; then - GEOM="" - WHAT="Screen" -elif [ "$SUBJECT" = "output" ] ; then - GEOM="" - OUTPUT=$(swaymsg -t get_outputs | jq -r '.[] | select(.focused)' | jq -r '.name') - WHAT="$OUTPUT" -elif [ "$SUBJECT" = "window" ] ; then - GEOM=$(swaymsg -t get_tree | jq -r '.. | select(.pid? and .visible?) | .rect | "\(.x),\(.y) \(.width)x\(.height)"' | slurp) - # Check if user exited slurp without selecting the area - if [ -z "$GEOM" ]; then - exit 1 - fi - WHAT="Window" -else - die "Unknown subject to take a screen shot from" "$SUBJECT" -fi - -if [ "$ACTION" = "copy" ] ; then - takeScreenshot - "$GEOM" "$OUTPUT" | wl-copy --type image/png || die "Clipboard error" - notifyOk "$WHAT copied to buffer" -else - if takeScreenshot "$FILE" "$GEOM" "$OUTPUT"; then - TITLE="Screenshot of $SUBJECT" - MESSAGE=$(basename "$FILE") - notifyOk "$MESSAGE" "$TITLE" - echo "$FILE" - else - notifyError "Error taking screenshot with grim" - fi -fi diff --git a/contrib/grimshot.1 b/contrib/grimshot.1 deleted file mode 100644 index 2c4c6a95..00000000 --- a/contrib/grimshot.1 +++ /dev/null @@ -1,109 +0,0 @@ -.\" 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" "2022-03-31" -.P -.SH NAME -.P -grimshot - a helper for screenshots within sway -.P -.SH SYNOPSIS -.P -\fBgrimshot\fR [--notify] [--cursor] (copy|save) [TARGET] [FILE] -.br -\fBgrimshot\fR check -.br -\fBgrimshot\fR usage -.P -.SH OPTIONS -.P -\fB--notify\fR -.RS 4 -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.\& -.P -.RE -\fBcopy\fR -.RS 4 -Copy the screenshot data (as image/png) into the clipboard.\& -.P -.RE -.SH DESCRIPTION -.P -Grimshot is an easy-to-use screenshot utility for sway.\& It provides a -convenient interface over grim, slurp and jq, and supports storing the -screenshot either directly to the clipboard using wl-copy or to a file.\& -.P -.SH EXAMPLES -.P -An example usage pattern is to add these bindings to your sway config: -.P -.nf -.RS 4 -# Screenshots: -# Super+P: Current window -# Super+Shift+p: Select area -# Super+Alt+p Current output -# Super+Ctrl+p Select a window - -bindsym Mod4+p exec grimshot save active -bindsym Mod4+Shift+p exec grimshot save area -bindsym Mod4+Mod1+p exec grimshot save output -bindsym Mod4+Ctrl+p exec grimshot save window -.fi -.RE -.P -.SH TARGETS -.P -grimshot can capture the following named targets: -.P -\fIactive\fR -.RS 4 -Captures the currently active window.\& -.P -.RE -\fIscreen\fR -.RS 4 -Captures the entire screen.\& This includes all visible outputs.\& -.P -.RE -\fIarea\fR -.RS 4 -Allows manually selecting a rectangular region, and captures that.\& -.P -.RE -\fIwindow\fR -.RS 4 -Allows manually selecting a single window (by clicking on it), and -captures it.\& -.P -.RE -\fIoutput\fR -.RS 4 -Captures the currently active output.\& -.P -.RE -.SH OUTPUT -.P -Grimshot will print the filename of the captured screenshot to stdout if called -with the \fIsave\fR subcommand.\& -.P -.SH SEE ALSO -.P -\fBgrim\fR(1) diff --git a/contrib/grimshot.1.scd b/contrib/grimshot.1.scd deleted file mode 100644 index e356f99d..00000000 --- a/contrib/grimshot.1.scd +++ /dev/null @@ -1,80 +0,0 @@ -grimshot(1) - -# NAME - -grimshot - a helper for screenshots within sway - -# SYNOPSIS - -*grimshot* [--notify] [--cursor] (copy|save) [TARGET] [FILE]++ -*grimshot* check++ -*grimshot* usage - -# OPTIONS - -*--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 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. - -*copy* - Copy the screenshot data (as image/png) into the clipboard. - -# DESCRIPTION - -Grimshot is an easy-to-use screenshot utility for sway. It provides a -convenient interface over grim, slurp and jq, and supports storing the -screenshot either directly to the clipboard using wl-copy or to a file. - -# EXAMPLES - -An example usage pattern is to add these bindings to your sway config: - -``` -# Screenshots: -# Super+P: Current window -# Super+Shift+p: Select area -# Super+Alt+p Current output -# Super+Ctrl+p Select a window - -bindsym Mod4+p exec grimshot save active -bindsym Mod4+Shift+p exec grimshot save area -bindsym Mod4+Mod1+p exec grimshot save output -bindsym Mod4+Ctrl+p exec grimshot save window -``` - -# TARGETS - -grimshot can capture the following named targets: - -_active_ - Captures the currently active window. - -_screen_ - Captures the entire screen. This includes all visible outputs. - -_area_ - Allows manually selecting a rectangular region, and captures that. - -_window_ - Allows manually selecting a single window (by clicking on it), and - captures it. - -_output_ - Captures the currently active output. - -# OUTPUT - -Grimshot will print the filename of the captured screenshot to stdout if called -with the _save_ subcommand. - -# SEE ALSO - -*grim*(1) diff --git a/contrib/inactive-windows-transparency.py b/contrib/inactive-windows-transparency.py deleted file mode 100755 index b81134dd..00000000 --- a/contrib/inactive-windows-transparency.py +++ /dev/null @@ -1,69 +0,0 @@ -#!/usr/bin/python - -# This script requires i3ipc-python package (install it from a system package manager -# or pip). -# It makes inactive windows transparent. Use `transparency_val` variable to control -# transparency strength in range of 0…1 or use the command line argument -o. - -import argparse -import i3ipc -import signal -import sys -from functools import partial - -def on_window_focus(inactive_opacity, ipc, event): - global prev_focused - global prev_workspace - - focused_workspace = ipc.get_tree().find_focused() - - if focused_workspace == None: - return - - focused = event.container - workspace = focused_workspace.workspace().num - - if focused.id != prev_focused.id: # https://github.com/swaywm/sway/issues/2859 - focused.command("opacity 1") - if workspace == prev_workspace: - prev_focused.command("opacity " + inactive_opacity) - prev_focused = focused - prev_workspace = workspace - - -def remove_opacity(ipc): - for workspace in ipc.get_tree().workspaces(): - for w in workspace: - w.command("opacity 1") - ipc.main_quit() - sys.exit(0) - - -if __name__ == "__main__": - transparency_val = "0.80" - - parser = argparse.ArgumentParser( - description="This script allows you to set the transparency of unfocused windows in sway." - ) - parser.add_argument( - "--opacity", - "-o", - type=str, - default=transparency_val, - help="set opacity value in range 0...1", - ) - args = parser.parse_args() - - ipc = i3ipc.Connection() - prev_focused = None - prev_workspace = ipc.get_tree().find_focused().workspace().num - - for window in ipc.get_tree(): - if window.focused: - prev_focused = window - else: - window.command("opacity " + args.opacity) - for sig in [signal.SIGINT, signal.SIGTERM]: - signal.signal(sig, lambda signal, frame: remove_opacity(ipc)) - ipc.on("window::focus", partial(on_window_focus, args.opacity)) - ipc.main() \ No newline at end of file diff --git a/flake.nix b/flake.nix index bd3edcea..ab09cb94 100644 --- a/flake.nix +++ b/flake.nix @@ -44,7 +44,7 @@ name = "swayfx-shell"; inputsFrom = [ self.packages.${system}.swayfx-unwrapped - pkgs.wlroots_0_16 + pkgs.wlroots_0_17 ]; nativeBuildInputs = with pkgs; [ cmake @@ -55,7 +55,7 @@ shellHook = with pkgs; '' ( mkdir -p "$PWD/subprojects" && cd "$PWD/subprojects" - cp -R --no-preserve=mode,ownership ${wlroots_0_16.src} wlroots + cp -R --no-preserve=mode,ownership ${wlroots_0_17.src} wlroots )''; }; } diff --git a/include/pango.h b/include/pango.h index 1db113c2..228e39cf 100644 --- a/include/pango.h +++ b/include/pango.h @@ -5,6 +5,7 @@ #include #include #include +#include "stringop.h" /** * Utility function which escape characters a & < > ' ". @@ -16,9 +17,9 @@ size_t escape_markup_text(const char *src, char *dest); 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 PangoFontDescription *desc, int *width, int *height, - int *baseline, double scale, bool markup, const char *fmt, ...); + int *baseline, double scale, bool markup, const char *fmt, ...) _SWAY_ATTRIB_PRINTF(8, 9); 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, ...); + double scale, bool markup, const char *fmt, ...) _SWAY_ATTRIB_PRINTF(5, 6); #endif diff --git a/include/stringop.h b/include/stringop.h index b29f59b2..19a50f23 100644 --- a/include/stringop.h +++ b/include/stringop.h @@ -5,6 +5,12 @@ #include #include "list.h" +#ifdef __GNUC__ +#define _SWAY_ATTRIB_PRINTF(start, end) __attribute__((format(printf, start, end))) +#else +#define _SWAY_ATTRIB_PRINTF(start, end) +#endif + void strip_whitespace(char *str); void strip_quotes(char *str); @@ -31,4 +37,7 @@ char *argsep(char **stringp, const char *delim, char *matched_delim); // Expand a path using shell replacements such as $HOME and ~ bool expand_path(char **path); +char *vformat_str(const char *fmt, va_list args) _SWAY_ATTRIB_PRINTF(1, 0); +char *format_str(const char *fmt, ...) _SWAY_ATTRIB_PRINTF(1, 2); + #endif diff --git a/include/sway/commands.h b/include/sway/commands.h index b8f5660a..8f21b989 100644 --- a/include/sway/commands.h +++ b/include/sway/commands.h @@ -3,13 +3,14 @@ #include #include "config.h" +#include "stringop.h" struct sway_container; typedef struct cmd_results *sway_cmd(int argc, char **argv); struct cmd_handler { - char *command; + const char *command; sway_cmd *handle; }; @@ -46,7 +47,7 @@ enum expected_args { struct cmd_results *checkarg(int argc, const char *name, enum expected_args type, int val); -const struct cmd_handler *find_handler(char *line, +const struct cmd_handler *find_handler(const char *line, const struct cmd_handler *cmd_handlers, size_t handlers_size); /** @@ -76,7 +77,7 @@ struct cmd_results *config_commands_command(char *exec); /** * Allocates a cmd_results object. */ -struct cmd_results *cmd_results_new(enum cmd_status status, const char *error, ...); +struct cmd_results *cmd_results_new(enum cmd_status status, const char *error, ...) _SWAY_ATTRIB_PRINTF(2, 3); /** * Frees a cmd_results object. */ @@ -174,8 +175,6 @@ sway_cmd cmd_max_render_time; sway_cmd cmd_mode; sway_cmd cmd_mouse_warping; sway_cmd cmd_move; -sway_cmd cmd_new_float; -sway_cmd cmd_new_window; sway_cmd cmd_nop; sway_cmd cmd_opacity; sway_cmd cmd_saturation; @@ -185,6 +184,7 @@ sway_cmd cmd_no_focus; sway_cmd cmd_output; sway_cmd cmd_permit; sway_cmd cmd_popup_during_fullscreen; +sway_cmd cmd_primary_selection; sway_cmd cmd_reject; sway_cmd cmd_reload; sway_cmd cmd_rename; @@ -290,10 +290,12 @@ sway_cmd input_cmd_map_to_region; sway_cmd input_cmd_middle_emulation; sway_cmd input_cmd_natural_scroll; sway_cmd input_cmd_pointer_accel; +sway_cmd input_cmd_rotation_angle; sway_cmd input_cmd_scroll_factor; sway_cmd input_cmd_repeat_delay; sway_cmd input_cmd_repeat_rate; sway_cmd input_cmd_scroll_button; +sway_cmd input_cmd_scroll_button_lock; sway_cmd input_cmd_scroll_method; sway_cmd input_cmd_tap; sway_cmd input_cmd_tap_button_map; diff --git a/include/sway/config.h b/include/sway/config.h index de49bbff..84e4ab8e 100644 --- a/include/sway/config.h +++ b/include/sway/config.h @@ -12,8 +12,10 @@ #include "../include/config.h" #include "gesture.h" #include "list.h" +#include "stringop.h" #include "swaynag.h" #include "tree/container.h" +#include "scenefx/types/fx/blur_data.h" #include "sway/input/tablet.h" #include "sway/tree/root.h" #include "wlr-layer-shell-unstable-v1-protocol.h" @@ -155,10 +157,12 @@ struct input_config { int middle_emulation; int natural_scroll; float pointer_accel; + float rotation_angle; float scroll_factor; int repeat_delay; int repeat_rate; int scroll_button; + int scroll_button_lock; int scroll_method; int send_events; int tap; @@ -470,15 +474,6 @@ enum xwayland_mode { XWAYLAND_MODE_IMMEDIATE, }; -struct blur_parameters { - int num_passes; - int radius; - float noise; - float brightness; - float contrast; - float saturation; -}; - /** * The configuration struct. The result of loading a config file. */ @@ -501,7 +496,7 @@ struct sway_config { bool blur_enabled; bool blur_xray; - struct blur_parameters blur_params; + struct blur_data blur_params; bool titlebar_separator; bool scratchpad_minimize; @@ -564,6 +559,7 @@ struct sway_config { bool auto_back_and_forth; bool show_marks; enum alignment title_align; + bool primary_selection; bool tiling_drag; int tiling_drag_threshold; @@ -657,7 +653,7 @@ void run_deferred_bindings(void); /** * Adds a warning entry to the swaynag instance used for errors. */ -void config_add_swaynag_warning(char *fmt, ...); +void config_add_swaynag_warning(char *fmt, ...) _SWAY_ATTRIB_PRINTF(1, 2); /** * Free config struct diff --git a/include/sway/criteria.h b/include/sway/criteria.h index 59f57f94..8da345ea 100644 --- a/include/sway/criteria.h +++ b/include/sway/criteria.h @@ -43,6 +43,7 @@ struct criteria { struct pattern *window_role; enum atom_name window_type; #endif + bool all; bool floating; bool tiling; char urgent; // 'l' for latest or 'o' for oldest diff --git a/include/sway/desktop/fx_renderer/fx_framebuffer.h b/include/sway/desktop/fx_renderer/fx_framebuffer.h deleted file mode 100644 index 3372cd00..00000000 --- a/include/sway/desktop/fx_renderer/fx_framebuffer.h +++ /dev/null @@ -1,27 +0,0 @@ -#ifndef FX_FRAMEBUFFER_H -#define FX_FRAMEBUFFER_H - -#include -#include -#include - -#include "sway/desktop/fx_renderer/fx_stencilbuffer.h" -#include "sway/desktop/fx_renderer/fx_texture.h" - -struct fx_framebuffer { - GLuint fb; - struct fx_stencilbuffer stencil_buffer; - struct fx_texture texture; -}; - -struct fx_framebuffer fx_framebuffer_create(); - -void fx_framebuffer_bind(struct fx_framebuffer *buffer); - -void fx_framebuffer_update(struct fx_framebuffer *buffer, int width, int height); - -void fx_framebuffer_add_stencil_buffer(struct fx_framebuffer *buffer, int width, int height); - -void fx_framebuffer_release(struct fx_framebuffer *buffer); - -#endif diff --git a/include/sway/desktop/fx_renderer/fx_renderer.h b/include/sway/desktop/fx_renderer/fx_renderer.h deleted file mode 100644 index 7643caaf..00000000 --- a/include/sway/desktop/fx_renderer/fx_renderer.h +++ /dev/null @@ -1,236 +0,0 @@ -#ifndef _SWAY_OPENGL_H -#define _SWAY_OPENGL_H - -#include -#include -#include -#include - -#include "sway/desktop/fx_renderer/fx_framebuffer.h" -#include "sway/desktop/fx_renderer/fx_texture.h" - -enum corner_location { TOP_LEFT, TOP_RIGHT, BOTTOM_RIGHT, BOTTOM_LEFT, ALL, NONE }; - -enum fx_tex_shader_source { - SHADER_SOURCE_TEXTURE_RGBA = 1, - SHADER_SOURCE_TEXTURE_RGBX = 2, - SHADER_SOURCE_TEXTURE_EXTERNAL = 3, -}; - -enum fx_rounded_quad_shader_source { - SHADER_SOURCE_QUAD_ROUND = 1, - SHADER_SOURCE_QUAD_ROUND_TOP_LEFT = 2, - SHADER_SOURCE_QUAD_ROUND_TOP_RIGHT = 3, - SHADER_SOURCE_QUAD_ROUND_BOTTOM_RIGHT = 4, - SHADER_SOURCE_QUAD_ROUND_BOTTOM_LEFT = 5, -}; - -struct decoration_data { - float alpha; - float saturation; - int corner_radius; - float dim; - float *dim_color; - bool has_titlebar; - bool discard_transparent; - bool blur; - bool shadow; -}; - -struct blur_shader { - GLuint program; - GLint proj; - GLint tex; - GLint pos_attrib; - GLint tex_attrib; - GLint radius; - GLint halfpixel; -}; - -struct effects_shader { - GLuint program; - GLint proj; - GLint tex; - GLint pos_attrib; - GLint tex_attrib; - GLfloat noise; - GLfloat brightness; - GLfloat contrast; - GLfloat saturation; -}; - -struct box_shadow_shader { - GLuint program; - GLint proj; - GLint color; - GLint pos_attrib; - GLint position; - GLint size; - GLint blur_sigma; - GLint corner_radius; -}; - -struct corner_shader { - GLuint program; - GLint proj; - GLint color; - GLint pos_attrib; - GLint is_top_left; - GLint is_top_right; - GLint is_bottom_left; - GLint is_bottom_right; - GLint position; - GLint radius; - GLint half_size; - GLint half_thickness; -}; - -struct quad_shader { - GLuint program; - GLint proj; - GLint color; - GLint pos_attrib; -}; - -struct rounded_quad_shader { - GLuint program; - GLint proj; - GLint color; - GLint pos_attrib; - GLint size; - GLint position; - GLint radius; -}; - -struct stencil_mask_shader { - GLuint program; - GLint proj; - GLint color; - GLint pos_attrib; - GLint half_size; - GLint position; - GLint radius; -}; - -struct tex_shader { - GLuint program; - GLint proj; - GLint tex; - GLint alpha; - GLint pos_attrib; - GLint tex_attrib; - GLint size; - GLint position; - GLint radius; - GLint saturation; - GLint dim; - GLint dim_color; - GLint has_titlebar; - GLint discard_transparent; -}; - -struct fx_renderer { - float projection[9]; - - int viewport_width, viewport_height; - - struct wlr_output *wlr_output; - - // The framebuffer used by wlroots - struct fx_framebuffer wlr_buffer; - // Contains the blurred background for tiled windows - struct fx_framebuffer blur_buffer; - // Contains the original pixels to draw over the areas where artifact are visible - struct fx_framebuffer blur_saved_pixels_buffer; - // Blur swaps between the two effects buffers everytime it scales the image - // Buffer used for effects - struct fx_framebuffer effects_buffer; - // Swap buffer used for effects - struct fx_framebuffer effects_buffer_swapped; - - // The region where there's blur - pixman_region32_t blur_padding_region; - - bool blur_buffer_dirty; - - struct { - bool OES_egl_image_external; - } exts; - - struct { - PFNGLEGLIMAGETARGETTEXTURE2DOESPROC glEGLImageTargetTexture2DOES; - } procs; - - struct { - struct box_shadow_shader box_shadow; - struct blur_shader blur1; - struct blur_shader blur2; - struct effects_shader blur_effects; - struct corner_shader corner; - struct quad_shader quad; - struct rounded_quad_shader rounded_quad; - struct rounded_quad_shader rounded_tl_quad; - struct rounded_quad_shader rounded_tr_quad; - struct rounded_quad_shader rounded_bl_quad; - struct rounded_quad_shader rounded_br_quad; - struct stencil_mask_shader stencil_mask; - struct tex_shader tex_rgba; - struct tex_shader tex_rgbx; - struct tex_shader tex_ext; - } shaders; -}; - -struct decoration_data get_undecorated_decoration_data(); - -struct fx_renderer *fx_renderer_create(struct wlr_egl *egl, struct wlr_output *output); - -void fx_renderer_fini(struct fx_renderer *renderer); - -void fx_renderer_begin(struct fx_renderer *renderer, int width, int height); - -void fx_renderer_end(struct fx_renderer *renderer); - -void fx_renderer_clear(const float color[static 4]); - -void fx_renderer_scissor(struct wlr_box *box); - -// Initialize the stenciling work -void fx_renderer_stencil_mask_init(); - -// Close the mask -void fx_renderer_stencil_mask_close(bool draw_inside_mask); - -// Finish stenciling and clear the buffer -void fx_renderer_stencil_mask_fini(); - -bool fx_render_subtexture_with_matrix(struct fx_renderer *renderer, struct fx_texture *fx_texture, - const struct wlr_fbox *src_box, const struct wlr_box *dst_box, const float matrix[static 9], - struct decoration_data deco_data); - -bool fx_render_texture_with_matrix(struct fx_renderer *renderer, struct fx_texture *fx_texture, - const struct wlr_box *dst_box, const float matrix[static 9], struct decoration_data deco_data); - -void fx_render_rect(struct fx_renderer *renderer, const struct wlr_box *box, - const float color[static 4], const float projection[static 9]); - -void fx_render_rounded_rect(struct fx_renderer *renderer, const struct wlr_box *box, - const float color[static 4], const float matrix[static 9], int radius, - enum corner_location corner_location); - -void fx_render_border_corner(struct fx_renderer *renderer, const struct wlr_box *box, - const float color[static 4], const float matrix[static 9], - enum corner_location corner_location, int radius, int border_thickness); - -void fx_render_box_shadow(struct fx_renderer *renderer, const struct wlr_box *box, - const struct wlr_box *inner_box, const float color[static 4], - const float matrix[static 9], int corner_radius, float blur_sigma); - -void fx_render_blur(struct fx_renderer *renderer, const float matrix[static 9], - struct fx_framebuffer **buffer, struct blur_shader *shader, - const struct wlr_box *box, int blur_radius); - -void fx_render_blur_effects(struct fx_renderer *renderer, const float matrix[static 9], - struct fx_framebuffer **buffer, float blur_noise, float blur_brightness, - float blur_contrast, float blur_saturation); - -#endif diff --git a/include/sway/desktop/fx_renderer/fx_stencilbuffer.h b/include/sway/desktop/fx_renderer/fx_stencilbuffer.h deleted file mode 100644 index 157c0282..00000000 --- a/include/sway/desktop/fx_renderer/fx_stencilbuffer.h +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef FX_STENCILBUFFER_H -#define FX_STENCILBUFFER_H - -#include -#include -#include - -struct fx_stencilbuffer { - GLuint rb; - int width; - int height; -}; - -struct fx_stencilbuffer fx_stencilbuffer_create(); - -void fx_stencilbuffer_release(struct fx_stencilbuffer *stencil_buffer); - -#endif diff --git a/include/sway/desktop/fx_renderer/fx_texture.h b/include/sway/desktop/fx_renderer/fx_texture.h deleted file mode 100644 index 62e635e6..00000000 --- a/include/sway/desktop/fx_renderer/fx_texture.h +++ /dev/null @@ -1,22 +0,0 @@ -#ifndef FX_TEXTURE_H -#define FX_TEXTURE_H - -#include -#include -#include - -struct fx_texture { - GLuint target; - GLuint id; - bool has_alpha; - int width; - int height; -}; - -struct fx_texture fx_texture_create(); - -struct fx_texture fx_texture_from_wlr_texture(struct wlr_texture *tex); - -void fx_texture_release(struct fx_texture *texture); - -#endif diff --git a/include/sway/desktop/fx_renderer/matrix.h b/include/sway/desktop/fx_renderer/matrix.h deleted file mode 100644 index 6931e8d2..00000000 --- a/include/sway/desktop/fx_renderer/matrix.h +++ /dev/null @@ -1,9 +0,0 @@ -#ifndef _MATRIX_H -#define _MATRIX_H - -#include - -void matrix_projection(float mat[static 9], int width, int height, - enum wl_output_transform transform); - -#endif diff --git a/include/sway/desktop/idle_inhibit_v1.h b/include/sway/desktop/idle_inhibit_v1.h index 58d54c68..84cc666d 100644 --- a/include/sway/desktop/idle_inhibit_v1.h +++ b/include/sway/desktop/idle_inhibit_v1.h @@ -1,8 +1,6 @@ #ifndef _SWAY_DESKTOP_IDLE_INHIBIT_V1_H #define _SWAY_DESKTOP_IDLE_INHIBIT_V1_H #include -#include -#include "sway/server.h" enum sway_idle_inhibit_mode { INHIBIT_IDLE_APPLICATION, // Application set inhibitor (when visible) @@ -16,12 +14,9 @@ struct sway_idle_inhibit_manager_v1 { struct wlr_idle_inhibit_manager_v1 *wlr_manager; struct wl_listener new_idle_inhibitor_v1; struct wl_list inhibitors; - - struct wlr_idle *idle; }; struct sway_idle_inhibitor_v1 { - struct sway_idle_inhibit_manager_v1 *manager; struct wlr_idle_inhibitor_v1 *wlr_inhibitor; struct sway_view *view; enum sway_idle_inhibit_mode mode; @@ -33,8 +28,7 @@ struct sway_idle_inhibitor_v1 { bool sway_idle_inhibit_v1_is_active( struct sway_idle_inhibitor_v1 *inhibitor); -void sway_idle_inhibit_v1_check_active( - struct sway_idle_inhibit_manager_v1 *manager); +void sway_idle_inhibit_v1_check_active(void); void sway_idle_inhibit_v1_user_inhibitor_register(struct sway_view *view, enum sway_idle_inhibit_mode mode); @@ -48,6 +42,6 @@ struct sway_idle_inhibitor_v1 *sway_idle_inhibit_v1_application_inhibitor_for_vi void sway_idle_inhibit_v1_user_inhibitor_destroy( struct sway_idle_inhibitor_v1 *inhibitor); -struct sway_idle_inhibit_manager_v1 *sway_idle_inhibit_manager_v1_create( - struct wl_display *wl_display, struct wlr_idle *idle); +bool sway_idle_inhibit_manager_v1_init(void); + #endif diff --git a/include/sway/desktop/launcher.h b/include/sway/desktop/launcher.h index 3b577f74..412068a9 100644 --- a/include/sway/desktop/launcher.h +++ b/include/sway/desktop/launcher.h @@ -2,14 +2,19 @@ #define _SWAY_LAUNCHER_H #include +#include +#include "sway/input/seat.h" struct launcher_ctx { pid_t pid; - char *name; + char *fallback_name; struct wlr_xdg_activation_token_v1 *token; struct wl_listener token_destroy; + struct sway_seat *seat; + struct wl_listener seat_destroy; bool activated; + bool had_focused_surface; struct sway_node *node; struct wl_listener node_destroy; @@ -25,7 +30,10 @@ void launcher_ctx_consume(struct launcher_ctx *ctx); void launcher_ctx_destroy(struct launcher_ctx *ctx); -struct launcher_ctx *launcher_ctx_create(void); +struct launcher_ctx *launcher_ctx_create_internal(void); + +struct launcher_ctx *launcher_ctx_create( + struct wlr_xdg_activation_token_v1 *token, struct sway_node *node); const char *launcher_ctx_get_token_name(struct launcher_ctx *ctx); diff --git a/include/sway/input/cursor.h b/include/sway/input/cursor.h index 8a2898dd..1e21c66f 100644 --- a/include/sway/input/cursor.h +++ b/include/sway/input/cursor.h @@ -35,7 +35,6 @@ struct sway_cursor { pixman_region32_t confine; // invalid if active_constraint == NULL 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; @@ -53,6 +52,7 @@ struct sway_cursor { struct wl_listener touch_down; struct wl_listener touch_up; + struct wl_listener touch_cancel; struct wl_listener touch_motion; struct wl_listener touch_frame; bool simulating_pointer_from_touch; @@ -64,6 +64,7 @@ struct sway_cursor { struct wl_listener tool_proximity; struct wl_listener tool_button; bool simulating_pointer_from_tool_tip; + bool simulating_pointer_from_tool_button; uint32_t tool_buttons; struct wl_listener request_set_cursor; @@ -107,6 +108,10 @@ void cursor_unhide(struct sway_cursor *cursor); int cursor_get_timeout(struct sway_cursor *cursor); void cursor_notify_key_press(struct sway_cursor *cursor); +void pointer_motion(struct sway_cursor *cursor, uint32_t time_msec, + struct wlr_input_device *device, double dx, double dy, + double dx_unaccel, double dy_unaccel); + void dispatch_cursor_button(struct sway_cursor *cursor, struct wlr_input_device *device, uint32_t time_msec, uint32_t button, enum wlr_button_state state); @@ -140,4 +145,6 @@ uint32_t get_mouse_button(const char *name, char **error); const char *get_mouse_button_name(uint32_t button); +void handle_request_set_cursor_shape(struct wl_listener *listener, void *data); + #endif diff --git a/include/sway/input/input-manager.h b/include/sway/input/input-manager.h index c9bd08f0..4bd51709 100644 --- a/include/sway/input/input-manager.h +++ b/include/sway/input/input-manager.h @@ -25,6 +25,7 @@ struct sway_input_manager { struct wlr_keyboard_shortcuts_inhibit_manager_v1 *keyboard_shortcuts_inhibit; struct wlr_virtual_keyboard_manager_v1 *virtual_keyboard; struct wlr_virtual_pointer_manager_v1 *virtual_pointer; + struct wlr_pointer_gestures_v1 *pointer_gestures; struct wl_listener new_input; struct wl_listener inhibit_activate; @@ -44,7 +45,7 @@ void input_manager_configure_xcursor(void); void input_manager_apply_input_config(struct input_config *input_config); -void input_manager_configure_all_inputs(void); +void input_manager_configure_all_input_mappings(void); void input_manager_reset_input(struct sway_input_device *input_device); diff --git a/include/sway/input/libinput.h b/include/sway/input/libinput.h index e4b1acc3..1f84a8e3 100644 --- a/include/sway/input/libinput.h +++ b/include/sway/input/libinput.h @@ -4,6 +4,9 @@ bool sway_input_configure_libinput_device(struct sway_input_device *device); +void sway_input_configure_libinput_device_send_events( + struct sway_input_device *device); + void sway_input_reset_libinput_device(struct sway_input_device *device); bool sway_libinput_device_is_builtin(struct sway_input_device *device); diff --git a/include/sway/input/seat.h b/include/sway/input/seat.h index e3a46872..6063ead3 100644 --- a/include/sway/input/seat.h +++ b/include/sway/input/seat.h @@ -4,6 +4,7 @@ #include #include #include +#include #include #include "sway/config.h" #include "sway/input/input-manager.h" @@ -11,6 +12,7 @@ #include "sway/input/text_input.h" struct sway_seat; +struct fx_render_context; struct sway_seatop_impl { void (*button)(struct sway_seat *seat, uint32_t time_msec, @@ -36,14 +38,21 @@ struct sway_seatop_impl { 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 (*touch_motion)(struct sway_seat *seat, + struct wlr_touch_motion_event *event, double lx, double ly); + void (*touch_up)(struct sway_seat *seat, + struct wlr_touch_up_event *event); + void (*touch_down)(struct sway_seat *seat, + struct wlr_touch_down_event *event, double lx, double ly); + void (*touch_cancel)(struct sway_seat *seat, + struct wlr_touch_cancel_event *event); void (*tablet_tool_motion)(struct sway_seat *seat, struct sway_tablet_tool *tool, uint32_t time_msec); void (*tablet_tool_tip)(struct sway_seat *seat, struct sway_tablet_tool *tool, uint32_t time_msec, enum wlr_tablet_tool_tip_state state); void (*end)(struct sway_seat *seat); void (*unref)(struct sway_seat *seat, struct sway_container *con); - void (*render)(struct sway_seat *seat, struct sway_output *output, - pixman_region32_t *damage); + void (*render)(struct sway_seat *seat, struct fx_render_context *ctx); bool allow_set_cursor; }; @@ -72,6 +81,7 @@ struct sway_drag_icon { struct wl_list link; // sway_root::drag_icons double x, y; // in layout-local coordinates + int dx, dy; // offset in surface-local coordinates struct wl_listener surface_commit; struct wl_listener map; @@ -94,8 +104,9 @@ struct sway_seat { struct sway_workspace *workspace; char *prev_workspace_name; // for workspace back_and_forth - // If the focused layer is set, views cannot receive keyboard focus struct wlr_layer_surface_v1 *focused_layer; + // If the exclusive layer is set, views cannot receive keyboard focus + bool has_exclusive_layer; // If exclusive_client is set, no other clients will receive input events struct wl_client *exclusive_client; @@ -157,6 +168,9 @@ void seat_add_device(struct sway_seat *seat, void seat_configure_device(struct sway_seat *seat, struct sway_input_device *device); +void seat_configure_device_mapping(struct sway_seat *seat, + struct sway_input_device *input_device); + void seat_reset_device(struct sway_seat *seat, struct sway_input_device *input_device); @@ -255,10 +269,13 @@ enum wlr_edges find_resize_edge(struct sway_container *cont, void seatop_begin_default(struct sway_seat *seat); void seatop_begin_down(struct sway_seat *seat, struct sway_container *con, - uint32_t time_msec, double sx, double sy); + double sx, double sy); void seatop_begin_down_on_surface(struct sway_seat *seat, - struct wlr_surface *surface, uint32_t time_msec, double sx, double sy); + struct wlr_surface *surface, double sx, double sy); + +void seatop_begin_touch_down(struct sway_seat *seat, struct wlr_surface *surface, + struct wlr_touch_down_event *event, double sx, double sy, double lx, double ly); void seatop_begin_move_floating(struct sway_seat *seat, struct sway_container *con); @@ -318,6 +335,18 @@ void seatop_swipe_update(struct sway_seat *seat, void seatop_swipe_end(struct sway_seat *seat, struct wlr_pointer_swipe_end_event *event); +void seatop_touch_motion(struct sway_seat *seat, + struct wlr_touch_motion_event *event, double lx, double ly); + +void seatop_touch_up(struct sway_seat *seat, + struct wlr_touch_up_event *event); + +void seatop_touch_down(struct sway_seat *seat, + struct wlr_touch_down_event *event, double lx, double ly); + +void seatop_touch_cancel(struct sway_seat *seat, + struct wlr_touch_cancel_event *event); + void seatop_rebase(struct sway_seat *seat, uint32_t time_msec); /** @@ -336,8 +365,7 @@ void seatop_unref(struct sway_seat *seat, struct sway_container *con); * Instructs a seatop to render anything that it needs to render * (eg. dropzone for move-tiling) */ -void seatop_render(struct sway_seat *seat, struct sway_output *output, - pixman_region32_t *damage); +void seatop_render(struct sway_seat *seat, struct fx_render_context *ctx); bool seatop_allows_set_cursor(struct sway_seat *seat); diff --git a/include/sway/input/text_input.h b/include/sway/input/text_input.h index c70fd935..214e61d1 100644 --- a/include/sway/input/text_input.h +++ b/include/sway/input/text_input.h @@ -4,12 +4,11 @@ #include #include #include -#include "sway/input/seat.h" /** * The relay structure manages the relationship between text-input and * input_method interfaces on a given seat. Multiple text-input interfaces may - * be bound to a relay, but at most one will be focused (reveiving events) at + * be bound to a relay, but at most one will be focused (receiving events) at * a time. At most one input-method interface may be bound to the seat. The * relay manages life cycle of both sides. When both sides are present and * focused, the relay passes messages between them. diff --git a/include/sway/ipc-server.h b/include/sway/ipc-server.h index bc4f781a..d4c00942 100644 --- a/include/sway/ipc-server.h +++ b/include/sway/ipc-server.h @@ -21,5 +21,6 @@ void ipc_event_mode(const char *mode, bool pango); void ipc_event_shutdown(const char *reason); void ipc_event_binding(struct sway_binding *binding); void ipc_event_input(const char *change, struct sway_input_device *device); +void ipc_event_output(void); #endif diff --git a/include/sway/layers.h b/include/sway/layers.h index 5871f0d8..bb7cbb22 100644 --- a/include/sway/layers.h +++ b/include/sway/layers.h @@ -60,6 +60,10 @@ struct sway_layer_subsurface { }; struct sway_output; + +struct wlr_layer_surface_v1 *toplevel_layer_surface_from_surface( + struct wlr_surface *surface); + void arrange_layers(struct sway_output *output); struct sway_layer_surface *layer_from_wlr_layer_surface_v1( diff --git a/include/sway/output.h b/include/sway/output.h index bd65237e..a60ddec8 100644 --- a/include/sway/output.h +++ b/include/sway/output.h @@ -6,23 +6,33 @@ #include #include #include "config.h" -#include "sway/desktop/fx_renderer/fx_renderer.h" +#include "scenefx/render/pass.h" #include "sway/tree/node.h" #include "sway/tree/view.h" +struct decoration_data get_undecorated_decoration_data(); + struct sway_server; struct sway_container; +struct decoration_data { + float alpha; + float saturation; + int corner_radius; + float dim; + float *dim_color; + bool has_titlebar; + bool discard_transparent; + bool blur; + bool shadow; +}; + struct render_data { + struct fx_render_context *ctx; pixman_region32_t *damage; struct wlr_box *clip_box; struct decoration_data deco_data; -}; - -struct blur_stencil_data { - struct fx_texture *stencil_texture; - const struct wlr_fbox *stencil_src_box; - float *stencil_matrix; + struct sway_view *view; }; struct sway_output_state { @@ -36,8 +46,6 @@ struct sway_output { struct sway_server *server; struct wl_list link; - struct fx_renderer *renderer; - struct wl_list layers[4]; // sway_layer_surface::link struct wlr_box usable_area; @@ -48,21 +56,20 @@ struct sway_output { 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 powered off - struct wlr_output_mode *current_mode; bool enabling, enabled; list_t *workspaces; struct sway_output_state current; + struct wl_listener layout_destroy; struct wl_listener destroy; struct wl_listener commit; - struct wl_listener mode; struct wl_listener present; struct wl_listener damage; struct wl_listener frame; struct wl_listener needs_frame; + struct wl_listener request_state; struct { struct wl_signal disable; @@ -72,6 +79,7 @@ struct sway_output { uint32_t refresh_nsec; int max_render_time; // In milliseconds struct wl_event_source *repaint_timer; + bool gamma_lut_changed; }; struct sway_output_non_desktop { @@ -80,6 +88,14 @@ struct sway_output_non_desktop { struct wl_listener destroy; }; +struct fx_render_context { + struct sway_output *output; + struct wlr_renderer *renderer; + pixman_region32_t *output_damage; + + struct fx_gles_render_pass *pass; +}; + struct sway_output *output_create(struct wlr_output *wlr_output); void output_destroy(struct sway_output *output); @@ -111,6 +127,9 @@ void output_damage_box(struct sway_output *output, struct wlr_box *box); void output_damage_whole_container(struct sway_output *output, struct sway_container *con); +bool output_match_name_or_id(struct sway_output *output, + const char *name_or_id); + // this ONLY includes the enabled outputs struct sway_output *output_by_name_or_id(const char *name_or_id); @@ -127,8 +146,7 @@ bool output_has_opaque_overlay_layer_surface(struct sway_output *output); struct sway_workspace *output_get_active_workspace(struct sway_output *output); -void output_render(struct sway_output *output, struct timespec *when, - pixman_region32_t *damage); +void output_render(struct fx_render_context *ctx); void output_surface_for_each_surface(struct sway_output *output, struct wlr_surface *surface, double ox, double oy, @@ -181,20 +199,16 @@ void output_get_box(struct sway_output *output, struct wlr_box *box); enum sway_container_layout output_get_default_layout( struct sway_output *output); -void render_rect(struct sway_output *output, - pixman_region32_t *output_damage, const struct wlr_box *_box, +void render_rect(struct fx_render_context *ctx, const struct wlr_box *_box, float color[static 4]); -void render_rounded_rect(struct sway_output *output, - pixman_region32_t *output_damage, const struct wlr_box *_box, - float color[static 4], int corner_radius, - enum corner_location corner_location); - -void render_blur(bool optimized, struct sway_output *output, - pixman_region32_t *output_damage, const struct wlr_box *dst_box, - pixman_region32_t *opaque_region, struct decoration_data *deco_data, - struct blur_stencil_data *stencil_data); +void render_rounded_rect(struct fx_render_context *ctx, const struct wlr_box *_box, + float color[static 4], int corner_radius, enum corner_location corner_location); +void render_blur(struct fx_render_context *ctx, struct wlr_texture *texture, + const struct wlr_fbox *src_box, const struct wlr_box *dst_box, + bool optimized_blur, pixman_region32_t *opaque_region, + struct decoration_data deco_data); void premultiply_alpha(float color[4], float opacity); @@ -204,6 +218,8 @@ enum wlr_direction opposite_direction(enum wlr_direction d); void handle_output_layout_change(struct wl_listener *listener, void *data); +void handle_gamma_control_set_gamma(struct wl_listener *listener, void *data); + void handle_output_manager_apply(struct wl_listener *listener, void *data); void handle_output_manager_test(struct wl_listener *listener, void *data); diff --git a/include/sway/server.h b/include/sway/server.h index 96c3623f..ef70ae4e 100644 --- a/include/sway/server.h +++ b/include/sway/server.h @@ -3,14 +3,12 @@ #include #include #include -#include #include #include #include #include #include #include -#include #include #include #include @@ -22,6 +20,7 @@ #include #include "config.h" #include "list.h" +#include "sway/desktop/idle_inhibit_v1.h" #if HAVE_XWAYLAND #include "sway/xwayland.h" #endif @@ -34,9 +33,10 @@ struct sway_server { const char *socket; struct wlr_backend *backend; + struct wlr_session *session; // secondary headless backend used for creating virtual outputs on-the-fly struct wlr_backend *headless_backend; - struct wlr_renderer *wlr_renderer; + struct wlr_renderer *renderer; struct wlr_allocator *allocator; struct wlr_compositor *compositor; @@ -51,9 +51,8 @@ struct sway_server { struct wl_listener new_output; 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 sway_idle_inhibit_manager_v1 idle_inhibit_manager_v1; struct wlr_layer_shell_v1 *layer_shell; struct wl_listener layer_shell_surface; @@ -91,6 +90,9 @@ struct sway_server { struct wl_listener output_manager_apply; struct wl_listener output_manager_test; + struct wlr_gamma_control_manager_v1 *gamma_control_manager_v1; + struct wl_listener gamma_control_set_gamma; + struct { bool locked; struct wlr_session_lock_manager_v1 *manager; @@ -110,9 +112,17 @@ struct sway_server { struct wlr_input_method_manager_v2 *input_method; struct wlr_text_input_manager_v3 *text_input; struct wlr_foreign_toplevel_manager_v1 *foreign_toplevel_manager; + struct wlr_content_type_manager_v1 *content_type_manager_v1; + struct wlr_data_control_manager_v1 *data_control_manager_v1; + struct wlr_screencopy_manager_v1 *screencopy_manager_v1; + struct wlr_export_dmabuf_manager_v1 *export_dmabuf_manager_v1; + struct wlr_security_context_manager_v1 *security_context_manager_v1; struct wlr_xdg_activation_v1 *xdg_activation_v1; struct wl_listener xdg_activation_v1_request_activate; + struct wl_listener xdg_activation_v1_new_token; + + struct wl_listener request_set_cursor_shape; struct wl_list pending_launcher_ctxs; // launcher_ctx::link @@ -174,6 +184,8 @@ void handle_xdg_decoration(struct wl_listener *listener, void *data); void handle_pointer_constraint(struct wl_listener *listener, void *data); void xdg_activation_v1_handle_request_activate(struct wl_listener *listener, void *data); +void xdg_activation_v1_handle_new_token(struct wl_listener *listener, + void *data); void set_rr_scheduling(void); diff --git a/include/sway/surface.h b/include/sway/surface.h index fb1cd775..a7a8ec3f 100644 --- a/include/sway/surface.h +++ b/include/sway/surface.h @@ -15,4 +15,10 @@ struct sway_surface { struct wl_event_source *frame_done_timer; }; +void surface_update_outputs(struct wlr_surface *surface); +void surface_enter_output(struct wlr_surface *surface, + struct sway_output *output); +void surface_leave_output(struct wlr_surface *surface, + struct sway_output *output); + #endif diff --git a/include/sway/swaynag.h b/include/sway/swaynag.h index 74d9ea18..03bd52c3 100644 --- a/include/sway/swaynag.h +++ b/include/sway/swaynag.h @@ -1,6 +1,7 @@ #ifndef _SWAY_SWAYNAG_H #define _SWAY_SWAYNAG_H #include +#include "stringop.h" struct swaynag_instance { struct wl_client *client; @@ -21,7 +22,7 @@ bool swaynag_spawn(const char *swaynag_command, // Write a log message to swaynag->fd[1]. This will fail when swaynag->detailed // is false. void swaynag_log(const char *swaynag_command, struct swaynag_instance *swaynag, - const char *fmt, ...); + const char *fmt, ...) _SWAY_ATTRIB_PRINTF(3, 4); // If swaynag->detailed, close swaynag->fd[1] so swaynag displays void swaynag_show(struct swaynag_instance *swaynag); diff --git a/include/sway/tree/container.h b/include/sway/tree/container.h index 3cd668f9..42fb71bf 100644 --- a/include/sway/tree/container.h +++ b/include/sway/tree/container.h @@ -119,6 +119,11 @@ struct sway_container { float saturation; + // Stores last output size and position for adjusting coordinates of + // scratchpad windows. + // Unused for non-scratchpad windows. + struct wlr_box transform; + float alpha; int corner_radius; @@ -206,6 +211,9 @@ size_t container_titlebar_height(void); void floating_calculate_constraints(int *min_width, int *max_width, int *min_height, int *max_height); +void floating_fix_coordinates(struct sway_container *con, + struct wlr_box *old, struct wlr_box *new); + void container_floating_resize_and_center(struct sway_container *con); void container_floating_set_default_size(struct sway_container *con); diff --git a/include/sway/tree/node.h b/include/sway/tree/node.h index 470ee3b5..03a389a4 100644 --- a/include/sway/tree/node.h +++ b/include/sway/tree/node.h @@ -1,5 +1,6 @@ #ifndef _SWAY_NODE_H #define _SWAY_NODE_H +#include #include #include "list.h" diff --git a/include/sway/tree/view.h b/include/sway/tree/view.h index 7b52aa4b..76cfdf3a 100644 --- a/include/sway/tree/view.h +++ b/include/sway/tree/view.h @@ -2,7 +2,7 @@ #define _SWAY_VIEW_H #include #include -#include "config.h" +#include "sway/config.h" #if HAVE_XWAYLAND #include #endif @@ -162,6 +162,8 @@ struct sway_xwayland_view { struct wl_listener set_window_type; struct wl_listener set_hints; struct wl_listener set_decorations; + struct wl_listener associate; + struct wl_listener dissociate; struct wl_listener map; struct wl_listener unmap; struct wl_listener destroy; @@ -179,6 +181,8 @@ struct sway_xwayland_unmanaged { struct wl_listener request_fullscreen; struct wl_listener commit; struct wl_listener set_geometry; + struct wl_listener associate; + struct wl_listener dissociate; struct wl_listener map; struct wl_listener unmap; struct wl_listener destroy; @@ -273,7 +277,12 @@ void view_set_activated(struct sway_view *view, bool activated); /** * Called when the view requests to be focused. */ -void view_request_activate(struct sway_view *view); +void view_request_activate(struct sway_view *view, struct sway_seat *seat); + +/* + * Called when the view requests urgent state + */ +void view_request_urgent(struct sway_view *view); /** * If possible, instructs the client to change their decoration mode. diff --git a/include/sway/tree/workspace.h b/include/sway/tree/workspace.h index d25afbb2..1e7d9b82 100644 --- a/include/sway/tree/workspace.h +++ b/include/sway/tree/workspace.h @@ -2,6 +2,7 @@ #define _SWAY_WORKSPACE_H #include +#include "sway/config.h" #include "sway/tree/container.h" #include "sway/tree/node.h" diff --git a/include/swaybar/bar.h b/include/swaybar/bar.h index 3ad0bdf3..197d2190 100644 --- a/include/swaybar/bar.h +++ b/include/swaybar/bar.h @@ -4,6 +4,7 @@ #include "config.h" #include "input.h" #include "pool-buffer.h" +#include "cursor-shape-v1-client-protocol.h" #include "wlr-layer-shell-unstable-v1-client-protocol.h" #include "xdg-output-unstable-v1-client-protocol.h" @@ -30,6 +31,7 @@ struct swaybar { struct wl_compositor *compositor; struct zwlr_layer_shell_v1 *layer_shell; struct zxdg_output_manager_v1 *xdg_output_manager; + struct wp_cursor_shape_manager_v1 *cursor_shape_manager; struct wl_shm *shm; struct swaybar_config *config; diff --git a/include/swaybar/tray/item.h b/include/swaybar/tray/item.h index c02a5582..73937a0c 100644 --- a/include/swaybar/tray/item.h +++ b/include/swaybar/tray/item.h @@ -4,6 +4,7 @@ #include #include #include +#include #include "swaybar/tray/tray.h" #include "list.h" diff --git a/include/swaynag/swaynag.h b/include/swaynag/swaynag.h index 2d68b6c9..fb9e9c21 100644 --- a/include/swaynag/swaynag.h +++ b/include/swaynag/swaynag.h @@ -4,6 +4,8 @@ #include #include "list.h" #include "pool-buffer.h" +#include "cursor-shape-v1-client-protocol.h" + #include "swaynag/types.h" #define SWAYNAG_MAX_HEIGHT 500 @@ -58,6 +60,7 @@ struct swaynag_button { struct swaynag_details { bool visible; char *message; + char *details_text; int x; int y; @@ -67,7 +70,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; }; @@ -84,6 +87,7 @@ struct swaynag { struct swaynag_output *output; struct zwlr_layer_shell_v1 *layer_shell; struct zwlr_layer_surface_v1 *layer_surface; + struct wp_cursor_shape_manager_v1 *cursor_shape_manager; struct wl_surface *surface; uint32_t width; diff --git a/include/swaynag/types.h b/include/swaynag/types.h index 18f218e0..9c3c50db 100644 --- a/include/swaynag/types.h +++ b/include/swaynag/types.h @@ -1,10 +1,13 @@ #ifndef _SWAYNAG_TYPES_H #define _SWAYNAG_TYPES_H +#include +#include +#include "list.h" + struct swaynag_type { char *name; - char *font; // Used for debugging. PangoFontDescription *font_description; char *output; uint32_t anchors; diff --git a/meson.build b/meson.build index 8d43af84..a8142519 100644 --- a/meson.build +++ b/meson.build @@ -1,7 +1,7 @@ project( 'sway', 'c', - version: '0.3.2', + version: '0.3.3', license: 'MIT', meson_version: '>=0.60.0', default_options: [ @@ -18,6 +18,7 @@ add_project_arguments( '-Wno-unused-parameter', '-Wno-unused-result', '-Wno-missing-braces', + '-Wno-format-zero-length', '-Wundef', '-Wvla', ], @@ -35,46 +36,26 @@ if is_freebsd add_project_arguments('-D_C11_SOURCE', language: 'c') endif +# Execute the scenefx subproject, if any +subproject( + 'scenefx', + required: false, +) +scenefx = dependency('scenefx') + # Execute the wlroots subproject, if any -wlroots_version = ['>=0.16.0', '<0.17.0'] +wlroots_version = ['>=0.17.0', '<0.18.0'] subproject( 'wlroots', default_options: ['examples=false'], required: false, version: wlroots_version, ) - -jsonc = dependency('json-c', version: '>=0.13') -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') -pangocairo = dependency('pangocairo') -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.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) -libudev = dependency('libudev') -bash_comp = dependency('bash-completion', required: false) -fish_comp = dependency('fish', required: false) -math = cc.find_library('m') -rt = cc.find_library('rt') -xcb_icccm = dependency('xcb-icccm', required: get_option('xwayland')) -threads = dependency('threads') # for pthread_setschedparam - wlroots_features = { 'xwayland': false, + 'libinput_backend': false, + 'session': false, } foreach name, _ : wlroots_features var_name = 'have_' + name.underscorify() @@ -85,6 +66,34 @@ 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 + +null_dep = dependency('', required: false) + +jsonc = dependency('json-c', version: '>=0.13') +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') +xkbcommon = dependency('xkbcommon', version: '>=1.5.0') +cairo = dependency('cairo') +pango = dependency('pango') +pangocairo = dependency('pangocairo') +gdk_pixbuf = dependency('gdk-pixbuf-2.0', required: get_option('gdk-pixbuf')) +pixman = dependency('pixman-1') +libevdev = dependency('libevdev') +libinput = wlroots_features['libinput_backend'] ? dependency('libinput', version: '>=1.21.0') : null_dep +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) +libudev = wlroots_features['libinput_backend'] ? dependency('libudev') : null_dep +math = cc.find_library('m') +rt = cc.find_library('rt') +xcb_icccm = dependency('xcb-icccm', required: get_option('xwayland')) +threads = dependency('threads') # for pthread_setschedparam + have_xwayland = xcb.found() and xcb_icccm.found() and wlroots_features['xwayland'] if get_option('sd-bus-provider') == 'auto' @@ -269,59 +278,7 @@ if get_option('default-wallpaper') install_data(wallpaper_files, install_dir: wallpaper_install_dir) endif -if get_option('zsh-completions') - zsh_files = files( - 'completions/zsh/_sway', - 'completions/zsh/_swaymsg', - ) - zsh_install_dir = join_paths(datadir, 'zsh', 'site-functions') - - install_data(zsh_files, install_dir: zsh_install_dir) -endif - -if get_option('bash-completions') - bash_files = files( - 'completions/bash/sway', - 'completions/bash/swaymsg', - ) - - if get_option('swaybar') - bash_files += files('completions/bash/swaybar') - endif - - if bash_comp.found() - bash_install_dir = bash_comp.get_variable( - pkgconfig: 'completionsdir', - pkgconfig_define: ['datadir', datadir] - ) - else - bash_install_dir = join_paths(datadir, 'bash-completion', 'completions') - endif - - install_data(bash_files, install_dir: bash_install_dir) -endif - -if get_option('fish-completions') - fish_files = files( - 'completions/fish/sway.fish', - 'completions/fish/swaymsg.fish', - ) - - if get_option('swaynag') - fish_files += files('completions/fish/swaynag.fish') - endif - - if fish_comp.found() - fish_install_dir = fish_comp.get_variable( - pkgconfig: 'completionsdir', - pkgconfig_define: ['datadir', datadir] - ) - else - fish_install_dir = join_paths(datadir, 'fish', 'vendor_completions.d') - endif - - install_data(fish_files, install_dir: fish_install_dir) -endif +subdir('completions') summary({ 'xwayland': have_xwayland, @@ -329,4 +286,3 @@ summary({ 'tray': have_tray, 'man-pages': scdoc.found(), }, bool_yn: true) - diff --git a/meson_options.txt b/meson_options.txt index 6ba67554..8d0d6509 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -6,6 +6,6 @@ option('swaybar', type: 'boolean', value: true, description: 'Enable support for option('swaynag', type: 'boolean', value: true, description: 'Enable support for swaynag') option('xwayland', type: 'feature', value: 'auto', description: 'Enable support for X11 applications') option('tray', type: 'feature', value: 'auto', description: 'Enable support for swaybar tray') -option('gdk-pixbuf', type: 'feature', value: 'auto', description: 'Enable support for more image formats in swaybg') +option('gdk-pixbuf', type: 'feature', value: 'auto', description: 'Enable support for more image formats in swaybar tray') option('man-pages', type: 'feature', value: 'auto', description: 'Generate and install man pages') option('sd-bus-provider', type: 'combo', choices: ['auto', 'libsystemd', 'libelogind', 'basu'], value: 'auto', description: 'Provider of the sd-bus library') diff --git a/protocols/meson.build b/protocols/meson.build index 904aead4..2992ac58 100644 --- a/protocols/meson.build +++ b/protocols/meson.build @@ -2,7 +2,7 @@ wl_protocol_dir = wayland_protos.get_variable('pkgdatadir') wayland_scanner_dep = dependency('wayland-scanner', native: true) wayland_scanner = find_program( - wayland_scanner_dep.get_variable(pkgconfig: 'wayland_scanner'), + wayland_scanner_dep.get_variable('wayland_scanner'), native: true, ) @@ -12,6 +12,8 @@ protocols = [ 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', + wl_protocol_dir / 'staging/content-type/content-type-v1.xml', + wl_protocol_dir / 'staging/cursor-shape/cursor-shape-v1.xml', 'wlr-layer-shell-unstable-v1.xml', 'idle.xml', 'wlr-input-inhibitor-unstable-v1.xml', diff --git a/sway/commands.c b/sway/commands.c index 8b326547..2e048203 100644 --- a/sway/commands.c +++ b/sway/commands.c @@ -127,6 +127,7 @@ static const struct cmd_handler config_handlers[] = { { "default_orientation", cmd_default_orientation }, { "include", cmd_include }, { "scratchpad_minimize", cmd_scratchpad_minimize }, + { "primary_selection", cmd_primary_selection }, { "swaybg_command", cmd_swaybg_command }, { "swaynag_command", cmd_swaynag_command }, { "workspace_layout", cmd_workspace_layout }, @@ -171,7 +172,7 @@ static int handler_compare(const void *_a, const void *_b) { return strcasecmp(a->command, b->command); } -const struct cmd_handler *find_handler(char *line, +const struct cmd_handler *find_handler(const char *line, const struct cmd_handler *handlers, size_t handlers_size) { if (!handlers || !handlers_size) { return NULL; @@ -404,10 +405,13 @@ struct cmd_results *config_command(char *exec, char **new_block) { sway_log(SWAY_INFO, "Config command: %s", exec); const struct cmd_handler *handler = find_core_handler(argv[0]); if (!handler || !handler->handle) { - const char *error = handler - ? "Command '%s' is shimmed, but unimplemented" - : "Unknown/invalid command '%s'"; - results = cmd_results_new(CMD_INVALID, error, argv[0]); + if (handler) { + results = cmd_results_new(CMD_INVALID, + "Command '%s' is shimmed, but unimplemented", argv[0]); + } else { + results = cmd_results_new(CMD_INVALID, + "Unknown/invalid command '%s'", argv[0]); + } goto cleanup; } @@ -509,20 +513,10 @@ struct cmd_results *cmd_results_new(enum cmd_status status, } results->status = status; if (format) { - char *error = NULL; va_list args; va_start(args, format); - int slen = vsnprintf(NULL, 0, format, args); + results->error = vformat_str(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/assign.c b/sway/commands/assign.c index 976bc3cc..f7d911f7 100644 --- a/sway/commands/assign.c +++ b/sway/commands/assign.c @@ -17,7 +17,7 @@ struct cmd_results *cmd_assign(int argc, char **argv) { char *err_str = NULL; struct criteria *criteria = criteria_parse(argv[0], &err_str); if (!criteria) { - error = cmd_results_new(CMD_INVALID, err_str); + error = cmd_results_new(CMD_INVALID, "%s", err_str); free(err_str); return error; } diff --git a/sway/commands/bar.c b/sway/commands/bar.c index 8571d282..22756acb 100644 --- a/sway/commands/bar.c +++ b/sway/commands/bar.c @@ -73,12 +73,10 @@ struct cmd_results *cmd_bar(int argc, char **argv) { } ++argv; --argc; } else if (config->reading && !config->current_bar) { - int len = snprintf(NULL, 0, "bar-%d", config->bars->length) + 1; - id = malloc(len * sizeof(char)); + id = format_str("bar-%d", config->bars->length); if (!id) { return cmd_results_new(CMD_FAILURE, "Unable to allocate bar id"); } - snprintf(id, len, "bar-%d", config->bars->length); } else if (!config->reading && strcmp(argv[0], "mode") != 0 && strcmp(argv[0], "hidden_state") != 0) { if (is_subcommand(argv[0])) { diff --git a/sway/commands/bar/bind.c b/sway/commands/bar/bind.c index b4b5bc45..8a837e3f 100644 --- a/sway/commands/bar/bind.c +++ b/sway/commands/bar/bind.c @@ -96,7 +96,7 @@ static struct cmd_results *bar_cmd_bind(int argc, char **argv, bool code, } if (message) { free_bar_binding(binding); - error = cmd_results_new(CMD_INVALID, message); + error = cmd_results_new(CMD_INVALID, "%s", message); free(message); return error; } else if (!binding->button) { diff --git a/sway/commands/bar/tray_bind.c b/sway/commands/bar/tray_bind.c index 243834ba..3dc9bc4c 100644 --- a/sway/commands/bar/tray_bind.c +++ b/sway/commands/bar/tray_bind.c @@ -26,7 +26,7 @@ static struct cmd_results *tray_bind(int argc, char **argv, bool code) { } if (message) { free(binding); - error = cmd_results_new(CMD_INVALID, message); + error = cmd_results_new(CMD_INVALID, "%s", message); free(message); return error; } else if (!binding->button) { diff --git a/sway/commands/bind.c b/sway/commands/bind.c index c0b383db..979e178f 100644 --- a/sway/commands/bind.c +++ b/sway/commands/bind.c @@ -127,7 +127,7 @@ static struct cmd_results *identify_key(const char* name, bool first_key, if (!button) { if (message) { struct cmd_results *error = - cmd_results_new(CMD_INVALID, message); + cmd_results_new(CMD_INVALID, "%s", message); free(message); return error; } else { @@ -143,7 +143,7 @@ static struct cmd_results *identify_key(const char* name, bool first_key, if (!button) { if (message) { struct cmd_results *error = - cmd_results_new(CMD_INVALID, message); + cmd_results_new(CMD_INVALID, "%s", message); free(message); return error; } else { @@ -182,7 +182,7 @@ static struct cmd_results *identify_key(const char* name, bool first_key, uint32_t button = get_mouse_bindsym(name, &message); if (message) { struct cmd_results *error = - cmd_results_new(CMD_INVALID, message); + cmd_results_new(CMD_INVALID, "%s", message); free(message); return error; } else if (button) { @@ -539,7 +539,7 @@ struct cmd_results *cmd_bind_or_unbind_switch(int argc, char **argv, free_switch_binding(binding); return cmd_results_new(CMD_FAILURE, "Invalid %s command (expected binding with the form " - ":)", bindtype, argc); + ":)", bindtype); } if (strcmp(split->items[0], "tablet") == 0) { binding->type = WLR_SWITCH_TYPE_TABLET_MODE; @@ -549,7 +549,8 @@ struct cmd_results *cmd_bind_or_unbind_switch(int argc, char **argv, free_switch_binding(binding); return cmd_results_new(CMD_FAILURE, "Invalid %s command (expected switch binding: " - "unknown switch %s)", bindtype, split->items[0]); + "unknown switch %s)", bindtype, + (const char *)split->items[0]); } if (strcmp(split->items[1], "on") == 0) { binding->trigger = SWAY_SWITCH_TRIGGER_ON; @@ -562,7 +563,7 @@ struct cmd_results *cmd_bind_or_unbind_switch(int argc, char **argv, return cmd_results_new(CMD_FAILURE, "Invalid %s command " "(expected switch state: unknown state %s)", - bindtype, split->items[1]); + bindtype, (const char *)split->items[1]); } list_free_items_and_destroy(split); diff --git a/sway/commands/blur.c b/sway/commands/blur.c index 5607d1e2..52e3fdb9 100644 --- a/sway/commands/blur.c +++ b/sway/commands/blur.c @@ -1,3 +1,4 @@ +#include "scenefx/render/fx_renderer/fx_effect_framebuffers.h" #include "sway/commands.h" #include "sway/config.h" #include "sway/output.h" @@ -21,10 +22,10 @@ struct cmd_results *cmd_blur(int argc, char **argv) { struct sway_output *output; wl_list_for_each(output, &root->all_outputs, link) { - if (output->renderer) { - output->renderer->blur_buffer_dirty = true; - output_damage_whole(output); - } + struct fx_effect_framebuffers *effect_fbos = + fx_effect_framebuffers_try_get(output->wlr_output); + effect_fbos->blur_buffer_dirty = true; + output_damage_whole(output); } return cmd_results_new(CMD_SUCCESS, NULL); diff --git a/sway/commands/blur_brightness.c b/sway/commands/blur_brightness.c index 79a40266..6ff60975 100644 --- a/sway/commands/blur_brightness.c +++ b/sway/commands/blur_brightness.c @@ -1,3 +1,4 @@ +#include "scenefx/render/fx_renderer/fx_effect_framebuffers.h" #include "sway/commands.h" #include "sway/config.h" #include "sway/output.h" @@ -18,10 +19,10 @@ struct cmd_results *cmd_blur_brightness(int argc, char **argv) { struct sway_output *output; wl_list_for_each(output, &root->all_outputs, link) { - if (output->renderer) { - output->renderer->blur_buffer_dirty = true; - output_damage_whole(output); - } + struct fx_effect_framebuffers *effect_fbos = + fx_effect_framebuffers_try_get(output->wlr_output); + effect_fbos->blur_buffer_dirty = true; + output_damage_whole(output); } return cmd_results_new(CMD_SUCCESS, NULL); diff --git a/sway/commands/blur_contrast.c b/sway/commands/blur_contrast.c index 61e3aec4..ba046e63 100644 --- a/sway/commands/blur_contrast.c +++ b/sway/commands/blur_contrast.c @@ -1,3 +1,4 @@ +#include "scenefx/render/fx_renderer/fx_effect_framebuffers.h" #include "sway/commands.h" #include "sway/config.h" #include "sway/output.h" @@ -18,10 +19,10 @@ struct cmd_results *cmd_blur_contrast(int argc, char **argv) { struct sway_output *output; wl_list_for_each(output, &root->all_outputs, link) { - if (output->renderer) { - output->renderer->blur_buffer_dirty = true; - output_damage_whole(output); - } + struct fx_effect_framebuffers *effect_fbos = + fx_effect_framebuffers_try_get(output->wlr_output); + effect_fbos->blur_buffer_dirty = true; + output_damage_whole(output); } return cmd_results_new(CMD_SUCCESS, NULL); diff --git a/sway/commands/blur_noise.c b/sway/commands/blur_noise.c index bd737911..358b20f5 100644 --- a/sway/commands/blur_noise.c +++ b/sway/commands/blur_noise.c @@ -1,3 +1,4 @@ +#include "scenefx/render/fx_renderer/fx_effect_framebuffers.h" #include "sway/commands.h" #include "sway/config.h" #include "sway/output.h" @@ -16,13 +17,13 @@ struct cmd_results *cmd_blur_noise(int argc, char **argv) { config->blur_params.noise = value; - struct sway_output *output; - wl_list_for_each(output, &root->all_outputs, link) { - if (output->renderer) { - output->renderer->blur_buffer_dirty = true; - output_damage_whole(output); - } - } + struct sway_output *output; + wl_list_for_each(output, &root->all_outputs, link) { + struct fx_effect_framebuffers *effect_fbos = + fx_effect_framebuffers_try_get(output->wlr_output); + effect_fbos->blur_buffer_dirty = true; + output_damage_whole(output); + } return cmd_results_new(CMD_SUCCESS, NULL); } diff --git a/sway/commands/blur_passes.c b/sway/commands/blur_passes.c index 0868a568..c79f99d1 100644 --- a/sway/commands/blur_passes.c +++ b/sway/commands/blur_passes.c @@ -1,3 +1,4 @@ +#include "scenefx/render/fx_renderer/fx_effect_framebuffers.h" #include "sway/commands.h" #include "sway/config.h" #include "sway/output.h" @@ -18,10 +19,10 @@ struct cmd_results *cmd_blur_passes(int argc, char **argv) { struct sway_output *output; wl_list_for_each(output, &root->all_outputs, link) { - if (output->renderer) { - output->renderer->blur_buffer_dirty = true; - output_damage_whole(output); - } + struct fx_effect_framebuffers *effect_fbos = + fx_effect_framebuffers_try_get(output->wlr_output); + effect_fbos->blur_buffer_dirty = true; + output_damage_whole(output); } return cmd_results_new(CMD_SUCCESS, NULL); diff --git a/sway/commands/blur_radius.c b/sway/commands/blur_radius.c index f6e7d4ed..85e90e77 100644 --- a/sway/commands/blur_radius.c +++ b/sway/commands/blur_radius.c @@ -1,3 +1,4 @@ +#include "scenefx/render/fx_renderer/fx_effect_framebuffers.h" #include "sway/commands.h" #include "sway/config.h" #include "sway/output.h" @@ -18,10 +19,10 @@ struct cmd_results *cmd_blur_radius(int argc, char **argv) { struct sway_output *output; wl_list_for_each(output, &root->all_outputs, link) { - if (output->renderer) { - output->renderer->blur_buffer_dirty = true; - output_damage_whole(output); - } + struct fx_effect_framebuffers *effect_fbos = + fx_effect_framebuffers_try_get(output->wlr_output); + effect_fbos->blur_buffer_dirty = true; + output_damage_whole(output); } return cmd_results_new(CMD_SUCCESS, NULL); diff --git a/sway/commands/blur_saturation.c b/sway/commands/blur_saturation.c index 35627363..ae6352d2 100644 --- a/sway/commands/blur_saturation.c +++ b/sway/commands/blur_saturation.c @@ -1,3 +1,4 @@ +#include "scenefx/render/fx_renderer/fx_effect_framebuffers.h" #include "sway/commands.h" #include "sway/config.h" #include "sway/output.h" @@ -18,10 +19,10 @@ struct cmd_results *cmd_blur_saturation(int argc, char **argv) { struct sway_output *output; wl_list_for_each(output, &root->all_outputs, link) { - if (output->renderer) { - output->renderer->blur_buffer_dirty = true; - output_damage_whole(output); - } + struct fx_effect_framebuffers *effect_fbos = + fx_effect_framebuffers_try_get(output->wlr_output); + effect_fbos->blur_buffer_dirty = true; + output_damage_whole(output); } return cmd_results_new(CMD_SUCCESS, NULL); diff --git a/sway/commands/blur_xray.c b/sway/commands/blur_xray.c index 045566d0..6986b361 100644 --- a/sway/commands/blur_xray.c +++ b/sway/commands/blur_xray.c @@ -1,3 +1,4 @@ +#include "scenefx/render/fx_renderer/fx_effect_framebuffers.h" #include "sway/commands.h" #include "sway/config.h" #include "sway/output.h" @@ -15,10 +16,10 @@ struct cmd_results *cmd_blur_xray(int argc, char **argv) { struct sway_output *output; wl_list_for_each(output, &root->all_outputs, link) { - if (output->renderer) { - output->renderer->blur_buffer_dirty = true; - output_damage_whole(output); - } + struct fx_effect_framebuffers *effect_fbos = + fx_effect_framebuffers_try_get(output->wlr_output); + effect_fbos->blur_buffer_dirty = true; + output_damage_whole(output); } return cmd_results_new(CMD_SUCCESS, NULL); diff --git a/sway/commands/exec_always.c b/sway/commands/exec_always.c index e6b09e64..8fca1909 100644 --- a/sway/commands/exec_always.c +++ b/sway/commands/exec_always.c @@ -9,7 +9,6 @@ #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" @@ -64,7 +63,7 @@ struct cmd_results *cmd_exec_process(int argc, char **argv) { } pid_t pid, child; - struct launcher_ctx *ctx = launcher_ctx_create(); + struct launcher_ctx *ctx = launcher_ctx_create_internal(); // Fork process if ((pid = fork()) == 0) { // Fork child process again diff --git a/sway/commands/floating_minmax_size.c b/sway/commands/floating_minmax_size.c index 3a1d606a..e8c24ace 100644 --- a/sway/commands/floating_minmax_size.c +++ b/sway/commands/floating_minmax_size.c @@ -23,16 +23,16 @@ static struct cmd_results *handle_command(int argc, char **argv, char *cmd_name, char *err; int width = (int)strtol(argv[0], &err, 10); if (*err) { - return cmd_results_new(CMD_INVALID, cmd_name, usage); + return cmd_results_new(CMD_INVALID, "%s", usage); } if (strcmp(argv[1], "x") != 0) { - return cmd_results_new(CMD_INVALID, cmd_name, usage); + return cmd_results_new(CMD_INVALID, "%s", usage); } int height = (int)strtol(argv[2], &err, 10); if (*err) { - return cmd_results_new(CMD_INVALID, cmd_name, usage); + return cmd_results_new(CMD_INVALID, "%s", usage); } *config_width = width; diff --git a/sway/commands/font.c b/sway/commands/font.c index dd80281f..74bb6b9f 100644 --- a/sway/commands/font.c +++ b/sway/commands/font.c @@ -33,10 +33,10 @@ struct cmd_results *cmd_font(int argc, char **argv) { return cmd_results_new(CMD_FAILURE, "Invalid font family."); } - const PangoFontMask flags = pango_font_description_get_set_fields(font_description); - if ((flags & PANGO_FONT_MASK_SIZE) == 0) { + 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, "Font size not given."); + return cmd_results_new(CMD_FAILURE, "Invalid font size."); } if (config->font_description != NULL) { diff --git a/sway/commands/for_window.c b/sway/commands/for_window.c index ee9f4647..905e6776 100644 --- a/sway/commands/for_window.c +++ b/sway/commands/for_window.c @@ -14,7 +14,7 @@ struct cmd_results *cmd_for_window(int argc, char **argv) { char *err_str = NULL; struct criteria *criteria = criteria_parse(argv[0], &err_str); if (!criteria) { - error = cmd_results_new(CMD_INVALID, err_str); + error = cmd_results_new(CMD_INVALID, "%s", err_str); free(err_str); return error; } diff --git a/sway/commands/hide_edge_borders.c b/sway/commands/hide_edge_borders.c index 9a1d8445..43bd6dc8 100644 --- a/sway/commands/hide_edge_borders.c +++ b/sway/commands/hide_edge_borders.c @@ -20,7 +20,7 @@ struct cmd_results *cmd_hide_edge_borders(int argc, char **argv) { } if (!argc) { - return cmd_results_new(CMD_INVALID, expected_syntax); + return cmd_results_new(CMD_INVALID, "%s", expected_syntax); } if (strcmp(argv[0], "none") == 0) { @@ -38,7 +38,7 @@ struct cmd_results *cmd_hide_edge_borders(int argc, char **argv) { config->hide_edge_borders = E_NONE; config->hide_edge_borders_smart = ESMART_NO_GAPS; } else { - return cmd_results_new(CMD_INVALID, expected_syntax); + return cmd_results_new(CMD_INVALID, "%s", expected_syntax); } config->hide_lone_tab = hide_lone_tab; diff --git a/sway/commands/inhibit_idle.c b/sway/commands/inhibit_idle.c index aebc2bf9..6125736a 100644 --- a/sway/commands/inhibit_idle.c +++ b/sway/commands/inhibit_idle.c @@ -41,7 +41,7 @@ struct cmd_results *cmd_inhibit_idle(int argc, char **argv) { sway_idle_inhibit_v1_user_inhibitor_destroy(inhibitor); } else { inhibitor->mode = mode; - sway_idle_inhibit_v1_check_active(server.idle_inhibit_manager_v1); + sway_idle_inhibit_v1_check_active(); } } else if (!clear) { sway_idle_inhibit_v1_user_inhibitor_register(con->view, mode); diff --git a/sway/commands/input.c b/sway/commands/input.c index ea531659..306c40f7 100644 --- a/sway/commands/input.c +++ b/sway/commands/input.c @@ -25,7 +25,9 @@ static const struct cmd_handler input_handlers[] = { { "pointer_accel", input_cmd_pointer_accel }, { "repeat_delay", input_cmd_repeat_delay }, { "repeat_rate", input_cmd_repeat_rate }, + { "rotation_angle", input_cmd_rotation_angle }, { "scroll_button", input_cmd_scroll_button }, + { "scroll_button_lock", input_cmd_scroll_button_lock }, { "scroll_factor", input_cmd_scroll_factor }, { "scroll_method", input_cmd_scroll_method }, { "tap", input_cmd_tap }, diff --git a/sway/commands/input/events.c b/sway/commands/input/events.c index 9405181a..08d99bf0 100644 --- a/sway/commands/input/events.c +++ b/sway/commands/input/events.c @@ -1,14 +1,19 @@ #include #include #include -#include +#include #include "sway/config.h" #include "sway/commands.h" #include "sway/input/input-manager.h" #include "log.h" +#if WLR_HAS_LIBINPUT_BACKEND +#include +#endif + static void toggle_supported_send_events_for_device(struct input_config *ic, struct sway_input_device *input_device) { +#if WLR_HAS_LIBINPUT_BACKEND struct wlr_input_device *wlr_device = input_device->wlr_device; if (!wlr_input_device_is_libinput(wlr_device)) { return; @@ -41,6 +46,7 @@ static void toggle_supported_send_events_for_device(struct input_config *ic, } ic->send_events = mode; +#endif } static int mode_for_name(const char *name) { @@ -56,6 +62,7 @@ static int mode_for_name(const char *name) { static void toggle_select_send_events_for_device(struct input_config *ic, struct sway_input_device *input_device, int argc, char **argv) { +#if WLR_HAS_LIBINPUT_BACKEND if (!wlr_input_device_is_libinput(input_device->wlr_device)) { return; } @@ -72,6 +79,7 @@ static void toggle_select_send_events_for_device(struct input_config *ic, } } ic->send_events = mode_for_name(argv[index % argc]); +#endif } static void toggle_send_events(int argc, char **argv) { diff --git a/sway/commands/input/map_from_region.c b/sway/commands/input/map_from_region.c index de00b714..4400e111 100644 --- a/sway/commands/input/map_from_region.c +++ b/sway/commands/input/map_from_region.c @@ -11,11 +11,21 @@ static bool parse_coords(const char *str, double *x, double *y, bool *mm) { *mm = false; char *end; - *x = strtod(str, &end); - if (end[0] != 'x') { - return false; + + // Check for "0x" prefix to avoid strtod treating the string as hex + if (str[0] == '0' && str[1] == 'x') { + if (strlen(str) < 3) { + return false; + } + *x = 0; + end = (char *)str + 2; + } else { + *x = strtod(str, &end); + if (end[0] != 'x') { + return false; + } + ++end; } - ++end; *y = strtod(end, &end); if (end[0] == 'm') { diff --git a/sway/commands/input/map_to_region.c b/sway/commands/input/map_to_region.c index 284b57d0..ad535db2 100644 --- a/sway/commands/input/map_to_region.c +++ b/sway/commands/input/map_to_region.c @@ -49,5 +49,5 @@ struct cmd_results *input_cmd_map_to_region(int argc, char **argv) { error: free(ic->mapped_to_region); ic->mapped_to_region = NULL; - return cmd_results_new(CMD_FAILURE, errstr); + return cmd_results_new(CMD_FAILURE, "%s", errstr); } diff --git a/sway/commands/input/rotation_angle.c b/sway/commands/input/rotation_angle.c new file mode 100644 index 00000000..5e278fff --- /dev/null +++ b/sway/commands/input/rotation_angle.c @@ -0,0 +1,29 @@ +#include +#include +#include +#include "sway/config.h" +#include "sway/commands.h" +#include "sway/input/input-manager.h" +#include "util.h" + +struct cmd_results *input_cmd_rotation_angle(int argc, char **argv) { + struct cmd_results *error = NULL; + if ((error = checkarg(argc, "rotation_angle", 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."); + } + + float rotation_angle = parse_float(argv[0]); + if (isnan(rotation_angle)) { + return cmd_results_new(CMD_INVALID, + "Invalid rotation_angle; expected float."); + } if (rotation_angle < 0 || rotation_angle > 360) { + return cmd_results_new(CMD_INVALID, "Input out of range [0, 360)"); + } + ic->rotation_angle = rotation_angle; + + return cmd_results_new(CMD_SUCCESS, NULL); +} diff --git a/sway/commands/input/scroll_button.c b/sway/commands/input/scroll_button.c index 6b331419..81f69a6d 100644 --- a/sway/commands/input/scroll_button.c +++ b/sway/commands/input/scroll_button.c @@ -21,7 +21,7 @@ struct cmd_results *input_cmd_scroll_button(int argc, char **argv) { char *message = NULL; uint32_t button = get_mouse_button(*argv, &message); if (message) { - error = cmd_results_new(CMD_INVALID, message); + error = cmd_results_new(CMD_INVALID, "%s", message); free(message); return error; } else if (button == SWAY_SCROLL_UP || button == SWAY_SCROLL_DOWN diff --git a/sway/commands/input/scroll_button_lock.c b/sway/commands/input/scroll_button_lock.c new file mode 100644 index 00000000..f96b6514 --- /dev/null +++ b/sway/commands/input/scroll_button_lock.c @@ -0,0 +1,26 @@ +#include +#include +#include +#include "sway/config.h" +#include "sway/commands.h" +#include "sway/input/input-manager.h" +#include "util.h" + +struct cmd_results *input_cmd_scroll_button_lock(int argc, char **argv) { + struct cmd_results *error = NULL; + if ((error = checkarg(argc, "scroll_button_lock", 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->scroll_button_lock = LIBINPUT_CONFIG_SCROLL_BUTTON_LOCK_ENABLED; + } else { + ic->scroll_button_lock = LIBINPUT_CONFIG_SCROLL_BUTTON_LOCK_DISABLED; + } + + return cmd_results_new(CMD_SUCCESS, NULL); +} diff --git a/sway/commands/layout.c b/sway/commands/layout.c index 2ba61b38..12ce4839 100644 --- a/sway/commands/layout.c +++ b/sway/commands/layout.c @@ -153,7 +153,7 @@ struct cmd_results *cmd_layout(int argc, char **argv) { workspace->output); } if (new_layout == L_NONE) { - return cmd_results_new(CMD_INVALID, expected_syntax); + return cmd_results_new(CMD_INVALID, "%s", expected_syntax); } if (new_layout != old_layout) { if (container) { diff --git a/sway/commands/move.c b/sway/commands/move.c index 7bd1fe3e..69ed06c0 100644 --- a/sway/commands/move.c +++ b/sway/commands/move.c @@ -206,9 +206,17 @@ static void container_move_to_workspace(struct sway_container *container, container_detach(container); workspace_add_floating(workspace, container); container_handle_fullscreen_reparent(container); - // If changing output, center it within the workspace + // If changing output, adjust the coordinates of the window. if (old_output != workspace->output && !container->pending.fullscreen_mode) { - container_floating_move_to_center(container); + struct wlr_box workspace_box, old_workspace_box; + workspace_get_box(workspace, &workspace_box); + workspace_get_box(old_workspace, &old_workspace_box); + floating_fix_coordinates(container, &old_workspace_box, &workspace_box); + if (container->scratchpad && workspace->output) { + struct wlr_box output_box; + output_get_box(workspace->output, &output_box); + container->transform = workspace_box; + } } } else { container_detach(container); @@ -462,7 +470,7 @@ static struct cmd_results *cmd_move_container(bool no_auto_back_and_forth, if (strcasecmp(argv[1], "number") == 0) { // move [window|container] [to] "workspace number x" if (argc < 3) { - return cmd_results_new(CMD_INVALID, expected_syntax); + return cmd_results_new(CMD_INVALID, "%s", expected_syntax); } if (!isdigit(argv[2][0])) { return cmd_results_new(CMD_INVALID, @@ -522,7 +530,7 @@ static struct cmd_results *cmd_move_container(bool no_auto_back_and_forth, } destination = &dest_con->node; } else { - return cmd_results_new(CMD_INVALID, expected_syntax); + return cmd_results_new(CMD_INVALID, "%s", expected_syntax); } if (destination->type == N_CONTAINER && @@ -821,7 +829,7 @@ static struct cmd_results *cmd_move_to_position(int argc, char **argv) { } if (!argc) { - return cmd_results_new(CMD_INVALID, expected_position_syntax); + return cmd_results_new(CMD_INVALID, "%s", expected_position_syntax); } bool absolute = false; @@ -831,19 +839,19 @@ static struct cmd_results *cmd_move_to_position(int argc, char **argv) { ++argv; } if (!argc) { - return cmd_results_new(CMD_INVALID, expected_position_syntax); + return cmd_results_new(CMD_INVALID, "%s", expected_position_syntax); } if (strcmp(argv[0], "position") == 0) { --argc; ++argv; } if (!argc) { - return cmd_results_new(CMD_INVALID, expected_position_syntax); + return cmd_results_new(CMD_INVALID, "%s", expected_position_syntax); } if (strcmp(argv[0], "cursor") == 0 || strcmp(argv[0], "mouse") == 0 || strcmp(argv[0], "pointer") == 0) { if (absolute) { - return cmd_results_new(CMD_INVALID, expected_position_syntax); + return cmd_results_new(CMD_INVALID, "%s", expected_position_syntax); } return cmd_move_to_position_pointer(container); } else if (strcmp(argv[0], "center") == 0) { @@ -865,7 +873,7 @@ static struct cmd_results *cmd_move_to_position(int argc, char **argv) { } if (argc < 2) { - return cmd_results_new(CMD_FAILURE, expected_position_syntax); + return cmd_results_new(CMD_FAILURE, "%s", expected_position_syntax); } struct movement_amount lx = { .amount = 0, .unit = MOVEMENT_UNIT_INVALID }; @@ -878,7 +886,7 @@ static struct cmd_results *cmd_move_to_position(int argc, char **argv) { } if (argc < 1) { - return cmd_results_new(CMD_FAILURE, expected_position_syntax); + return cmd_results_new(CMD_FAILURE, "%s", expected_position_syntax); } struct movement_amount ly = { .amount = 0, .unit = MOVEMENT_UNIT_INVALID }; @@ -887,7 +895,7 @@ static struct cmd_results *cmd_move_to_position(int argc, char **argv) { argc -= num_consumed_args; argv += num_consumed_args; if (argc > 0) { - return cmd_results_new(CMD_INVALID, expected_position_syntax); + return cmd_results_new(CMD_INVALID, "%s", expected_position_syntax); } if (ly.unit == MOVEMENT_UNIT_INVALID) { return cmd_results_new(CMD_INVALID, "Invalid y position specified"); @@ -1033,13 +1041,13 @@ struct cmd_results *cmd_move(int argc, char **argv) { } if (!argc) { - return cmd_results_new(CMD_INVALID, expected_full_syntax); + return cmd_results_new(CMD_INVALID, "%s", expected_full_syntax); } // Only `move [window|container] [to] workspace` supports // `--no-auto-back-and-forth` so treat others as invalid syntax if (no_auto_back_and_forth && strcasecmp(argv[0], "workspace") != 0) { - return cmd_results_new(CMD_INVALID, expected_full_syntax); + return cmd_results_new(CMD_INVALID, "%s", expected_full_syntax); } if (strcasecmp(argv[0], "workspace") == 0 || @@ -1053,5 +1061,5 @@ struct cmd_results *cmd_move(int argc, char **argv) { strcasecmp(argv[1], "position") == 0)) { return cmd_move_to_position(argc, argv); } - return cmd_results_new(CMD_INVALID, expected_full_syntax); + return cmd_results_new(CMD_INVALID, "%s", expected_full_syntax); } diff --git a/sway/commands/no_focus.c b/sway/commands/no_focus.c index 2001e04f..ccfdec82 100644 --- a/sway/commands/no_focus.c +++ b/sway/commands/no_focus.c @@ -13,7 +13,7 @@ struct cmd_results *cmd_no_focus(int argc, char **argv) { char *err_str = NULL; struct criteria *criteria = criteria_parse(argv[0], &err_str); if (!criteria) { - error = cmd_results_new(CMD_INVALID, err_str); + error = cmd_results_new(CMD_INVALID, "%s", err_str); free(err_str); return error; } diff --git a/sway/commands/output.c b/sway/commands/output.c index df32c673..462dffd2 100644 --- a/sway/commands/output.c +++ b/sway/commands/output.c @@ -111,7 +111,10 @@ struct cmd_results *cmd_output(int argc, char **argv) { if (!config->reloading && !config->validating) { apply_output_config_to_outputs(output); if (background) { - spawn_swaybg(); + if (!spawn_swaybg()) { + return cmd_results_new(CMD_FAILURE, + "Failed to apply background configuration"); + } } } diff --git a/sway/commands/output/background.c b/sway/commands/output/background.c index 67f212ff..d691295f 100644 --- a/sway/commands/output/background.c +++ b/sway/commands/output/background.c @@ -123,7 +123,10 @@ struct cmd_results *output_cmd_background(int argc, char **argv) { src); config_add_swaynag_warning("Unable to access background file '%s'", src); + struct cmd_results *result = cmd_results_new(CMD_FAILURE, + "unable to access background file '%s'", src); free(src); + return result; } else { output->background = src; output->background_option = strdup(mode); diff --git a/sway/commands/primary_selection.c b/sway/commands/primary_selection.c new file mode 100644 index 00000000..585b079d --- /dev/null +++ b/sway/commands/primary_selection.c @@ -0,0 +1,23 @@ +#include +#include +#include "sway/config.h" +#include "sway/commands.h" +#include "util.h" + +struct cmd_results *cmd_primary_selection(int argc, char **argv) { + struct cmd_results *error = NULL; + if ((error = checkarg(argc, "primary_selection", EXPECTED_EQUAL_TO, 1))) { + return error; + } + + bool primary_selection = parse_boolean(argv[0], true); + + if (config->reloading && config->primary_selection != primary_selection) { + return cmd_results_new(CMD_FAILURE, + "primary_selection can only be enabled/disabled at launch"); + } + + config->primary_selection = parse_boolean(argv[0], true); + + return cmd_results_new(CMD_SUCCESS, NULL); +} diff --git a/sway/commands/rename.c b/sway/commands/rename.c index 60a66d58..0d36cc21 100644 --- a/sway/commands/rename.c +++ b/sway/commands/rename.c @@ -26,7 +26,7 @@ struct cmd_results *cmd_rename(int argc, char **argv) { "Can't run this command while there's no outputs connected."); } if (strcasecmp(argv[0], "workspace") != 0) { - return cmd_results_new(CMD_INVALID, expected_syntax); + return cmd_results_new(CMD_INVALID, "%s", expected_syntax); } int argn = 1; @@ -65,7 +65,7 @@ struct cmd_results *cmd_rename(int argc, char **argv) { ++argn; // move past "to" if (argn >= argc) { - return cmd_results_new(CMD_INVALID, expected_syntax); + return cmd_results_new(CMD_INVALID, "%s", expected_syntax); } char *new_name = join_args(argv + argn, argc - argn); diff --git a/sway/commands/resize.c b/sway/commands/resize.c index 425069de..32b746ea 100644 --- a/sway/commands/resize.c +++ b/sway/commands/resize.c @@ -75,6 +75,10 @@ void container_resize_tiled(struct sway_container *con, return; } + if (container_is_scratchpad_hidden_or_child(con)) { + return; + } + // For HORIZONTAL or VERTICAL, we are growing in two directions so select // both adjacent siblings. For RIGHT or DOWN, just select the next sibling. // For LEFT or UP, convert it to a RIGHT or DOWN resize and reassign con to @@ -249,16 +253,35 @@ static struct cmd_results *resize_adjust_tiled(uint32_t axis, struct movement_amount *amount) { struct sway_container *current = config->handler_context.container; + if (container_is_scratchpad_hidden_or_child(current)) { + return cmd_results_new(CMD_FAILURE, "Cannot resize a hidden scratchpad container"); + } + if (amount->unit == MOVEMENT_UNIT_DEFAULT) { amount->unit = MOVEMENT_UNIT_PPT; } if (amount->unit == MOVEMENT_UNIT_PPT) { + struct sway_container *parent = current->pending.parent; float pct = amount->amount / 100.0f; if (is_horizontal(axis)) { - amount->amount = (float)current->pending.width * pct; + while (parent && parent->pending.layout != L_HORIZ) { + parent = parent->pending.parent; + } + if (parent) { + amount->amount = (float)parent->pending.width * pct; + } else { + amount->amount = (float)current->pending.workspace->width * pct; + } } else { - amount->amount = (float)current->pending.height * pct; + while (parent && parent->pending.layout != L_VERT) { + parent = parent->pending.parent; + } + if (parent) { + amount->amount = (float)parent->pending.height * pct; + } else { + amount->amount = (float)current->pending.workspace->height * pct; + } } } @@ -277,6 +300,11 @@ static struct cmd_results *resize_adjust_tiled(uint32_t axis, */ static struct cmd_results *resize_set_tiled(struct sway_container *con, struct movement_amount *width, struct movement_amount *height) { + + if (container_is_scratchpad_hidden_or_child(con)) { + return cmd_results_new(CMD_FAILURE, "Cannot resize a hidden scratchpad container"); + } + if (width->amount) { if (width->unit == MOVEMENT_UNIT_PPT || width->unit == MOVEMENT_UNIT_DEFAULT) { @@ -415,7 +443,7 @@ static struct cmd_results *cmd_resize_set(int argc, char **argv) { argc -= num_consumed_args; argv += num_consumed_args; if (width.unit == MOVEMENT_UNIT_INVALID) { - return cmd_results_new(CMD_INVALID, usage); + return cmd_results_new(CMD_INVALID, "%s", usage); } } @@ -427,10 +455,10 @@ static struct cmd_results *cmd_resize_set(int argc, char **argv) { } int num_consumed_args = parse_movement_amount(argc, argv, &height); if (argc > num_consumed_args) { - return cmd_results_new(CMD_INVALID, usage); + return cmd_results_new(CMD_INVALID, "%s", usage); } if (width.unit == MOVEMENT_UNIT_INVALID) { - return cmd_results_new(CMD_INVALID, usage); + return cmd_results_new(CMD_INVALID, "%s", usage); } } @@ -462,7 +490,7 @@ static struct cmd_results *cmd_resize_adjust(int argc, char **argv, "[ px|ppt [or px|ppt]]'"; uint32_t axis = parse_resize_axis(*argv); if (axis == WLR_EDGE_NONE) { - return cmd_results_new(CMD_INVALID, usage); + return cmd_results_new(CMD_INVALID, "%s", usage); } --argc; ++argv; @@ -473,7 +501,7 @@ static struct cmd_results *cmd_resize_adjust(int argc, char **argv, argc -= num_consumed_args; argv += num_consumed_args; if (first_amount.unit == MOVEMENT_UNIT_INVALID) { - return cmd_results_new(CMD_INVALID, usage); + return cmd_results_new(CMD_INVALID, "%s", usage); } } else { first_amount.amount = 10; @@ -483,7 +511,7 @@ static struct cmd_results *cmd_resize_adjust(int argc, char **argv, // "or" if (argc) { if (strcmp(*argv, "or") != 0) { - return cmd_results_new(CMD_INVALID, usage); + return cmd_results_new(CMD_INVALID, "%s", usage); } --argc; ++argv; } @@ -493,10 +521,10 @@ static struct cmd_results *cmd_resize_adjust(int argc, char **argv, if (argc) { int num_consumed_args = parse_movement_amount(argc, argv, &second_amount); if (argc > num_consumed_args) { - return cmd_results_new(CMD_INVALID, usage); + return cmd_results_new(CMD_INVALID, "%s", usage); } if (second_amount.unit == MOVEMENT_UNIT_INVALID) { - return cmd_results_new(CMD_INVALID, usage); + return cmd_results_new(CMD_INVALID, "%s", usage); } } else { second_amount.amount = 0; @@ -566,5 +594,5 @@ struct cmd_results *cmd_resize(int argc, char **argv) { const char usage[] = "Expected 'resize " " [] [px|ppt]'"; - return cmd_results_new(CMD_INVALID, usage); + return cmd_results_new(CMD_INVALID, "%s", usage); } diff --git a/sway/commands/seat/cursor.c b/sway/commands/seat/cursor.c index 504a9f5e..5a8a3bc8 100644 --- a/sway/commands/seat/cursor.c +++ b/sway/commands/seat/cursor.c @@ -18,7 +18,7 @@ static struct cmd_results *handle_command(struct sway_cursor *cursor, int argc, char **argv) { if (strcasecmp(argv[0], "move") == 0) { if (argc < 3) { - return cmd_results_new(CMD_INVALID, expected_syntax); + return cmd_results_new(CMD_INVALID, "%s", expected_syntax); } int delta_x = strtol(argv[1], NULL, 10); int delta_y = strtol(argv[2], NULL, 10); @@ -27,7 +27,7 @@ static struct cmd_results *handle_command(struct sway_cursor *cursor, wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat); } else if (strcasecmp(argv[0], "set") == 0) { if (argc < 3) { - return cmd_results_new(CMD_INVALID, expected_syntax); + return cmd_results_new(CMD_INVALID, "%s", expected_syntax); } // map absolute coords (0..1,0..1) to root container coords float x = strtof(argv[1], NULL) / root->width; @@ -37,7 +37,7 @@ static struct cmd_results *handle_command(struct sway_cursor *cursor, wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat); } else { if (argc < 2) { - return cmd_results_new(CMD_INVALID, expected_syntax); + return cmd_results_new(CMD_INVALID, "%s", expected_syntax); } struct cmd_results *error = NULL; if ((error = press_or_release(cursor, argv[0], argv[1]))) { @@ -92,14 +92,14 @@ static struct cmd_results *press_or_release(struct sway_cursor *cursor, } else if (strcasecmp(action, "release") == 0) { state = WLR_BUTTON_RELEASED; } else { - return cmd_results_new(CMD_INVALID, expected_syntax); + return cmd_results_new(CMD_INVALID, "%s", expected_syntax); } char *message = NULL; button = get_mouse_button(button_str, &message); if (message) { struct cmd_results *error = - cmd_results_new(CMD_INVALID, message); + cmd_results_new(CMD_INVALID, "%s", message); free(message); return error; } else if (button == SWAY_SCROLL_UP || button == SWAY_SCROLL_DOWN diff --git a/sway/commands/seat/idle.c b/sway/commands/seat/idle.c index 82428f2c..62b94db2 100644 --- a/sway/commands/seat/idle.c +++ b/sway/commands/seat/idle.c @@ -3,6 +3,7 @@ #include #include #include +#include "log.h" #include "sway/commands.h" #include "sway/config.h" #include "sway/input/seat.h" @@ -69,5 +70,10 @@ struct cmd_results *seat_cmd_idle_wake(int argc, char **argv) { return cmd_results_new(CMD_FAILURE, "Invalid idle source"); } config->handler_context.seat_config->idle_wake_sources = sources; + sway_log(SWAY_INFO, "Warning: seat idle_wake is deprecated"); + if (config->reading) { + config_add_swaynag_warning("seat idle_wake is deprecated. " + "Only seat idle_inhibit is supported."); + } return cmd_results_new(CMD_SUCCESS, NULL); } diff --git a/sway/commands/shadows.c b/sway/commands/shadows.c index ae7fc019..a213de8f 100644 --- a/sway/commands/shadows.c +++ b/sway/commands/shadows.c @@ -4,8 +4,6 @@ #include "sway/tree/arrange.h" #include "sway/tree/view.h" #include "sway/tree/container.h" -#include "log.h" -#include "stringop.h" #include "util.h" struct cmd_results *cmd_shadows(int argc, char **argv) { diff --git a/sway/commands/shadows_on_csd.c b/sway/commands/shadows_on_csd.c index c9f56dd1..24f2bea4 100644 --- a/sway/commands/shadows_on_csd.c +++ b/sway/commands/shadows_on_csd.c @@ -2,10 +2,6 @@ #include "sway/commands.h" #include "sway/config.h" #include "sway/tree/arrange.h" -#include "sway/tree/view.h" -#include "sway/tree/container.h" -#include "log.h" -#include "stringop.h" #include "util.h" struct cmd_results *cmd_shadows_on_csd(int argc, char **argv) { diff --git a/sway/commands/split.c b/sway/commands/split.c index c8a2cfc1..500a497d 100644 --- a/sway/commands/split.c +++ b/sway/commands/split.c @@ -32,7 +32,7 @@ static struct cmd_results *do_split(int layout) { return cmd_results_new(CMD_SUCCESS, NULL); } -static struct cmd_results *do_unsplit() { +static struct cmd_results *do_unsplit(void) { struct sway_container *con = config->handler_context.container; struct sway_workspace *ws = config->handler_context.workspace; diff --git a/sway/commands/swap.c b/sway/commands/swap.c index 9355944d..d44eb006 100644 --- a/sway/commands/swap.c +++ b/sway/commands/swap.c @@ -5,6 +5,7 @@ #include "sway/commands.h" #include "sway/output.h" #include "sway/tree/arrange.h" +#include "sway/tree/container.h" #include "sway/tree/root.h" #include "sway/tree/view.h" #include "sway/tree/workspace.h" @@ -13,180 +14,6 @@ static const char expected_syntax[] = "Expected 'swap container with id|con_id|mark '"; -static void swap_places(struct sway_container *con1, - struct sway_container *con2) { - struct sway_container *temp = malloc(sizeof(struct sway_container)); - temp->pending.x = con1->pending.x; - temp->pending.y = con1->pending.y; - temp->pending.width = con1->pending.width; - temp->pending.height = con1->pending.height; - temp->width_fraction = con1->width_fraction; - temp->height_fraction = con1->height_fraction; - temp->pending.parent = con1->pending.parent; - temp->pending.workspace = con1->pending.workspace; - bool temp_floating = container_is_floating(con1); - - con1->pending.x = con2->pending.x; - con1->pending.y = con2->pending.y; - con1->pending.width = con2->pending.width; - con1->pending.height = con2->pending.height; - con1->width_fraction = con2->width_fraction; - con1->height_fraction = con2->height_fraction; - - con2->pending.x = temp->pending.x; - con2->pending.y = temp->pending.y; - con2->pending.width = temp->pending.width; - con2->pending.height = temp->pending.height; - con2->width_fraction = temp->width_fraction; - con2->height_fraction = temp->height_fraction; - - int temp_index = container_sibling_index(con1); - if (con2->pending.parent) { - container_insert_child(con2->pending.parent, con1, - container_sibling_index(con2)); - } else if (container_is_floating(con2)) { - workspace_add_floating(con2->pending.workspace, con1); - } else { - workspace_insert_tiling(con2->pending.workspace, con1, - container_sibling_index(con2)); - } - if (temp->pending.parent) { - container_insert_child(temp->pending.parent, con2, temp_index); - } else if (temp_floating) { - workspace_add_floating(temp->pending.workspace, con2); - } else { - workspace_insert_tiling(temp->pending.workspace, con2, temp_index); - } - - free(temp); -} - -static void swap_focus(struct sway_container *con1, - struct sway_container *con2, struct sway_seat *seat, - struct sway_container *focus) { - if (focus == con1 || focus == con2) { - struct sway_workspace *ws1 = con1->pending.workspace; - struct sway_workspace *ws2 = con2->pending.workspace; - enum sway_container_layout layout1 = container_parent_layout(con1); - enum sway_container_layout layout2 = container_parent_layout(con2); - if (focus == con1 && (layout2 == L_TABBED || layout2 == L_STACKED)) { - if (workspace_is_visible(ws2)) { - seat_set_focus(seat, &con2->node); - } - seat_set_focus_container(seat, ws1 != ws2 ? con2 : con1); - } else if (focus == con2 && (layout1 == L_TABBED - || layout1 == L_STACKED)) { - if (workspace_is_visible(ws1)) { - seat_set_focus(seat, &con1->node); - } - seat_set_focus_container(seat, ws1 != ws2 ? con1 : con2); - } else if (ws1 != ws2) { - seat_set_focus_container(seat, focus == con1 ? con2 : con1); - } else { - seat_set_focus_container(seat, focus); - } - } else { - seat_set_focus_container(seat, focus); - } - - if (root->fullscreen_global) { - seat_set_focus(seat, - seat_get_focus_inactive(seat, &root->fullscreen_global->node)); - } -} - -void container_swap(struct sway_container *con1, struct sway_container *con2) { - if (!sway_assert(con1 && con2, "Cannot swap with nothing")) { - return; - } - if (!sway_assert(!container_has_ancestor(con1, con2) - && !container_has_ancestor(con2, con1), - "Cannot swap ancestor and descendant")) { - return; - } - - sway_log(SWAY_DEBUG, "Swapping containers %zu and %zu", - con1->node.id, con2->node.id); - - bool scratch1 = con1->scratchpad; - bool hidden1 = container_is_scratchpad_hidden(con1); - bool scratch2 = con2->scratchpad; - bool hidden2 = container_is_scratchpad_hidden(con2); - if (scratch1) { - if (hidden1) { - root_scratchpad_show(con1); - } - root_scratchpad_remove_container(con1); - } - if (scratch2) { - if (hidden2) { - root_scratchpad_show(con2); - } - root_scratchpad_remove_container(con2); - } - - enum sway_fullscreen_mode fs1 = con1->pending.fullscreen_mode; - if (fs1) { - container_fullscreen_disable(con1); - } - enum sway_fullscreen_mode fs2 = con2->pending.fullscreen_mode; - if (fs2) { - container_fullscreen_disable(con2); - } - - struct sway_seat *seat = config->handler_context.seat; - struct sway_container *focus = seat_get_focused_container(seat); - struct sway_workspace *vis1 = - output_get_active_workspace(con1->pending.workspace->output); - struct sway_workspace *vis2 = - output_get_active_workspace(con2->pending.workspace->output); - if (!sway_assert(vis1 && vis2, "con1 or con2 are on an output without a" - "workspace. This should not happen")) { - return; - } - - char *stored_prev_name = NULL; - if (seat->prev_workspace_name) { - stored_prev_name = strdup(seat->prev_workspace_name); - } - - swap_places(con1, con2); - - if (!workspace_is_visible(vis1)) { - seat_set_focus(seat, seat_get_focus_inactive(seat, &vis1->node)); - } - if (!workspace_is_visible(vis2)) { - seat_set_focus(seat, seat_get_focus_inactive(seat, &vis2->node)); - } - - swap_focus(con1, con2, seat, focus); - - if (stored_prev_name) { - free(seat->prev_workspace_name); - seat->prev_workspace_name = stored_prev_name; - } - - if (scratch1) { - root_scratchpad_add_container(con2, NULL); - if (!hidden1) { - root_scratchpad_show(con2); - } - } - if (scratch2) { - root_scratchpad_add_container(con1, NULL); - if (!hidden2) { - root_scratchpad_show(con1); - } - } - - if (fs1) { - container_set_fullscreen(con2, fs1); - } - if (fs2) { - container_set_fullscreen(con1, fs2); - } -} - static bool test_con_id(struct sway_container *container, void *data) { size_t *con_id = data; return container->node.id == *con_id; @@ -219,7 +46,7 @@ struct cmd_results *cmd_swap(int argc, char **argv) { } if (strcasecmp(argv[0], "container") || strcasecmp(argv[1], "with")) { - return cmd_results_new(CMD_INVALID, expected_syntax); + return cmd_results_new(CMD_INVALID, "%s", expected_syntax); } struct sway_container *current = config->handler_context.container; @@ -238,7 +65,7 @@ struct cmd_results *cmd_swap(int argc, char **argv) { other = root_find_container(test_mark, value); } else { free(value); - return cmd_results_new(CMD_INVALID, expected_syntax); + return cmd_results_new(CMD_INVALID, "%s", expected_syntax); } if (!other) { diff --git a/sway/commands/workspace.c b/sway/commands/workspace.c index a6a0beda..03e488ba 100644 --- a/sway/commands/workspace.c +++ b/sway/commands/workspace.c @@ -61,7 +61,7 @@ static struct cmd_results *cmd_workspace_gaps(int argc, char **argv, const char expected[] = "Expected 'workspace gaps " "inner|outer|horizontal|vertical|top|right|bottom|left '"; if (gaps_location == 0) { - return cmd_results_new(CMD_INVALID, expected); + return cmd_results_new(CMD_INVALID, "%s", expected); } struct cmd_results *error = NULL; if ((error = checkarg(argc, "workspace", EXPECTED_EQUAL_TO, @@ -79,7 +79,7 @@ static struct cmd_results *cmd_workspace_gaps(int argc, char **argv, char *end; int amount = strtol(argv[gaps_location + 2], &end, 10); if (strlen(end)) { - return cmd_results_new(CMD_FAILURE, expected); + return cmd_results_new(CMD_FAILURE, "%s", expected); } bool valid = false; @@ -110,7 +110,7 @@ static struct cmd_results *cmd_workspace_gaps(int argc, char **argv, } } if (!valid) { - return cmd_results_new(CMD_INVALID, expected); + return cmd_results_new(CMD_INVALID, "%s", expected); } // Prevent invalid gaps configurations. @@ -174,7 +174,7 @@ struct cmd_results *cmd_workspace(int argc, char **argv) { } if (root->fullscreen_global) { - return cmd_results_new(CMD_FAILURE, "workspace", + return cmd_results_new(CMD_FAILURE, "Can't switch workspaces while fullscreen global"); } diff --git a/sway/config.c b/sway/config.c index 0203080c..616965c1 100644 --- a/sway/config.c +++ b/sway/config.c @@ -38,7 +38,7 @@ struct sway_config *config = NULL; static struct xkb_state *keysym_translation_state_create( struct xkb_rule_names rules) { - struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); + struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_SECURE_GETENV); struct xkb_keymap *xkb_keymap = xkb_keymap_new_from_names( context, &rules, @@ -280,6 +280,7 @@ static void config_defaults(struct sway_config *config) { config->title_align = ALIGN_LEFT; config->tiling_drag = true; config->tiling_drag_threshold = 9; + config->primary_selection = true; config->smart_gaps = SMART_GAPS_OFF; config->gaps_inner = 0; @@ -511,6 +512,11 @@ bool load_main_config(const char *file, bool is_active, bool validating) { old_config->xwayland ? "enabled" : "disabled"); config->xwayland = old_config->xwayland; + // primary_selection can only be enabled/disabled at launch + sway_log(SWAY_DEBUG, "primary_selection will remain %s", + old_config->primary_selection ? "enabled" : "disabled"); + config->primary_selection = old_config->primary_selection; + if (!config->validating) { if (old_config->swaybg_client != NULL) { wl_client_destroy(old_config->swaybg_client); @@ -960,23 +966,18 @@ void config_add_swaynag_warning(char *fmt, ...) { if (config->reading && !config->validating) { va_list args; va_start(args, fmt); - size_t length = vsnprintf(NULL, 0, fmt, args) + 1; + char *str = vformat_str(fmt, args); va_end(args); - - char *temp = malloc(length + 1); - if (!temp) { - sway_log(SWAY_ERROR, "Failed to allocate buffer for warning."); + if (str == NULL) { return; } - va_start(args, fmt); - vsnprintf(temp, length, fmt, args); - va_end(args); - swaynag_log(config->swaynag_command, &config->swaynag_config_errors, "Warning on line %i (%s) '%s': %s", config->current_config_line_number, config->current_config_path, - config->current_config_line, temp); + config->current_config_line, str); + + free(str); } } @@ -1016,7 +1017,7 @@ char *do_var_replacement(char *str) { int offset = find - str; strncpy(newptr, str, offset); newptr += offset; - strncpy(newptr, var->value, vvlen); + memcpy(newptr, var->value, vvlen); newptr += vvlen; strcpy(newptr, find + vnlen); free(str); diff --git a/sway/config/bar.c b/sway/config/bar.c index d1b342e6..a8389244 100644 --- a/sway/config/bar.c +++ b/sway/config/bar.c @@ -256,7 +256,6 @@ static void invoke_swaybar(struct bar_config *bar) { } sway_log(SWAY_DEBUG, "Spawned swaybar %s", bar->id); - return; } void load_swaybar(struct bar_config *bar) { diff --git a/sway/config/input.c b/sway/config/input.c index a98204df..44c2be28 100644 --- a/sway/config/input.c +++ b/sway/config/input.c @@ -31,9 +31,11 @@ struct input_config *new_input_config(const char* identifier) { input->middle_emulation = INT_MIN; input->natural_scroll = INT_MIN; input->accel_profile = INT_MIN; + input->rotation_angle = FLT_MIN; input->pointer_accel = FLT_MIN; input->scroll_factor = FLT_MIN; input->scroll_button = INT_MIN; + input->scroll_button_lock = INT_MIN; input->scroll_method = INT_MIN; input->left_handed = INT_MIN; input->repeat_delay = INT_MIN; @@ -74,6 +76,9 @@ void merge_input_config(struct input_config *dst, struct input_config *src) { if (src->natural_scroll != INT_MIN) { dst->natural_scroll = src->natural_scroll; } + if (src->rotation_angle != FLT_MIN) { + dst->rotation_angle = src->rotation_angle; + } if (src->pointer_accel != FLT_MIN) { dst->pointer_accel = src->pointer_accel; } @@ -92,6 +97,9 @@ void merge_input_config(struct input_config *dst, struct input_config *src) { if (src->scroll_button != INT_MIN) { dst->scroll_button = src->scroll_button; } + if (src->scroll_button_lock != INT_MIN) { + dst->scroll_button_lock = src->scroll_button_lock; + } if (src->send_events != INT_MIN) { dst->send_events = src->send_events; } diff --git a/sway/config/output.c b/sway/config/output.c index 9c7082d0..3c1822d8 100644 --- a/sway/config/output.c +++ b/sway/config/output.c @@ -6,10 +6,10 @@ #include #include #include +#include #include #include #include -#include #include "sway/config.h" #include "sway/input/cursor.h" #include "sway/output.h" @@ -17,6 +17,10 @@ #include "log.h" #include "util.h" +#if WLR_HAS_DRM_BACKEND +#include +#endif + int output_name_cmp(const void *item, const void *data) { const struct output_config *output = item; const char *name = data; @@ -149,25 +153,16 @@ static void merge_wildcard_on_all(struct output_config *wildcard) { } static void merge_id_on_name(struct output_config *oc) { - char *id_on_name = NULL; - char id[128]; - char *name = NULL; - struct sway_output *output; - wl_list_for_each(output, &root->all_outputs, link) { - name = output->wlr_output->name; - output_get_identifier(id, sizeof(id), output); - if (strcmp(name, oc->name) == 0 || strcmp(id, oc->name) == 0) { - size_t length = snprintf(NULL, 0, "%s on %s", id, name) + 1; - id_on_name = malloc(length); - if (!id_on_name) { - sway_log(SWAY_ERROR, "Failed to allocate id on name string"); - return; - } - snprintf(id_on_name, length, "%s on %s", id, name); - break; - } + struct sway_output *output = all_output_by_name_or_id(oc->name); + if (output == NULL) { + return; } + const char *name = output->wlr_output->name; + char id[128]; + output_get_identifier(id, sizeof(id), output); + + char *id_on_name = format_str("%s on %s", id, name); if (!id_on_name) { return; } @@ -253,7 +248,9 @@ static void set_mode(struct wlr_output *output, struct wlr_output_state *pending // Not all floating point integers can be represented exactly // as (int)(1000 * mHz / 1000.f) // round() the result to avoid any error - int mhz = (int)round(refresh_rate * 1000); + int mhz = (int)roundf(refresh_rate * 1000); + // If no target refresh rate is given, match highest available + mhz = mhz <= 0 ? INT_MAX : mhz; if (wl_list_empty(&output->modes) || custom) { sway_log(SWAY_DEBUG, "Assigning custom mode to %s", output->name); @@ -263,29 +260,35 @@ static void set_mode(struct wlr_output *output, struct wlr_output_state *pending } struct wlr_output_mode *mode, *best = NULL; + int best_diff_mhz = INT_MAX; wl_list_for_each(mode, &output->modes, link) { if (mode->width == width && mode->height == height) { - if (mode->refresh == mhz) { - best = mode; - break; - } - if (best == NULL || mode->refresh > best->refresh) { + int diff_mhz = abs(mode->refresh - mhz); + if (diff_mhz < best_diff_mhz) { + best_diff_mhz = diff_mhz; best = mode; + if (best_diff_mhz == 0) { + break; + } } } } - if (!best) { - sway_log(SWAY_ERROR, "Configured mode for %s not available", output->name); - sway_log(SWAY_INFO, "Picking preferred mode instead"); - best = wlr_output_preferred_mode(output); + if (best) { + sway_log(SWAY_INFO, "Assigning configured mode (%dx%d@%.3fHz) to %s", + best->width, best->height, best->refresh / 1000.f, output->name); } else { - sway_log(SWAY_DEBUG, "Assigning configured mode to %s", output->name); + best = wlr_output_preferred_mode(output); + sway_log(SWAY_INFO, "Configured mode (%dx%d@%.3fHz) not available, " + "applying preferred mode (%dx%d@%.3fHz)", + width, height, refresh_rate, + best->width, best->height, best->refresh / 1000.f); } wlr_output_state_set_mode(pending, best); } static void set_modeline(struct wlr_output *output, struct wlr_output_state *pending, drmModeModeInfo *drm_mode) { +#if WLR_HAS_DRM_BACKEND if (!wlr_output_is_drm(output)) { sway_log(SWAY_ERROR, "Modeline can only be set to DRM output"); return; @@ -295,6 +298,9 @@ static void set_modeline(struct wlr_output *output, if (mode) { wlr_output_state_set_mode(pending, mode); } +#else + sway_log(SWAY_ERROR, "Modeline can only be set to DRM output"); +#endif } /* Some manufacturers hardcode the aspect-ratio of the output in the physical @@ -436,9 +442,11 @@ static void queue_output_config(struct output_config *oc, enum wl_output_transform tr = WL_OUTPUT_TRANSFORM_NORMAL; if (oc && oc->transform >= 0) { tr = oc->transform; +#if WLR_HAS_DRM_BACKEND } else if (wlr_output_is_drm(wlr_output)) { tr = wlr_drm_connector_get_panel_orientation(wlr_output); sway_log(SWAY_DEBUG, "Auto-detected output transform: %d", tr); +#endif } if (wlr_output->transform != tr) { sway_log(SWAY_DEBUG, "Set %s transform to %d", oc->name, tr); @@ -450,6 +458,16 @@ static void queue_output_config(struct output_config *oc, float scale; if (oc && oc->scale > 0) { scale = oc->scale; + + // The factional-scale-v1 protocol uses increments of 120ths to send + // the scale factor to the client. Adjust the scale so that we use the + // same value as the clients'. + float adjusted_scale = round(scale * 120) / 120; + if (scale != adjusted_scale) { + sway_log(SWAY_INFO, "Adjusting output scale from %f to %f", + scale, adjusted_scale); + scale = adjusted_scale; + } } else { scale = compute_default_scale(wlr_output, pending); sway_log(SWAY_DEBUG, "Auto-detected output scale: %f", scale); @@ -499,10 +517,6 @@ bool apply_output_config(struct output_config *oc, struct sway_output *output) { struct wlr_output_state pending = {0}; queue_output_config(oc, output, &pending); - if (!oc || oc->power != 0) { - output->current_mode = pending.mode; - } - sway_log(SWAY_DEBUG, "Committing output %s", wlr_output->name); if (!wlr_output_commit_state(wlr_output, &pending)) { // Failed to commit output changes, maybe the output is missing a CRTC. @@ -576,7 +590,7 @@ bool apply_output_config(struct output_config *oc, struct sway_output *output) { // Reconfigure all devices, since input config may have been applied before // this output came online, and some config items (like map_to_output) are // dependent on an output being present. - input_manager_configure_all_inputs(); + input_manager_configure_all_input_mappings(); // Reconfigure the cursor images, since the scale may have changed. input_manager_configure_xcursor(); return true; @@ -619,9 +633,7 @@ static struct output_config *get_output_config(char *identifier, struct output_config *oc_name = NULL; struct output_config *oc_id = NULL; - size_t length = snprintf(NULL, 0, "%s on %s", identifier, name) + 1; - char *id_on_name = malloc(length); - snprintf(id_on_name, length, "%s on %s", identifier, name); + char *id_on_name = format_str("%s on %s", identifier, name); int i = list_seq_find(config->output_configs, output_name_cmp, id_on_name); if (i >= 0) { oc_id_on_name = config->output_configs->items[i]; @@ -708,12 +720,11 @@ void apply_output_config_to_outputs(struct output_config *oc) { // this is during startup then there will be no container and config // will be applied during normal "new output" event from wlroots. bool wildcard = strcmp(oc->name, "*") == 0; - char id[128]; struct sway_output *sway_output, *tmp; wl_list_for_each_safe(sway_output, tmp, &root->all_outputs, link) { - char *name = sway_output->wlr_output->name; - output_get_identifier(id, sizeof(id), sway_output); - if (wildcard || !strcmp(name, oc->name) || !strcmp(id, oc->name)) { + if (output_match_name_or_id(sway_output, oc->name)) { + char id[128]; + output_get_identifier(id, sizeof(id), sway_output); struct output_config *current = get_output_config(id, sway_output); if (!current) { // No stored output config matched, apply oc directly @@ -814,7 +825,9 @@ static bool _spawn_swaybg(char **command) { setenv("WAYLAND_SOCKET", wayland_socket_str, true); execvp(command[0], command); - sway_log_errno(SWAY_ERROR, "execvp failed"); + sway_log_errno(SWAY_ERROR, "failed to execute '%s' " + "(background configuration probably not applied)", + command[0]); _exit(EXIT_FAILURE); } _exit(EXIT_SUCCESS); @@ -824,12 +837,13 @@ static bool _spawn_swaybg(char **command) { sway_log_errno(SWAY_ERROR, "close failed"); return false; } - if (waitpid(pid, NULL, 0) < 0) { + int fork_status = 0; + if (waitpid(pid, &fork_status, 0) < 0) { sway_log_errno(SWAY_ERROR, "waitpid failed"); return false; } - return true; + return WIFEXITED(fork_status) && WEXITSTATUS(fork_status) == EXIT_SUCCESS; } bool spawn_swaybg(void) { diff --git a/sway/config/seat.c b/sway/config/seat.c index 84260aa3..6d5d91ae 100644 --- a/sway/config/seat.c +++ b/sway/config/seat.c @@ -99,7 +99,6 @@ static void seat_attachment_config_free( struct seat_attachment_config *attachment) { free(attachment->identifier); free(attachment); - return; } static struct seat_attachment_config *seat_attachment_config_copy( diff --git a/sway/criteria.c b/sway/criteria.c index d7326bea..78ea8b8a 100644 --- a/sway/criteria.c +++ b/sway/criteria.c @@ -19,6 +19,7 @@ bool criteria_is_empty(struct criteria *criteria) { return !criteria->title && !criteria->shell + && !criteria->all && !criteria->app_id && !criteria->con_mark && !criteria->con_id @@ -456,6 +457,7 @@ static enum atom_name parse_window_type(const char *type) { #endif enum criteria_token { + T_ALL, T_APP_ID, T_CON_ID, T_CON_MARK, @@ -478,7 +480,9 @@ enum criteria_token { }; static enum criteria_token token_from_name(char *name) { - if (strcmp(name, "app_id") == 0) { + if (strcmp(name, "all") == 0) { + return T_ALL; + } else if (strcmp(name, "app_id") == 0) { return T_APP_ID; } else if (strcmp(name, "con_id") == 0) { return T_CON_ID; @@ -524,8 +528,8 @@ static bool parse_token(struct criteria *criteria, char *name, char *value) { return false; } - // Require value, unless token is floating or tiled - if (!value && token != T_FLOATING && token != T_TILING) { + // Require value, unless token is all, floating or tiled + if (!value && token != T_ALL && token != T_FLOATING && token != T_TILING) { const char *fmt = "Token '%s' requires a value"; int len = strlen(fmt) + strlen(name) - 1; error = malloc(len); @@ -535,6 +539,9 @@ static bool parse_token(struct criteria *criteria, char *name, char *value) { char *endptr = NULL; switch (token) { + case T_ALL: + criteria->all = true; + break; case T_TITLE: pattern_create(&criteria->title, value); break; diff --git a/sway/desktop/fx_renderer/fx_framebuffer.c b/sway/desktop/fx_renderer/fx_framebuffer.c deleted file mode 100644 index dd8c27b1..00000000 --- a/sway/desktop/fx_renderer/fx_framebuffer.c +++ /dev/null @@ -1,89 +0,0 @@ -#include "log.h" -#include "sway/desktop/fx_renderer/fx_framebuffer.h" -#include "sway/desktop/fx_renderer/fx_stencilbuffer.h" -#include "sway/desktop/fx_renderer/fx_texture.h" - -struct fx_framebuffer fx_framebuffer_create() { - return (struct fx_framebuffer) { - .fb = -1, - .stencil_buffer = fx_stencilbuffer_create(), - .texture = fx_texture_create(), - }; -} - -void fx_framebuffer_bind(struct fx_framebuffer *buffer) { - glBindFramebuffer(GL_FRAMEBUFFER, buffer->fb); -} - -void fx_framebuffer_update(struct fx_framebuffer *buffer, int width, int height) { - bool first_alloc = false; - - if (buffer->fb == (uint32_t) -1) { - glGenFramebuffers(1, &buffer->fb); - first_alloc = true; - } - - if (buffer->texture.id == 0) { - first_alloc = true; - glGenTextures(1, &buffer->texture.id); - glBindTexture(GL_TEXTURE_2D, buffer->texture.id); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - } - - if (first_alloc || buffer->texture.width != width || buffer->texture.height != height) { - glBindTexture(GL_TEXTURE_2D, buffer->texture.id); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); - - glBindFramebuffer(GL_FRAMEBUFFER, buffer->fb); - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, - buffer->texture.id, 0); - buffer->texture.target = GL_TEXTURE_2D; - buffer->texture.has_alpha = false; - buffer->texture.width = width; - buffer->texture.height = height; - - GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); - if (status != GL_FRAMEBUFFER_COMPLETE) { - sway_log(SWAY_ERROR, "Framebuffer incomplete, couldn't create! (FB status: %i)", status); - return; - } - sway_log(SWAY_DEBUG, "Framebuffer created, status %i", status); - } - - glBindTexture(GL_TEXTURE_2D, 0); -} - -void fx_framebuffer_add_stencil_buffer(struct fx_framebuffer *buffer, int width, int height) { - bool first_alloc = false; - - if (buffer->stencil_buffer.rb == (uint32_t) -1) { - glGenRenderbuffers(1, &buffer->stencil_buffer.rb); - first_alloc = true; - } - - if (first_alloc || buffer->stencil_buffer.width != width || buffer->stencil_buffer.height != height) { - glBindRenderbuffer(GL_RENDERBUFFER, buffer->stencil_buffer.rb); - glRenderbufferStorage(GL_RENDERBUFFER, GL_STENCIL_INDEX8, width, height); - buffer->stencil_buffer.width = width; - buffer->stencil_buffer.height = height; - } - - glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, buffer->stencil_buffer.rb); -} - -void fx_framebuffer_release(struct fx_framebuffer *buffer) { - // Release the framebuffer - if (buffer->fb != (uint32_t) -1 && buffer->fb) { - glDeleteFramebuffers(1, &buffer->fb); - } - buffer->fb = -1; - - // Release the stencil buffer - fx_stencilbuffer_release(&buffer->stencil_buffer); - - // Release the texture - fx_texture_release(&buffer->texture); -} diff --git a/sway/desktop/fx_renderer/fx_renderer.c b/sway/desktop/fx_renderer/fx_renderer.c deleted file mode 100644 index f06d93b5..00000000 --- a/sway/desktop/fx_renderer/fx_renderer.c +++ /dev/null @@ -1,961 +0,0 @@ -/* - The original wlr_renderer was heavily referenced in making this project - https://gitlab.freedesktop.org/wlroots/wlroots/-/tree/master/render/gles2 -*/ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "log.h" -#include "sway/desktop/fx_renderer/fx_framebuffer.h" -#include "sway/desktop/fx_renderer/fx_renderer.h" -#include "sway/desktop/fx_renderer/fx_stencilbuffer.h" -#include "sway/desktop/fx_renderer/fx_texture.h" -#include "sway/desktop/fx_renderer/matrix.h" -#include "sway/server.h" - -// shaders -#include "blur1_frag_src.h" -#include "blur2_frag_src.h" -#include "blur_effects_frag_src.h" -#include "box_shadow_frag_src.h" -#include "common_vert_src.h" -#include "corner_frag_src.h" -#include "quad_frag_src.h" -#include "quad_round_frag_src.h" -#include "stencil_mask_frag_src.h" -#include "tex_frag_src.h" - -static const GLfloat verts[] = { - 1, 0, // top right - 0, 0, // top left - 1, 1, // bottom right - 0, 1, // bottom left -}; - -static GLuint compile_shader(GLuint type, const GLchar *src) { - GLuint shader = glCreateShader(type); - glShaderSource(shader, 1, &src, NULL); - glCompileShader(shader); - - GLint ok; - glGetShaderiv(shader, GL_COMPILE_STATUS, &ok); - if (ok == GL_FALSE) { - sway_log(SWAY_ERROR, "Failed to compile shader"); - glDeleteShader(shader); - shader = 0; - } - - return shader; -} - -static GLuint link_program(const GLchar *frag_src) { - const GLchar *vert_src = common_vert_src; - GLuint vert = compile_shader(GL_VERTEX_SHADER, vert_src); - if (!vert) { - goto error; - } - - GLuint frag = compile_shader(GL_FRAGMENT_SHADER, frag_src); - if (!frag) { - glDeleteShader(vert); - goto error; - } - - GLuint prog = glCreateProgram(); - glAttachShader(prog, vert); - glAttachShader(prog, frag); - glLinkProgram(prog); - - glDetachShader(prog, vert); - glDetachShader(prog, frag); - glDeleteShader(vert); - glDeleteShader(frag); - - GLint ok; - glGetProgramiv(prog, GL_LINK_STATUS, &ok); - if (ok == GL_FALSE) { - sway_log(SWAY_ERROR, "Failed to link shader"); - glDeleteProgram(prog); - goto error; - } - - return prog; - -error: - return 0; -} - -static bool link_blur_program(struct blur_shader *shader, const char *shader_program) { - GLuint prog; - shader->program = prog = link_program(shader_program); - if (!shader->program) { - return false; - } - shader->proj = glGetUniformLocation(prog, "proj"); - shader->tex = glGetUniformLocation(prog, "tex"); - shader->pos_attrib = glGetAttribLocation(prog, "pos"); - shader->tex_attrib = glGetAttribLocation(prog, "texcoord"); - shader->radius = glGetUniformLocation(prog, "radius"); - shader->halfpixel = glGetUniformLocation(prog, "halfpixel"); - - return true; -} - -static bool link_blur_effects_program(struct effects_shader *shader, const char *shader_program) { - GLuint prog; - shader->program = prog = link_program(shader_program); - if (!shader->program) { - return false; - } - shader->proj = glGetUniformLocation(prog, "proj"); - shader->tex = glGetUniformLocation(prog, "tex"); - shader->pos_attrib = glGetAttribLocation(prog, "pos"); - shader->tex_attrib = glGetAttribLocation(prog, "texcoord"); - shader->noise = glGetUniformLocation(prog, "noise"); - shader->brightness = glGetUniformLocation(prog, "brightness"); - shader->contrast = glGetUniformLocation(prog, "contrast"); - shader->saturation = glGetUniformLocation(prog, "saturation"); - - return true; - -} - -static bool link_box_shadow_program(struct box_shadow_shader *shader) { - GLuint prog; - shader->program = prog = link_program(box_shadow_frag_src); - if (!shader->program) { - return false; - } - shader->proj = glGetUniformLocation(prog, "proj"); - shader->color = glGetUniformLocation(prog, "color"); - shader->pos_attrib = glGetAttribLocation(prog, "pos"); - shader->position = glGetUniformLocation(prog, "position"); - shader->size = glGetUniformLocation(prog, "size"); - shader->blur_sigma = glGetUniformLocation(prog, "blur_sigma"); - shader->corner_radius = glGetUniformLocation(prog, "corner_radius"); - - return true; -} - -static bool link_corner_program(struct corner_shader *shader) { - GLuint prog; - shader->program = prog = link_program(corner_frag_src); - if (!shader->program) { - return false; - } - shader->proj = glGetUniformLocation(prog, "proj"); - shader->color = glGetUniformLocation(prog, "color"); - shader->pos_attrib = glGetAttribLocation(prog, "pos"); - shader->position = glGetUniformLocation(prog, "position"); - shader->half_size = glGetUniformLocation(prog, "half_size"); - shader->half_thickness = glGetUniformLocation(prog, "half_thickness"); - shader->radius = glGetUniformLocation(prog, "radius"); - shader->is_top_left = glGetUniformLocation(prog, "is_top_left"); - shader->is_top_right = glGetUniformLocation(prog, "is_top_right"); - shader->is_bottom_left = glGetUniformLocation(prog, "is_bottom_left"); - shader->is_bottom_right = glGetUniformLocation(prog, "is_bottom_right"); - - return true; -} - -static bool link_quad_program(struct quad_shader *shader) { - GLuint prog; - shader->program = prog = link_program(quad_frag_src); - if (!shader->program) { - return false; - } - - shader->proj = glGetUniformLocation(prog, "proj"); - shader->color = glGetUniformLocation(prog, "color"); - shader->pos_attrib = glGetAttribLocation(prog, "pos"); - - return true; -} - -static bool link_rounded_quad_program(struct rounded_quad_shader *shader, - enum fx_rounded_quad_shader_source source) { - GLchar quad_src[2048]; - snprintf(quad_src, sizeof(quad_src), - "#define SOURCE %d\n%s", source, quad_round_frag_src); - - GLuint prog; - shader->program = prog = link_program(quad_src); - if (!shader->program) { - return false; - } - - shader->proj = glGetUniformLocation(prog, "proj"); - shader->color = glGetUniformLocation(prog, "color"); - shader->pos_attrib = glGetAttribLocation(prog, "pos"); - shader->size = glGetUniformLocation(prog, "size"); - shader->position = glGetUniformLocation(prog, "position"); - shader->radius = glGetUniformLocation(prog, "radius"); - - return true; -} - -static bool link_stencil_mask_program(struct stencil_mask_shader *shader) { - GLuint prog; - shader->program = prog = link_program(stencil_mask_frag_src); - if (!shader->program) { - return false; - } - - shader->proj = glGetUniformLocation(prog, "proj"); - shader->color = glGetUniformLocation(prog, "color"); - shader->pos_attrib = glGetAttribLocation(prog, "pos"); - shader->position = glGetUniformLocation(prog, "position"); - shader->half_size = glGetUniformLocation(prog, "half_size"); - shader->radius = glGetUniformLocation(prog, "radius"); - - return true; -} - -static bool link_tex_program(struct tex_shader *shader, - enum fx_tex_shader_source source) { - GLchar frag_src[2048]; - snprintf(frag_src, sizeof(frag_src), - "#define SOURCE %d\n%s", source, tex_frag_src); - - GLuint prog; - shader->program = prog = link_program(frag_src); - if (!shader->program) { - return false; - } - - shader->proj = glGetUniformLocation(prog, "proj"); - shader->tex = glGetUniformLocation(prog, "tex"); - shader->alpha = glGetUniformLocation(prog, "alpha"); - shader->dim = glGetUniformLocation(prog, "dim"); - shader->dim_color = glGetUniformLocation(prog, "dim_color"); - shader->pos_attrib = glGetAttribLocation(prog, "pos"); - shader->tex_attrib = glGetAttribLocation(prog, "texcoord"); - shader->size = glGetUniformLocation(prog, "size"); - shader->position = glGetUniformLocation(prog, "position"); - shader->radius = glGetUniformLocation(prog, "radius"); - shader->saturation = glGetUniformLocation(prog, "saturation"); - shader->has_titlebar = glGetUniformLocation(prog, "has_titlebar"); - shader->discard_transparent = glGetUniformLocation(prog, "discard_transparent"); - - return true; -} - -static bool check_gl_ext(const char *exts, const char *ext) { - size_t extlen = strlen(ext); - const char *end = exts + strlen(exts); - - while (exts < end) { - if (exts[0] == ' ') { - exts++; - continue; - } - size_t n = strcspn(exts, " "); - if (n == extlen && strncmp(ext, exts, n) == 0) { - return true; - } - exts += n; - } - return false; -} - -static void load_gl_proc(void *proc_ptr, const char *name) { - void *proc = (void *)eglGetProcAddress(name); - if (proc == NULL) { - sway_log(SWAY_ERROR, "GLES2 RENDERER: eglGetProcAddress(%s) failed", name); - abort(); - } - *(void **)proc_ptr = proc; -} - -struct fx_renderer *fx_renderer_create(struct wlr_egl *egl, struct wlr_output *wlr_output) { - struct fx_renderer *renderer = calloc(1, sizeof(struct fx_renderer)); - if (renderer == NULL) { - return NULL; - } - - renderer->wlr_output = wlr_output; - - // TODO: wlr_egl_make_current or eglMakeCurrent? - // TODO: assert instead of conditional statement? - 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; - } - - renderer->wlr_buffer = fx_framebuffer_create(); - renderer->blur_buffer = fx_framebuffer_create(); - renderer->blur_saved_pixels_buffer = fx_framebuffer_create(); - renderer->effects_buffer = fx_framebuffer_create(); - renderer->effects_buffer_swapped = fx_framebuffer_create(); - - renderer->blur_buffer_dirty = true; - - // get extensions - const char *exts_str = (const char *)glGetString(GL_EXTENSIONS); - if (exts_str == NULL) { - sway_log(SWAY_ERROR, "GLES2 RENDERER: Failed to get GL_EXTENSIONS"); - return NULL; - } - - sway_log(SWAY_INFO, "Creating swayfx GLES2 renderer"); - sway_log(SWAY_INFO, "Using %s", glGetString(GL_VERSION)); - sway_log(SWAY_INFO, "GL vendor: %s", glGetString(GL_VENDOR)); - sway_log(SWAY_INFO, "GL renderer: %s", glGetString(GL_RENDERER)); - sway_log(SWAY_INFO, "Supported GLES2 extensions: %s", exts_str); - - // TODO: the rest of the gl checks - if (check_gl_ext(exts_str, "GL_OES_EGL_image_external")) { - renderer->exts.OES_egl_image_external = true; - load_gl_proc(&renderer->procs.glEGLImageTargetTexture2DOES, - "glEGLImageTargetTexture2DOES"); - } - - // blur shaders - if (!link_blur_program(&renderer->shaders.blur1, blur1_frag_src)) { - goto error; - } - if (!link_blur_program(&renderer->shaders.blur2, blur2_frag_src)) { - goto error; - } - // effects shader - if (!link_blur_effects_program(&renderer->shaders.blur_effects, blur_effects_frag_src)) { - goto error; - } - // box shadow shader - if (!link_box_shadow_program(&renderer->shaders.box_shadow)) { - goto error; - } - // corner border shader - if (!link_corner_program(&renderer->shaders.corner)) { - goto error; - } - // quad fragment shader - if (!link_quad_program(&renderer->shaders.quad)) { - goto error; - } - // rounded quad fragment shaders - if (!link_rounded_quad_program(&renderer->shaders.rounded_quad, - SHADER_SOURCE_QUAD_ROUND)) { - goto error; - } - if (!link_rounded_quad_program(&renderer->shaders.rounded_tl_quad, - SHADER_SOURCE_QUAD_ROUND_TOP_LEFT)) { - goto error; - } - if (!link_rounded_quad_program(&renderer->shaders.rounded_tr_quad, - SHADER_SOURCE_QUAD_ROUND_TOP_RIGHT)) { - goto error; - } - if (!link_rounded_quad_program(&renderer->shaders.rounded_bl_quad, - SHADER_SOURCE_QUAD_ROUND_BOTTOM_LEFT)) { - goto error; - } - if (!link_rounded_quad_program(&renderer->shaders.rounded_br_quad, - SHADER_SOURCE_QUAD_ROUND_BOTTOM_RIGHT)) { - goto error; - } - // stencil mask shader - if (!link_stencil_mask_program(&renderer->shaders.stencil_mask)) { - goto error; - } - // fragment shaders - if (!link_tex_program(&renderer->shaders.tex_rgba, SHADER_SOURCE_TEXTURE_RGBA)) { - goto error; - } - if (!link_tex_program(&renderer->shaders.tex_rgbx, SHADER_SOURCE_TEXTURE_RGBX)) { - goto error; - } - if (!link_tex_program(&renderer->shaders.tex_ext, SHADER_SOURCE_TEXTURE_EXTERNAL)) { - goto error; - } - - if (!eglMakeCurrent(wlr_egl_get_display(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; - -error: - glDeleteProgram(renderer->shaders.blur1.program); - glDeleteProgram(renderer->shaders.blur2.program); - glDeleteProgram(renderer->shaders.blur_effects.program); - glDeleteProgram(renderer->shaders.box_shadow.program); - glDeleteProgram(renderer->shaders.corner.program); - glDeleteProgram(renderer->shaders.quad.program); - glDeleteProgram(renderer->shaders.rounded_quad.program); - glDeleteProgram(renderer->shaders.rounded_bl_quad.program); - glDeleteProgram(renderer->shaders.rounded_br_quad.program); - glDeleteProgram(renderer->shaders.rounded_tl_quad.program); - glDeleteProgram(renderer->shaders.rounded_tr_quad.program); - glDeleteProgram(renderer->shaders.stencil_mask.program); - glDeleteProgram(renderer->shaders.tex_rgba.program); - glDeleteProgram(renderer->shaders.tex_rgbx.program); - glDeleteProgram(renderer->shaders.tex_ext.program); - - if (!eglMakeCurrent(wlr_egl_get_display(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); - - sway_log(SWAY_ERROR, "GLES2 RENDERER: Error Initializing Shaders"); - return NULL; -} - -void fx_renderer_fini(struct fx_renderer *renderer) { - fx_framebuffer_release(&renderer->blur_buffer); - fx_framebuffer_release(&renderer->blur_saved_pixels_buffer); - fx_framebuffer_release(&renderer->effects_buffer); - fx_framebuffer_release(&renderer->effects_buffer_swapped); -} - -void fx_renderer_begin(struct fx_renderer *renderer, int width, int height) { - glViewport(0, 0, width, height); - renderer->viewport_width = width; - renderer->viewport_height = height; - - // Store the wlr FBO - renderer->wlr_buffer.fb = - wlr_gles2_renderer_get_current_fbo(renderer->wlr_output->renderer); - // Get the fx_texture - struct wlr_texture *wlr_texture = wlr_texture_from_buffer( - renderer->wlr_output->renderer, renderer->wlr_output->back_buffer); - renderer->wlr_buffer.texture = fx_texture_from_wlr_texture(wlr_texture); - wlr_texture_destroy(wlr_texture); - // Add the stencil to the wlr fbo - fx_framebuffer_add_stencil_buffer(&renderer->wlr_buffer, width, height); - - // Create the framebuffers - fx_framebuffer_update(&renderer->blur_saved_pixels_buffer, width, height); - fx_framebuffer_update(&renderer->effects_buffer, width, height); - fx_framebuffer_update(&renderer->effects_buffer_swapped, width, height); - - // Add a stencil buffer to the main buffer & bind the main buffer - fx_framebuffer_bind(&renderer->wlr_buffer); - - pixman_region32_init(&renderer->blur_padding_region); - - // refresh projection matrix - matrix_projection(renderer->projection, width, height, - WL_OUTPUT_TRANSFORM_FLIPPED_180); - - glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); -} - -void fx_renderer_end(struct fx_renderer *renderer) { - pixman_region32_fini(&renderer->blur_padding_region); -} - -void fx_renderer_clear(const float color[static 4]) { - glClearColor(color[0], color[1], color[2], color[3]); - glClearStencil(0); - glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); -} - -void fx_renderer_scissor(struct wlr_box *box) { - if (box) { - glScissor(box->x, box->y, box->width, box->height); - glEnable(GL_SCISSOR_TEST); - } else { - glDisable(GL_SCISSOR_TEST); - } -} - -void fx_renderer_stencil_mask_init() { - glClearStencil(0); - glClear(GL_STENCIL_BUFFER_BIT); - glEnable(GL_STENCIL_TEST); - - glStencilFunc(GL_ALWAYS, 1, 0xFF); - glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE); - // Disable writing to color buffer - glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); -} - -void fx_renderer_stencil_mask_close(bool draw_inside_mask) { - // Reenable writing to color buffer - glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); - if (draw_inside_mask) { - glStencilFunc(GL_EQUAL, 1, 0xFF); - glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE); - return; - } - glStencilFunc(GL_NOTEQUAL, 1, 0xFF); - glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); -} - -void fx_renderer_stencil_mask_fini() { - glClearStencil(0); - glClear(GL_STENCIL_BUFFER_BIT); - glDisable(GL_STENCIL_TEST); -} - -bool fx_render_subtexture_with_matrix(struct fx_renderer *renderer, struct fx_texture *fx_texture, - const struct wlr_fbox *src_box, const struct wlr_box *dst_box, const float matrix[static 9], - struct decoration_data deco_data) { - // Fixes corner radii not being "round" when the radii is larger than - // the height/width - int min_size = MIN(dst_box->height, dst_box->width) * 0.5; - if (deco_data.corner_radius > min_size) { - deco_data.corner_radius = min_size; - } - - struct tex_shader *shader = NULL; - - switch (fx_texture->target) { - case GL_TEXTURE_2D: - if (fx_texture->has_alpha) { - shader = &renderer->shaders.tex_rgba; - } else { - shader = &renderer->shaders.tex_rgbx; - } - break; - case GL_TEXTURE_EXTERNAL_OES: - shader = &renderer->shaders.tex_ext; - - if (!renderer->exts.OES_egl_image_external) { - sway_log(SWAY_ERROR, "Failed to render texture: " - "GL_TEXTURE_EXTERNAL_OES not supported"); - return false; - } - break; - default: - sway_log(SWAY_ERROR, "Aborting render"); - abort(); - } - - float gl_matrix[9]; - wlr_matrix_multiply(gl_matrix, renderer->projection, matrix); - - // OpenGL ES 2 requires the glUniformMatrix3fv transpose parameter to be set - // to GL_FALSE - wlr_matrix_transpose(gl_matrix, gl_matrix); - - // if there's no opacity or rounded corners we don't need to blend - if (!fx_texture->has_alpha && deco_data.alpha == 1.0 && !deco_data.corner_radius) { - glDisable(GL_BLEND); - } else { - glEnable(GL_BLEND); - } - - glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); - - glActiveTexture(GL_TEXTURE0); - glBindTexture(fx_texture->target, fx_texture->id); - - glTexParameteri(fx_texture->target, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - - glUseProgram(shader->program); - - float* dim_color = deco_data.dim_color; - - glUniformMatrix3fv(shader->proj, 1, GL_FALSE, gl_matrix); - glUniform1i(shader->tex, 0); - glUniform2f(shader->size, dst_box->width, dst_box->height); - glUniform2f(shader->position, dst_box->x, dst_box->y); - glUniform1f(shader->alpha, deco_data.alpha); - glUniform1f(shader->dim, deco_data.dim); - glUniform4f(shader->dim_color, dim_color[0], dim_color[1], dim_color[2], dim_color[3]); - glUniform1f(shader->has_titlebar, deco_data.has_titlebar); - glUniform1f(shader->discard_transparent, deco_data.discard_transparent); - glUniform1f(shader->saturation, deco_data.saturation); - glUniform1f(shader->radius, deco_data.corner_radius); - - const GLfloat x1 = src_box->x / fx_texture->width; - const GLfloat y1 = src_box->y / fx_texture->height; - const GLfloat x2 = (src_box->x + src_box->width) / fx_texture->width; - const GLfloat y2 = (src_box->y + src_box->height) / fx_texture->height; - const GLfloat texcoord[] = { - x2, y1, // top right - x1, y1, // top left - x2, y2, // bottom right - x1, y2, // bottom left - }; - - glVertexAttribPointer(shader->pos_attrib, 2, GL_FLOAT, GL_FALSE, 0, verts); - glVertexAttribPointer(shader->tex_attrib, 2, GL_FLOAT, GL_FALSE, 0, texcoord); - - glEnableVertexAttribArray(shader->pos_attrib); - glEnableVertexAttribArray(shader->tex_attrib); - - glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); - - glDisableVertexAttribArray(shader->pos_attrib); - glDisableVertexAttribArray(shader->tex_attrib); - - glBindTexture(fx_texture->target, 0); - - return true; -} - -bool fx_render_texture_with_matrix(struct fx_renderer *renderer, struct fx_texture *texture, - const struct wlr_box *dst_box, const float matrix[static 9], - struct decoration_data deco_data) { - struct wlr_fbox src_box = { - .x = 0, - .y = 0, - .width = texture->width, - .height = texture->height, - }; - return fx_render_subtexture_with_matrix(renderer, texture, &src_box, - dst_box, matrix, deco_data); -} - -void fx_render_rect(struct fx_renderer *renderer, const struct wlr_box *box, - const float color[static 4], const float projection[static 9]) { - if (box->width == 0 || box->height == 0) { - return; - } - assert(box->width > 0 && box->height > 0); - float matrix[9]; - wlr_matrix_project_box(matrix, box, WL_OUTPUT_TRANSFORM_NORMAL, 0, projection); - - float gl_matrix[9]; - wlr_matrix_multiply(gl_matrix, renderer->projection, matrix); - - // TODO: investigate why matrix is flipped prior to this cmd - // wlr_matrix_multiply(gl_matrix, flip_180, gl_matrix); - - wlr_matrix_transpose(gl_matrix, gl_matrix); - - if (color[3] == 1.0) { - glDisable(GL_BLEND); - } else { - glEnable(GL_BLEND); - } - - struct quad_shader shader = renderer->shaders.quad; - glUseProgram(shader.program); - - glUniformMatrix3fv(shader.proj, 1, GL_FALSE, gl_matrix); - glUniform4f(shader.color, color[0], color[1], color[2], color[3]); - - glVertexAttribPointer(shader.pos_attrib, 2, GL_FLOAT, GL_FALSE, - 0, verts); - - glEnableVertexAttribArray(shader.pos_attrib); - - glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); - - glDisableVertexAttribArray(shader.pos_attrib); -} - -void fx_render_rounded_rect(struct fx_renderer *renderer, const struct wlr_box *box, - const float color[static 4], const float matrix[static 9], int radius, - enum corner_location corner_location) { - if (box->width == 0 || box->height == 0) { - return; - } - assert(box->width > 0 && box->height > 0); - - // Fixes corner radii not being "round" when the radii is larger than - // the height/width - int min_size = MIN(box->height, box->width) * 0.5; - if (radius > min_size) { - radius = min_size; - } - - struct rounded_quad_shader *shader = NULL; - - switch (corner_location) { - case ALL: - shader = &renderer->shaders.rounded_quad; - break; - case TOP_LEFT: - shader = &renderer->shaders.rounded_tl_quad; - break; - case TOP_RIGHT: - shader = &renderer->shaders.rounded_tr_quad; - break; - case BOTTOM_LEFT: - shader = &renderer->shaders.rounded_bl_quad; - break; - case BOTTOM_RIGHT: - shader = &renderer->shaders.rounded_br_quad; - break; - default: - sway_log(SWAY_ERROR, "Invalid Corner Location. Aborting render"); - abort(); - } - - float gl_matrix[9]; - wlr_matrix_multiply(gl_matrix, renderer->projection, matrix); - - // TODO: investigate why matrix is flipped prior to this cmd - // wlr_matrix_multiply(gl_matrix, flip_180, gl_matrix); - - wlr_matrix_transpose(gl_matrix, gl_matrix); - - glEnable(GL_BLEND); - - glUseProgram(shader->program); - - glUniformMatrix3fv(shader->proj, 1, GL_FALSE, gl_matrix); - glUniform4f(shader->color, color[0], color[1], color[2], color[3]); - - // rounded corners - glUniform2f(shader->size, box->width, box->height); - glUniform2f(shader->position, box->x, box->y); - glUniform1f(shader->radius, radius); - - glVertexAttribPointer(shader->pos_attrib, 2, GL_FLOAT, GL_FALSE, - 0, verts); - - glEnableVertexAttribArray(shader->pos_attrib); - - glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); - - glDisableVertexAttribArray(shader->pos_attrib); -} - -void fx_render_border_corner(struct fx_renderer *renderer, const struct wlr_box *box, - const float color[static 4], const float matrix[static 9], - enum corner_location corner_location, int radius, int border_thickness) { - if (border_thickness == 0 || box->width == 0 || box->height == 0) { - return; - } - assert(box->width > 0 && box->height > 0); - - // Fixes corner radii not being "round" when the radii is larger than - // the height/width - int min_size = MIN(box->height, box->width) * 0.5; - if (radius > min_size) { - radius = min_size; - } - - float gl_matrix[9]; - wlr_matrix_multiply(gl_matrix, renderer->projection, matrix); - - // TODO: investigate why matrix is flipped prior to this cmd - // wlr_matrix_multiply(gl_matrix, flip_180, gl_matrix); - - wlr_matrix_transpose(gl_matrix, gl_matrix); - - if (color[3] == 1.0 && !radius) { - glDisable(GL_BLEND); - } else { - glEnable(GL_BLEND); - } - - struct corner_shader shader = renderer->shaders.corner; - - glUseProgram(shader.program); - - glUniformMatrix3fv(shader.proj, 1, GL_FALSE, gl_matrix); - glUniform4f(shader.color, color[0], color[1], color[2], color[3]); - - glUniform1f(shader.is_top_left, corner_location == TOP_LEFT); - glUniform1f(shader.is_top_right, corner_location == TOP_RIGHT); - glUniform1f(shader.is_bottom_left, corner_location == BOTTOM_LEFT); - glUniform1f(shader.is_bottom_right, corner_location == BOTTOM_RIGHT); - - glUniform2f(shader.position, box->x, box->y); - glUniform1f(shader.radius, radius); - glUniform2f(shader.half_size, box->width / 2.0, box->height / 2.0); - glUniform1f(shader.half_thickness, border_thickness / 2.0); - - glVertexAttribPointer(shader.pos_attrib, 2, GL_FLOAT, GL_FALSE, - 0, verts); - - glEnableVertexAttribArray(shader.pos_attrib); - - glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); - - glDisableVertexAttribArray(shader.pos_attrib); -} - -void fx_render_stencil_mask(struct fx_renderer *renderer, const struct wlr_box *box, - const float matrix[static 9], int corner_radius) { - if (box->width == 0 || box->height == 0) { - return; - } - assert(box->width > 0 && box->height > 0); - - // Fixes corner radii not being "round" when the radii is larger than - // the height/width - int min_size = MIN(box->height, box->width) * 0.5; - if (corner_radius > min_size) { - corner_radius = min_size; - } - - // TODO: just pass gl_matrix? - float gl_matrix[9]; - wlr_matrix_multiply(gl_matrix, renderer->projection, matrix); - - // TODO: investigate why matrix is flipped prior to this cmd - // wlr_matrix_multiply(gl_matrix, flip_180, gl_matrix); - - wlr_matrix_transpose(gl_matrix, gl_matrix); - - glEnable(GL_BLEND); - - struct stencil_mask_shader shader = renderer->shaders.stencil_mask; - - glUseProgram(shader.program); - - glUniformMatrix3fv(shader.proj, 1, GL_FALSE, gl_matrix); - - glUniform2f(shader.half_size, box->width * 0.5, box->height * 0.5); - glUniform2f(shader.position, box->x, box->y); - glUniform1f(shader.radius, corner_radius); - - glVertexAttribPointer(shader.pos_attrib, 2, GL_FLOAT, GL_FALSE, - 0, verts); - - glEnableVertexAttribArray(shader.pos_attrib); - - glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); - - glDisableVertexAttribArray(shader.pos_attrib); -} - -// TODO: alpha input arg? -void fx_render_box_shadow(struct fx_renderer *renderer, const struct wlr_box *box, - const struct wlr_box *inner_box, const float color[static 4], - const float matrix[static 9], int corner_radius, float blur_sigma) { - if (box->width == 0 || box->height == 0) { - return; - } - assert(box->width > 0 && box->height > 0); - - // Fixes corner radii not being "round" when the radii is larger than - // the height/width - int min_size = MIN(box->height, box->width) * 0.5; - if (corner_radius > min_size) { - corner_radius = min_size; - } - - float gl_matrix[9]; - wlr_matrix_multiply(gl_matrix, renderer->projection, matrix); - - // TODO: investigate why matrix is flipped prior to this cmd - // wlr_matrix_multiply(gl_matrix, flip_180, gl_matrix); - - wlr_matrix_transpose(gl_matrix, gl_matrix); - - fx_renderer_stencil_mask_init(); - // Draw the rounded rect as a mask - fx_render_stencil_mask(renderer, inner_box, matrix, corner_radius); - fx_renderer_stencil_mask_close(false); - - // blending will practically always be needed (unless we have a madman - // who uses opaque shadows with zero sigma), so just enable it - glEnable(GL_BLEND); - - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - - struct box_shadow_shader shader = renderer->shaders.box_shadow; - - glUseProgram(shader.program); - - glUniformMatrix3fv(shader.proj, 1, GL_FALSE, gl_matrix); - glUniform4f(shader.color, color[0], color[1], color[2], color[3]); - glUniform1f(shader.blur_sigma, blur_sigma); - glUniform1f(shader.corner_radius, corner_radius); - - glUniform2f(shader.size, box->width, box->height); - glUniform2f(shader.position, box->x, box->y); - - glVertexAttribPointer(shader.pos_attrib, 2, GL_FLOAT, GL_FALSE, - 0, verts); - - glEnableVertexAttribArray(shader.pos_attrib); - - glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); - - glDisableVertexAttribArray(shader.pos_attrib); - - glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); - - fx_renderer_stencil_mask_fini(); -} - -void fx_render_blur(struct fx_renderer *renderer, const float matrix[static 9], - struct fx_framebuffer **buffer, struct blur_shader *shader, - const struct wlr_box *box, int blur_radius) { - glDisable(GL_BLEND); - glDisable(GL_STENCIL_TEST); - - glActiveTexture(GL_TEXTURE0); - - glBindTexture((*buffer)->texture.target, (*buffer)->texture.id); - - glTexParameteri((*buffer)->texture.target, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - - glUseProgram(shader->program); - - // OpenGL ES 2 requires the glUniformMatrix3fv transpose parameter to be set - // to GL_FALSE - float gl_matrix[9]; - wlr_matrix_transpose(gl_matrix, matrix); - glUniformMatrix3fv(shader->proj, 1, GL_FALSE, gl_matrix); - - glUniform1i(shader->tex, 0); - glUniform1f(shader->radius, blur_radius); - - if (shader == &renderer->shaders.blur1) { - glUniform2f(shader->halfpixel, 0.5f / (renderer->viewport_width / 2.0f), 0.5f / (renderer->viewport_height / 2.0f)); - } else { - glUniform2f(shader->halfpixel, 0.5f / (renderer->viewport_width * 2.0f), 0.5f / (renderer->viewport_height * 2.0f)); - } - - glVertexAttribPointer(shader->pos_attrib, 2, GL_FLOAT, GL_FALSE, 0, verts); - glVertexAttribPointer(shader->tex_attrib, 2, GL_FLOAT, GL_FALSE, 0, verts); - - glEnableVertexAttribArray(shader->pos_attrib); - glEnableVertexAttribArray(shader->tex_attrib); - - glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); - - glDisableVertexAttribArray(shader->pos_attrib); - glDisableVertexAttribArray(shader->tex_attrib); - -} - -void fx_render_blur_effects(struct fx_renderer *renderer, const float matrix[static 9], - struct fx_framebuffer **buffer, float blur_noise, float blur_brightness, - float blur_contrast, float blur_saturation) { - struct effects_shader shader = renderer->shaders.blur_effects; - - glActiveTexture(GL_TEXTURE0); - glBindTexture((*buffer)->texture.target, (*buffer)->texture.id); - glTexParameteri((*buffer)->texture.target, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - - glUseProgram(shader.program); - - // OpenGL ES 2 requires the glUniformMatrix3fv transpose parameter to be set - // to GL_FALSE - float gl_matrix[9]; - wlr_matrix_transpose(gl_matrix, matrix); - glUniformMatrix3fv(shader.proj, 1, GL_FALSE, gl_matrix); - - glUniform1i(shader.tex, 0); - glUniform1f(shader.noise, blur_noise); - glUniform1f(shader.brightness, blur_brightness); - glUniform1f(shader.contrast, blur_contrast); - glUniform1f(shader.saturation, blur_saturation); - - glVertexAttribPointer(shader.pos_attrib, 2, GL_FLOAT, GL_FALSE, 0, verts); - glVertexAttribPointer(shader.tex_attrib, 2, GL_FLOAT, GL_FALSE, 0, verts); - - glEnableVertexAttribArray(shader.pos_attrib); - glEnableVertexAttribArray(shader.tex_attrib); - - glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); - - glDisableVertexAttribArray(shader.pos_attrib); - glDisableVertexAttribArray(shader.tex_attrib); -} diff --git a/sway/desktop/fx_renderer/fx_stencilbuffer.c b/sway/desktop/fx_renderer/fx_stencilbuffer.c deleted file mode 100644 index 5b99ff79..00000000 --- a/sway/desktop/fx_renderer/fx_stencilbuffer.c +++ /dev/null @@ -1,21 +0,0 @@ -#include -#include - -#include "sway/desktop/fx_renderer/fx_stencilbuffer.h" - -struct fx_stencilbuffer fx_stencilbuffer_create() { - return (struct fx_stencilbuffer) { - .rb = -1, - .width = -1, - .height = -1, - }; -} - -void fx_stencilbuffer_release(struct fx_stencilbuffer *stencil_buffer) { - if (stencil_buffer->rb != (uint32_t) -1 && stencil_buffer->rb) { - glDeleteRenderbuffers(1, &stencil_buffer->rb); - } - stencil_buffer->rb = -1; - stencil_buffer->width = -1; - stencil_buffer->height = -1; -} diff --git a/sway/desktop/fx_renderer/fx_texture.c b/sway/desktop/fx_renderer/fx_texture.c deleted file mode 100644 index cc5d14c8..00000000 --- a/sway/desktop/fx_renderer/fx_texture.c +++ /dev/null @@ -1,37 +0,0 @@ -#include -#include - -#include "sway/desktop/fx_renderer/fx_texture.h" - -struct fx_texture fx_texture_create() { - return (struct fx_texture) { - .id = 0, - .target = 0, - .width = -1, - .height = -1, - }; -} - -struct fx_texture fx_texture_from_wlr_texture(struct wlr_texture *texture) { - assert(wlr_texture_is_gles2(texture)); - - struct wlr_gles2_texture_attribs texture_attrs; - wlr_gles2_texture_get_attribs(texture, &texture_attrs); - - return (struct fx_texture) { - .target = texture_attrs.target, - .id = texture_attrs.tex, - .has_alpha = texture_attrs.has_alpha, - .width = texture->width, - .height = texture->height, - }; -} - -void fx_texture_release(struct fx_texture *texture) { - if (texture->id) { - glDeleteTextures(1, &texture->id); - } - texture->id = 0; - texture->width = -1; - texture->height = -1; -} diff --git a/sway/desktop/fx_renderer/matrix.c b/sway/desktop/fx_renderer/matrix.c deleted file mode 100644 index 9c9dabec..00000000 --- a/sway/desktop/fx_renderer/matrix.c +++ /dev/null @@ -1,70 +0,0 @@ -#include -#include -#include - -#include "sway/desktop/fx_renderer/matrix.h" - -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, - }, -}; - -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; -} diff --git a/sway/desktop/fx_renderer/shaders/blur1.frag b/sway/desktop/fx_renderer/shaders/blur1.frag deleted file mode 100644 index e7cb1be8..00000000 --- a/sway/desktop/fx_renderer/shaders/blur1.frag +++ /dev/null @@ -1,18 +0,0 @@ -precision mediump float; -varying mediump vec2 v_texcoord; -uniform sampler2D tex; - -uniform float radius; -uniform vec2 halfpixel; - -void main() { - vec2 uv = v_texcoord * 2.0; - - vec4 sum = texture2D(tex, uv) * 4.0; - sum += texture2D(tex, uv - halfpixel.xy * radius); - sum += texture2D(tex, uv + halfpixel.xy * radius); - sum += texture2D(tex, uv + vec2(halfpixel.x, -halfpixel.y) * radius); - sum += texture2D(tex, uv - vec2(halfpixel.x, -halfpixel.y) * radius); - - gl_FragColor = sum / 8.0; -} diff --git a/sway/desktop/fx_renderer/shaders/blur2.frag b/sway/desktop/fx_renderer/shaders/blur2.frag deleted file mode 100644 index 3755a294..00000000 --- a/sway/desktop/fx_renderer/shaders/blur2.frag +++ /dev/null @@ -1,22 +0,0 @@ -precision mediump float; -varying mediump vec2 v_texcoord; -uniform sampler2D tex; - -uniform float radius; -uniform vec2 halfpixel; - -void main() { - vec2 uv = v_texcoord / 2.0; - - vec4 sum = texture2D(tex, uv + vec2(-halfpixel.x * 2.0, 0.0) * radius); - - sum += texture2D(tex, uv + vec2(-halfpixel.x, halfpixel.y) * radius) * 2.0; - sum += texture2D(tex, uv + vec2(0.0, halfpixel.y * 2.0) * radius); - sum += texture2D(tex, uv + vec2(halfpixel.x, halfpixel.y) * radius) * 2.0; - sum += texture2D(tex, uv + vec2(halfpixel.x * 2.0, 0.0) * radius); - sum += texture2D(tex, uv + vec2(halfpixel.x, -halfpixel.y) * radius) * 2.0; - sum += texture2D(tex, uv + vec2(0.0, -halfpixel.y * 2.0) * radius); - sum += texture2D(tex, uv + vec2(-halfpixel.x, -halfpixel.y) * radius) * 2.0; - - gl_FragColor = sum / 12.0; -} diff --git a/sway/desktop/fx_renderer/shaders/blur_effects.frag b/sway/desktop/fx_renderer/shaders/blur_effects.frag deleted file mode 100644 index 2fc16c15..00000000 --- a/sway/desktop/fx_renderer/shaders/blur_effects.frag +++ /dev/null @@ -1,54 +0,0 @@ -precision mediump float; -varying vec2 v_texcoord; -uniform sampler2D tex; - -uniform float noise; -uniform float brightness; -uniform float contrast; -uniform float saturation; - -mat4 brightnessMatrix() { - float b = brightness - 1.0; - return mat4(1, 0, 0, 0, - 0, 1, 0, 0, - 0, 0, 1, 0, - b, b, b, 1); -} - -mat4 contrastMatrix() { - float t = (1.0 - contrast) / 2.0; - return mat4(contrast, 0, 0, 0, - 0, contrast, 0, 0, - 0, 0, contrast, 0, - t, t, t, 1); -} - -mat4 saturationMatrix() { - vec3 luminance = vec3(0.3086, 0.6094, 0.0820); - float oneMinusSat = 1.0 - saturation; - vec3 red = vec3(luminance.x * oneMinusSat); - red+= vec3(saturation, 0, 0); - vec3 green = vec3(luminance.y * oneMinusSat); - green += vec3(0, saturation, 0); - vec3 blue = vec3(luminance.z * oneMinusSat); - blue += vec3(0, 0, saturation); - return mat4(red, 0, - green, 0, - blue, 0, - 0, 0, 0, 1); -} - -// Fast generative noise function -float hash(vec2 p) { - return fract(sin(dot(p, vec2(12.9898, 78.233))) * 43758.5453); -} - -void main() { - vec4 color = texture2D(tex, v_texcoord); - color *= brightnessMatrix() * contrastMatrix() * saturationMatrix(); - float noiseHash = hash(v_texcoord); - float noiseAmount = (mod(noiseHash, 1.0) - 0.5); - color.rgb += noiseAmount * noise; - - gl_FragColor = color; -} diff --git a/sway/desktop/fx_renderer/shaders/box_shadow.frag b/sway/desktop/fx_renderer/shaders/box_shadow.frag deleted file mode 100644 index c9b2b91f..00000000 --- a/sway/desktop/fx_renderer/shaders/box_shadow.frag +++ /dev/null @@ -1,74 +0,0 @@ -// Writeup: https://madebyevan.com/shaders/fast-rounded-rectangle-shadows/ - -precision mediump float; -varying vec4 v_color; -varying vec2 v_texcoord; - -uniform vec2 position; -uniform vec2 size; -uniform float blur_sigma; -uniform float corner_radius; - -float gaussian(float x, float sigma) { - const float pi = 3.141592653589793; - return exp(-(x * x) / (2.0 * sigma * sigma)) / (sqrt(2.0 * pi) * sigma); -} - -// approximates the error function, needed for the gaussian integral -vec2 erf(vec2 x) { - vec2 s = sign(x), a = abs(x); - x = 1.0 + (0.278393 + (0.230389 + 0.078108 * (a * a)) * a) * a; - x *= x; - return s - s / (x * x); -} - -// return the blurred mask along the x dimension -float roundedBoxShadowX(float x, float y, float sigma, float corner, vec2 halfSize) { - float delta = min(halfSize.y - corner - abs(y), 0.0); - float curved = halfSize.x - corner + sqrt(max(0.0, corner * corner - delta * delta)); - vec2 integral = 0.5 + 0.5 * erf((x + vec2(-curved, curved)) * (sqrt(0.5) / sigma)); - return integral.y - integral.x; -} - -// return the mask for the shadow of a box from lower to upper -float roundedBoxShadow(vec2 lower, vec2 upper, vec2 point, float sigma, float corner_radius) { - // Center everything to make the math easier - vec2 center = (lower + upper) * 0.5; - vec2 halfSize = (upper - lower) * 0.5; - point -= center; - - // The signal is only non-zero in a limited range, so don't waste samples - float low = point.y - halfSize.y; - float high = point.y + halfSize.y; - float start = clamp(-3.0 * sigma, low, high); - float end = clamp(3.0 * sigma, low, high); - - // Accumulate samples (we can get away with surprisingly few samples) - float step = (end - start) / 4.0; - float y = start + step * 0.5; - float value = 0.0; - for (int i = 0; i < 4; i++) { - value += roundedBoxShadowX(point.x, point.y - y, sigma, corner_radius, halfSize) * gaussian(y, sigma) * step; - y += step; - } - - return value; -} - -// per-pixel "random" number between 0 and 1 -float random() { - return fract(sin(dot(vec2(12.9898, 78.233), gl_FragCoord.xy)) * 43758.5453); -} - -void main() { - float frag_alpha = v_color.a * roundedBoxShadow( - position + blur_sigma, - position + size - blur_sigma, - gl_FragCoord.xy, blur_sigma * 0.5, - corner_radius); - - // dither the alpha to break up color bands - frag_alpha += (random() - 0.5) / 128.0; - - gl_FragColor = vec4(v_color.rgb, frag_alpha); -} diff --git a/sway/desktop/fx_renderer/shaders/common.vert b/sway/desktop/fx_renderer/shaders/common.vert deleted file mode 100644 index 811e0f2d..00000000 --- a/sway/desktop/fx_renderer/shaders/common.vert +++ /dev/null @@ -1,12 +0,0 @@ -uniform mat3 proj; -uniform vec4 color; -attribute vec2 pos; -attribute vec2 texcoord; -varying vec4 v_color; -varying vec2 v_texcoord; - -void main() { - gl_Position = vec4(proj * vec3(pos, 1.0), 1.0); - v_color = color; - v_texcoord = texcoord; -} diff --git a/sway/desktop/fx_renderer/shaders/corner.frag b/sway/desktop/fx_renderer/shaders/corner.frag deleted file mode 100644 index 7699299a..00000000 --- a/sway/desktop/fx_renderer/shaders/corner.frag +++ /dev/null @@ -1,36 +0,0 @@ -precision mediump float; -varying vec4 v_color; -varying vec2 v_texcoord; - -uniform bool is_top_left; -uniform bool is_top_right; -uniform bool is_bottom_left; -uniform bool is_bottom_right; - -uniform vec2 position; -uniform float radius; -uniform vec2 half_size; -uniform float half_thickness; - -float roundedBoxSDF(vec2 center, vec2 size, float radius) { - return length(max(abs(center) - size + radius, 0.0)) - radius; -} - -void main() { - vec2 center = gl_FragCoord.xy - position - half_size; - float distance = roundedBoxSDF(center, half_size - half_thickness, radius + half_thickness); - float smoothedAlphaOuter = 1.0 - smoothstep(-1.0, 1.0, distance - half_thickness); - // Create an inner circle that isn't as anti-aliased as the outer ring - float smoothedAlphaInner = 1.0 - smoothstep(-1.0, 0.5, distance + half_thickness); - gl_FragColor = mix(vec4(0), v_color, smoothedAlphaOuter - smoothedAlphaInner); - - if (is_top_left && (center.y > 0.0 || center.x > 0.0)) { - discard; - } else if (is_top_right && (center.y > 0.0 || center.x < 0.0)) { - discard; - } else if (is_bottom_left && (center.y < 0.0 || center.x > 0.0)) { - discard; - } else if (is_bottom_right && (center.y < 0.0 || center.x < 0.0)) { - discard; - } -} diff --git a/sway/desktop/fx_renderer/shaders/embed.sh b/sway/desktop/fx_renderer/shaders/embed.sh deleted file mode 100644 index 47f07892..00000000 --- a/sway/desktop/fx_renderer/shaders/embed.sh +++ /dev/null @@ -1,11 +0,0 @@ -#!/bin/sh -eu - -var=${1:-data} -hex="$(od -A n -t x1 -v)" - -echo "static const char $var[] = {" -for byte in $hex; do - echo " 0x$byte," -done -echo " 0x00," -echo "};" diff --git a/sway/desktop/fx_renderer/shaders/meson.build b/sway/desktop/fx_renderer/shaders/meson.build deleted file mode 100644 index 19f76dc2..00000000 --- a/sway/desktop/fx_renderer/shaders/meson.build +++ /dev/null @@ -1,27 +0,0 @@ -embed = find_program('./embed.sh', native: true) - -shaders = [ - 'blur1.frag', - 'blur2.frag', - 'blur_effects.frag', - 'box_shadow.frag', - 'common.vert', - 'corner.frag', - 'quad.frag', - 'quad_round.frag', - 'stencil_mask.frag', - 'tex.frag', -] - -foreach name : shaders - output = name.underscorify() + '_src.h' - var = name.underscorify() + '_src' - sway_sources += custom_target( - output, - command: [embed, var], - input: name, - output: output, - feed: true, - capture: true, - ) -endforeach diff --git a/sway/desktop/fx_renderer/shaders/quad.frag b/sway/desktop/fx_renderer/shaders/quad.frag deleted file mode 100644 index 7c763272..00000000 --- a/sway/desktop/fx_renderer/shaders/quad.frag +++ /dev/null @@ -1,7 +0,0 @@ -precision mediump float; -varying vec4 v_color; -varying vec2 v_texcoord; - -void main() { - gl_FragColor = v_color; -} diff --git a/sway/desktop/fx_renderer/shaders/quad_round.frag b/sway/desktop/fx_renderer/shaders/quad_round.frag deleted file mode 100644 index 02e99028..00000000 --- a/sway/desktop/fx_renderer/shaders/quad_round.frag +++ /dev/null @@ -1,39 +0,0 @@ -#define SOURCE_QUAD_ROUND 1 -#define SOURCE_QUAD_ROUND_TOP_LEFT 2 -#define SOURCE_QUAD_ROUND_TOP_RIGHT 3 -#define SOURCE_QUAD_ROUND_BOTTOM_RIGHT 4 -#define SOURCE_QUAD_ROUND_BOTTOM_LEFT 5 - -#if !defined(SOURCE) -#error "Missing shader preamble" -#endif - -precision mediump float; -varying vec4 v_color; -varying vec2 v_texcoord; - -uniform vec2 size; -uniform vec2 position; -uniform float radius; - -vec2 getCornerDist() { -#if SOURCE == SOURCE_QUAD_ROUND - vec2 half_size = size * 0.5; - return abs(gl_FragCoord.xy - position - half_size) - half_size + radius; -#elif SOURCE == SOURCE_QUAD_ROUND_TOP_LEFT - return abs(gl_FragCoord.xy - position - size) - size + radius; -#elif SOURCE == SOURCE_QUAD_ROUND_TOP_RIGHT - return abs(gl_FragCoord.xy - position - vec2(0, size.y)) - size + radius; -#elif SOURCE == SOURCE_QUAD_ROUND_BOTTOM_RIGHT - return abs(gl_FragCoord.xy - position) - size + radius; -#elif SOURCE == SOURCE_QUAD_ROUND_BOTTOM_LEFT - return abs(gl_FragCoord.xy - position - vec2(size.x, 0)) - size + radius; -#endif -} - -void main() { - vec2 q = getCornerDist(); - float dist = min(max(q.x,q.y), 0.0) + length(max(q, 0.0)) - radius; - float smoothedAlpha = 1.0 - smoothstep(-1.0, 0.5, dist); - gl_FragColor = mix(vec4(0), v_color, smoothedAlpha); -} diff --git a/sway/desktop/fx_renderer/shaders/stencil_mask.frag b/sway/desktop/fx_renderer/shaders/stencil_mask.frag deleted file mode 100644 index ee033070..00000000 --- a/sway/desktop/fx_renderer/shaders/stencil_mask.frag +++ /dev/null @@ -1,17 +0,0 @@ -precision mediump float; -varying vec2 v_texcoord; - -uniform vec2 half_size; -uniform vec2 position; -uniform float radius; - -void main() { - vec2 q = abs(gl_FragCoord.xy - position - half_size) - half_size + radius; - float dist = min(max(q.x,q.y), 0.0) + length(max(q, 0.0)) - radius; - float smoothedAlpha = 1.0 - smoothstep(-1.0, 0.5, dist); - gl_FragColor = mix(vec4(0.0), vec4(1.0), smoothedAlpha); - - if (gl_FragColor.a < 1.0) { - discard; - } -} diff --git a/sway/desktop/fx_renderer/shaders/tex.frag b/sway/desktop/fx_renderer/shaders/tex.frag deleted file mode 100644 index 77501887..00000000 --- a/sway/desktop/fx_renderer/shaders/tex.frag +++ /dev/null @@ -1,67 +0,0 @@ -#define SOURCE_TEXTURE_RGBA 1 -#define SOURCE_TEXTURE_RGBX 2 -#define SOURCE_TEXTURE_EXTERNAL 3 - -#if !defined(SOURCE) -#error "Missing shader preamble" -#endif - -#if SOURCE == SOURCE_TEXTURE_EXTERNAL -#extension GL_OES_EGL_image_external : require -#endif - -precision mediump float; - -varying vec2 v_texcoord; - -#if SOURCE == SOURCE_TEXTURE_EXTERNAL -uniform samplerExternalOES tex; -#elif SOURCE == SOURCE_TEXTURE_RGBA || SOURCE == SOURCE_TEXTURE_RGBX -uniform sampler2D tex; -#endif - -uniform float alpha; -uniform float dim; -uniform vec4 dim_color; -uniform vec2 size; -uniform vec2 position; -uniform float radius; -uniform float saturation; -uniform bool has_titlebar; -uniform bool discard_transparent; - -const vec3 saturation_weight = vec3(0.2125, 0.7154, 0.0721); - -vec4 sample_texture() { -#if SOURCE == SOURCE_TEXTURE_RGBA || SOURCE == SOURCE_TEXTURE_EXTERNAL - return texture2D(tex, v_texcoord); -#elif SOURCE == SOURCE_TEXTURE_RGBX - return vec4(texture2D(tex, v_texcoord).rgb, 1.0); -#endif -} - -void main() { - vec4 color = sample_texture(); - // Saturation - if (saturation != 1.0) { - vec4 pixColor = texture2D(tex, v_texcoord); - vec3 irgb = pixColor.rgb; - vec3 target = vec3(dot(irgb, saturation_weight)); - color = vec4(mix(target, irgb, saturation), pixColor.a); - } - // Dimming - gl_FragColor = mix(color, dim_color, dim) * alpha; - - if (!has_titlebar || gl_FragCoord.y - position.y > radius) { - vec2 corner_distance = min(gl_FragCoord.xy - position, size + position - gl_FragCoord.xy); - if (max(corner_distance.x, corner_distance.y) < radius) { - float d = radius - distance(corner_distance, vec2(radius)); - float smooth = smoothstep(-1.0, 0.5, d); - gl_FragColor = mix(vec4(0), gl_FragColor, smooth); - } - } - - if (discard_transparent && gl_FragColor.a == 0.0) { - discard; - } -} diff --git a/sway/desktop/idle_inhibit_v1.c b/sway/desktop/idle_inhibit_v1.c index 3a4d0b87..f3af7aa1 100644 --- a/sway/desktop/idle_inhibit_v1.c +++ b/sway/desktop/idle_inhibit_v1.c @@ -1,5 +1,4 @@ #include -#include #include #include "log.h" #include "sway/desktop/idle_inhibit_v1.h" @@ -12,7 +11,7 @@ static void destroy_inhibitor(struct sway_idle_inhibitor_v1 *inhibitor) { wl_list_remove(&inhibitor->link); wl_list_remove(&inhibitor->destroy.link); - sway_idle_inhibit_v1_check_active(inhibitor->manager); + sway_idle_inhibit_v1_check_active(); free(inhibitor); } @@ -35,7 +34,6 @@ void handle_idle_inhibitor_v1(struct wl_listener *listener, void *data) { return; } - inhibitor->manager = manager; inhibitor->mode = INHIBIT_IDLE_APPLICATION; inhibitor->wlr_inhibitor = wlr_inhibitor; wl_list_insert(&manager->inhibitors, &inhibitor->link); @@ -43,33 +41,34 @@ void handle_idle_inhibitor_v1(struct wl_listener *listener, void *data) { inhibitor->destroy.notify = handle_destroy; wl_signal_add(&wlr_inhibitor->events.destroy, &inhibitor->destroy); - sway_idle_inhibit_v1_check_active(manager); + sway_idle_inhibit_v1_check_active(); } void sway_idle_inhibit_v1_user_inhibitor_register(struct sway_view *view, enum sway_idle_inhibit_mode mode) { + struct sway_idle_inhibit_manager_v1 *manager = &server.idle_inhibit_manager_v1; + struct sway_idle_inhibitor_v1 *inhibitor = calloc(1, sizeof(struct sway_idle_inhibitor_v1)); if (!inhibitor) { return; } - inhibitor->manager = server.idle_inhibit_manager_v1; inhibitor->mode = mode; inhibitor->view = view; - wl_list_insert(&inhibitor->manager->inhibitors, &inhibitor->link); + wl_list_insert(&manager->inhibitors, &inhibitor->link); inhibitor->destroy.notify = handle_destroy; wl_signal_add(&view->events.unmap, &inhibitor->destroy); - sway_idle_inhibit_v1_check_active(inhibitor->manager); + sway_idle_inhibit_v1_check_active(); } struct sway_idle_inhibitor_v1 *sway_idle_inhibit_v1_user_inhibitor_for_view( struct sway_view *view) { + struct sway_idle_inhibit_manager_v1 *manager = &server.idle_inhibit_manager_v1; struct sway_idle_inhibitor_v1 *inhibitor; - wl_list_for_each(inhibitor, &server.idle_inhibit_manager_v1->inhibitors, - link) { + wl_list_for_each(inhibitor, &manager->inhibitors, link) { if (inhibitor->mode != INHIBIT_IDLE_APPLICATION && inhibitor->view == view) { return inhibitor; @@ -80,9 +79,9 @@ struct sway_idle_inhibitor_v1 *sway_idle_inhibit_v1_user_inhibitor_for_view( struct sway_idle_inhibitor_v1 *sway_idle_inhibit_v1_application_inhibitor_for_view( struct sway_view *view) { + struct sway_idle_inhibit_manager_v1 *manager = &server.idle_inhibit_manager_v1; struct sway_idle_inhibitor_v1 *inhibitor; - wl_list_for_each(inhibitor, &server.idle_inhibit_manager_v1->inhibitors, - link) { + wl_list_for_each(inhibitor, &manager->inhibitors, link) { if (inhibitor->mode == INHIBIT_IDLE_APPLICATION && view_from_wlr_surface(inhibitor->wlr_inhibitor->surface) == view) { return inhibitor; @@ -131,8 +130,8 @@ bool sway_idle_inhibit_v1_is_active(struct sway_idle_inhibitor_v1 *inhibitor) { return false; } -void sway_idle_inhibit_v1_check_active( - struct sway_idle_inhibit_manager_v1 *manager) { +void sway_idle_inhibit_v1_check_active(void) { + struct sway_idle_inhibit_manager_v1 *manager = &server.idle_inhibit_manager_v1; struct sway_idle_inhibitor_v1 *inhibitor; bool inhibited = false; wl_list_for_each(inhibitor, &manager->inhibitors, link) { @@ -140,28 +139,21 @@ void sway_idle_inhibit_v1_check_active( break; } } - wlr_idle_set_enabled(manager->idle, NULL, !inhibited); wlr_idle_notifier_v1_set_inhibited(server.idle_notifier_v1, inhibited); } -struct sway_idle_inhibit_manager_v1 *sway_idle_inhibit_manager_v1_create( - struct wl_display *wl_display, struct wlr_idle *idle) { - struct sway_idle_inhibit_manager_v1 *manager = - calloc(1, sizeof(struct sway_idle_inhibit_manager_v1)); - if (!manager) { - return NULL; +bool sway_idle_inhibit_manager_v1_init(void) { + struct sway_idle_inhibit_manager_v1 *manager = &server.idle_inhibit_manager_v1; + + manager->wlr_manager = wlr_idle_inhibit_v1_create(server.wl_display); + if (!manager->wlr_manager) { + return false; } - manager->wlr_manager = wlr_idle_inhibit_v1_create(wl_display); - if (!manager->wlr_manager) { - free(manager); - return NULL; - } - manager->idle = idle; wl_signal_add(&manager->wlr_manager->events.new_inhibitor, &manager->new_idle_inhibitor_v1); manager->new_idle_inhibitor_v1.notify = handle_idle_inhibitor_v1; wl_list_init(&manager->inhibitors); - return manager; + return true; } diff --git a/sway/desktop/launcher.c b/sway/desktop/launcher.c index 48e5d24c..0c02b997 100644 --- a/sway/desktop/launcher.c +++ b/sway/desktop/launcher.c @@ -67,9 +67,12 @@ void launcher_ctx_destroy(struct launcher_ctx *ctx) { } wl_list_remove(&ctx->node_destroy.link); wl_list_remove(&ctx->token_destroy.link); + if (ctx->seat) { + wl_list_remove(&ctx->seat_destroy.link); + } wl_list_remove(&ctx->link); wlr_xdg_activation_token_v1_destroy(ctx->token); - free(ctx->name); + free(ctx->fallback_name); free(ctx); } @@ -114,22 +117,22 @@ struct sway_workspace *launcher_ctx_get_workspace( break; case N_OUTPUT: output = ctx->node->sway_output; - ws = workspace_by_name(ctx->name); + ws = workspace_by_name(ctx->fallback_name); if (!ws) { sway_log(SWAY_DEBUG, "Creating workspace %s for pid %d because it disappeared", - ctx->name, ctx->pid); + ctx->fallback_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); + ws = workspace_create(output, ctx->fallback_name); } break; case N_ROOT: - ws = workspace_create(NULL, ctx->name); + ws = workspace_create(NULL, ctx->fallback_name); break; } @@ -148,8 +151,8 @@ static void ctx_handle_node_destroy(struct wl_listener *listener, void *data) { 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); + free(ctx->fallback_name); + ctx->fallback_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 @@ -178,21 +181,43 @@ static void token_handle_destroy(struct wl_listener *listener, void *data) { 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."); +struct launcher_ctx *launcher_ctx_create(struct wlr_xdg_activation_token_v1 *token, + struct sway_node *node) { + struct launcher_ctx *ctx = calloc(1, sizeof(struct launcher_ctx)); + + const char *fallback_name = NULL; + struct sway_workspace *ws = NULL; + switch (node->type) { + case N_CONTAINER: + // Unimplemented + free(ctx); + return NULL; + case N_WORKSPACE: + ws = node->sway_workspace; + fallback_name = ws->name; + break; + case N_OUTPUT:; + struct sway_output *output = node->sway_output; + ws = output_get_active_workspace(output); + fallback_name = ws ? ws->name : NULL; + break; + case N_ROOT: + // Unimplemented + free(ctx); 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); + if (!fallback_name) { + // TODO: implement a better fallback. + free(ctx); + return NULL; + } + + ctx->fallback_name = strdup(fallback_name); ctx->token = token; - ctx->node = &ws->node; + ctx->node = node; + // Having surface set means that the focus check in wlroots has passed + ctx->had_focused_surface = token->surface != NULL; ctx->node_destroy.notify = ctx_handle_node_destroy; wl_signal_add(&ctx->node->events.destroy, &ctx->node_destroy); @@ -202,6 +227,38 @@ struct launcher_ctx *launcher_ctx_create() { wl_list_init(&ctx->link); wl_list_insert(&server.pending_launcher_ctxs, &ctx->link); + + token->data = ctx; + return ctx; +} + +static void launch_ctx_handle_seat_destroy(struct wl_listener *listener, void *data) { + struct launcher_ctx *ctx = wl_container_of(listener, ctx, seat_destroy); + ctx->seat = NULL; + wl_list_remove(&ctx->seat_destroy.link); +} + +// Creates a context with a new token for the internal launcher +struct launcher_ctx *launcher_ctx_create_internal(void) { + 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 wlr_xdg_activation_token_v1 *token = + wlr_xdg_activation_token_v1_create(server.xdg_activation_v1); + + struct launcher_ctx *ctx = launcher_ctx_create(token, &ws->node); + if (!ctx) { + wlr_xdg_activation_token_v1_destroy(token); + return NULL; + } + ctx->seat = seat; + ctx->seat_destroy.notify = launch_ctx_handle_seat_destroy; + wl_signal_add(&seat->wlr_seat->events.destroy, &ctx->seat_destroy); + return ctx; } diff --git a/sway/desktop/layer_shell.c b/sway/desktop/layer_shell.c index d0d56755..81429053 100644 --- a/sway/desktop/layer_shell.c +++ b/sway/desktop/layer_shell.c @@ -1,3 +1,4 @@ +#include #include #include #include @@ -14,10 +15,44 @@ #include "sway/layers.h" #include "sway/output.h" #include "sway/server.h" +#include "sway/surface.h" #include "sway/tree/arrange.h" #include "sway/tree/workspace.h" #include "wlr-layer-shell-unstable-v1-protocol.h" +struct wlr_layer_surface_v1 *toplevel_layer_surface_from_surface( + struct wlr_surface *surface) { + struct wlr_layer_surface_v1 *layer; + do { + if (!surface) { + return NULL; + } + // Topmost layer surface + if ((layer = wlr_layer_surface_v1_try_from_wlr_surface(surface))) { + return layer; + } + // Layer subsurface + if (wlr_subsurface_try_from_wlr_surface(surface)) { + surface = wlr_surface_get_root_surface(surface); + continue; + } + + // Layer surface popup + struct wlr_xdg_surface *xdg_surface = NULL; + if ((xdg_surface = wlr_xdg_surface_try_from_wlr_surface(surface)) && + xdg_surface->role == WLR_XDG_SURFACE_ROLE_POPUP && xdg_surface->popup != NULL) { + if (!xdg_surface->popup->parent) { + return NULL; + } + surface = wlr_surface_get_root_surface(xdg_surface->popup->parent); + continue; + } + + // Return early if the surface is not a layer/xdg_popup/sub surface + return NULL; + } while (true); +} + static void layer_parse_criteria(struct sway_layer_surface *sway_layer) { enum zwlr_layer_shell_v1_layer layer = sway_layer->layer; if (layer == ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND) { @@ -233,8 +268,9 @@ void arrange_layers(struct sway_output *output) { for (size_t i = 0; i < nlayers; ++i) { wl_list_for_each_reverse(layer, &output->layers[layers_above_shell[i]], link) { - if (layer->layer_surface->current.keyboard_interactive && - layer->layer_surface->mapped) { + if (layer->layer_surface->current.keyboard_interactive + == ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_EXCLUSIVE && + layer->layer_surface->surface->mapped) { topmost = layer; break; } @@ -246,10 +282,12 @@ void arrange_layers(struct sway_output *output) { struct sway_seat *seat; wl_list_for_each(seat, &server.input->seats, link) { + seat->has_exclusive_layer = false; if (topmost != NULL) { seat_set_focus_layer(seat, topmost->layer_surface); } else if (seat->focused_layer && - !seat->focused_layer->current.keyboard_interactive) { + seat->focused_layer->current.keyboard_interactive + != ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_EXCLUSIVE) { seat_set_focus_layer(seat, NULL); } } @@ -268,7 +306,7 @@ static struct sway_layer_surface *find_mapped_layer_by_client( &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], link) { struct wl_resource *resource = lsurface->layer_surface->resource; if (wl_resource_get_client(resource) == client - && lsurface->layer_surface->mapped) { + && lsurface->layer_surface->surface->mapped) { return lsurface; } } @@ -309,13 +347,15 @@ static void handle_surface_commit(struct wl_listener *listener, void *data) { // Rerender the static blur on change if (layer->layer == ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND || layer->layer == ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM) { - output->renderer->blur_buffer_dirty = true; + struct fx_effect_framebuffers *effect_fbos = + fx_effect_framebuffers_try_get(output->wlr_output); + effect_fbos->blur_buffer_dirty = true; } bool layer_changed = false; if (layer_surface->current.committed != 0 - || layer->mapped != layer_surface->mapped) { - layer->mapped = layer_surface->mapped; + || layer->mapped != layer_surface->surface->mapped) { + layer->mapped = layer_surface->surface->mapped; layer_changed = layer->layer != layer_surface->current.layer; if (layer_changed) { wl_list_remove(&layer->link); @@ -376,7 +416,7 @@ static void handle_destroy(struct wl_listener *listener, void *data) { wl_container_of(listener, sway_layer, destroy); sway_log(SWAY_DEBUG, "Layer surface destroyed (%s)", sway_layer->layer_surface->namespace); - if (sway_layer->layer_surface->mapped) { + if (sway_layer->layer_surface->surface->mapped) { unmap(sway_layer); } @@ -400,7 +440,9 @@ static void handle_destroy(struct wl_listener *listener, void *data) { // Rerender the static blur if (sway_layer->layer == ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND || sway_layer->layer == ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM) { - output->renderer->blur_buffer_dirty = true; + struct fx_effect_framebuffers *effect_fbos = + fx_effect_framebuffers_try_get(output->wlr_output); + effect_fbos->blur_buffer_dirty = true; } arrange_layers(output); @@ -420,8 +462,6 @@ static void handle_map(struct wl_listener *listener, void *data) { layer_parse_criteria(sway_layer); 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, - sway_layer->layer_surface->output); cursor_rebase_all(); } @@ -491,9 +531,9 @@ static struct sway_layer_subsurface *create_subsurface( wl_list_insert(&layer_surface->subsurfaces, &subsurface->link); subsurface->map.notify = subsurface_handle_map; - wl_signal_add(&wlr_subsurface->events.map, &subsurface->map); + wl_signal_add(&wlr_subsurface->surface->events.map, &subsurface->map); subsurface->unmap.notify = subsurface_handle_unmap; - wl_signal_add(&wlr_subsurface->events.unmap, &subsurface->unmap); + wl_signal_add(&wlr_subsurface->surface->events.unmap, &subsurface->unmap); subsurface->destroy.notify = subsurface_handle_destroy; wl_signal_add(&wlr_subsurface->events.destroy, &subsurface->destroy); subsurface->commit.notify = subsurface_handle_commit; @@ -548,7 +588,7 @@ static void popup_handle_map(struct wl_listener *listener, void *data) { 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); + surface_enter_output(popup->wlr_popup->base->surface, wlr_output->data); popup_damage(popup, true); } @@ -608,9 +648,9 @@ static struct sway_layer_popup *create_popup(struct wlr_xdg_popup *wlr_popup, popup->parent_layer = parent; popup->map.notify = popup_handle_map; - wl_signal_add(&wlr_popup->base->events.map, &popup->map); + wl_signal_add(&wlr_popup->base->surface->events.map, &popup->map); popup->unmap.notify = popup_handle_unmap; - wl_signal_add(&wlr_popup->base->events.unmap, &popup->unmap); + wl_signal_add(&wlr_popup->base->surface->events.unmap, &popup->unmap); popup->destroy.notify = popup_handle_destroy; wl_signal_add(&wlr_popup->base->events.destroy, &popup->destroy); popup->commit.notify = popup_handle_commit; @@ -698,9 +738,9 @@ void handle_layer_shell_surface(struct wl_listener *listener, void *data) { sway_layer->destroy.notify = handle_destroy; wl_signal_add(&layer_surface->events.destroy, &sway_layer->destroy); sway_layer->map.notify = handle_map; - wl_signal_add(&layer_surface->events.map, &sway_layer->map); + wl_signal_add(&layer_surface->surface->events.map, &sway_layer->map); sway_layer->unmap.notify = handle_unmap; - wl_signal_add(&layer_surface->events.unmap, &sway_layer->unmap); + wl_signal_add(&layer_surface->surface->events.unmap, &sway_layer->unmap); sway_layer->new_popup.notify = handle_new_popup; wl_signal_add(&layer_surface->events.new_popup, &sway_layer->new_popup); sway_layer->new_subsurface.notify = handle_new_subsurface; @@ -722,6 +762,8 @@ void handle_layer_shell_surface(struct wl_listener *listener, void *data) { wl_list_insert(&output->layers[layer_surface->pending.layer], &sway_layer->link); + surface_enter_output(layer_surface->surface, output); + // Temporarily set the layer's current state to pending // So that we can easily arrange it struct wlr_layer_surface_v1_state old_state = layer_surface->current; diff --git a/sway/desktop/output.c b/sway/desktop/output.c index ca883144..2ccb28f2 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -1,15 +1,18 @@ #define _POSIX_C_SOURCE 200809L #include +#include +#include #include #include #include #include -#include +#include #include +#include #include #include #include -#include +#include #include #include #include @@ -18,10 +21,12 @@ #include #include "config.h" #include "log.h" +#include "scenefx/render/pass.h" #include "sway/config.h" #include "sway/desktop/transaction.h" #include "sway/input/input-manager.h" #include "sway/input/seat.h" +#include "sway/ipc-server.h" #include "sway/layers.h" #include "sway/output.h" #include "sway/server.h" @@ -32,13 +37,27 @@ #include "sway/tree/view.h" #include "sway/tree/workspace.h" +#if WLR_HAS_DRM_BACKEND +#include +#include +#endif + +bool output_match_name_or_id(struct sway_output *output, + const char *name_or_id) { + if (strcmp(name_or_id, "*") == 0) { + return true; + } + + char identifier[128]; + output_get_identifier(identifier, sizeof(identifier), output); + return strcasecmp(identifier, name_or_id) == 0 + || strcasecmp(output->wlr_output->name, name_or_id) == 0; +} + struct sway_output *output_by_name_or_id(const char *name_or_id) { for (int i = 0; i < root->outputs->length; ++i) { struct sway_output *output = root->outputs->items[i]; - char identifier[128]; - output_get_identifier(identifier, sizeof(identifier), output); - if (strcasecmp(identifier, name_or_id) == 0 - || strcasecmp(output->wlr_output->name, name_or_id) == 0) { + if (output_match_name_or_id(output, name_or_id)) { return output; } } @@ -48,10 +67,7 @@ struct sway_output *output_by_name_or_id(const char *name_or_id) { struct sway_output *all_output_by_name_or_id(const char *name_or_id) { struct sway_output *output; wl_list_for_each(output, &root->all_outputs, link) { - char identifier[128]; - output_get_identifier(identifier, sizeof(identifier), output); - if (strcasecmp(identifier, name_or_id) == 0 - || strcasecmp(output->wlr_output->name, name_or_id) == 0) { + if (output_match_name_or_id(output, name_or_id)) { return output; } } @@ -262,7 +278,7 @@ void output_drag_icons_for_each_surface(struct sway_output *output, double ox = drag_icon->x - output->lx; double oy = drag_icon->y - output->ly; - if (drag_icon->wlr_drag_icon->mapped) { + if (drag_icon->wlr_drag_icon->surface->mapped) { output_surface_for_each_surface(output, drag_icon->wlr_drag_icon->surface, ox, oy, iterator, user_data); @@ -292,7 +308,7 @@ static void output_for_each_surface(struct sway_output *output, if (lock_surface->output != output->wlr_output) { continue; } - if (!lock_surface->mapped) { + if (!lock_surface->surface->mapped) { continue; } @@ -369,14 +385,14 @@ overlay: } static int scale_length(int length, int offset, float scale) { - return round((offset + length) * scale) - round(offset * scale); + return roundf((offset + length) * scale) - roundf(offset * scale); } void scale_box(struct wlr_box *box, float scale) { box->width = scale_length(box->width, box->x, scale); box->height = scale_length(box->height, box->y, scale); - box->x = round(box->x * scale); - box->y = round(box->y * scale); + box->x = roundf(box->x * scale); + box->y = roundf(box->y * scale); } struct sway_workspace *output_get_active_workspace(struct sway_output *output) { @@ -454,7 +470,7 @@ static void count_surface_iterator(struct sway_output *output, } static bool scan_out_fullscreen_view(struct sway_output *output, - struct sway_view *view) { + struct wlr_output_state *pending, struct sway_view *view) { struct wlr_output *wlr_output = output->wlr_output; struct sway_workspace *workspace = output->current.active_workspace; if (!sway_assert(workspace, "Expected an active workspace")) { @@ -500,6 +516,12 @@ static bool scan_out_fullscreen_view(struct sway_output *output, if (n_surfaces != 1) { return false; } + size_t n_popups = 0; + output_view_for_each_popup_surface(output, view, + count_surface_iterator, &n_popups); + if (n_popups > 0) { + return false; + } if (surface->buffer == NULL) { return false; @@ -510,24 +532,56 @@ static bool scan_out_fullscreen_view(struct sway_output *output, return false; } - wlr_output_attach_buffer(wlr_output, &surface->buffer->base); - if (!wlr_output_test(wlr_output)) { + if (!wlr_output_is_direct_scanout_allowed(wlr_output)) { return false; } - wlr_presentation_surface_sampled_on_output(server.presentation, surface, + wlr_output_state_set_buffer(pending, &surface->buffer->base); + + if (!wlr_output_test_state(wlr_output, pending)) { + return false; + } + + wlr_presentation_surface_scanned_out_on_output(server.presentation, surface, wlr_output); - return wlr_output_commit(wlr_output); + return wlr_output_commit_state(wlr_output, pending); +} + +static void get_frame_damage(struct sway_output *output, + pixman_region32_t *frame_damage) { + struct wlr_output *wlr_output = output->wlr_output; + + int width, height; + wlr_output_transformed_resolution(wlr_output, &width, &height); + + pixman_region32_init(frame_damage); + + enum wl_output_transform transform = + wlr_output_transform_invert(wlr_output->transform); + wlr_region_transform(frame_damage, &output->damage_ring.current, + transform, width, height); + + if (debug.damage != DAMAGE_DEFAULT) { + pixman_region32_union_rect(frame_damage, frame_damage, + 0, 0, wlr_output->width, wlr_output->height); + } } static int output_repaint_timer_handler(void *data) { struct sway_output *output = data; - if (output->wlr_output == NULL) { + struct wlr_output *wlr_output = output->wlr_output; + if (wlr_output == NULL) { return 0; } - output->wlr_output->frame_pending = false; + wlr_output->frame_pending = false; + + if (!wlr_output->needs_frame && + !output->gamma_lut_changed && + !pixman_region32_not_empty(&output->damage_ring.current)) { + return 0; + } struct sway_workspace *workspace = output->current.active_workspace; if (workspace == NULL) { @@ -539,13 +593,33 @@ static int output_repaint_timer_handler(void *data) { fullscreen_con = workspace->current.fullscreen; } + struct wlr_output_state pending = {0}; + + if (output->gamma_lut_changed) { + output->gamma_lut_changed = false; + struct wlr_gamma_control_v1 *gamma_control = + wlr_gamma_control_manager_v1_get_control( + server.gamma_control_manager_v1, wlr_output); + if (!wlr_gamma_control_v1_apply(gamma_control, &pending)) { + goto out; + } + if (!wlr_output_test_state(wlr_output, &pending)) { + wlr_output_state_finish(&pending); + pending = (struct wlr_output_state){0}; + wlr_gamma_control_v1_send_failed_and_destroy(gamma_control); + } + } + + pending.committed |= WLR_OUTPUT_STATE_DAMAGE; + get_frame_damage(output, &pending.damage); + if (fullscreen_con && fullscreen_con->view && !debug.noscanout // Only output to monitor without compositing when saturation is changed && fullscreen_con->saturation == 1.0f) { // Try to scan-out the fullscreen view static bool last_scanned_out = false; bool scanned_out = - scan_out_fullscreen_view(output, fullscreen_con->view); + scan_out_fullscreen_view(output, &pending, fullscreen_con->view); if (scanned_out && !last_scanned_out) { sway_log(SWAY_DEBUG, "Scanning out fullscreen view on %s", @@ -559,32 +633,72 @@ static int output_repaint_timer_handler(void *data) { last_scanned_out = scanned_out; if (scanned_out) { - return 0; + goto out; } } + if (!wlr_output_configure_primary_swapchain(wlr_output, &pending, &wlr_output->swapchain)) { + goto out; + } + int buffer_age; - if (!wlr_output_attach_render(output->wlr_output, &buffer_age)) { - return 0; + struct wlr_buffer *buffer = wlr_swapchain_acquire(wlr_output->swapchain, &buffer_age); + if (buffer == NULL) { + goto out; + } + + struct fx_gles_render_pass *render_pass = fx_renderer_begin_buffer_pass( + wlr_output->renderer, buffer, wlr_output, + &(struct wlr_buffer_pass_options) { + .timer = NULL, + } + ); + if (render_pass == NULL) { + wlr_buffer_unlock(buffer); + goto out; } 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; + + if (debug.damage == DAMAGE_RERENDER) { + int width, height; + wlr_output_transformed_resolution(wlr_output, &width, &height); + pixman_region32_union_rect(&damage, &damage, 0, 0, width, height); } + struct fx_render_context ctx = { + .output_damage = &damage, + .renderer = wlr_output->renderer, + .output = output, + .pass = render_pass, + }; + struct timespec now; clock_gettime(CLOCK_MONOTONIC, &now); - output_render(output, &now, &damage); + output_render(&ctx); pixman_region32_fini(&damage); + if (!wlr_render_pass_submit(&render_pass->base)) { + wlr_buffer_unlock(buffer); + goto out; + } + + wlr_output_state_set_buffer(&pending, buffer); + wlr_buffer_unlock(buffer); + + if (!wlr_output_commit_state(wlr_output, &pending)) { + goto out; + } + + wlr_damage_ring_rotate(&output->damage_ring); + output->last_frame = now; + +out: + wlr_output_state_finish(&pending); return 0; } @@ -610,9 +724,7 @@ static void handle_frame(struct wl_listener *listener, void *user_data) { if (output->max_render_time != 0) { struct timespec now; - clockid_t presentation_clock - = wlr_backend_get_presentation_clock(server.backend); - clock_gettime(presentation_clock, &now); + clock_gettime(CLOCK_MONOTONIC, &now); const long NSEC_IN_SECONDS = 1000000000; struct timespec predicted_refresh = output->last_presentation; @@ -690,14 +802,13 @@ static void damage_surface_iterator(struct sway_output *output, pixman_region32_init(&damage); wlr_surface_get_effective_damage(surface, &damage); wlr_region_scale(&damage, &damage, output->wlr_output->scale); - if (ceil(output->wlr_output->scale) > surface->current.scale) { + if (ceilf(output->wlr_output->scale) > surface->current.scale) { // When scaling up a surface, it'll become blurry so we need to // expand the damage region wlr_region_expand(&damage, &damage, - ceil(output->wlr_output->scale) - surface->current.scale); + ceilf(output->wlr_output->scale) - surface->current.scale); } pixman_region32_translate(&damage, box.x, box.y); - if (wlr_damage_ring_add(&output->damage_ring, &damage)) { wlr_output_schedule_frame(output->wlr_output); } @@ -754,19 +865,33 @@ static void damage_child_views_iterator(struct sway_container *con, void output_damage_whole_container(struct sway_output *output, struct sway_container *con) { - int shadow_sigma = con->shadow_enabled ? config->shadow_blur_sigma : 0; - // Pad the box by 1px, because the width is a double and might be a fraction struct wlr_box box = { - .x = con->current.x - output->lx - 1 - shadow_sigma + config->shadow_offset_x, - .y = con->current.y - output->ly - 1 - shadow_sigma + config->shadow_offset_y, - .width = con->current.width + 2 + shadow_sigma * 2, - .height = con->current.height + 2 + shadow_sigma * 2, + .x = con->current.x - output->lx - 1, + .y = con->current.y - output->ly - 1, + .width = con->current.width + 2, + .height = con->current.height + 2, }; scale_box(&box, output->wlr_output->scale); if (wlr_damage_ring_add_box(&output->damage_ring, &box)) { wlr_output_schedule_frame(output->wlr_output); } + + // Shadow damage + if (con->shadow_enabled && config_should_parameters_shadow()) { + const int shadow_sigma = config->shadow_blur_sigma; + struct wlr_box shadow_box = { + .x = con->current.x - output->lx - 1 - shadow_sigma + config->shadow_offset_x, + .y = con->current.y - output->ly - 1 - shadow_sigma + config->shadow_offset_y, + .width = con->current.width + 2 + (shadow_sigma * 2), + .height = con->current.height + 2 + (shadow_sigma * 2), + }; + scale_box(&shadow_box, output->wlr_output->scale); + if (wlr_damage_ring_add_box(&output->damage_ring, &shadow_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); @@ -790,38 +915,35 @@ static void update_output_manager_config(struct sway_server *server) { 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 (!wlr_box_empty(&output_box)) { - config_head->state.x = output_box.x; - config_head->state.y = output_box.y; - } + config_head->state.enabled = !wlr_box_empty(&output_box); + config_head->state.x = output_box.x; + config_head->state.y = output_box.y; } wlr_output_manager_v1_set_configuration(server->output_manager_v1, config); + + ipc_event_output(); } -static void handle_destroy(struct wl_listener *listener, void *data) { - struct sway_output *output = wl_container_of(listener, output, destroy); +static void begin_destroy(struct sway_output *output) { struct sway_server *server = output->server; if (output->enabled) { output_disable(output); } - fx_renderer_fini(output->renderer); - output_begin_destroy(output); wl_list_remove(&output->link); + wl_list_remove(&output->layout_destroy.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); + wl_list_remove(&output->request_state.link); wlr_damage_ring_finish(&output->damage_ring); @@ -833,35 +955,14 @@ static void handle_destroy(struct wl_listener *listener, void *data) { update_output_manager_config(server); } -static void handle_mode(struct wl_listener *listener, void *data) { - struct sway_output *output = wl_container_of(listener, output, mode); - if (!output->enabled && !output->enabling) { - struct output_config *oc = find_output_config(output); - if (output->wlr_output->current_mode != NULL && - (!oc || oc->enabled)) { - // We want to enable this output, but it didn't work last time, - // possibly because we hadn't enough CRTCs. Try again now that the - // output has a mode. - sway_log(SWAY_DEBUG, "Output %s has gained a CRTC, " - "trying to enable it", output->wlr_output->name); - apply_output_config(oc, output); - } - return; - } - if (!output->enabled) { - return; - } +static void handle_destroy(struct wl_listener *listener, void *data) { + struct sway_output *output = wl_container_of(listener, output, destroy); + begin_destroy(output); +} - 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); +static void handle_layout_destroy(struct wl_listener *listener, void *data) { + struct sway_output *output = wl_container_of(listener, output, layout_destroy); + begin_destroy(output); } static void update_textures(struct sway_container *con, void *data) { @@ -869,6 +970,12 @@ static void update_textures(struct sway_container *con, void *data) { container_update_marks_textures(con); } +static void update_output_scale_iterator(struct sway_output *output, + struct sway_view *view, struct wlr_surface *surface, + struct wlr_box *box, void *user_data) { + surface_update_outputs(surface); +} + static void handle_commit(struct wl_listener *listener, void *data) { struct sway_output *output = wl_container_of(listener, output, commit); struct wlr_output_event_commit *event = data; @@ -877,11 +984,20 @@ static void handle_commit(struct wl_listener *listener, void *data) { return; } - if (event->committed & WLR_OUTPUT_STATE_SCALE) { + if (event->state->committed & WLR_OUTPUT_STATE_SCALE) { output_for_each_container(output, update_textures, NULL); + output_for_each_surface(output, update_output_scale_iterator, NULL); } - if (event->committed & (WLR_OUTPUT_STATE_TRANSFORM | WLR_OUTPUT_STATE_SCALE)) { + if (event->state->committed & ( + WLR_OUTPUT_STATE_MODE | + WLR_OUTPUT_STATE_TRANSFORM | + WLR_OUTPUT_STATE_SCALE)) { + // Mark optimized blur as dirty + struct fx_effect_framebuffers *effect_fbos = + fx_effect_framebuffers_try_get(output->wlr_output); + effect_fbos->blur_buffer_dirty = true; + arrange_layers(output); arrange_output(output); transaction_commit_dirty(); @@ -889,12 +1005,19 @@ 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)) { + if (event->state->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); } + + // Next time the output is enabled, try to re-apply the gamma LUT + if ((event->state->committed & WLR_OUTPUT_STATE_ENABLED) && !output->wlr_output->enabled) { + output->gamma_lut_changed = true; + } } static void handle_present(struct wl_listener *listener, void *data) { @@ -909,6 +1032,13 @@ static void handle_present(struct wl_listener *listener, void *data) { output->refresh_nsec = output_event->refresh; } +static void handle_request_state(struct wl_listener *listener, void *data) { + struct sway_output *output = + wl_container_of(listener, output, request_state); + const struct wlr_output_event_request_state *event = data; + wlr_output_commit_state(output->wlr_output, event->state); +} + static unsigned int last_headless_num = 0; void handle_new_output(struct wl_listener *listener, void *data) { @@ -931,16 +1061,18 @@ 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 WLR_HAS_DRM_BACKEND if (server->drm_lease_manager) { wlr_drm_lease_v1_manager_offer_output(server->drm_lease_manager, wlr_output); } +#endif list_add(root->non_desktop_outputs, non_desktop); return; } if (!wlr_output_init_render(wlr_output, server->allocator, - server->wlr_renderer)) { + server->renderer)) { sway_log(SWAY_ERROR, "Failed to init output render"); return; } @@ -952,20 +1084,12 @@ void handle_new_output(struct wl_listener *listener, void *data) { output->server = server; wlr_damage_ring_init(&output->damage_ring); - // Init FX Renderer - struct wlr_egl *egl = wlr_gles2_renderer_get_egl(server->wlr_renderer); - output->renderer = fx_renderer_create(egl, wlr_output); - if (!output->renderer) { - sway_log(SWAY_ERROR, "Failed to create fx_renderer"); - abort(); - } - + wl_signal_add(&root->output_layout->events.destroy, &output->layout_destroy); + output->layout_destroy.notify = handle_layout_destroy; wl_signal_add(&wlr_output->events.destroy, &output->destroy); output->destroy.notify = handle_destroy; wl_signal_add(&wlr_output->events.commit, &output->commit); output->commit.notify = handle_commit; - wl_signal_add(&wlr_output->events.mode, &output->mode); - output->mode.notify = handle_mode; wl_signal_add(&wlr_output->events.present, &output->present); output->present.notify = handle_present; wl_signal_add(&wlr_output->events.damage, &output->damage); @@ -974,6 +1098,8 @@ void handle_new_output(struct wl_listener *listener, void *data) { output->frame.notify = handle_frame; wl_signal_add(&wlr_output->events.needs_frame, &output->needs_frame); output->needs_frame.notify = handle_needs_frame; + wl_signal_add(&wlr_output->events.request_state, &output->request_state); + output->request_state.notify = handle_request_state; output->repaint_timer = wl_event_loop_add_timer(server->wl_event_loop, output_repaint_timer_handler, output); @@ -997,6 +1123,21 @@ void handle_output_layout_change(struct wl_listener *listener, update_output_manager_config(server); } +void handle_gamma_control_set_gamma(struct wl_listener *listener, void *data) { + struct sway_server *server = + wl_container_of(listener, server, gamma_control_set_gamma); + const struct wlr_gamma_control_manager_v1_set_gamma_event *event = data; + + struct sway_output *output = event->output->data; + + if(!output) { + return; + } + + output->gamma_lut_changed = true; + wlr_output_schedule_frame(output->wlr_output); +} + static void output_manager_apply(struct sway_server *server, struct wlr_output_configuration_v1 *config, bool test_only) { // TODO: perform atomic tests on the whole backend atomically diff --git a/sway/desktop/render.c b/sway/desktop/render.c index 429924c2..cbf99177 100644 --- a/sway/desktop/render.c +++ b/sway/desktop/render.c @@ -1,28 +1,25 @@ -#include -#include -#include +#define _POSIX_C_SOURCE 200809L +#include +#include +#include #include #include #include #include -#include +#include #include #include -#include #include #include -#include #include -#include +#include +#include #include - #include "config.h" -#include "log.h" #include "sway/config.h" -#include "sway/desktop/fx_renderer/fx_framebuffer.h" -#include "sway/desktop/fx_renderer/fx_renderer.h" #include "sway/input/input-manager.h" #include "sway/input/seat.h" +#include "sway/layers.h" #include "sway/output.h" #include "sway/server.h" #include "sway/tree/arrange.h" @@ -31,6 +28,56 @@ #include "sway/tree/view.h" #include "sway/tree/workspace.h" +static void transform_output_damage(pixman_region32_t *damage, struct wlr_output *output) { + int ow, oh; + wlr_output_transformed_resolution(output, &ow, &oh); + enum wl_output_transform transform = + wlr_output_transform_invert(output->transform); + wlr_region_transform(damage, damage, transform, ow, oh); +} + +static void transform_output_box(struct wlr_box *box, struct wlr_output *output) { + int ow, oh; + wlr_output_transformed_resolution(output, &ow, &oh); + enum wl_output_transform transform = + wlr_output_transform_invert(output->transform); + wlr_box_transform(box, box, transform, ow, oh); +} + +// TODO: Remove this ugly abomination with a complete border rework... +static void transform_corner_location(enum corner_location *corner_location, struct wlr_output *output) { + if (*corner_location == ALL) { + return; + } + switch (wlr_output_transform_invert(output->transform)) { + case WL_OUTPUT_TRANSFORM_NORMAL: + return; + case WL_OUTPUT_TRANSFORM_90: + *corner_location = (*corner_location + 1) % 4; + return; + case WL_OUTPUT_TRANSFORM_180: + *corner_location = (*corner_location + 2) % 4; + return; + case WL_OUTPUT_TRANSFORM_270: + *corner_location = (*corner_location + 3) % 4; + return; + case WL_OUTPUT_TRANSFORM_FLIPPED: + *corner_location = (*corner_location + (1 - 2 * (*corner_location % 2))) % 4; + return; + case WL_OUTPUT_TRANSFORM_FLIPPED_90: + *corner_location = (*corner_location + (4 - 2 * (*corner_location % 2))) % 4; + return; + case WL_OUTPUT_TRANSFORM_FLIPPED_180: + *corner_location = (*corner_location + (3 - 2 * (*corner_location % 2))) % 4; + return; + case WL_OUTPUT_TRANSFORM_FLIPPED_270: + *corner_location = (*corner_location + (2 - 2 * (*corner_location % 2))) % 4; + return; + default: + return; + } +} + struct decoration_data get_undecorated_decoration_data() { return (struct decoration_data) { .alpha = 1.0f, @@ -45,33 +92,6 @@ struct decoration_data get_undecorated_decoration_data() { }; } -// TODO: Remove this ugly abomination with a complete border rework... -enum corner_location get_rotated_corner(enum corner_location corner_location, - enum wl_output_transform transform) { - if (corner_location == ALL || corner_location == NONE) { - return corner_location; - } - switch (transform) { - case WL_OUTPUT_TRANSFORM_NORMAL: - return corner_location; - case WL_OUTPUT_TRANSFORM_90: - return (corner_location + 1) % 4; - case WL_OUTPUT_TRANSFORM_180: - return (corner_location + 2) % 4; - case WL_OUTPUT_TRANSFORM_270: - return (corner_location + 3) % 4; - case WL_OUTPUT_TRANSFORM_FLIPPED: - return (corner_location + (1 - 2 * (corner_location % 2))) % 4; - case WL_OUTPUT_TRANSFORM_FLIPPED_90: - return (corner_location + (4 - 2 * (corner_location % 2))) % 4; - case WL_OUTPUT_TRANSFORM_FLIPPED_180: - return (corner_location + (3 - 2 * (corner_location % 2))) % 4; - case WL_OUTPUT_TRANSFORM_FLIPPED_270: - return (corner_location + (2 - 2 * (corner_location % 2))) % 4; - } - return corner_location; -} - /** * Apply scale to a width or height. * @@ -85,380 +105,260 @@ enum corner_location get_rotated_corner(enum corner_location corner_location, * scaled to 2px. */ static int scale_length(int length, int offset, float scale) { - return round((offset + length) * scale) - round(offset * scale); + return roundf((offset + length) * scale) - roundf(offset * scale); } -static void scissor_output(struct wlr_output *wlr_output, - pixman_box32_t *rect) { - struct sway_output *output = wlr_output->data; - struct fx_renderer *renderer = output->renderer; - assert(renderer); - - struct wlr_box box = { - .x = rect->x1, - .y = rect->y1, - .width = rect->x2 - rect->x1, - .height = rect->y2 - rect->y1, - }; - - int ow, oh; - wlr_output_transformed_resolution(wlr_output, &ow, &oh); - - enum wl_output_transform transform = wlr_output_transform_invert(wlr_output->transform); - wlr_box_transform(&box, &box, transform, ow, oh); - - fx_renderer_scissor(&box); -} - -static void set_scale_filter(struct wlr_output *wlr_output, - struct fx_texture *texture, enum scale_filter_mode scale_filter) { - glBindTexture(texture->target, texture->id); - - switch (scale_filter) { +static enum wlr_scale_filter_mode get_scale_filter(struct sway_output *output) { + switch (output->scale_filter) { case SCALE_FILTER_LINEAR: - glTexParameteri(texture->target, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - break; + return WLR_SCALE_FILTER_BILINEAR; case SCALE_FILTER_NEAREST: - glTexParameteri(texture->target, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - break; - case SCALE_FILTER_DEFAULT: - case SCALE_FILTER_SMART: - assert(false); // unreachable + return WLR_SCALE_FILTER_NEAREST; + default: + abort(); // unreachable } } -pixman_region32_t create_damage(const struct wlr_box damage_box, pixman_region32_t *output_damage) { +static void render_texture(struct fx_render_context *ctx, struct wlr_texture *texture, + const struct wlr_fbox *_src_box, const struct wlr_box *dst_box, + const struct wlr_box *_clip_box, enum wl_output_transform transform, + struct decoration_data deco_data) { + struct sway_output *output = ctx->output; + + struct wlr_box proj_box = *dst_box; + + struct wlr_fbox src_box = {0}; + if (_src_box) { + src_box = *_src_box; + } + pixman_region32_t damage; - pixman_region32_init(&damage); - pixman_region32_union_rect(&damage, &damage, damage_box.x, damage_box.y, - damage_box.width, damage_box.height); - pixman_region32_intersect(&damage, &damage, output_damage); - return damage; -} + pixman_region32_init_rect(&damage, proj_box.x, proj_box.y, + proj_box.width, proj_box.height); + pixman_region32_intersect(&damage, &damage, ctx->output_damage); -struct wlr_box get_monitor_box(struct wlr_output *output) { - int width, height; - wlr_output_transformed_resolution(output, &width, &height); - struct wlr_box monitor_box = { 0, 0, width, height }; - return monitor_box; -} + struct wlr_box clip_box = {0}; + if (_clip_box) { + pixman_region32_intersect_rect(&damage, &damage, + _clip_box->x, _clip_box->y, _clip_box->width, _clip_box->height); -static void render_texture(struct wlr_output *wlr_output, - pixman_region32_t *output_damage, struct fx_texture *texture, - const struct wlr_fbox *src_box, const struct wlr_box *dst_box, - const float matrix[static 9], struct decoration_data deco_data) { - struct sway_output *output = wlr_output->data; - struct fx_renderer *renderer = output->renderer; + clip_box = *_clip_box; + } - pixman_region32_t damage = create_damage(*dst_box, output_damage); bool damaged = pixman_region32_not_empty(&damage); if (!damaged) { goto damage_finish; } - // ensure the box is updated as per the output orientation - struct wlr_box transformed_box; - int width, height; - wlr_output_transformed_resolution(wlr_output, &width, &height); - wlr_box_transform(&transformed_box, dst_box, - wlr_output_transform_invert(wlr_output->transform), width, height); + transform_output_box(&proj_box, output->wlr_output); + transform_output_box(&clip_box, output->wlr_output); + transform_output_damage(&damage, output->wlr_output); + transform = wlr_output_transform_compose(transform, output->wlr_output->transform); - int nrects; - pixman_box32_t *rects = pixman_region32_rectangles(&damage, &nrects); - for (int i = 0; i < nrects; ++i) { - scissor_output(wlr_output, &rects[i]); - set_scale_filter(wlr_output, texture, output->scale_filter); - if (src_box != NULL) { - fx_render_subtexture_with_matrix(renderer, texture, src_box, &transformed_box, - matrix, deco_data); - } else { - fx_render_texture_with_matrix(renderer, texture, &transformed_box, matrix, deco_data); + fx_render_pass_add_texture(ctx->pass, &(struct fx_render_texture_options) { + .base = { + .texture = texture, + .src_box = src_box, + .dst_box = proj_box, + .transform = transform, + .alpha = &deco_data.alpha, + .clip = &damage, + .filter_mode = get_scale_filter(output), + }, + .clip_box = &clip_box, + .corner_radius = deco_data.corner_radius, + .has_titlebar = deco_data.has_titlebar, + .dim = deco_data.dim, + .dim_color = { + .r = deco_data.dim_color[0], + .g = deco_data.dim_color[1], + .b = deco_data.dim_color[2], + .a = deco_data.dim_color[3], } - } + }); damage_finish: pixman_region32_fini(&damage); } -/* Renders the blur for each damaged rect and swaps the buffer */ -void render_blur_segments(struct fx_renderer *renderer, - const float matrix[static 9], pixman_region32_t* damage, - struct fx_framebuffer **buffer, struct blur_shader* shader, - const struct wlr_box *box, int blur_radius) { - if (*buffer == &renderer->effects_buffer) { - fx_framebuffer_bind(&renderer->effects_buffer_swapped); - } else { - fx_framebuffer_bind(&renderer->effects_buffer); - } +void render_blur(struct fx_render_context *ctx, struct wlr_texture *texture, + const struct wlr_fbox *src_box, const struct wlr_box *dst_box, + bool optimized_blur, pixman_region32_t *opaque_region, + struct decoration_data deco_data) { + struct sway_output *output = ctx->output; - if (pixman_region32_not_empty(damage)) { - int nrects; - pixman_box32_t *rects = pixman_region32_rectangles(damage, &nrects); - for (int i = 0; i < nrects; ++i) { - const pixman_box32_t box = rects[i]; - struct wlr_box new_box = { box.x1, box.y1, box.x2 - box.x1, box.y2 - box.y1 }; - fx_renderer_scissor(&new_box); - fx_render_blur(renderer, matrix, buffer, shader, &new_box, blur_radius); - } - } - - if (*buffer != &renderer->effects_buffer) { - *buffer = &renderer->effects_buffer; - } else { - *buffer = &renderer->effects_buffer_swapped; - } -} - -// Blurs the main_buffer content and returns the blurred framebuffer -struct fx_framebuffer *get_main_buffer_blur(struct fx_renderer *renderer, struct sway_output *output, - pixman_region32_t *original_damage, const struct wlr_box *box) { - struct wlr_output *wlr_output = output->wlr_output; - struct wlr_box monitor_box = get_monitor_box(wlr_output); - - const enum wl_output_transform transform = wlr_output_transform_invert(wlr_output->transform); - float matrix[9]; - wlr_matrix_project_box(matrix, &monitor_box, transform, 0, wlr_output->transform_matrix); - - float gl_matrix[9]; - wlr_matrix_multiply(gl_matrix, renderer->projection, matrix); + struct wlr_box proj_box = *dst_box; pixman_region32_t damage; - pixman_region32_init(&damage); - pixman_region32_copy(&damage, original_damage); - wlr_region_transform(&damage, &damage, transform, monitor_box.width, monitor_box.height); - - wlr_region_expand(&damage, &damage, config_get_blur_size()); - - // Initially blur main_buffer content into the effects_buffers - struct fx_framebuffer *current_buffer = &renderer->wlr_buffer; - - // Bind to blur framebuffer - fx_framebuffer_bind(&renderer->effects_buffer); - glBindTexture(renderer->wlr_buffer.texture.target, renderer->wlr_buffer.texture.id); - - // damage region will be scaled, make a temp - pixman_region32_t tempDamage; - pixman_region32_init(&tempDamage); - - int blur_radius = config->blur_params.radius; - int blur_passes = config->blur_params.num_passes; - - // Downscale - for (int i = 0; i < blur_passes; ++i) { - wlr_region_scale(&tempDamage, &damage, 1.0f / (1 << (i + 1))); - render_blur_segments(renderer, gl_matrix, &tempDamage, ¤t_buffer, - &renderer->shaders.blur1, box, blur_radius); - } - - // Upscale - for (int i = blur_passes - 1; i >= 0; --i) { - // when upsampling we make the region twice as big - wlr_region_scale(&tempDamage, &damage, 1.0f / (1 << i)); - render_blur_segments(renderer, gl_matrix, &tempDamage, ¤t_buffer, - &renderer->shaders.blur2, box, blur_radius); - } - - float blur_noise = config->blur_params.noise; - float blur_brightness = config->blur_params.brightness; - float blur_contrast = config->blur_params.contrast; - float blur_saturation = config->blur_params.saturation; - - // Render additional blur effects like saturation, noise, contrast, etc... - if (config_should_parameters_blur_effects() && pixman_region32_not_empty(&damage)) { - if (current_buffer == &renderer->effects_buffer) { - fx_framebuffer_bind(&renderer->effects_buffer_swapped); - } else { - fx_framebuffer_bind(&renderer->effects_buffer); - } - int nrects; - pixman_box32_t *rects = pixman_region32_rectangles(&damage, &nrects); - for (int i = 0; i < nrects; ++i) { - const pixman_box32_t box = rects[i]; - struct wlr_box new_box = { box.x1, box.y1, box.x2 - box.x1, box.y2 - box.y1 }; - fx_renderer_scissor(&new_box); - fx_render_blur_effects(renderer, gl_matrix, ¤t_buffer, blur_noise, - blur_brightness, blur_contrast, blur_saturation); - } - if (current_buffer != &renderer->effects_buffer) { - current_buffer = &renderer->effects_buffer; - } else { - current_buffer = &renderer->effects_buffer_swapped; - } - } - - pixman_region32_fini(&tempDamage); - pixman_region32_fini(&damage); - - // Bind back to the default buffer - fx_framebuffer_bind(&renderer->wlr_buffer); - - return current_buffer; -} - -void render_blur(bool optimized, struct sway_output *output, - pixman_region32_t *output_damage, const struct wlr_box *dst_box, - pixman_region32_t *opaque_region, struct decoration_data *deco_data, - struct blur_stencil_data *stencil_data) { - struct wlr_output *wlr_output = output->wlr_output; - struct fx_renderer *renderer = output->renderer; - - // Check if damage is inside of box rect - pixman_region32_t damage = create_damage(*dst_box, output_damage); - - pixman_region32_t translucent_region; - pixman_region32_init(&translucent_region); + pixman_region32_init_rect(&damage, proj_box.x, proj_box.y, + proj_box.width, proj_box.height); + pixman_region32_intersect(&damage, &damage, ctx->output_damage); if (!pixman_region32_not_empty(&damage)) { goto damage_finish; } - // Gets the translucent region - pixman_box32_t surface_box = { 0, 0, dst_box->width, dst_box->height }; - pixman_region32_copy(&translucent_region, opaque_region); - pixman_region32_inverse(&translucent_region, &translucent_region, &surface_box); - if (!pixman_region32_not_empty(&translucent_region)) { - goto damage_finish; - } + transform_output_box(&proj_box, output->wlr_output); + transform_output_damage(&damage, output->wlr_output); - struct fx_framebuffer *buffer = &renderer->blur_buffer; - if (!buffer->texture.id || !optimized) { - pixman_region32_translate(&translucent_region, dst_box->x, dst_box->y); - pixman_region32_intersect(&translucent_region, &translucent_region, &damage); - - // Render the blur into its own buffer - buffer = get_main_buffer_blur(renderer, output, &translucent_region, dst_box); - } - - // Get a stencil of the window ignoring transparent regions - if (deco_data->discard_transparent && stencil_data) { - fx_renderer_scissor(NULL); - fx_renderer_stencil_mask_init(); - - render_texture(wlr_output, output_damage, stencil_data->stencil_texture, stencil_data->stencil_src_box, - dst_box, stencil_data->stencil_matrix, *deco_data); - - fx_renderer_stencil_mask_close(true); - } - - // Draw the blurred texture - struct wlr_box monitor_box = get_monitor_box(wlr_output); - enum wl_output_transform transform = wlr_output_transform_invert(wlr_output->transform); - float matrix[9]; - wlr_matrix_project_box(matrix, &monitor_box, transform, 0.0, wlr_output->transform_matrix); - - struct decoration_data blur_deco_data = get_undecorated_decoration_data(); - blur_deco_data.corner_radius = deco_data->corner_radius; - blur_deco_data.has_titlebar = deco_data->has_titlebar; - render_texture(wlr_output, &damage, &buffer->texture, NULL, dst_box, matrix, blur_deco_data); - - // Finish stenciling - if (deco_data->discard_transparent && stencil_data) { - fx_renderer_stencil_mask_fini(); - } + struct fx_render_blur_pass_options blur_options = { + .tex_options = { + .base = { + .texture = texture, + .src_box = *src_box, + .dst_box = proj_box, + .transform = WL_OUTPUT_TRANSFORM_NORMAL, + .alpha = &deco_data.alpha, + .clip = &damage, + .filter_mode = WLR_SCALE_FILTER_BILINEAR, + }, + .clip_box = &proj_box, + .corner_radius = deco_data.corner_radius, + .discard_transparent = false, + }, + .opaque_region = opaque_region, + .use_optimized_blur = optimized_blur, + .blur_data = &config->blur_params, + .ignore_transparent = deco_data.discard_transparent, + }; + // Render the actual blur behind the surface + fx_render_pass_add_blur(ctx->pass, &blur_options); damage_finish: pixman_region32_fini(&damage); - pixman_region32_fini(&translucent_region); } // _box.x and .y are expected to be layout-local // _box.width and .height are expected to be output-buffer-local -void render_box_shadow(struct sway_output *output, pixman_region32_t *output_damage, - const struct wlr_box *_box, const float color[static 4], float blur_sigma, - float corner_radius, float offset_x, float offset_y) { - struct wlr_output *wlr_output = output->wlr_output; - struct fx_renderer *renderer = output->renderer; +void render_box_shadow(struct fx_render_context *ctx, const struct wlr_box *_box, + const float color[static 4], float blur_sigma, float corner_radius, + float offset_x, float offset_y) { + struct wlr_output *wlr_output = ctx->output->wlr_output; struct wlr_box box; memcpy(&box, _box, sizeof(struct wlr_box)); - box.x -= blur_sigma - offset_x; - box.y -= blur_sigma - offset_y; - box.width += 2 * blur_sigma; - box.height += 2 * blur_sigma; - pixman_region32_t damage = create_damage(box, output_damage); + // Extend the size of the box while also considering the shadow offset + struct wlr_box shadow_box; + memcpy(&shadow_box, _box, sizeof(struct wlr_box)); + shadow_box.x -= blur_sigma - offset_x; + shadow_box.y -= blur_sigma - offset_y; + shadow_box.width += blur_sigma * 2; + shadow_box.height += blur_sigma * 2; - // don't damage area behind window since we dont render it anyway - struct wlr_box inner_box; - memcpy(&inner_box, _box, sizeof(struct wlr_box)); - inner_box.x += corner_radius; - inner_box.y += corner_radius; - inner_box.width -= 2 * corner_radius; - inner_box.height -= 2 * corner_radius; - pixman_region32_t inner_damage = create_damage(inner_box, output_damage); - pixman_region32_subtract(&damage, &damage, &inner_damage); - pixman_region32_fini(&inner_damage); - - bool damaged = pixman_region32_not_empty(&damage); - if (!damaged) { + pixman_region32_t damage; + pixman_region32_init_rect(&damage, shadow_box.x, shadow_box.y, + shadow_box.width, shadow_box.height); + pixman_region32_intersect(&damage, &damage, ctx->output_damage); + if (!pixman_region32_not_empty(&damage)) { goto damage_finish; } - float matrix[9]; - wlr_matrix_project_box(matrix, &box, WL_OUTPUT_TRANSFORM_NORMAL, 0, - wlr_output->transform_matrix); + transform_output_damage(&damage, wlr_output); + transform_output_box(&box, wlr_output); + transform_output_box(&shadow_box, wlr_output); - int width, height; - wlr_output_transformed_resolution(wlr_output, &width, &height); - enum wl_output_transform transform = wlr_output_transform_invert(wlr_output->transform); - // ensure the shadow_box is updated as per the output orientation - struct wlr_box transformed_shadow_box; - wlr_box_transform(&transformed_shadow_box, &box, transform, width, height); - // ensure the box is updated as per the output orientation - struct wlr_box transformed_box; - wlr_box_transform(&transformed_box, _box, transform, width, height); + struct shadow_data shadow_data = { + .enabled = true, + .offset_x = offset_x, + .offset_y = offset_y, + .color = { + .r = color[0], + .g = color[1], + .b = color[2], + .a = color[3], + }, + .blur_sigma = blur_sigma, + }; + struct fx_render_box_shadow_options shadow_options = { + .shadow_box = shadow_box, + .clip_box = box, + .clip = &damage, + .shadow_data = &shadow_data, + .corner_radius = corner_radius, + }; + fx_render_pass_add_box_shadow(ctx->pass, &shadow_options); - int nrects; - pixman_box32_t *rects = pixman_region32_rectangles(&damage, &nrects); - for (int i = 0; i < nrects; ++i) { - scissor_output(wlr_output, &rects[i]); +damage_finish: + pixman_region32_fini(&damage); +} - fx_render_box_shadow(renderer, &transformed_shadow_box, &transformed_box, - color, matrix, corner_radius, blur_sigma); +// _box.x and .y are expected to be layout-local +// _box.width and .height are expected to be output-buffer-local +void render_rounded_border_corner(struct fx_render_context *ctx, const struct wlr_box *_box, + const float color[static 4], int corner_radius, int border_thickness, + enum corner_location location) { + struct wlr_output *wlr_output = ctx->output->wlr_output; + + struct wlr_box box = *_box; + const int size = MAX(box.width, box.height); + box.width = size; + box.height = size; + box.x -= ctx->output->lx * wlr_output->scale; + box.y -= ctx->output->ly * wlr_output->scale; + + pixman_region32_t damage; + pixman_region32_init_rect(&damage, box.x, box.y, box.width, box.height); + pixman_region32_intersect(&damage, &damage, ctx->output_damage); + if (!pixman_region32_not_empty(&damage)) { + goto damage_finish; } + transform_output_damage(&damage, wlr_output); + transform_output_box(&box, wlr_output); + transform_corner_location(&location, wlr_output); + + struct fx_render_rounded_border_corner_options border_corner_options = { + .base = { + .box = box, + .color = { + .r = color[0], + .g = color[1], + .b = color[2], + .a = color[3], + }, + .clip = &damage, // Render with the original extended clip region + }, + .corner_radius = corner_radius, + .border_thickness = border_thickness, + .corner_location = location + }; + fx_render_pass_add_rounded_border_corner(ctx->pass, &border_corner_options); + damage_finish: pixman_region32_fini(&damage); } static void render_surface_iterator(struct sway_output *output, - struct sway_view *view, struct wlr_surface *surface, + struct sway_view *_view, struct wlr_surface *surface, struct wlr_box *_box, void *_data) { struct render_data *data = _data; struct wlr_output *wlr_output = output->wlr_output; - pixman_region32_t *output_damage = data->damage; struct wlr_texture *texture = wlr_surface_get_texture(surface); if (!texture) { return; } - struct wlr_box proj_box = *_box; - - scale_box(&proj_box, wlr_output->scale); - - float matrix[9]; - enum wl_output_transform transform = wlr_output_transform_invert(surface->current.transform); - wlr_matrix_project_box(matrix, &proj_box, transform, 0.0, wlr_output->transform_matrix); + struct wlr_fbox src_box; + wlr_surface_get_buffer_source_box(surface, &src_box); struct wlr_box dst_box = *_box; - struct wlr_box *clip_box = data->clip_box; - if (clip_box != NULL) { - dst_box.width = fmin(dst_box.width, clip_box->width); - dst_box.height = fmin(dst_box.height, clip_box->height); - dst_box.x = fmax(dst_box.x, clip_box->x); - dst_box.y = fmax(dst_box.y, clip_box->y); - } + struct wlr_box clip_box = *_box; + if (data->clip_box != NULL) { + clip_box.width = fmin(dst_box.width, data->clip_box->width); + clip_box.height = fmin(dst_box.height, data->clip_box->height); + clip_box.x = fmax(dst_box.x, data->clip_box->x); + clip_box.y = fmax(dst_box.y, data->clip_box->y); + } scale_box(&dst_box, wlr_output->scale); + scale_box(&clip_box, wlr_output->scale); struct decoration_data deco_data = data->deco_data; deco_data.corner_radius *= wlr_output->scale; - struct wlr_fbox src_box; - wlr_surface_get_buffer_source_box(surface, &src_box); - struct fx_texture fx_texture = fx_texture_from_wlr_texture(texture); - // render blur + struct sway_view *view = data->view; bool is_subsurface = view ? view->surface != surface : false; if (deco_data.blur && config_should_parameters_blur() && !is_subsurface) { pixman_region32_t opaque_region; @@ -474,13 +374,11 @@ static void render_surface_iterator(struct sway_output *output, } if (has_alpha) { - struct wlr_box monitor_box = get_monitor_box(wlr_output); - wlr_box_transform(&monitor_box, &monitor_box, - wlr_output_transform_invert(wlr_output->transform), monitor_box.width, monitor_box.height); - struct blur_stencil_data stencil_data = { &fx_texture, &src_box, matrix }; - bool should_optimize_blur = view ? !container_is_floating_or_child(view->container) || config->blur_xray : false; - render_blur(should_optimize_blur, output, output_damage, &dst_box, - &opaque_region, &deco_data, &stencil_data); + bool should_optimize_blur = view ? + !container_is_floating_or_child(view->container) || config->blur_xray + : false; + render_blur(data->ctx, texture, &src_box, &clip_box, + should_optimize_blur, &opaque_region, deco_data); } pixman_region32_fini(&opaque_region); @@ -488,11 +386,10 @@ static void render_surface_iterator(struct sway_output *output, deco_data.discard_transparent = false; - // Render surface texture - render_texture(wlr_output, output_damage, &fx_texture, &src_box, &dst_box, - matrix, deco_data); + render_texture(data->ctx, texture, + &src_box, &dst_box, &clip_box, surface->current.transform, deco_data); - wlr_presentation_surface_sampled_on_output(server.presentation, surface, + wlr_presentation_surface_textured_on_output(server.presentation, surface, wlr_output); } @@ -500,225 +397,144 @@ static void render_surface_iterator(struct sway_output *output, static void render_layer_iterator(struct sway_output *output, struct sway_view *view, struct wlr_surface *surface, struct wlr_box *_box, void *_data) { + // render the layer's surface + render_surface_iterator(output, view, surface, _box, _data); + struct render_data *data = _data; struct decoration_data deco_data = data->deco_data; // Ignore effects if this is a subsurface - if (!wlr_surface_is_layer_surface(surface)) { + if (!wlr_layer_surface_v1_try_from_wlr_surface(surface)) { deco_data = get_undecorated_decoration_data(); } - // render the layer's surface - render_surface_iterator(output, view, surface, _box, _data); - // render shadow if (deco_data.shadow && config_should_parameters_shadow()) { - int corner_radius = deco_data.corner_radius *= output->wlr_output->scale; - int offset_x = config->shadow_offset_x * output->wlr_output->scale; - int offset_y = config->shadow_offset_y * output->wlr_output->scale; - scale_box(_box, output->wlr_output->scale); - render_box_shadow(output, data->damage, _box, config->shadow_color, - config->shadow_blur_sigma, corner_radius, offset_x, offset_y); + float output_scale = output->wlr_output->scale; + struct wlr_box box = *_box; + scale_box(&box, output_scale); + render_box_shadow(data->ctx, &box, config->shadow_color, config->shadow_blur_sigma * output_scale, + deco_data.corner_radius * output_scale, config->shadow_offset_x * output_scale, config->shadow_offset_y * output_scale); } } -static void render_layer_toplevel(struct sway_output *output, - pixman_region32_t *damage, struct wl_list *layer_surfaces) { +static void render_layer_toplevel(struct fx_render_context *ctx, struct wl_list *layer_surfaces) { struct render_data data = { - .damage = damage, .deco_data = get_undecorated_decoration_data(), + .ctx = ctx, }; - output_layer_for_each_toplevel_surface(output, layer_surfaces, + output_layer_for_each_toplevel_surface(ctx->output, layer_surfaces, render_layer_iterator, &data); } -static void render_layer_popups(struct sway_output *output, - pixman_region32_t *damage, struct wl_list *layer_surfaces) { + static void render_layer_popups(struct fx_render_context *ctx, struct wl_list *layer_surfaces) { struct render_data data = { - .damage = damage, .deco_data = get_undecorated_decoration_data(), + .ctx = ctx, }; - output_layer_for_each_popup_surface(output, layer_surfaces, + output_layer_for_each_popup_surface(ctx->output, layer_surfaces, render_layer_iterator, &data); } #if HAVE_XWAYLAND -static void render_unmanaged(struct sway_output *output, - pixman_region32_t *damage, struct wl_list *unmanaged) { +static void render_unmanaged(struct fx_render_context *ctx, struct wl_list *unmanaged) { struct render_data data = { - .damage = damage, .deco_data = get_undecorated_decoration_data(), + .ctx = ctx, }; - output_unmanaged_for_each_surface(output, unmanaged, + output_unmanaged_for_each_surface(ctx->output, unmanaged, render_surface_iterator, &data); } #endif -static void render_drag_icons(struct sway_output *output, - pixman_region32_t *damage, struct wl_list *drag_icons) { +static void render_drag_icons(struct fx_render_context *ctx, struct wl_list *drag_icons) { struct render_data data = { - .damage = damage, .deco_data = get_undecorated_decoration_data(), + .ctx = ctx, }; - output_drag_icons_for_each_surface(output, drag_icons, + output_drag_icons_for_each_surface(ctx->output, drag_icons, render_surface_iterator, &data); } -void render_whole_output(struct fx_renderer *renderer, struct wlr_output *wlr_output, - pixman_region32_t *output_damage, struct fx_texture *texture) { - struct wlr_box monitor_box = get_monitor_box(wlr_output); - enum wl_output_transform transform = wlr_output_transform_invert(wlr_output->transform); - float matrix[9]; - wlr_matrix_project_box(matrix, &monitor_box, transform, 0.0, wlr_output->transform_matrix); - - render_texture(wlr_output, output_damage, texture, NULL, &monitor_box, matrix, get_undecorated_decoration_data()); -} - -void render_output_blur(struct sway_output *output, pixman_region32_t *damage) { - struct wlr_output *wlr_output = output->wlr_output; - struct fx_renderer *renderer = output->renderer; - - struct wlr_box monitor_box = get_monitor_box(wlr_output); - pixman_region32_t fake_damage; - pixman_region32_init_rect(&fake_damage, 0, 0, monitor_box.width, monitor_box.height); - - // Render the blur - struct fx_framebuffer *buffer = get_main_buffer_blur(renderer, output, &fake_damage, &monitor_box); - - // Render the newly blurred content into the blur_buffer - fx_framebuffer_update(&renderer->blur_buffer, - output->renderer->viewport_width, output->renderer->viewport_height); - fx_framebuffer_bind(&renderer->blur_buffer); - - // Clear the damaged region of the blur_buffer - float clear_color[] = { 0, 0, 0, 0 }; - 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); - } - render_whole_output(renderer, wlr_output, &fake_damage, &buffer->texture); - fx_framebuffer_bind(&renderer->wlr_buffer); - - pixman_region32_fini(&fake_damage); - - renderer->blur_buffer_dirty = false; -} - // _box.x and .y are expected to be layout-local // _box.width and .height are expected to be output-buffer-local -void render_rect(struct sway_output *output, - pixman_region32_t *output_damage, const struct wlr_box *_box, +void render_rect(struct fx_render_context *ctx, const struct wlr_box *_box, float color[static 4]) { - struct wlr_output *wlr_output = output->wlr_output; - struct fx_renderer *renderer = output->renderer; + struct wlr_output *wlr_output = ctx->output->wlr_output; - struct wlr_box box; - memcpy(&box, _box, sizeof(struct wlr_box)); - box.x -= output->lx * wlr_output->scale; - box.y -= output->ly * wlr_output->scale; + struct wlr_box box = *_box; + box.x -= ctx->output->lx * wlr_output->scale; + box.y -= ctx->output->ly * wlr_output->scale; - pixman_region32_t damage = create_damage(box, output_damage); + pixman_region32_t damage; + pixman_region32_init_rect(&damage, box.x, box.y, + box.width, box.height); + pixman_region32_intersect(&damage, &damage, ctx->output_damage); bool damaged = pixman_region32_not_empty(&damage); if (!damaged) { goto damage_finish; } - 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_render_rect(renderer, &box, color, wlr_output->transform_matrix); - } + transform_output_damage(&damage, wlr_output); + transform_output_box(&box, wlr_output); + + fx_render_pass_add_rect(ctx->pass, &(struct fx_render_rect_options){ + .base = { + .box = box, + .color = { + .r = color[0], + .g = color[1], + .b = color[2], + .a = color[3], + }, + .clip = &damage, + }, + }); damage_finish: pixman_region32_fini(&damage); } -void render_rounded_rect(struct sway_output *output, pixman_region32_t *output_damage, - const struct wlr_box *_box, float color[static 4], int corner_radius, - enum corner_location corner_location) { - struct wlr_output *wlr_output = output->wlr_output; - struct fx_renderer *renderer = output->renderer; +void render_rounded_rect(struct fx_render_context *ctx, const struct wlr_box *_box, + float color[static 4], int corner_radius, enum corner_location corner_location) { + if (!corner_radius) { + render_rect(ctx, _box, color); + return; + } - struct wlr_box box; - memcpy(&box, _box, sizeof(struct wlr_box)); - box.x -= output->lx * wlr_output->scale; - box.y -= output->ly * wlr_output->scale; + struct wlr_output *wlr_output = ctx->output->wlr_output; - pixman_region32_t damage = create_damage(box, output_damage); + struct wlr_box box = *_box; + box.x -= ctx->output->lx * wlr_output->scale; + box.y -= ctx->output->ly * wlr_output->scale; + + pixman_region32_t damage; + pixman_region32_init_rect(&damage, box.x, box.y, + box.width, box.height); + pixman_region32_intersect(&damage, &damage, ctx->output_damage); bool damaged = pixman_region32_not_empty(&damage); if (!damaged) { goto damage_finish; } - float matrix[9]; - wlr_matrix_project_box(matrix, &box, WL_OUTPUT_TRANSFORM_NORMAL, 0, - wlr_output->transform_matrix); + transform_output_damage(&damage, wlr_output); + transform_output_box(&box, wlr_output); + transform_corner_location(&corner_location, wlr_output); - enum wl_output_transform transform = wlr_output_transform_invert(wlr_output->transform); - - // ensure the box is updated as per the output orientation - struct wlr_box transformed_box; - int width, height; - wlr_output_transformed_resolution(wlr_output, &width, &height); - wlr_box_transform(&transformed_box, &box, transform, width, height); - - corner_location = get_rotated_corner(corner_location, transform); - - 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_render_rounded_rect(renderer, &transformed_box, color, matrix, - corner_radius, corner_location); - } - -damage_finish: - pixman_region32_fini(&damage); -} - -// _box.x and .y are expected to be layout-local -// _box.width and .height are expected to be output-buffer-local -void render_border_corner(struct sway_output *output, pixman_region32_t *output_damage, - const struct wlr_box *_box, const float color[static 4], int corner_radius, - int border_thickness, enum corner_location corner_location) { - struct wlr_output *wlr_output = output->wlr_output; - struct fx_renderer *renderer = output->renderer; - - struct wlr_box box; - memcpy(&box, _box, sizeof(struct wlr_box)); - box.x -= output->lx * wlr_output->scale; - box.y -= output->ly * wlr_output->scale; - - pixman_region32_t damage = create_damage(box, output_damage); - bool damaged = pixman_region32_not_empty(&damage); - if (!damaged) { - goto damage_finish; - } - - float matrix[9]; - wlr_matrix_project_box(matrix, &box, WL_OUTPUT_TRANSFORM_NORMAL, 0, - wlr_output->transform_matrix); - - enum wl_output_transform transform = wlr_output_transform_invert(wlr_output->transform); - - // ensure the box is updated as per the output orientation - struct wlr_box transformed_box; - int width, height; - wlr_output_transformed_resolution(wlr_output, &width, &height); - wlr_box_transform(&transformed_box, &box, transform, width, height); - - corner_location = get_rotated_corner(corner_location, transform); - - 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_render_border_corner(renderer, &transformed_box, color, matrix, - corner_location, corner_radius, border_thickness); - } + fx_render_pass_add_rounded_rect(ctx->pass, &(struct fx_render_rounded_rect_options){ + .base = { + .box = box, + .color = { + .r = color[0], + .g = color[1], + .b = color[2], + .a = color[3], + }, + .clip = &damage, + }, + .corner_radius = corner_radius, + .corner_location = corner_location + }); damage_finish: pixman_region32_fini(&damage); @@ -731,17 +547,18 @@ void premultiply_alpha(float color[4], float opacity) { color[2] *= color[3]; } -static void render_view_toplevels(struct sway_view *view, struct sway_output *output, - pixman_region32_t *damage, struct decoration_data deco_data) { +static void render_view_toplevels(struct fx_render_context *ctx, + struct sway_view *view, struct decoration_data deco_data) { struct render_data data = { - .damage = damage, .deco_data = deco_data, + .view = view, + .ctx = ctx, }; // Clip the window to its view size, ignoring CSD struct wlr_box clip_box; struct sway_container_state state = view->container->current; - clip_box.x = state.x - output->lx; - clip_box.y = state.y - output->ly; + clip_box.x = floor(state.x) - ctx->output->lx; + clip_box.y = floor(state.y) - ctx->output->ly; clip_box.width = state.width; clip_box.height = state.height; @@ -768,27 +585,36 @@ static void render_view_toplevels(struct sway_view *view, struct sway_output *ou } data.clip_box = &clip_box; - output_view_for_each_surface(output, view, render_surface_iterator, &data); + // Render all toplevels without descending into popups + double ox = view->container->surface_x - + ctx->output->lx - view->geometry.x; + double oy = view->container->surface_y - + ctx->output->ly - view->geometry.y; + output_surface_for_each_surface(ctx->output, view->surface, ox, oy, + render_surface_iterator, &data); } -static void render_view_popups(struct sway_view *view, struct sway_output *output, - pixman_region32_t *damage, struct decoration_data deco_data) { +static void render_view_popups(struct fx_render_context *ctx, struct sway_view *view, + struct decoration_data deco_data) { struct render_data data = { - .damage = damage, .deco_data = deco_data, + .view = view, + .ctx = ctx, }; - output_view_for_each_popup_surface(output, view, + output_view_for_each_popup_surface(ctx->output, view, render_surface_iterator, &data); } -static void render_saved_view(struct sway_view *view, struct sway_output *output, - pixman_region32_t *damage, struct decoration_data deco_data) { +static void render_saved_view(struct fx_render_context *ctx, struct sway_view *view, struct decoration_data deco_data) { + struct sway_output *output = ctx->output; struct wlr_output *wlr_output = output->wlr_output; if (wl_list_empty(&view->saved_buffers)) { return; } + deco_data.corner_radius *= wlr_output->scale; + struct sway_saved_buffer *saved_buf; wl_list_for_each(saved_buf, &view->saved_buffers, link) { if (!saved_buf->buffer->texture) { @@ -814,55 +640,46 @@ static void render_saved_view(struct sway_view *view, struct sway_output *output } struct wlr_box dst_box = proj_box; - scale_box(&proj_box, wlr_output->scale); - - float matrix[9]; - enum wl_output_transform transform = wlr_output_transform_invert(saved_buf->transform); - wlr_matrix_project_box(matrix, &proj_box, transform, 0, wlr_output->transform_matrix); + struct wlr_box clip_box = proj_box; + // Clip to actual geometry, clipping the CSD struct sway_container_state state = view->container->current; - dst_box.x = state.x - output->lx; - dst_box.y = state.y - output->ly; - dst_box.width = state.width; - dst_box.height = state.height; + clip_box.x = state.x - output->lx; + clip_box.y = state.y - output->ly; + clip_box.width = state.width; + clip_box.height = state.height; if (state.border == B_PIXEL || state.border == B_NORMAL) { - dst_box.x += state.border_thickness; - dst_box.width -= state.border_thickness * 2; + clip_box.x += state.border_thickness; + clip_box.width -= state.border_thickness * 2; if (deco_data.has_titlebar) { // Shift the box downward to compensate for the titlebar int titlebar_thickness = container_titlebar_height(); - dst_box.y += titlebar_thickness; - dst_box.height -= state.border_thickness + titlebar_thickness; + clip_box.y += titlebar_thickness; + clip_box.height -= state.border_thickness + titlebar_thickness; } else { // Regular border - dst_box.y += state.border_thickness; - dst_box.height -= state.border_thickness * 2; + clip_box.y += state.border_thickness; + clip_box.height -= state.border_thickness * 2; } } + scale_box(&dst_box, wlr_output->scale); - - deco_data.corner_radius *= wlr_output->scale; - - struct fx_texture fx_texture = fx_texture_from_wlr_texture(saved_buf->buffer->texture); + scale_box(&clip_box, wlr_output->scale); // render blur if (deco_data.blur && config_should_parameters_blur()) { - struct wlr_gles2_texture_attribs attribs; - wlr_gles2_texture_get_attribs(saved_buf->buffer->texture, &attribs); - + struct fx_texture_attribs attribs; + fx_texture_get_attribs(saved_buf->buffer->texture, &attribs); if (deco_data.alpha < 1.0 || attribs.has_alpha) { pixman_region32_t opaque_region; pixman_region32_init(&opaque_region); pixman_region32_union_rect(&opaque_region, &opaque_region, 0, 0, 0, 0); - struct wlr_box monitor_box = get_monitor_box(wlr_output); - wlr_box_transform(&monitor_box, &monitor_box, - wlr_output_transform_invert(wlr_output->transform), monitor_box.width, monitor_box.height); - struct blur_stencil_data stencil_data = { &fx_texture, &saved_buf->source_box, matrix }; bool should_optimize_blur = !container_is_floating_or_child(view->container) || config->blur_xray; - render_blur(should_optimize_blur, output, damage, &dst_box, &opaque_region, - &deco_data, &stencil_data); + render_blur(ctx, saved_buf->buffer->texture, + &saved_buf->source_box, &clip_box, should_optimize_blur, + &opaque_region, deco_data); pixman_region32_fini(&opaque_region); } @@ -870,54 +687,52 @@ static void render_saved_view(struct sway_view *view, struct sway_output *output deco_data.discard_transparent = false; - // Render saved surface texture - render_texture(wlr_output, damage, &fx_texture, - &saved_buf->source_box, &dst_box, matrix, deco_data); - } + render_texture(ctx, saved_buf->buffer->texture, + &saved_buf->source_box, &dst_box, &clip_box, saved_buf->transform, deco_data); - // FIXME: we should set the surface that this saved buffer originates from - // as sampled here. - // https://github.com/swaywm/sway/pull/4465#discussion_r321082059 + // FIXME: we should set the surface that this saved buffer originates from + // as sampled here. + // https://github.com/swaywm/sway/pull/4465#discussion_r321082059 + } } /** * Render a view's surface, shadow, and left/bottom/right borders. */ -static void render_view(struct sway_output *output, pixman_region32_t *damage, - struct sway_container *con, struct border_colors *colors, - struct decoration_data deco_data) { +static void render_view(struct fx_render_context *ctx, struct sway_container *con, + struct border_colors *colors, struct decoration_data deco_data) { struct sway_view *view = con->view; - struct sway_container_state *state = &con->current; - // render view if (!wl_list_empty(&view->saved_buffers)) { - render_saved_view(view, output, damage, deco_data); + render_saved_view(ctx, view, deco_data); } else if (view->surface) { - render_view_toplevels(view, output, damage, deco_data); + render_view_toplevels(ctx, view, deco_data); } + struct sway_container_state *state = &con->current; + if (state->border == B_CSD && !config->shadows_on_csd_enabled) { return; } - float output_scale = output->wlr_output->scale; struct wlr_box box; + int corner_radius = deco_data.corner_radius; + float output_scale = ctx->output->wlr_output->scale; // render shadow - if (con->shadow_enabled && config->shadow_blur_sigma > 0 && config->shadow_color[3] > 0.0) { - box.x = floor(state->x) - output->lx; - box.y = floor(state->y) - output->ly; + if (con->shadow_enabled && config_should_parameters_shadow()) { + box.x = floor(state->x) - ctx->output->lx; + box.y = floor(state->y) - ctx->output->ly; box.width = state->width; box.height = state->height; scale_box(&box, output_scale); - int scaled_corner_radius = deco_data.corner_radius == 0 ? - 0 : (deco_data.corner_radius + state->border_thickness) * output_scale; + int shadow_corner_radius = corner_radius == 0 ? 0 : corner_radius + state->border_thickness; float* shadow_color = view_is_urgent(view) || state->focused ? - config->shadow_color : config->shadow_inactive_color; - int offset_x = config->shadow_offset_x * output->wlr_output->scale; - int offset_y = config->shadow_offset_y * output->wlr_output->scale; - render_box_shadow(output, damage, &box, shadow_color, config->shadow_blur_sigma, - scaled_corner_radius, offset_x, offset_y); + config->shadow_color : config->shadow_inactive_color; + + render_box_shadow(ctx, &box, shadow_color, config->shadow_blur_sigma * output_scale, + shadow_corner_radius * output_scale, config->shadow_offset_x * output_scale, + config->shadow_offset_y * output_scale); } if (state->border == B_NONE || state->border == B_CSD) { @@ -928,22 +743,17 @@ static void render_view(struct sway_output *output, pixman_region32_t *damage, if (state->border_left) { memcpy(&color, colors->child_border, sizeof(float) * 4); - premultiply_alpha(color, deco_data.alpha); + premultiply_alpha(color, con->alpha); box.x = floor(state->x); box.y = floor(state->content_y); box.width = state->border_thickness; - box.height = state->content_height; - // adjust sizing for rounded border corners - if (deco_data.corner_radius) { - if (!deco_data.has_titlebar) { - box.y += deco_data.corner_radius; - box.height -= 2 * deco_data.corner_radius; - } else { - box.height -= deco_data.corner_radius; - } + box.height = state->content_height - corner_radius; + if (corner_radius && !deco_data.has_titlebar) { + box.y += corner_radius; + box.height -= corner_radius; } scale_box(&box, output_scale); - render_rect(output, damage, &box, color); + render_rect(ctx, &box, color); } list_t *siblings = container_get_current_siblings(con); @@ -956,22 +766,17 @@ static void render_view(struct sway_output *output, pixman_region32_t *damage, } else { memcpy(&color, colors->child_border, sizeof(float) * 4); } - premultiply_alpha(color, deco_data.alpha); + premultiply_alpha(color, con->alpha); box.x = floor(state->content_x + state->content_width); box.y = floor(state->content_y); box.width = state->border_thickness; - box.height = state->content_height; - // adjust sizing for rounded border corners - if (deco_data.corner_radius) { - if (!deco_data.has_titlebar) { - box.y += deco_data.corner_radius; - box.height -= 2 * deco_data.corner_radius; - } else { - box.height -= deco_data.corner_radius; - } + box.height = state->content_height - corner_radius; + if (corner_radius && !deco_data.has_titlebar) { + box.y += corner_radius; + box.height -= corner_radius; } scale_box(&box, output_scale); - render_rect(output, damage, &box, color); + render_rect(ctx, &box, color); } if (state->border_bottom) { @@ -980,42 +785,40 @@ static void render_view(struct sway_output *output, pixman_region32_t *damage, } else { memcpy(&color, colors->child_border, sizeof(float) * 4); } - premultiply_alpha(color, deco_data.alpha); + premultiply_alpha(color, con->alpha); box.x = floor(state->x); box.y = floor(state->content_y + state->content_height); box.width = state->width; box.height = state->border_thickness; - // adjust sizing for rounded border corners if (deco_data.corner_radius) { - box.x += deco_data.corner_radius + state->border_thickness; - box.width -= 2 * (deco_data.corner_radius + state->border_thickness); + box.x += corner_radius + state->border_thickness; + box.width -= 2 * (corner_radius + state->border_thickness); } scale_box(&box, output_scale); - render_rect(output, damage, &box, color); + render_rect(ctx, &box, color); - // rounded bottom left & bottom right border corners - if (deco_data.corner_radius) { - int size = 2 * (deco_data.corner_radius + state->border_thickness); - int scaled_thickness = state->border_thickness * output_scale; - int scaled_corner_radius = deco_data.corner_radius * output_scale; + if (corner_radius && state->border_thickness > 0) { + int size = 2 * (corner_radius + state->border_thickness); + int scaled_corner_radius = corner_radius * output_scale; + int scaled_border_thickness = state->border_thickness * output_scale; if (state->border_left) { - box.width = size; - box.height = size; box.x = floor(state->x); box.y = floor(state->y + state->height - size); - scale_box(&box, output_scale); - render_border_corner(output, damage, &box, color, - scaled_corner_radius, scaled_thickness, BOTTOM_LEFT); - } - if (state->border_right) { box.width = size; box.height = size; + scale_box(&box, output_scale); + render_rounded_border_corner(ctx, &box, color, scaled_corner_radius, + scaled_border_thickness, BOTTOM_LEFT); + } + if (state->border_right) { box.x = floor(state->x + state->width - size); box.y = floor(state->y + state->height - size); + box.width = size; + box.height = size; scale_box(&box, output_scale); - render_border_corner(output, damage, &box, color, - scaled_corner_radius, scaled_thickness, BOTTOM_RIGHT); + render_rounded_border_corner(ctx, &box, color, scaled_corner_radius, + scaled_border_thickness, BOTTOM_RIGHT); } } } @@ -1030,13 +833,13 @@ static void render_view(struct sway_output *output, pixman_region32_t *damage, * The height is: 1px border, 3px padding, font height, 3px padding, 1px border * The left side is: 1px border, 2px padding, title */ -static void render_titlebar(struct sway_output *output, - pixman_region32_t *output_damage, struct sway_container *con, int x, int y, - int width, struct border_colors *colors, float alpha, int corner_radius, +static void render_titlebar(struct fx_render_context *ctx, struct sway_container *con, + int x, int y, int width, struct border_colors *colors, int corner_radius, enum corner_location corner_location, struct wlr_texture *title_texture, struct wlr_texture *marks_texture) { struct wlr_box box; float color[4]; + struct sway_output *output = ctx->output; float output_scale = output->wlr_output->scale; double output_x = output->lx; double output_y = output->ly; @@ -1046,17 +849,15 @@ static void render_titlebar(struct sway_output *output, enum alignment title_align = config->title_align; // value by which all heights should be adjusted to counteract removed bottom border int bottom_border_compensation = config->titlebar_separator ? 0 : titlebar_border_thickness; - - if (corner_location == NONE) { - corner_radius = 0; - } + corner_radius *= output_scale; // Single pixel bar above title memcpy(&color, colors->border, sizeof(float) * 4); - premultiply_alpha(color, alpha); + premultiply_alpha(color, con->alpha); box.x = x; box.y = y; box.width = width; + box.height = titlebar_border_thickness; if (corner_radius) { if (corner_location != TOP_RIGHT) { box.x += corner_radius; @@ -1066,47 +867,43 @@ static void render_titlebar(struct sway_output *output, } else { box.width -= corner_radius; } - } else { - box.x += titlebar_border_thickness; - box.width -= titlebar_border_thickness * 2; } - box.height = titlebar_border_thickness; scale_box(&box, output_scale); - render_rect(output, output_damage, &box, color); + render_rect(ctx, &box, color); // Single pixel bar below title - if (config->titlebar_separator) { + if (!bottom_border_compensation) { box.x = x; box.y = y + container_titlebar_height() - titlebar_border_thickness; box.width = width; box.height = titlebar_border_thickness; scale_box(&box, output_scale); - render_rect(output, output_damage, &box, color); + render_rect(ctx, &box, color); } - // Single pixel bar left edge + // Single pixel left edge box.x = x; box.y = y; box.width = titlebar_border_thickness; - box.height = container_titlebar_height() + bottom_border_compensation; + box.height = container_titlebar_height() - titlebar_border_thickness + bottom_border_compensation; if (corner_radius && corner_location != TOP_RIGHT) { box.height -= corner_radius; box.y += corner_radius; } scale_box(&box, output_scale); - render_rect(output, output_damage, &box, color); + render_rect(ctx, &box, color); - // Single pixel bar right edge + // Single pixel right edge box.x = x + width - titlebar_border_thickness; box.y = y; box.width = titlebar_border_thickness; - box.height = container_titlebar_height() + bottom_border_compensation; + box.height = container_titlebar_height() - titlebar_border_thickness + bottom_border_compensation; if (corner_radius && corner_location != TOP_LEFT) { box.height -= corner_radius; box.y += corner_radius; } scale_box(&box, output_scale); - render_rect(output, output_damage, &box, color); + render_rect(ctx, &box, color); // if corner_radius: single pixel corners if (corner_radius) { @@ -1117,8 +914,8 @@ static void render_titlebar(struct sway_output *output, box.width = corner_radius * 2; box.height = corner_radius * 2; scale_box(&box, output_scale); - render_border_corner(output, output_damage, &box, color, - corner_radius, titlebar_border_thickness, TOP_LEFT); + render_rounded_border_corner(ctx, &box, color, corner_radius, + titlebar_border_thickness * output_scale, TOP_LEFT); } // right corner @@ -1128,8 +925,8 @@ static void render_titlebar(struct sway_output *output, box.width = corner_radius * 2; box.height = corner_radius * 2; scale_box(&box, output_scale); - render_border_corner(output, output_damage, &box, color, - corner_radius, titlebar_border_thickness, TOP_RIGHT); + render_rounded_border_corner(ctx, &box, color, corner_radius, + titlebar_border_thickness * output_scale, TOP_RIGHT); } } @@ -1138,7 +935,7 @@ static void render_titlebar(struct sway_output *output, size_t inner_width = width - titlebar_h_padding * 2; // output-buffer local - int ob_inner_x = round(inner_x * output_scale); + int ob_inner_x = roundf(inner_x * output_scale); int ob_inner_width = scale_length(inner_width, inner_x, output_scale); int ob_bg_height = scale_length( (titlebar_v_padding - titlebar_border_thickness) * 2 + @@ -1146,7 +943,7 @@ static void render_titlebar(struct sway_output *output, // title marks textures should have no eyecandy struct decoration_data deco_data = get_undecorated_decoration_data(); - deco_data.alpha = alpha; + deco_data.alpha = con->alpha; // Marks int ob_marks_x = 0; // output-buffer-local @@ -1176,31 +973,26 @@ static void render_titlebar(struct sway_output *output, texture_box.y = round((bg_y - output_y) * output_scale) + ob_padding_above; - float matrix[9]; - wlr_matrix_project_box(matrix, &texture_box, - WL_OUTPUT_TRANSFORM_NORMAL, - 0.0, output->wlr_output->transform_matrix); - - if (ob_inner_width < texture_box.width) { - texture_box.width = ob_inner_width; + struct wlr_box clip_box = texture_box; + if (ob_inner_width < clip_box.width) { + clip_box.width = ob_inner_width; } - struct fx_texture fx_texture = fx_texture_from_wlr_texture(marks_texture); - render_texture(output->wlr_output, output_damage, &fx_texture, - NULL, &texture_box, matrix, deco_data); + render_texture(ctx, marks_texture, + NULL, &texture_box, &clip_box, WL_OUTPUT_TRANSFORM_NORMAL, deco_data); // Padding above memcpy(&color, colors->background, sizeof(float) * 4); - premultiply_alpha(color, alpha); - box.x = texture_box.x + round(output_x * output_scale); - box.y = round((y + titlebar_border_thickness) * output_scale); - box.width = texture_box.width; + premultiply_alpha(color, con->alpha); + box.x = clip_box.x + round(output_x * output_scale); + box.y = roundf((y + titlebar_border_thickness) * output_scale); + box.width = clip_box.width; box.height = ob_padding_above; - render_rect(output, output_damage, &box, color); + render_rect(ctx, &box, color); // Padding below - box.y += ob_padding_above + texture_box.height; + box.y += ob_padding_above + clip_box.height; box.height = ob_padding_below + bottom_border_compensation; - render_rect(output, output_damage, &box, color); + render_rect(ctx, &box, color); } // Title text @@ -1223,7 +1015,7 @@ static void render_titlebar(struct sway_output *output, // The title texture might be shorter than the config->font_height, // in which case we need to pad it above and below. - int ob_padding_above = round((titlebar_v_padding - + int ob_padding_above = roundf((titlebar_v_padding - titlebar_border_thickness) * output_scale); int ob_padding_below = ob_bg_height - ob_padding_above - texture_box.height; @@ -1252,32 +1044,27 @@ static void render_titlebar(struct sway_output *output, texture_box.y = round((bg_y - output_y) * output_scale) + ob_padding_above; - float matrix[9]; - wlr_matrix_project_box(matrix, &texture_box, - WL_OUTPUT_TRANSFORM_NORMAL, - 0.0, output->wlr_output->transform_matrix); - - if (ob_inner_width - ob_marks_width < texture_box.width) { - texture_box.width = ob_inner_width - ob_marks_width; + struct wlr_box clip_box = texture_box; + if (ob_inner_width - ob_marks_width < clip_box.width) { + clip_box.width = ob_inner_width - ob_marks_width; } - struct fx_texture fx_texture = fx_texture_from_wlr_texture(title_texture); - render_texture(output->wlr_output, output_damage, &fx_texture, - NULL, &texture_box, matrix, deco_data); + render_texture(ctx, title_texture, + NULL, &texture_box, &clip_box, WL_OUTPUT_TRANSFORM_NORMAL, deco_data); // Padding above memcpy(&color, colors->background, sizeof(float) * 4); - premultiply_alpha(color, alpha); - box.x = texture_box.x + round(output_x * output_scale); - box.y = round((y + titlebar_border_thickness) * output_scale); - box.width = texture_box.width; + premultiply_alpha(color, con->alpha); + box.x = clip_box.x + round(output_x * output_scale); + box.y = roundf((y + titlebar_border_thickness) * output_scale); + box.width = clip_box.width; box.height = ob_padding_above; - render_rect(output, output_damage, &box, color); + render_rect(ctx, &box, color); // Padding below - box.y += ob_padding_above + texture_box.height; + box.y += ob_padding_above + clip_box.height; box.height = ob_padding_below + bottom_border_compensation; - render_rect(output, output_damage, &box, color); + render_rect(ctx, &box, color); } // Determine the left + right extends of the textures (output-buffer local) @@ -1309,9 +1096,9 @@ static void render_titlebar(struct sway_output *output, box.width = ob_right_x - ob_left_x - ob_left_width; if (box.width > 0) { box.x = ob_left_x + ob_left_width + round(output_x * output_scale); - box.y = round(bg_y * output_scale); + box.y = roundf(bg_y * output_scale); box.height = ob_bg_height + bottom_border_compensation; - render_rect(output, output_damage, &box, color); + render_rect(ctx, &box, color); } // Padding on left side @@ -1325,10 +1112,10 @@ static void render_titlebar(struct sway_output *output, if (box.x + box.width < left_x) { box.width += left_x - box.x - box.width; } - if (corner_radius && corner_location != TOP_RIGHT) { - render_rounded_rect(output, output_damage, &box, color, corner_radius, TOP_LEFT); + if (corner_radius && (corner_location == TOP_LEFT || corner_location == ALL)) { + render_rounded_rect(ctx, &box, color, corner_radius, TOP_LEFT); } else { - render_rect(output, output_damage, &box, color); + render_rect(ctx, &box, color); } // Padding on right side @@ -1343,67 +1130,57 @@ static void render_titlebar(struct sway_output *output, box.width += box.x - right_rx; box.x = right_rx; } - if (corner_radius && corner_location != TOP_LEFT) { - render_rounded_rect(output, output_damage, &box, color, corner_radius, TOP_RIGHT); + if (corner_radius && (corner_location == TOP_RIGHT || corner_location == ALL)) { + render_rounded_rect(ctx, &box, color, corner_radius, TOP_RIGHT); } else { - render_rect(output, output_damage, &box, color); + render_rect(ctx, &box, color); } } /** * Render the top border line for a view using "border pixel". */ -static void render_top_border(struct sway_output *output, - pixman_region32_t *output_damage, struct sway_container_state *state, - struct border_colors *colors, float alpha, int corner_radius) { +static void render_top_border(struct fx_render_context *ctx, struct sway_container *con, + struct border_colors *colors, int corner_radius) { + struct sway_container_state *state = &con->current; if (!state->border_top) { return; } struct wlr_box box; float color[4]; - float output_scale = output->wlr_output->scale; + float output_scale = ctx->output->wlr_output->scale; // Child border - top edge memcpy(&color, colors->child_border, sizeof(float) * 4); - premultiply_alpha(color, alpha); - box.x = floor(state->x); + premultiply_alpha(color, con->alpha); + box.x = floor(state->x) + corner_radius; box.y = floor(state->y); - box.width = state->width; + box.width = state->width - (2 * corner_radius); box.height = state->border_thickness; - - // adjust sizing for rounded border corners - if (corner_radius) { - box.x += corner_radius + state->border_thickness; - box.width -= 2 * (corner_radius + state->border_thickness); - } scale_box(&box, output_scale); - render_rect(output, output_damage, &box, color); + render_rect(ctx, &box, color); - // render rounded top corner borders if corner_radius is set > 0 - if (corner_radius) { + if (corner_radius && state->border_thickness > 0) { int size = 2 * (corner_radius + state->border_thickness); - int scaled_thickness = state->border_thickness * output_scale; int scaled_corner_radius = corner_radius * output_scale; - - // top left + int scaled_border_thickness = state->border_thickness * output_scale; if (state->border_left) { - box.width = size; - box.height = size; box.x = floor(state->x); box.y = floor(state->y); - scale_box(&box, output_scale); - render_border_corner(output, output_damage, &box, color, - scaled_corner_radius, scaled_thickness, TOP_LEFT); - } - // top right - if (state->border_right) { box.width = size; box.height = size; + scale_box(&box, output_scale); + render_rounded_border_corner(ctx, &box, color, scaled_corner_radius, + scaled_border_thickness, TOP_LEFT); + } + if (state->border_right) { box.x = floor(state->x + state->width - size); box.y = floor(state->y); + box.width = size; + box.height = size; scale_box(&box, output_scale); - render_border_corner(output, output_damage, &box, color, - scaled_corner_radius, scaled_thickness, TOP_RIGHT); + render_rounded_border_corner(ctx, &box, color, scaled_corner_radius, + scaled_border_thickness, TOP_RIGHT); } } } @@ -1416,17 +1193,17 @@ struct parent_data { struct sway_container *active_child; }; -static void render_container(struct sway_output *output, - pixman_region32_t *damage, struct sway_container *con, bool parent_focused); +static void render_container(struct fx_render_context *ctx, + struct sway_container *con, bool parent_focused); +// TODO: no rounding top corners when rendering with titlebar /** * Render a container's children using a L_HORIZ or L_VERT layout. * * Wrap child views in borders and leave child containers borderless because * they'll apply their own borders to their children. */ -static void render_containers_linear(struct sway_output *output, - pixman_region32_t *damage, struct parent_data *parent) { +static void render_containers_linear(struct fx_render_context *ctx, struct parent_data *parent) { for (int i = 0; i < parent->children->length; ++i) { struct sway_container *child = parent->children->items[i]; @@ -1469,7 +1246,7 @@ static void render_containers_linear(struct sway_output *output, .dim = child->current.focused || parent->focused ? 0.0f : child->dim, // no corner radius if no gaps (allows smart_gaps to work as expected) .corner_radius = config->smart_corner_radius && - output->current.active_workspace->current_gaps.top == 0 + ctx->output->current.active_workspace->current_gaps.top == 0 ? 0 : child->corner_radius, .saturation = child->saturation, .has_titlebar = has_titlebar, @@ -1477,16 +1254,16 @@ static void render_containers_linear(struct sway_output *output, .discard_transparent = false, .shadow = child->shadow_enabled, }; - render_view(output, damage, child, colors, deco_data); + render_view(ctx, child, colors, deco_data); if (has_titlebar) { - render_titlebar(output, damage, child, floor(state->x), floor(state->y), - state->width, colors, deco_data.alpha, deco_data.corner_radius, + render_titlebar(ctx, child, floor(state->x), floor(state->y), + state->width, colors, deco_data.corner_radius, ALL, title_texture, marks_texture); } else if (state->border == B_PIXEL) { - render_top_border(output, damage, state, colors, deco_data.alpha, deco_data.corner_radius); + render_top_border(ctx, child, colors, deco_data.corner_radius); } } else { - render_container(output, damage, child, + render_container(ctx, child, parent->focused || child->current.focused); } } @@ -1503,8 +1280,7 @@ static bool container_has_focused_child(struct sway_container *con) { /** * Render a container's children using the L_TABBED layout. */ -static void render_containers_tabbed(struct sway_output *output, - pixman_region32_t *damage, struct parent_data *parent) { +static void render_containers_tabbed(struct fx_render_context *ctx, struct parent_data *parent) { if (!parent->children->length) { return; } @@ -1520,7 +1296,7 @@ static void render_containers_tabbed(struct sway_output *output, .dim = current->current.focused || parent->focused ? 0.0f : current->dim, // no corner radius if no gaps (allows smart_gaps to work as expected) .corner_radius = config->smart_corner_radius && - output->current.active_workspace->current_gaps.top == 0 + ctx->output->current.active_workspace->current_gaps.top == 0 ? 0 : current->corner_radius, .saturation = current->saturation, .has_titlebar = true, @@ -1570,19 +1346,20 @@ static void render_containers_tabbed(struct sway_output *output, } // only round outer corners - enum corner_location corner_location = NONE; - if (i == 0) { - if (i == parent->children->length - 1) { - corner_location = ALL; - } else { + int corner_radius = deco_data.corner_radius; + enum corner_location corner_location = ALL; + if (parent->children->length > 1) { + if (i == 0) { corner_location = TOP_LEFT; + } else if (i == parent->children->length - 1) { + corner_location = TOP_RIGHT; + } else { + corner_radius = 0; } - } else if (i == parent->children->length - 1) { - corner_location = TOP_RIGHT; } - render_titlebar(output, damage, child, x, parent->box.y, tab_width, colors, - deco_data.alpha, deco_data.corner_radius, corner_location, title_texture, marks_texture); + render_titlebar(ctx, child, x, parent->box.y, tab_width, colors, + corner_radius, corner_location, title_texture, marks_texture); if (child == current) { current_colors = colors; @@ -1591,9 +1368,9 @@ static void render_containers_tabbed(struct sway_output *output, // Render surface and left/right/bottom borders if (current->view) { - render_view(output, damage, current, current_colors, deco_data); + render_view(ctx, current, current_colors, deco_data); } else { - render_container(output, damage, current, + render_container(ctx, current, parent->focused || current->current.focused); } } @@ -1601,8 +1378,7 @@ static void render_containers_tabbed(struct sway_output *output, /** * Render a container's children using the L_STACKED layout. */ -static void render_containers_stacked(struct sway_output *output, - pixman_region32_t *damage, struct parent_data *parent) { +static void render_containers_stacked(struct fx_render_context *ctx, struct parent_data *parent) { if (!parent->children->length) { return; } @@ -1618,7 +1394,7 @@ static void render_containers_stacked(struct sway_output *output, .dim = current->current.focused || parent->focused ? 0.0f : current->dim, .saturation = current->saturation, .corner_radius = config->smart_corner_radius && - output->current.active_workspace->current_gaps.top == 0 + ctx->output->current.active_workspace->current_gaps.top == 0 ? 0 : current->corner_radius, .has_titlebar = true, .blur = current->blur_enabled, @@ -1661,8 +1437,8 @@ static void render_containers_stacked(struct sway_output *output, int y = parent->box.y + titlebar_height * i; int corner_radius = i != 0 ? 0 : deco_data.corner_radius; - render_titlebar(output, damage, child, parent->box.x, y, parent->box.width, - colors, deco_data.alpha, corner_radius, ALL, title_texture, marks_texture); + render_titlebar(ctx, child, parent->box.x, y, parent->box.width, colors, + corner_radius, ALL, title_texture, marks_texture); if (child == current) { current_colors = colors; @@ -1671,19 +1447,18 @@ static void render_containers_stacked(struct sway_output *output, // Render surface and left/right/bottom borders if (current->view) { - render_view(output, damage, current, current_colors, deco_data); + render_view(ctx, current, current_colors, deco_data); } else { - render_container(output, damage, current, + render_container(ctx, current, parent->focused || current->current.focused); } } -static void render_containers(struct sway_output *output, - pixman_region32_t *damage, struct parent_data *parent) { +static void render_containers(struct fx_render_context *ctx, struct parent_data *parent) { if (config->hide_lone_tab && parent->children->length == 1) { struct sway_container *child = parent->children->items[0]; if (child->view) { - render_containers_linear(output,damage, parent); + render_containers_linear(ctx, parent); return; } } @@ -1692,19 +1467,19 @@ static void render_containers(struct sway_output *output, case L_NONE: case L_HORIZ: case L_VERT: - render_containers_linear(output, damage, parent); + render_containers_linear(ctx, parent); break; case L_STACKED: - render_containers_stacked(output, damage, parent); + render_containers_stacked(ctx, parent); break; case L_TABBED: - render_containers_tabbed(output, damage, parent); + render_containers_tabbed(ctx, parent); break; } } -static void render_container(struct sway_output *output, - pixman_region32_t *damage, struct sway_container *con, bool focused) { +static void render_container(struct fx_render_context *ctx, + struct sway_container *con, bool focused) { struct parent_data data = { .layout = con->current.layout, .box = { @@ -1717,11 +1492,11 @@ static void render_container(struct sway_output *output, .focused = focused, .active_child = con->current.focused_inactive_child, }; - render_containers(output, damage, &data); + render_containers(ctx, &data); } -static void render_workspace(struct sway_output *output, - pixman_region32_t *damage, struct sway_workspace *ws, bool focused) { +static void render_workspace(struct fx_render_context *ctx, + struct sway_workspace *ws, bool focused) { struct parent_data data = { .layout = ws->current.layout, .box = { @@ -1734,11 +1509,11 @@ static void render_workspace(struct sway_output *output, .focused = focused, .active_child = ws->current.focused_inactive_child, }; - render_containers(output, damage, &data); + render_containers(ctx, &data); } -static void render_floating_container(struct sway_output *soutput, - pixman_region32_t *damage, struct sway_container *con) { +static void render_floating_container(struct fx_render_context *ctx, + struct sway_container *con) { struct sway_container_state *state = &con->current; if (con->view) { struct sway_view *view = con->view; @@ -1774,21 +1549,19 @@ static void render_floating_container(struct sway_output *soutput, .discard_transparent = false, .shadow = con->shadow_enabled, }; - render_view(soutput, damage, con, colors, deco_data); + render_view(ctx, con, colors, deco_data); if (has_titlebar) { - render_titlebar(soutput, damage, con, floor(state->x), floor(state->y), - state->width, colors, deco_data.alpha, deco_data.corner_radius, - ALL, title_texture, marks_texture); + render_titlebar(ctx, con, floor(con->current.x), floor(con->current.y), con->current.width, + colors, deco_data.corner_radius, ALL, title_texture, marks_texture); } else if (state->border == B_PIXEL) { - render_top_border(soutput, damage, state, colors, deco_data.alpha, deco_data.corner_radius); + render_top_border(ctx, con, colors, deco_data.corner_radius); } } else { - render_container(soutput, damage, con, state->focused); + render_container(ctx, con, state->focused); } } -static void render_floating(struct sway_output *soutput, - pixman_region32_t *damage) { +static void render_floating(struct fx_render_context *ctx) { for (int i = 0; i < root->outputs->length; ++i) { struct sway_output *output = root->outputs->items[i]; for (int j = 0; j < output->current.workspaces->length; ++j) { @@ -1801,24 +1574,26 @@ static void render_floating(struct sway_output *soutput, if (floater->current.fullscreen_mode != FULLSCREEN_NONE) { continue; } - render_floating_container(soutput, damage, floater); + render_floating_container(ctx, floater); } } } } -static void render_seatops(struct sway_output *output, - pixman_region32_t *damage) { +static void render_seatops(struct fx_render_context *ctx) { struct sway_seat *seat; wl_list_for_each(seat, &server.input->seats, link) { - seatop_render(seat, output, damage); + seatop_render(seat, ctx); } } -void output_render(struct sway_output *output, struct timespec *when, - pixman_region32_t *damage) { - struct wlr_output *wlr_output = output->wlr_output; - struct fx_renderer *renderer = output->renderer; +void output_render(struct fx_render_context *ctx) { + struct wlr_output *wlr_output = ctx->output->wlr_output; + struct sway_output *output = ctx->output; + pixman_region32_t *damage = ctx->output_damage; + + struct fx_effect_framebuffers *effect_fbos = ctx->pass->fx_effect_framebuffers; + struct fx_renderer *renderer = fx_get_renderer(ctx->renderer); struct sway_workspace *workspace = output->current.active_workspace; if (workspace == NULL) { @@ -1830,47 +1605,45 @@ void output_render(struct sway_output *output, struct timespec *when, fullscreen_con = workspace->current.fullscreen; } - // TODO: generate the monitor box in fx_renderer (since it already has a wlr_output) - struct wlr_box monitor_box = get_monitor_box(wlr_output); - wlr_box_transform(&monitor_box, &monitor_box, - wlr_output_transform_invert(wlr_output->transform), - monitor_box.width, monitor_box.height); - - fx_renderer_begin(renderer, monitor_box.width, monitor_box.height); - - int output_width, output_height; - wlr_output_transformed_resolution(wlr_output, &output_width, &output_height); - - if (debug.damage == DAMAGE_RERENDER) { - pixman_region32_union_rect(damage, damage, 0, 0, output_width, output_height); - } - if (!pixman_region32_not_empty(damage)) { // Output isn't damaged but needs buffer swap goto renderer_end; } if (debug.damage == DAMAGE_HIGHLIGHT) { - fx_renderer_clear((float[]){1, 1, 0, 1}); + fx_render_pass_add_rect(ctx->pass, &(struct fx_render_rect_options){ + .base = { + .box = { .width = wlr_output->width, .height = wlr_output->height }, + .color = { .r = 1, .g = 1, .b = 0, .a = 1 }, + }, + }); } + pixman_region32_t transformed_damage; + pixman_region32_init(&transformed_damage); + pixman_region32_copy(&transformed_damage, damage); + transform_output_damage(&transformed_damage, wlr_output); if (server.session_lock.locked) { - float clear_color[] = {0.0f, 0.0f, 0.0f, 1.0f}; + struct wlr_render_color clear_color = { + .a = 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); + clear_color.r = 1.f; } + fx_render_pass_add_rect(ctx->pass, &(struct fx_render_rect_options){ + .base = { + .box = { .width = wlr_output->width, .height = wlr_output->height }, + .color = clear_color, + .clip = &transformed_damage, + }, + }); + if (server.session_lock.lock != NULL) { struct render_data data = { - .damage = damage, .deco_data = get_undecorated_decoration_data(), + .ctx = ctx, }; struct wlr_session_lock_surface_v1 *lock_surface; @@ -1878,7 +1651,7 @@ void output_render(struct sway_output *output, struct timespec *when, if (lock_surface->output != wlr_output) { continue; } - if (!lock_surface->mapped) { + if (!lock_surface->surface->mapped) { continue; } @@ -1894,46 +1667,47 @@ void output_render(struct sway_output *output, struct timespec *when, } if (fullscreen_con) { - float clear_color[] = {0.0f, 0.0f, 0.0f, 1.0f}; - - 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); - } + fx_render_pass_add_rect(ctx->pass, &(struct fx_render_rect_options){ + .base = { + .box = { .width = wlr_output->width, .height = wlr_output->height }, + .color = { .r = 0, .g = 0, .b = 0, .a = 1 }, + .clip = &transformed_damage, + }, + }); if (fullscreen_con->view) { struct decoration_data deco_data = get_undecorated_decoration_data(); deco_data.saturation = fullscreen_con->saturation; if (!wl_list_empty(&fullscreen_con->view->saved_buffers)) { - render_saved_view(fullscreen_con->view, output, damage, deco_data); + render_saved_view(ctx, fullscreen_con->view, deco_data); } else if (fullscreen_con->view->surface) { - render_view_toplevels(fullscreen_con->view, output, damage, deco_data); + render_view_toplevels(ctx, fullscreen_con->view, deco_data); } } else { - render_container(output, damage, fullscreen_con, - fullscreen_con->current.focused); + render_container(ctx, fullscreen_con, fullscreen_con->current.focused); } for (int i = 0; i < workspace->current.floating->length; ++i) { struct sway_container *floater = workspace->current.floating->items[i]; if (container_is_transient_for(floater, fullscreen_con)) { - render_floating_container(output, damage, floater); + render_floating_container(ctx, floater); } } #if HAVE_XWAYLAND - render_unmanaged(output, damage, &root->xwayland_unmanaged); + render_unmanaged(ctx, &root->xwayland_unmanaged); #endif } else { + int output_width, output_height; + wlr_output_transformed_resolution(wlr_output, &output_width, &output_height); + pixman_region32_t blur_region; pixman_region32_init(&blur_region); bool workspace_has_blur = workspace_get_blur_info(workspace, &blur_region); // Expand the damage to compensate for blur if (workspace_has_blur) { // Skip the blur artifact prevention if damaging the whole viewport - if (renderer->blur_buffer_dirty) { + if (effect_fbos->blur_buffer_dirty) { // Needs to be extended before clearing pixman_region32_union_rect(damage, damage, 0, 0, output_width, output_height); @@ -1970,51 +1744,62 @@ void output_render(struct sway_output *output, struct timespec *when, pixman_region32_fini(&extended_damage); // Capture the padding pixels before blur for later use - fx_framebuffer_bind(&renderer->blur_saved_pixels_buffer); - // TODO: Investigate blitting instead - render_whole_output(renderer, wlr_output, - &renderer->blur_padding_region, &renderer->wlr_buffer.texture); - fx_framebuffer_bind(&renderer->wlr_buffer); + fx_renderer_read_to_buffer(ctx->pass, &renderer->blur_padding_region, + ctx->pass->fx_effect_framebuffers->blur_saved_pixels_buffer, + ctx->pass->buffer, true); } } pixman_region32_fini(&blur_region); - float clear_color[] = {0.25f, 0.25f, 0.25f, 1.0f}; + fx_render_pass_add_rect(ctx->pass, &(struct fx_render_rect_options){ + .base = { + .box = { .width = wlr_output->width, .height = wlr_output->height }, + .color = { .r = 0.25f, .g = 0.25f, .b = 0.25f, .a = 1 }, + .clip = &transformed_damage, + }, + }); - 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); - } - - render_layer_toplevel(output, damage, + render_layer_toplevel(ctx, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND]); - render_layer_toplevel(output, damage, + render_layer_toplevel(ctx, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM]); - // check if the background needs to be blurred - if (workspace_has_blur && renderer->blur_buffer_dirty) { - render_output_blur(output, damage); + // Check if the background needs to be blurred. + // Render optimized/x-ray blur + if (workspace_has_blur && effect_fbos->blur_buffer_dirty) { + const float opacity = 1.0f; + struct fx_render_blur_pass_options blur_options = { + .tex_options = { + .base = { + .transform = WL_OUTPUT_TRANSFORM_NORMAL, + .alpha = &opacity, + .blend_mode = WLR_RENDER_BLEND_MODE_NONE, + }, + .corner_radius = 0, + .discard_transparent = false, + }, + .blur_data = &config->blur_params, + }; + fx_render_pass_add_optimized_blur(ctx->pass, &blur_options); } - render_workspace(output, damage, workspace, workspace->current.focused); - render_floating(output, damage); + render_workspace(ctx, workspace, workspace->current.focused); + render_floating(ctx); #if HAVE_XWAYLAND - render_unmanaged(output, damage, &root->xwayland_unmanaged); + render_unmanaged(ctx, &root->xwayland_unmanaged); #endif - render_layer_toplevel(output, damage, + render_layer_toplevel(ctx, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]); - render_layer_popups(output, damage, + render_layer_popups(ctx, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND]); - render_layer_popups(output, damage, + render_layer_popups(ctx, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM]); - render_layer_popups(output, damage, + render_layer_popups(ctx, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]); } - render_seatops(output, damage); + render_seatops(ctx); struct sway_seat *seat = input_manager_current_seat(); struct sway_container *focus = seat_get_focused_container(seat); @@ -2032,51 +1817,25 @@ void output_render(struct sway_output *output, struct timespec *when, .discard_transparent = false, .shadow = false, }; - render_view_popups(focus->view, output, damage, deco_data); + render_view_popups(ctx, focus->view, deco_data); } render_overlay: - render_layer_toplevel(output, damage, + render_layer_toplevel(ctx, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY]); - render_layer_popups(output, damage, + render_layer_popups(ctx, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY]); - render_drag_icons(output, damage, &root->drag_icons); + render_drag_icons(ctx, &root->drag_icons); renderer_end: // Not needed if we damaged the whole viewport - if (!renderer->blur_buffer_dirty) { + if (!effect_fbos->blur_buffer_dirty) { // Render the saved pixels over the blur artifacts - // TODO: Investigate blitting instead - render_whole_output(renderer, wlr_output, &renderer->blur_padding_region, - &renderer->blur_saved_pixels_buffer.texture); + fx_renderer_read_to_buffer(ctx->pass, &renderer->blur_padding_region, + ctx->pass->buffer, + ctx->pass->fx_effect_framebuffers->blur_saved_pixels_buffer, true); } - fx_renderer_end(output->renderer); - fx_renderer_scissor(NULL); - - // Draw the software cursors - wlr_renderer_begin(output->server->wlr_renderer, wlr_output->width, wlr_output->height); - wlr_output_render_software_cursors(wlr_output, damage); - wlr_renderer_end(output->server->wlr_renderer); - - pixman_region32_t frame_damage; - pixman_region32_init(&frame_damage); - - enum wl_output_transform transform = wlr_output_transform_invert(wlr_output->transform); - wlr_region_transform(&frame_damage, damage, transform, output_width, output_height); - - if (debug.damage != DAMAGE_DEFAULT) { - pixman_region32_union_rect(&frame_damage, &frame_damage, - 0, 0, wlr_output->width, wlr_output->height); - } - - wlr_output_set_damage(wlr_output, &frame_damage); - pixman_region32_fini(&frame_damage); - - if (!wlr_output_commit(wlr_output)) { - return; - } - - wlr_damage_ring_rotate(&output->damage_ring); - output->last_frame = *when; + pixman_region32_fini(&transformed_damage); + wlr_output_add_software_cursors_to_render_pass(wlr_output, &ctx->pass->base, damage); } diff --git a/sway/desktop/surface.c b/sway/desktop/surface.c index 1d7b536d..5932eaa2 100644 --- a/sway/desktop/surface.c +++ b/sway/desktop/surface.c @@ -2,8 +2,10 @@ #include #include #include +#include #include "sway/server.h" #include "sway/surface.h" +#include "sway/output.h" static void handle_destroy(struct wl_listener *listener, void *data) { struct sway_surface *surface = wl_container_of(listener, surface, destroy); @@ -44,3 +46,27 @@ void handle_compositor_new_surface(struct wl_listener *listener, void *data) { wl_resource_post_no_memory(wlr_surface->resource); } } + +void surface_update_outputs(struct wlr_surface *surface) { + float scale = 1; + struct wlr_surface_output *surface_output; + wl_list_for_each(surface_output, &surface->current_outputs, link) { + if (surface_output->output->scale > scale) { + scale = surface_output->output->scale; + } + } + wlr_fractional_scale_v1_notify_scale(surface, scale); + wlr_surface_set_preferred_buffer_scale(surface, ceil(scale)); +} + +void surface_enter_output(struct wlr_surface *surface, + struct sway_output *output) { + wlr_surface_send_enter(surface, output->wlr_output); + surface_update_outputs(surface); +} + +void surface_leave_output(struct wlr_surface *surface, + struct sway_output *output) { + wlr_surface_send_leave(surface, output->wlr_output); + surface_update_outputs(surface); +} diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c index f5a3a053..6947e138 100644 --- a/sway/desktop/transaction.c +++ b/sway/desktop/transaction.c @@ -344,7 +344,7 @@ static void transaction_progress(void) { server.queued_transaction = NULL; if (!server.pending_transaction) { - sway_idle_inhibit_v1_check_active(server.idle_inhibit_manager_v1); + sway_idle_inhibit_v1_check_active(); return; } diff --git a/sway/desktop/xdg_shell.c b/sway/desktop/xdg_shell.c index 2d79727d..6dde98bd 100644 --- a/sway/desktop/xdg_shell.c +++ b/sway/desktop/xdg_shell.c @@ -67,7 +67,13 @@ 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_popup; - struct sway_output *output = view->container->pending.workspace->output; + struct sway_workspace *workspace = view->container->pending.workspace; + if (!workspace) { + // is null if in the scratchpad + return; + } + + struct sway_output *output = workspace->output; // the output box expressed in the coordinate system of the toplevel parent // of the popup @@ -98,8 +104,8 @@ static struct sway_xdg_popup *popup_create( wl_signal_add(&xdg_surface->events.destroy, &popup->destroy); popup->destroy.notify = popup_handle_destroy; - wl_signal_add(&xdg_surface->events.map, &popup->child.surface_map); - wl_signal_add(&xdg_surface->events.unmap, &popup->child.surface_unmap); + wl_signal_add(&xdg_surface->surface->events.map, &popup->child.surface_map); + wl_signal_add(&xdg_surface->surface->events.unmap, &popup->child.surface_unmap); popup_unconstrain(popup); @@ -163,12 +169,19 @@ static void set_tiled(struct sway_view *view, bool tiled) { if (xdg_shell_view_from_view(view) == NULL) { return; } - enum wlr_edges edges = WLR_EDGE_NONE; - if (tiled) { - edges = WLR_EDGE_LEFT | WLR_EDGE_RIGHT | WLR_EDGE_TOP | - WLR_EDGE_BOTTOM; + if (wl_resource_get_version(view->wlr_xdg_toplevel->resource) >= + XDG_TOPLEVEL_STATE_TILED_LEFT_SINCE_VERSION) { + 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(view->wlr_xdg_toplevel, edges); + } else { + // The version is too low for the tiled state; configure as maximized instead + // to stop the client from drawing decorations outside of the toplevel geometry. + wlr_xdg_toplevel_set_maximized(view->wlr_xdg_toplevel, tiled); } - wlr_xdg_toplevel_set_tiled(view->wlr_xdg_toplevel, edges); } static void set_fullscreen(struct sway_view *view, bool fullscreen) { @@ -288,6 +301,11 @@ static void handle_commit(struct wl_listener *listener, void *data) { memcpy(&view->geometry, &new_geo, sizeof(struct wlr_box)); if (container_is_floating(view->container)) { view_update_size(view); + // Only set the toplevel size the current container actually has a size. + if (view->container->current.width) { + wlr_xdg_toplevel_set_size(view->wlr_xdg_toplevel, view->geometry.width, + view->geometry.height); + } transaction_commit_dirty_client(); } else { view_center_surface(view); @@ -356,7 +374,7 @@ static void handle_request_fullscreen(struct wl_listener *listener, void *data) struct wlr_xdg_toplevel *toplevel = xdg_shell_view->view.wlr_xdg_toplevel; struct sway_view *view = &xdg_shell_view->view; - if (!toplevel->base->mapped) { + if (!toplevel->base->surface->mapped) { return; } @@ -550,10 +568,10 @@ void handle_xdg_shell_surface(struct wl_listener *listener, void *data) { 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); + wl_signal_add(&xdg_surface->surface->events.map, &xdg_shell_view->map); xdg_shell_view->unmap.notify = handle_unmap; - wl_signal_add(&xdg_surface->events.unmap, &xdg_shell_view->unmap); + wl_signal_add(&xdg_surface->surface->events.unmap, &xdg_shell_view->unmap); xdg_shell_view->destroy.notify = handle_destroy; wl_signal_add(&xdg_surface->events.destroy, &xdg_shell_view->destroy); diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c index 55a14c0b..709795e8 100644 --- a/sway/desktop/xwayland.c +++ b/sway/desktop/xwayland.c @@ -125,8 +125,10 @@ 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) { + struct sway_xwayland_unmanaged *surface = + wl_container_of(listener, surface, request_activate); + struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface; + if (xsurface->surface == NULL || !xsurface->surface->mapped) { return; } struct sway_seat *seat = input_manager_current_seat(); @@ -138,12 +140,29 @@ static void unmanaged_handle_request_activate(struct wl_listener *listener, void seat_set_focus_surface(seat, xsurface->surface, false); } +static void unmanaged_handle_associate(struct wl_listener *listener, void *data) { + struct sway_xwayland_unmanaged *surface = + wl_container_of(listener, surface, associate); + struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface; + wl_signal_add(&xsurface->surface->events.map, &surface->map); + surface->map.notify = unmanaged_handle_map; + wl_signal_add(&xsurface->surface->events.unmap, &surface->unmap); + surface->unmap.notify = unmanaged_handle_unmap; +} + +static void unmanaged_handle_dissociate(struct wl_listener *listener, void *data) { + struct sway_xwayland_unmanaged *surface = + wl_container_of(listener, surface, dissociate); + wl_list_remove(&surface->map.link); + wl_list_remove(&surface->unmap.link); +} + static void unmanaged_handle_destroy(struct wl_listener *listener, void *data) { struct sway_xwayland_unmanaged *surface = wl_container_of(listener, surface, destroy); wl_list_remove(&surface->request_configure.link); - wl_list_remove(&surface->map.link); - wl_list_remove(&surface->unmap.link); + wl_list_remove(&surface->associate.link); + wl_list_remove(&surface->dissociate.link); wl_list_remove(&surface->destroy.link); wl_list_remove(&surface->override_redirect.link); wl_list_remove(&surface->request_activate.link); @@ -151,6 +170,7 @@ static void unmanaged_handle_destroy(struct wl_listener *listener, void *data) { } static void handle_map(struct wl_listener *listener, void *data); +static void handle_associate(struct wl_listener *listener, void *data); struct sway_xwayland_view *create_xwayland_view(struct wlr_xwayland_surface *xsurface); @@ -159,14 +179,22 @@ static void unmanaged_handle_override_redirect(struct wl_listener *listener, voi wl_container_of(listener, surface, override_redirect); struct wlr_xwayland_surface *xsurface = surface->wlr_xwayland_surface; - bool mapped = xsurface->mapped; + bool associated = xsurface->surface != NULL; + bool mapped = associated && xsurface->surface->mapped; if (mapped) { unmanaged_handle_unmap(&surface->unmap, NULL); } + if (associated) { + unmanaged_handle_dissociate(&surface->dissociate, NULL); + } unmanaged_handle_destroy(&surface->destroy, NULL); xsurface->data = NULL; + struct sway_xwayland_view *xwayland_view = create_xwayland_view(xsurface); + if (associated) { + handle_associate(&xwayland_view->associate, NULL); + } if (mapped) { handle_map(&xwayland_view->map, xsurface); } @@ -186,10 +214,10 @@ static struct sway_xwayland_unmanaged *create_unmanaged( wl_signal_add(&xsurface->events.request_configure, &surface->request_configure); surface->request_configure.notify = unmanaged_handle_request_configure; - wl_signal_add(&xsurface->events.map, &surface->map); - surface->map.notify = unmanaged_handle_map; - wl_signal_add(&xsurface->events.unmap, &surface->unmap); - surface->unmap.notify = unmanaged_handle_unmap; + wl_signal_add(&xsurface->events.associate, &surface->associate); + surface->associate.notify = unmanaged_handle_associate; + wl_signal_add(&xsurface->events.dissociate, &surface->dissociate); + surface->dissociate.notify = unmanaged_handle_dissociate; wl_signal_add(&xsurface->events.destroy, &surface->destroy); surface->destroy.notify = unmanaged_handle_destroy; wl_signal_add(&xsurface->events.set_override_redirect, &surface->override_redirect); @@ -472,8 +500,8 @@ static void handle_destroy(struct wl_listener *listener, void *data) { 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); - wl_list_remove(&xwayland_view->map.link); - wl_list_remove(&xwayland_view->unmap.link); + wl_list_remove(&xwayland_view->associate.link); + wl_list_remove(&xwayland_view->dissociate.link); wl_list_remove(&xwayland_view->override_redirect.link); view_begin_destroy(&xwayland_view->view); } @@ -495,8 +523,8 @@ static void handle_unmap(struct wl_listener *listener, void *data) { static void handle_map(struct wl_listener *listener, void *data) { struct sway_xwayland_view *xwayland_view = wl_container_of(listener, xwayland_view, map); - struct wlr_xwayland_surface *xsurface = data; struct sway_view *view = &xwayland_view->view; + struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; view->natural_width = xsurface->width; view->natural_height = xsurface->height; @@ -512,20 +540,30 @@ static void handle_map(struct wl_listener *listener, void *data) { transaction_commit_dirty(); } +static void handle_dissociate(struct wl_listener *listener, void *data); + static void handle_override_redirect(struct wl_listener *listener, void *data) { struct sway_xwayland_view *xwayland_view = wl_container_of(listener, xwayland_view, override_redirect); - struct wlr_xwayland_surface *xsurface = data; struct sway_view *view = &xwayland_view->view; + struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; - bool mapped = xsurface->mapped; + bool associated = xsurface->surface != NULL; + bool mapped = associated && xsurface->surface->mapped; if (mapped) { handle_unmap(&xwayland_view->unmap, NULL); } + if (associated) { + handle_dissociate(&xwayland_view->dissociate, NULL); + } handle_destroy(&xwayland_view->destroy, view); xsurface->data = NULL; + struct sway_xwayland_unmanaged *unmanaged = create_unmanaged(xsurface); + if (associated) { + unmanaged_handle_associate(&unmanaged->associate, NULL); + } if (mapped) { unmanaged_handle_map(&unmanaged->map, xsurface); } @@ -537,7 +575,7 @@ static void handle_request_configure(struct wl_listener *listener, void *data) { struct wlr_xwayland_surface_configure_event *ev = data; struct sway_view *view = &xwayland_view->view; struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; - if (!xsurface->mapped) { + if (xsurface->surface == NULL || !xsurface->surface->mapped) { wlr_xwayland_surface_configure(xsurface, ev->x, ev->y, ev->width, ev->height); return; @@ -566,7 +604,7 @@ static void handle_request_fullscreen(struct wl_listener *listener, void *data) wl_container_of(listener, xwayland_view, request_fullscreen); struct sway_view *view = &xwayland_view->view; struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; - if (!xsurface->mapped) { + if (xsurface->surface == NULL || !xsurface->surface->mapped) { return; } container_set_fullscreen(view->container, xsurface->fullscreen); @@ -581,7 +619,7 @@ static void handle_request_minimize(struct wl_listener *listener, void *data) { wl_container_of(listener, xwayland_view, request_minimize); struct sway_view *view = &xwayland_view->view; struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; - if (!xsurface->mapped) { + if (xsurface->surface == NULL || !xsurface->surface->mapped) { return; } @@ -617,7 +655,7 @@ static void handle_request_move(struct wl_listener *listener, void *data) { wl_container_of(listener, xwayland_view, request_move); struct sway_view *view = &xwayland_view->view; struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; - if (!xsurface->mapped) { + if (xsurface->surface == NULL || !xsurface->surface->mapped) { return; } if (!container_is_floating(view->container) || @@ -633,7 +671,7 @@ static void handle_request_resize(struct wl_listener *listener, void *data) { wl_container_of(listener, xwayland_view, request_resize); struct sway_view *view = &xwayland_view->view; struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; - if (!xsurface->mapped) { + if (xsurface->surface == NULL || !xsurface->surface->mapped) { return; } if (!container_is_floating(view->container)) { @@ -649,10 +687,10 @@ static void handle_request_activate(struct wl_listener *listener, void *data) { wl_container_of(listener, xwayland_view, request_activate); struct sway_view *view = &xwayland_view->view; struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; - if (!xsurface->mapped) { + if (xsurface->surface == NULL || !xsurface->surface->mapped) { return; } - view_request_activate(view); + view_request_activate(view, NULL); transaction_commit_dirty(); } @@ -662,7 +700,7 @@ static void handle_set_title(struct wl_listener *listener, void *data) { wl_container_of(listener, xwayland_view, set_title); struct sway_view *view = &xwayland_view->view; struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; - if (!xsurface->mapped) { + if (xsurface->surface == NULL || !xsurface->surface->mapped) { return; } view_update_title(view, false); @@ -674,7 +712,7 @@ static void handle_set_class(struct wl_listener *listener, void *data) { wl_container_of(listener, xwayland_view, set_class); struct sway_view *view = &xwayland_view->view; struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; - if (!xsurface->mapped) { + if (xsurface->surface == NULL || !xsurface->surface->mapped) { return; } view_execute_criteria(view); @@ -685,7 +723,7 @@ static void handle_set_role(struct wl_listener *listener, void *data) { wl_container_of(listener, xwayland_view, set_role); struct sway_view *view = &xwayland_view->view; struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; - if (!xsurface->mapped) { + if (xsurface->surface == NULL || !xsurface->surface->mapped) { return; } view_execute_criteria(view); @@ -721,7 +759,7 @@ static void handle_set_window_type(struct wl_listener *listener, void *data) { wl_container_of(listener, xwayland_view, set_window_type); struct sway_view *view = &xwayland_view->view; struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; - if (!xsurface->mapped) { + if (xsurface->surface == NULL || !xsurface->surface->mapped) { return; } view_execute_criteria(view); @@ -732,7 +770,7 @@ static void handle_set_hints(struct wl_listener *listener, void *data) { wl_container_of(listener, xwayland_view, set_hints); struct sway_view *view = &xwayland_view->view; struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; - if (!xsurface->mapped) { + if (xsurface->surface == NULL || !xsurface->surface->mapped) { return; } const bool hints_urgency = xcb_icccm_wm_hints_get_urgency(xsurface->hints); @@ -747,6 +785,24 @@ static void handle_set_hints(struct wl_listener *listener, void *data) { } } +static void handle_associate(struct wl_listener *listener, void *data) { + struct sway_xwayland_view *xwayland_view = + wl_container_of(listener, xwayland_view, associate); + struct wlr_xwayland_surface *xsurface = + xwayland_view->view.wlr_xwayland_surface; + wl_signal_add(&xsurface->surface->events.unmap, &xwayland_view->unmap); + xwayland_view->unmap.notify = handle_unmap; + wl_signal_add(&xsurface->surface->events.map, &xwayland_view->map); + xwayland_view->map.notify = handle_map; +} + +static void handle_dissociate(struct wl_listener *listener, void *data) { + struct sway_xwayland_view *xwayland_view = + wl_container_of(listener, xwayland_view, dissociate); + wl_list_remove(&xwayland_view->map.link); + wl_list_remove(&xwayland_view->unmap.link); +} + struct sway_view *view_from_wlr_xwayland_surface( struct wlr_xwayland_surface *xsurface) { return xsurface->data; @@ -816,11 +872,11 @@ struct sway_xwayland_view *create_xwayland_view(struct wlr_xwayland_surface *xsu &xwayland_view->set_decorations); xwayland_view->set_decorations.notify = handle_set_decorations; - wl_signal_add(&xsurface->events.unmap, &xwayland_view->unmap); - xwayland_view->unmap.notify = handle_unmap; + wl_signal_add(&xsurface->events.associate, &xwayland_view->associate); + xwayland_view->associate.notify = handle_associate; - wl_signal_add(&xsurface->events.map, &xwayland_view->map); - xwayland_view->map.notify = handle_map; + wl_signal_add(&xsurface->events.dissociate, &xwayland_view->dissociate); + xwayland_view->dissociate.notify = handle_dissociate; wl_signal_add(&xsurface->events.set_override_redirect, &xwayland_view->override_redirect); diff --git a/sway/input/cursor.c b/sway/input/cursor.c index a5f1204b..36aab93e 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c @@ -7,7 +7,7 @@ #include #include #include -#include +#include #include #include #include @@ -53,12 +53,10 @@ static struct wlr_surface *layer_surface_at(struct sway_output *output, } static bool surface_is_xdg_popup(struct wlr_surface *surface) { - if (wlr_surface_is_xdg_surface(surface)) { - struct wlr_xdg_surface *xdg_surface = - wlr_xdg_surface_from_wlr_surface(surface); - return xdg_surface->role == WLR_XDG_SURFACE_ROLE_POPUP; - } - return false; + struct wlr_xdg_surface *xdg_surface = + wlr_xdg_surface_try_from_wlr_surface(surface); + return xdg_surface != NULL && xdg_surface->role == WLR_XDG_SURFACE_ROLE_POPUP && + xdg_surface->popup != NULL; } static struct wlr_surface *layer_surface_popup_at(struct sway_output *output, @@ -239,7 +237,7 @@ void cursor_update_image(struct sway_cursor *cursor, // Try a node's resize edge enum wlr_edges edge = find_resize_edge(node->sway_container, NULL, cursor); if (edge == WLR_EDGE_NONE) { - cursor_set_image(cursor, "left_ptr", NULL); + cursor_set_image(cursor, "default", NULL); } else if (container_is_floating(node->sway_container)) { cursor_set_image(cursor, wlr_xcursor_get_resize_name(edge), NULL); } else { @@ -250,12 +248,12 @@ void cursor_update_image(struct sway_cursor *cursor, } } } else { - cursor_set_image(cursor, "left_ptr", NULL); + cursor_set_image(cursor, "default", NULL); } } static void cursor_hide(struct sway_cursor *cursor) { - wlr_cursor_set_image(cursor->cursor, NULL, 0, 0, 0, 0, 0, 0); + wlr_cursor_unset_image(cursor->cursor); cursor->hidden = true; wlr_seat_pointer_notify_clear_focus(cursor->seat->wlr_seat); } @@ -367,7 +365,7 @@ void cursor_unhide(struct sway_cursor *cursor) { wl_event_source_timer_update(cursor->hide_source, cursor_get_timeout(cursor)); } -static void pointer_motion(struct sway_cursor *cursor, uint32_t time_msec, +void pointer_motion(struct sway_cursor *cursor, uint32_t time_msec, struct wlr_input_device *device, double dx, double dy, double dx_unaccel, double dy_unaccel) { wlr_relative_pointer_manager_v1_send_relative_motion( @@ -482,43 +480,16 @@ static void handle_touch_down(struct wl_listener *listener, void *data) { cursor_hide(cursor); 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->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); seat->touch_id = event->touch_id; seat->touch_x = lx; seat->touch_y = ly; - if (surface && wlr_surface_accepts_touch(wlr_seat, surface)) { - if (seat_is_input_allowed(seat, surface)) { - wlr_seat_touch_notify_down(wlr_seat, surface, event->time_msec, - event->touch_id, sx, sy); - - if (focused_node) { - seat_set_focus(seat, focused_node); - } - } - } else if (!cursor->simulating_pointer_from_touch && - (!surface || seat_is_input_allowed(seat, surface))) { - // Fallback to cursor simulation. - // The pointer_touch_id state is needed, so drags are not aborted when over - // a surface supporting touch and multi touch events don't interfere. - cursor->simulating_pointer_from_touch = true; - cursor->pointer_touch_id = seat->touch_id; - double dx, dy; - dx = lx - cursor->cursor->x; - dy = ly - cursor->cursor->y; - 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); - } + seatop_touch_down(seat, event, lx, ly); } static void handle_touch_up(struct wl_listener *listener, void *data) { @@ -526,7 +497,7 @@ static void handle_touch_up(struct wl_listener *listener, void *data) { 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; + struct sway_seat *seat = cursor->seat; if (cursor->simulating_pointer_from_touch) { if (cursor->pointer_touch_id == cursor->seat->touch_id) { @@ -535,7 +506,25 @@ static void handle_touch_up(struct wl_listener *listener, void *data) { event->time_msec, BTN_LEFT, WLR_BUTTON_RELEASED); } } else { - wlr_seat_touch_notify_up(wlr_seat, event->time_msec, event->touch_id); + seatop_touch_up(seat, event); + } +} + +static void handle_touch_cancel(struct wl_listener *listener, void *data) { + struct sway_cursor *cursor = wl_container_of(listener, cursor, touch_cancel); + struct wlr_touch_cancel_event *event = data; + cursor_handle_activity_from_device(cursor, &event->touch->base); + + struct sway_seat *seat = cursor->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->touch->base, + event->time_msec, BTN_LEFT, WLR_BUTTON_RELEASED); + } + } else { + seatop_touch_cancel(seat, event); } } @@ -546,19 +535,14 @@ static void handle_touch_motion(struct wl_listener *listener, void *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->touch->base, event->x, event->y, &lx, &ly); - double sx, sy; - node_at_coords(cursor->seat, lx, ly, &surface, &sx, &sy); if (seat->touch_id == event->touch_id) { seat->touch_x = lx; seat->touch_y = ly; - struct sway_drag_icon *drag_icon; wl_list_for_each(drag_icon, &root->drag_icons, link) { if (drag_icon->seat == seat) { @@ -575,9 +559,8 @@ static void handle_touch_motion(struct wl_listener *listener, void *data) { 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, - event->touch_id, sx, sy); + } else { + seatop_touch_motion(seat, event, lx, ly); } } @@ -836,7 +819,34 @@ static void handle_tool_button(struct wl_listener *listener, void *data) { node_at_coords(cursor->seat, cursor->cursor->x, cursor->cursor->y, &surface, &sx, &sy); - if (!surface || !wlr_surface_accepts_tablet_v2(tablet_v2, surface)) { + // TODO: floating resize should support graphics tablet events + struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(cursor->seat->wlr_seat); + uint32_t modifiers = keyboard ? wlr_keyboard_get_modifiers(keyboard) : 0; + bool mod_pressed = modifiers & config->floating_mod; + + bool surface_supports_tablet_events = + surface && wlr_surface_accepts_tablet_v2(tablet_v2, surface); + + // Simulate pointer when: + // 1. The modifier key is pressed, OR + // 2. The surface under the cursor does not support tablet events. + bool should_simulate_pointer = mod_pressed || !surface_supports_tablet_events; + + // Similar to tool tip, we need to selectively simulate mouse events, but we + // want to make sure that it is always consistent. Because all tool buttons + // currently map to BTN_RIGHT, we need to keep count of how many tool + // buttons are currently pressed down so we can send consistent events. + // + // The logic follows: + // - If we are already simulating the pointer, we should continue to do so + // until at least no tool button is held down. + // - If we should simulate the pointer and no tool button is currently held + // down, begin simulating the pointer. + // - If neither of the above are true, send the tablet events. + if ((cursor->tool_buttons > 0 && cursor->simulating_pointer_from_tool_button) + || (cursor->tool_buttons == 0 && should_simulate_pointer)) { + cursor->simulating_pointer_from_tool_button = true; + // TODO: the user may want to configure which tool buttons are mapped to // which simulated pointer buttons switch (event->state) { @@ -845,22 +855,35 @@ static void handle_tool_button(struct wl_listener *listener, void *data) { 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) { + if (cursor->tool_buttons <= 1) { dispatch_cursor_button(cursor, &event->tablet->base, event->time_msec, BTN_RIGHT, event->state); } - cursor->tool_buttons--; break; } wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat); - return; + } else { + cursor->simulating_pointer_from_tool_button = false; + + wlr_tablet_v2_tablet_tool_notify_button(sway_tool->tablet_v2_tool, + event->button, (enum zwp_tablet_pad_v2_button_state)event->state); } - wlr_tablet_v2_tablet_tool_notify_button(sway_tool->tablet_v2_tool, - event->button, (enum zwp_tablet_pad_v2_button_state)event->state); + // Update tool button count. + switch (event->state) { + case WLR_BUTTON_PRESSED: + cursor->tool_buttons++; + break; + case WLR_BUTTON_RELEASED: + if (cursor->tool_buttons == 0) { + sway_log(SWAY_ERROR, "inconsistent tablet tool button events"); + } else { + cursor->tool_buttons--; + } + break; + } } static void check_constraint_region(struct sway_cursor *cursor) { @@ -1046,10 +1069,9 @@ void cursor_set_image(struct sway_cursor *cursor, const char *image, } if (!image) { - wlr_cursor_set_image(cursor->cursor, NULL, 0, 0, 0, 0, 0, 0); + wlr_cursor_unset_image(cursor->cursor); } else if (!current_image || strcmp(current_image, image) != 0) { - wlr_xcursor_manager_set_cursor_image(cursor->xcursor_manager, image, - cursor->cursor); + wlr_cursor_set_xcursor(cursor->cursor, cursor->xcursor_manager, image); } } @@ -1096,6 +1118,7 @@ void sway_cursor_destroy(struct sway_cursor *cursor) { wl_list_remove(&cursor->frame.link); wl_list_remove(&cursor->touch_down.link); wl_list_remove(&cursor->touch_up.link); + wl_list_remove(&cursor->touch_cancel.link); wl_list_remove(&cursor->touch_motion.link); wl_list_remove(&cursor->touch_frame.link); wl_list_remove(&cursor->tool_axis.link); @@ -1132,9 +1155,6 @@ 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); - 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); @@ -1177,6 +1197,9 @@ struct sway_cursor *sway_cursor_create(struct sway_seat *seat) { wl_signal_add(&wlr_cursor->events.touch_up, &cursor->touch_up); cursor->touch_up.notify = handle_touch_up; + wl_signal_add(&wlr_cursor->events.touch_cancel, &cursor->touch_cancel); + cursor->touch_cancel.notify = handle_touch_cancel; + wl_signal_add(&wlr_cursor->events.touch_motion, &cursor->touch_motion); cursor->touch_motion.notify = handle_touch_motion; @@ -1269,11 +1292,7 @@ uint32_t get_mouse_bindsym(const char *name, char **error) { // Get event code from name int code = libevdev_event_code_from_name(EV_KEY, name); if (code == -1) { - size_t len = snprintf(NULL, 0, "Unknown event %s", name) + 1; - *error = malloc(len); - if (*error) { - snprintf(*error, len, "Unknown event %s", name); - } + *error = format_str("Unknown event %s", name); return 0; } return code; @@ -1295,13 +1314,8 @@ uint32_t get_mouse_bindcode(const char *name, char **error) { } const char *event = libevdev_event_code_get_name(EV_KEY, code); if (!event || strncmp(event, "BTN_", strlen("BTN_")) != 0) { - size_t len = snprintf(NULL, 0, "Event code %d (%s) is not a button", - code, event ? event : "(null)") + 1; - *error = malloc(len); - if (*error) { - snprintf(*error, len, "Event code %d (%s) is not a button", - code, event ? event : "(null)"); - } + *error = format_str("Event code %d (%s) is not a button", + code, event ? event : "(null)"); return 0; } return code; @@ -1454,3 +1468,26 @@ void sway_cursor_constrain(struct sway_cursor *cursor, wl_signal_add(&constraint->surface->events.commit, &cursor->constraint_commit); } + +void handle_request_set_cursor_shape(struct wl_listener *listener, void *data) { + const struct wlr_cursor_shape_manager_v1_request_set_shape_event *event = data; + struct sway_seat *seat = event->seat_client->seat->data; + + if (!seatop_allows_set_cursor(seat)) { + return; + } + + struct wl_client *focused_client = NULL; + struct wlr_surface *focused_surface = seat->wlr_seat->pointer_state.focused_surface; + if (focused_surface != NULL) { + focused_client = wl_resource_get_client(focused_surface->resource); + } + + // TODO: check cursor mode + if (focused_client == NULL || event->seat_client->client != focused_client) { + sway_log(SWAY_DEBUG, "denying request to set cursor from unfocused client"); + return; + } + + cursor_set_image(seat->cursor, wlr_cursor_shape_v1_name(event->shape), focused_client); +} diff --git a/sway/input/input-manager.c b/sway/input/input-manager.c index 39f4b795..288fddc4 100644 --- a/sway/input/input-manager.c +++ b/sway/input/input-manager.c @@ -3,7 +3,7 @@ #include #include #include -#include +#include #include #include #include @@ -22,6 +22,10 @@ #include "list.h" #include "log.h" +#if WLR_HAS_LIBINPUT_BACKEND +#include +#endif + #define DEFAULT_SEAT "seat0" struct input_config *current_input_config = NULL; @@ -76,20 +80,13 @@ char *input_device_get_identifier(struct wlr_input_device *device) { } } - const char *fmt = "%d:%d:%s"; - int len = snprintf(NULL, 0, fmt, vendor, product, name) + 1; - char *identifier = malloc(len); - if (!identifier) { - sway_log(SWAY_ERROR, "Unable to allocate unique input device name"); - return NULL; - } - - snprintf(identifier, len, fmt, vendor, product, name); + char *identifier = format_str("%d:%d:%s", vendor, product, name); free(name); return identifier; } static bool device_is_touchpad(struct sway_input_device *device) { +#if WLR_HAS_LIBINPUT_BACKEND if (device->wlr_device->type != WLR_INPUT_DEVICE_POINTER || !wlr_input_device_is_libinput(device->wlr_device)) { return false; @@ -99,6 +96,9 @@ static bool device_is_touchpad(struct sway_input_device *device) { wlr_libinput_get_device_handle(device->wlr_device); return libinput_device_config_tap_get_finger_count(libinput_device) > 0; +#else + return false; +#endif } const char *input_device_get_type(struct sway_input_device *device) { @@ -236,7 +236,11 @@ static void handle_new_input(struct wl_listener *listener, void *data) { apply_input_type_config(input_device); +#if WLR_HAS_LIBINPUT_BACKEND bool config_changed = sway_input_configure_libinput_device(input_device); +#else + bool config_changed = false; +#endif wl_signal_add(&device->events.destroy, &input_device->device_destroy); input_device->device_destroy.notify = handle_device_destroy; @@ -491,6 +495,8 @@ struct sway_input_manager *input_manager_create(struct sway_server *server) { wl_signal_add(&input->keyboard_shortcuts_inhibit->events.new_inhibitor, &input->keyboard_shortcuts_inhibit_new_inhibitor); + input->pointer_gestures = wlr_pointer_gestures_v1_create(server->wl_display); + return input; } @@ -528,11 +534,27 @@ static void retranslate_keysyms(struct input_config *input_config) { return; } } + + for (int i = 0; i < config->input_type_configs->length; ++i) { + struct input_config *ic = config->input_type_configs->items[i]; + if (ic->xkb_layout || ic->xkb_file) { + // this is the first config with xkb_layout or xkb_file + if (ic->identifier == input_config->identifier) { + translate_keysyms(ic); + } + + return; + } + } } static void input_manager_configure_input( struct sway_input_device *input_device) { +#if WLR_HAS_LIBINPUT_BACKEND bool config_changed = sway_input_configure_libinput_device(input_device); +#else + bool config_changed = false; +#endif struct sway_seat *seat = NULL; wl_list_for_each(seat, &server.input->seats, link) { seat_configure_device(seat, input_device); @@ -542,10 +564,20 @@ static void input_manager_configure_input( } } -void input_manager_configure_all_inputs(void) { - struct sway_input_device *input_device = NULL; +void input_manager_configure_all_input_mappings(void) { + struct sway_input_device *input_device; wl_list_for_each(input_device, &server.input->devices, link) { - input_manager_configure_input(input_device); + struct sway_seat *seat; + wl_list_for_each(seat, &server.input->seats, link) { + seat_configure_device_mapping(seat, input_device); + } + +#if WLR_HAS_LIBINPUT_BACKEND + // Input devices mapped to unavailable outputs get their libinput + // send_events setting switched to false. We need to re-enable this + // when the output appears. + sway_input_configure_libinput_device_send_events(input_device); +#endif } } @@ -567,7 +599,9 @@ void input_manager_apply_input_config(struct input_config *input_config) { } void input_manager_reset_input(struct sway_input_device *input_device) { +#if WLR_HAS_LIBINPUT_BACKEND sway_input_reset_libinput_device(input_device); +#endif struct sway_seat *seat = NULL; wl_list_for_each(seat, &server.input->seats, link) { seat_reset_device(seat, input_device); diff --git a/sway/input/keyboard.c b/sway/input/keyboard.c index c5a646c4..8927287f 100644 --- a/sway/input/keyboard.c +++ b/sway/input/keyboard.c @@ -1,10 +1,9 @@ #include #include #include +#include #include -#include #include -#include #include #include #include @@ -16,6 +15,10 @@ #include "sway/ipc-server.h" #include "log.h" +#if WLR_HAS_SESSION +#include +#endif + static struct modifier_key { char *name; uint32_t mod; @@ -264,14 +267,12 @@ static bool keyboard_execute_compositor_binding(struct sway_keyboard *keyboard, xkb_keysym_t keysym = pressed_keysyms[i]; if (keysym >= XKB_KEY_XF86Switch_VT_1 && keysym <= XKB_KEY_XF86Switch_VT_12) { - if (wlr_backend_is_multi(server.backend)) { - struct wlr_session *session = - wlr_backend_get_session(server.backend); - if (session) { - unsigned vt = keysym - XKB_KEY_XF86Switch_VT_1 + 1; - wlr_session_change_vt(session, vt); - } +#if WLR_HAS_SESSION + if (server.session) { + unsigned vt = keysym - XKB_KEY_XF86Switch_VT_1 + 1; + wlr_session_change_vt(server.session, vt); } +#endif return true; } } @@ -715,23 +716,11 @@ struct sway_keyboard *sway_keyboard_create(struct sway_seat *seat, static void handle_xkb_context_log(struct xkb_context *context, enum xkb_log_level level, const char *format, va_list args) { - va_list args_copy; - va_copy(args_copy, args); - size_t length = vsnprintf(NULL, 0, format, args_copy) + 1; - va_end(args_copy); + char *error = vformat_str(format, args); - char *error = malloc(length); - if (!error) { - sway_log(SWAY_ERROR, "Failed to allocate libxkbcommon log message"); - return; - } - - va_copy(args_copy, args); - vsnprintf(error, length, format, args_copy); - va_end(args_copy); - - if (error[length - 2] == '\n') { - error[length - 2] = '\0'; + size_t len = strlen(error); + if (error[len - 1] == '\n') { + error[len - 1] = '\0'; } sway_log_importance_t importance = SWAY_DEBUG; @@ -752,7 +741,7 @@ static void handle_xkb_context_log(struct xkb_context *context, struct xkb_keymap *sway_keyboard_compile_keymap(struct input_config *ic, char **error) { - struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); + struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_SECURE_GETENV); if (!sway_assert(context, "cannot create XKB context")) { return NULL; } @@ -766,13 +755,8 @@ struct xkb_keymap *sway_keyboard_compile_keymap(struct input_config *ic, if (!keymap_file) { sway_log_errno(SWAY_ERROR, "cannot read xkb file %s", ic->xkb_file); if (error) { - size_t len = snprintf(NULL, 0, "cannot read xkb file %s: %s", - ic->xkb_file, strerror(errno)) + 1; - *error = malloc(len); - if (*error) { - snprintf(*error, len, "cannot read xkb_file %s: %s", - ic->xkb_file, strerror(errno)); - } + *error = format_str("cannot read xkb file %s: %s", + ic->xkb_file, strerror(errno)); } goto cleanup; } diff --git a/sway/input/libinput.c b/sway/input/libinput.c index 53019301..0266c7a9 100644 --- a/sway/input/libinput.c +++ b/sway/input/libinput.c @@ -79,6 +79,16 @@ static bool set_accel_speed(struct libinput_device *device, double speed) { return true; } +static bool set_rotation_angle(struct libinput_device *device, double angle) { + if (!libinput_device_config_rotation_is_available(device) || + libinput_device_config_rotation_get_angle(device) == angle) { + return false; + } + sway_log(SWAY_DEBUG, "rotation_set_angle(%f)", angle); + log_status(libinput_device_config_rotation_set_angle(device, angle)); + return true; +} + static bool set_accel_profile(struct libinput_device *device, enum libinput_config_accel_profile profile) { if (!libinput_device_config_accel_is_available(device) || @@ -156,6 +166,18 @@ static bool set_scroll_button(struct libinput_device *dev, uint32_t button) { return true; } +static bool set_scroll_button_lock(struct libinput_device *dev, + enum libinput_config_scroll_button_lock_state lock) { + uint32_t scroll = libinput_device_config_scroll_get_methods(dev); + if ((scroll & ~LIBINPUT_CONFIG_SCROLL_NO_SCROLL) == 0 || + libinput_device_config_scroll_get_button_lock(dev) == lock) { + return false; + } + sway_log(SWAY_DEBUG, "scroll_set_button_lock(%" PRIu32 ")", lock); + log_status(libinput_device_config_scroll_set_button_lock(dev, lock)); + return true; +} + static bool set_dwt(struct libinput_device *device, bool dwt) { if (!libinput_device_config_dwt_is_available(device) || libinput_device_config_dwt_get_enabled(device) == dwt) { @@ -197,6 +219,26 @@ static bool set_calibration_matrix(struct libinput_device *dev, float mat[6]) { return changed; } +static bool configure_send_events(struct libinput_device *device, + struct input_config *ic) { + if (ic->mapped_to_output && + strcmp("*", ic->mapped_to_output) != 0 && + !output_by_name_or_id(ic->mapped_to_output)) { + sway_log(SWAY_DEBUG, + "%s '%s' is mapped to offline output '%s'; disabling input", + ic->input_type, ic->identifier, ic->mapped_to_output); + return set_send_events(device, LIBINPUT_CONFIG_SEND_EVENTS_DISABLED); + } else if (ic->send_events != INT_MIN) { + return set_send_events(device, ic->send_events); + } else { + // Have to reset to the default mode here, otherwise if ic->send_events + // is unset and a mapped output just came online after being disabled, + // we'd remain stuck sending no events. + return set_send_events(device, + libinput_device_config_send_events_get_default_mode(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)) { @@ -208,24 +250,7 @@ bool sway_input_configure_libinput_device(struct sway_input_device *input_device sway_log(SWAY_DEBUG, "sway_input_configure_libinput_device('%s' on '%s')", ic->identifier, input_device->identifier); - bool changed = false; - if (ic->mapped_to_output && - !output_by_name_or_id(ic->mapped_to_output)) { - sway_log(SWAY_DEBUG, - "%s '%s' is mapped to offline output '%s'; disabling input", - ic->input_type, ic->identifier, ic->mapped_to_output); - changed |= set_send_events(device, - LIBINPUT_CONFIG_SEND_EVENTS_DISABLED); - } else if (ic->send_events != INT_MIN) { - changed |= set_send_events(device, ic->send_events); - } else { - // Have to reset to the default mode here, otherwise if ic->send_events - // is unset and a mapped output just came online after being disabled, - // we'd remain stuck sending no events. - changed |= set_send_events(device, - libinput_device_config_send_events_get_default_mode(device)); - } - + bool changed = configure_send_events(device, ic); if (ic->tap != INT_MIN) { changed |= set_tap(device, ic->tap); } @@ -241,6 +266,9 @@ bool sway_input_configure_libinput_device(struct sway_input_device *input_device if (ic->pointer_accel != FLT_MIN) { changed |= set_accel_speed(device, ic->pointer_accel); } + if (ic->rotation_angle != FLT_MIN) { + changed |= set_rotation_angle(device, ic->rotation_angle); + } if (ic->accel_profile != INT_MIN) { changed |= set_accel_profile(device, ic->accel_profile); } @@ -262,6 +290,9 @@ bool sway_input_configure_libinput_device(struct sway_input_device *input_device if (ic->scroll_button != INT_MIN) { changed |= set_scroll_button(device, ic->scroll_button); } + if (ic->scroll_button_lock != INT_MIN) { + changed |= set_scroll_button_lock(device, ic->scroll_button_lock); + } if (ic->dwt != INT_MIN) { changed |= set_dwt(device, ic->dwt); } @@ -275,6 +306,22 @@ bool sway_input_configure_libinput_device(struct sway_input_device *input_device return changed; } +void sway_input_configure_libinput_device_send_events( + 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; + } + + struct libinput_device *device = + wlr_libinput_get_device_handle(input_device->wlr_device); + bool changed = configure_send_events(device, ic); + + if (changed) { + ipc_event_input("libinput_config", input_device); + } +} + void sway_input_reset_libinput_device(struct sway_input_device *input_device) { if (!wlr_input_device_is_libinput(input_device->wlr_device)) { return; @@ -298,6 +345,8 @@ void sway_input_reset_libinput_device(struct sway_input_device *input_device) { libinput_device_config_tap_get_default_drag_lock_enabled(device)); changed |= set_accel_speed(device, libinput_device_config_accel_get_default_speed(device)); + changed |= set_rotation_angle(device, + libinput_device_config_rotation_get_default_angle(device)); changed |= set_accel_profile(device, libinput_device_config_accel_get_default_profile(device)); changed |= set_natural_scroll(device, diff --git a/sway/input/seat.c b/sway/input/seat.c index 28210bb5..43289598 100644 --- a/sway/input/seat.c +++ b/sway/input/seat.c @@ -4,9 +4,9 @@ #include #include #include +#include #include #include -#include #include #include #include @@ -99,22 +99,10 @@ void seat_destroy(struct sway_seat *seat) { void seat_idle_notify_activity(struct sway_seat *seat, enum sway_input_idle_source source) { - uint32_t mask = seat->idle_inhibit_sources; - struct wlr_idle_timeout *timeout; - int ntimers = 0, nidle = 0; - wl_list_for_each(timeout, &server.idle->idle_timers, link) { - ++ntimers; - if (timeout->idle_state) { - ++nidle; - } - } - if (nidle == ntimers) { - mask = seat->idle_wake_sources; - } - 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); + if ((source & seat->idle_inhibit_sources) == 0) { + return; } + wlr_idle_notifier_v1_notify_activity(server.idle_notifier_v1, seat->wlr_seat); } /** @@ -366,7 +354,7 @@ static void handle_new_node(struct wl_listener *listener, void *data) { } static void drag_icon_damage_whole(struct sway_drag_icon *icon) { - if (!icon->wlr_drag_icon->mapped) { + if (!icon->wlr_drag_icon->surface->mapped) { return; } desktop_damage_surface(icon->wlr_drag_icon->surface, icon->x, icon->y, true); @@ -382,8 +370,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 + wlr_icon->surface->sx; - icon->y = cursor->y + wlr_icon->surface->sy; + icon->x = cursor->x + icon->dx; + icon->y = cursor->y + icon->dy; break; case WLR_DRAG_GRAB_KEYBOARD_TOUCH:; struct wlr_touch_point *point = @@ -391,8 +379,8 @@ void drag_icon_update_position(struct sway_drag_icon *icon) { if (point == NULL) { return; } - icon->x = seat->touch_x + wlr_icon->surface->sx; - icon->y = seat->touch_y + wlr_icon->surface->sy; + icon->x = seat->touch_x + icon->dx; + icon->y = seat->touch_y + icon->dy; } drag_icon_damage_whole(icon); @@ -402,6 +390,9 @@ static void drag_icon_handle_surface_commit(struct wl_listener *listener, void *data) { struct sway_drag_icon *icon = wl_container_of(listener, icon, surface_commit); + struct wlr_drag_icon *wlr_icon = icon->wlr_drag_icon; + icon->dx += wlr_icon->surface->current.dx; + icon->dy += wlr_icon->surface->current.dy; drag_icon_update_position(icon); } @@ -507,9 +498,9 @@ static void handle_start_drag(struct wl_listener *listener, void *data) { icon->surface_commit.notify = drag_icon_handle_surface_commit; wl_signal_add(&wlr_drag_icon->surface->events.commit, &icon->surface_commit); icon->unmap.notify = drag_icon_handle_unmap; - wl_signal_add(&wlr_drag_icon->events.unmap, &icon->unmap); + wl_signal_add(&wlr_drag_icon->surface->events.unmap, &icon->unmap); icon->map.notify = drag_icon_handle_map; - wl_signal_add(&wlr_drag_icon->events.map, &icon->map); + wl_signal_add(&wlr_drag_icon->surface->events.map, &icon->map); icon->destroy.notify = drag_icon_handle_destroy; wl_signal_add(&wlr_drag_icon->events.destroy, &icon->destroy); @@ -667,7 +658,7 @@ static void seat_update_capabilities(struct sway_seat *seat) { } else { wlr_seat_set_capabilities(seat->wlr_seat, caps); if ((previous_caps & WL_SEAT_CAPABILITY_POINTER) == 0) { - cursor_set_image(seat->cursor, "left_ptr", NULL); + cursor_set_image(seat->cursor, "default", NULL); } } } @@ -714,12 +705,21 @@ static bool is_touch_or_tablet_tool(struct sway_seat_device *seat_device) { } } -static void seat_apply_input_config(struct sway_seat *seat, +static void seat_apply_input_mapping(struct sway_seat *seat, struct sway_seat_device *sway_device) { struct input_config *ic = input_device_get_config(sway_device->input_device); - sway_log(SWAY_DEBUG, "Applying input config to %s", + switch (sway_device->input_device->wlr_device->type) { + case WLR_INPUT_DEVICE_POINTER: + case WLR_INPUT_DEVICE_TOUCH: + case WLR_INPUT_DEVICE_TABLET_TOOL: + break; + default: + return; // these devices don't support mappings + } + + sway_log(SWAY_DEBUG, "Applying input mapping to %s", sway_device->input_device->identifier); const char *mapped_to_output = ic == NULL ? NULL : ic->mapped_to_output; @@ -747,6 +747,7 @@ static void seat_apply_input_config(struct sway_seat *seat, mapped_to_output = NULL; break; } +#if WLR_HAS_LIBINPUT_BACKEND 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(); @@ -755,6 +756,10 @@ static void seat_apply_input_config(struct sway_seat *seat, mapped_to_output, sway_device->input_device->identifier); } } +#else + (void)is_touch_or_tablet_tool; + (void)get_builtin_output_name; +#endif if (mapped_to_output == NULL) { return; } @@ -798,12 +803,9 @@ static void seat_apply_input_config(struct sway_seat *seat, static void seat_configure_pointer(struct sway_seat *seat, struct sway_seat_device *sway_device) { - if ((seat->wlr_seat->capabilities & WL_SEAT_CAPABILITY_POINTER) == 0) { - seat_configure_xcursor(seat); - } + seat_configure_xcursor(seat); wlr_cursor_attach_input_device(seat->cursor->cursor, sway_device->input_device->wlr_device); - seat_apply_input_config(seat, sway_device); wl_event_source_timer_update( seat->cursor->hide_source, cursor_get_timeout(seat->cursor)); } @@ -838,7 +840,6 @@ static void seat_configure_switch(struct sway_seat *seat, if (!seat_device->switch_device) { sway_switch_create(seat, seat_device); } - seat_apply_input_config(seat, seat_device); sway_switch_configure(seat_device->switch_device); } @@ -846,7 +847,6 @@ static void seat_configure_touch(struct sway_seat *seat, struct sway_seat_device *sway_device) { wlr_cursor_attach_input_device(seat->cursor->cursor, sway_device->input_device->wlr_device); - seat_apply_input_config(seat, sway_device); } static void seat_configure_tablet_tool(struct sway_seat *seat, @@ -857,7 +857,6 @@ static void seat_configure_tablet_tool(struct sway_seat *seat, sway_configure_tablet(sway_device->tablet); wlr_cursor_attach_input_device(seat->cursor->cursor, sway_device->input_device->wlr_device); - seat_apply_input_config(seat, sway_device); } static void seat_configure_tablet_pad(struct sway_seat *seat, @@ -914,6 +913,18 @@ void seat_configure_device(struct sway_seat *seat, seat_configure_tablet_pad(seat, seat_device); break; } + + seat_apply_input_mapping(seat, seat_device); +} + +void seat_configure_device_mapping(struct sway_seat *seat, + struct sway_input_device *input_device) { + struct sway_seat_device *seat_device = seat_get_device(seat, input_device); + if (!seat_device) { + return; + } + + seat_apply_input_mapping(seat, seat_device); } void seat_reset_device(struct sway_seat *seat, @@ -1030,7 +1041,7 @@ void seat_configure_xcursor(struct sway_seat *seat) { wlr_xcursor_manager_load(server.xwayland.xcursor_manager, 1); struct wlr_xcursor *xcursor = wlr_xcursor_manager_get_xcursor( - server.xwayland.xcursor_manager, "left_ptr", 1); + server.xwayland.xcursor_manager, "default", 1); if (xcursor != NULL) { struct wlr_xcursor_image *image = xcursor->images[0]; wlr_xwayland_set_cursor( @@ -1056,26 +1067,27 @@ void seat_configure_xcursor(struct sway_seat *seat) { sway_log(SWAY_ERROR, "Cannot create XCursor manager for theme '%s'", cursor_theme); } - } - for (int i = 0; i < root->outputs->length; ++i) { - struct sway_output *sway_output = root->outputs->items[i]; - struct wlr_output *output = sway_output->wlr_output; - bool result = - wlr_xcursor_manager_load(seat->cursor->xcursor_manager, - output->scale); - if (!result) { - sway_log(SWAY_ERROR, - "Cannot load xcursor theme for output '%s' with scale %f", - output->name, output->scale); + + for (int i = 0; i < root->outputs->length; ++i) { + struct sway_output *sway_output = root->outputs->items[i]; + struct wlr_output *output = sway_output->wlr_output; + bool result = + wlr_xcursor_manager_load(seat->cursor->xcursor_manager, + output->scale); + if (!result) { + sway_log(SWAY_ERROR, + "Cannot load xcursor theme for output '%s' with scale %f", + output->name, output->scale); + } } - } - // Reset the cursor so that we apply it to outputs that just appeared - cursor_set_image(seat->cursor, NULL, NULL); - cursor_set_image(seat->cursor, "left_ptr", NULL); - wlr_cursor_warp(seat->cursor->cursor, NULL, seat->cursor->cursor->x, - seat->cursor->cursor->y); + // Reset the cursor so that we apply it to outputs that just appeared + cursor_set_image(seat->cursor, NULL, NULL); + cursor_set_image(seat->cursor, "default", NULL); + wlr_cursor_warp(seat->cursor->cursor, NULL, seat->cursor->cursor->x, + seat->cursor->cursor->y); + } } bool seat_is_input_allowed(struct sway_seat *seat, @@ -1286,11 +1298,15 @@ static void seat_set_workspace_focus(struct sway_seat *seat, struct sway_node *n } void seat_set_focus(struct sway_seat *seat, struct sway_node *node) { - if (seat->focused_layer) { + // Prevents the layer from losing focus if it has keyboard exclusivity + if (seat->has_exclusive_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 if (seat->focused_layer) { + seat_set_focus_layer(seat, NULL); + seat_set_workspace_focus(seat, node); } else { seat_set_workspace_focus(seat, node); } @@ -1338,14 +1354,20 @@ void seat_set_focus_layer(struct sway_seat *seat, seat_set_focus(seat, previous); } return; - } else if (!layer || seat->focused_layer == layer) { + } else if (!layer) { return; } - assert(layer->mapped); - seat_set_focus_surface(seat, layer->surface, true); - if (layer->current.layer >= ZWLR_LAYER_SHELL_V1_LAYER_TOP) { - seat->focused_layer = layer; + assert(layer->surface->mapped); + if (layer->current.layer >= ZWLR_LAYER_SHELL_V1_LAYER_TOP && + layer->current.keyboard_interactive + == ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_EXCLUSIVE) { + seat->has_exclusive_layer = true; } + if (seat->focused_layer == layer) { + return; + } + seat_set_focus_surface(seat, layer->surface, true); + seat->focused_layer = layer; } void seat_set_exclusive_client(struct sway_seat *seat, @@ -1609,6 +1631,32 @@ void seatop_pointer_axis(struct sway_seat *seat, } } +void seatop_touch_motion(struct sway_seat *seat, struct wlr_touch_motion_event *event, + double lx, double ly) { + if (seat->seatop_impl->touch_motion) { + seat->seatop_impl->touch_motion(seat, event, lx, ly); + } +} + +void seatop_touch_up(struct sway_seat *seat, struct wlr_touch_up_event *event) { + if (seat->seatop_impl->touch_up) { + seat->seatop_impl->touch_up(seat, event); + } +} + +void seatop_touch_down(struct sway_seat *seat, struct wlr_touch_down_event *event, + double lx, double ly) { + if (seat->seatop_impl->touch_down) { + seat->seatop_impl->touch_down(seat, event, lx, ly); + } +} + +void seatop_touch_cancel(struct sway_seat *seat, struct wlr_touch_cancel_event *event) { + if (seat->seatop_impl->touch_cancel) { + seat->seatop_impl->touch_cancel(seat, event); + } +} + void seatop_tablet_tool_tip(struct sway_seat *seat, struct sway_tablet_tool *tool, uint32_t time_msec, enum wlr_tablet_tool_tip_state state) { @@ -1697,10 +1745,9 @@ void seatop_end(struct sway_seat *seat) { seat->seatop_impl = NULL; } -void seatop_render(struct sway_seat *seat, struct sway_output *output, - pixman_region32_t *damage) { +void seatop_render(struct sway_seat *seat, struct fx_render_context *ctx) { if (seat->seatop_impl->render) { - seat->seatop_impl->render(seat, output, damage); + seat->seatop_impl->render(seat, ctx); } } diff --git a/sway/input/seatop_default.c b/sway/input/seatop_default.c index 84acefdf..1dce6dae 100644 --- a/sway/input/seatop_default.c +++ b/sway/input/seatop_default.c @@ -2,6 +2,7 @@ #include #include #include +#include #include #include #include "gesture.h" @@ -9,6 +10,7 @@ #include "sway/input/cursor.h" #include "sway/input/seat.h" #include "sway/input/tablet.h" +#include "sway/layers.h" #include "sway/output.h" #include "sway/tree/view.h" #include "sway/tree/workspace.h" @@ -228,14 +230,15 @@ static void handle_tablet_tool_tip(struct sway_seat *seat, struct sway_container *cont = node && node->type == N_CONTAINER ? node->sway_container : NULL; - if (wlr_surface_is_layer_surface(surface)) { + struct wlr_layer_surface_v1 *layer; +#if HAVE_XWAYLAND + struct wlr_xwayland_surface *xsurface; +#endif + if ((layer = wlr_layer_surface_v1_try_from_wlr_surface(surface)) && + layer->current.keyboard_interactive) { // Handle tapping a layer surface - struct wlr_layer_surface_v1 *layer = - wlr_layer_surface_v1_from_wlr_surface(surface); - if (layer->current.keyboard_interactive) { - seat_set_focus_layer(seat, layer); - transaction_commit_dirty(); - } + seat_set_focus_layer(seat, layer); + transaction_commit_dirty(); } else if (cont) { bool is_floating_or_child = container_is_floating_or_child(cont); bool is_fullscreen_or_child = container_is_fullscreen_or_child(cont); @@ -260,20 +263,17 @@ static void handle_tablet_tool_tip(struct sway_seat *seat, // Handle tapping on a container surface seat_set_focus_container(seat, cont); - seatop_begin_down(seat, node->sway_container, time_msec, sx, sy); + seatop_begin_down(seat, node->sway_container, sx, sy); } #if HAVE_XWAYLAND // Handle tapping on an xwayland unmanaged view - else if (wlr_surface_is_xwayland_surface(surface)) { - struct wlr_xwayland_surface *xsurface = - wlr_xwayland_surface_from_wlr_surface(surface); - if (xsurface->override_redirect && - wlr_xwayland_or_surface_wants_focus(xsurface)) { - struct wlr_xwayland *xwayland = server.xwayland.wlr_xwayland; - wlr_xwayland_set_seat(xwayland, seat->wlr_seat); - seat_set_focus_surface(seat, xsurface->surface, false); - transaction_commit_dirty(); - } + else if ((xsurface = wlr_xwayland_surface_try_from_wlr_surface(surface)) && + xsurface->override_redirect && + wlr_xwayland_or_surface_wants_focus(xsurface)) { + struct wlr_xwayland *xwayland = server.xwayland.wlr_xwayland; + wlr_xwayland_set_seat(xwayland, seat->wlr_seat); + seat_set_focus_surface(seat, xsurface->surface, false); + transaction_commit_dirty(); } #endif @@ -367,16 +367,15 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec, return; } - // Handle clicking a layer surface - if (surface && wlr_surface_is_layer_surface(surface)) { - struct wlr_layer_surface_v1 *layer = - wlr_layer_surface_v1_from_wlr_surface(surface); + // Handle clicking a layer surface and its popups/subsurfaces + struct wlr_layer_surface_v1 *layer = NULL; + if ((layer = toplevel_layer_surface_from_surface(surface))) { if (layer->current.keyboard_interactive) { seat_set_focus_layer(seat, layer); transaction_commit_dirty(); } if (state == WLR_BUTTON_PRESSED) { - seatop_begin_down_on_surface(seat, surface, time_msec, sx, sy); + seatop_begin_down_on_surface(seat, surface, sx, sy); } seat_pointer_notify_button(seat, time_msec, button, state); return; @@ -501,7 +500,7 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec, // Handle mousedown on a container surface if (surface && cont && state == WLR_BUTTON_PRESSED) { - seatop_begin_down(seat, cont, time_msec, sx, sy); + seatop_begin_down(seat, cont, sx, sy); seat_pointer_notify_button(seat, time_msec, button, WLR_BUTTON_PRESSED); return; } @@ -514,18 +513,16 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec, #if HAVE_XWAYLAND // Handle clicking on xwayland unmanaged view - if (surface && wlr_surface_is_xwayland_surface(surface)) { - struct wlr_xwayland_surface *xsurface = - wlr_xwayland_surface_from_wlr_surface(surface); - if (xsurface->override_redirect && - wlr_xwayland_or_surface_wants_focus(xsurface)) { - struct wlr_xwayland *xwayland = server.xwayland.wlr_xwayland; - wlr_xwayland_set_seat(xwayland, seat->wlr_seat); - seat_set_focus_surface(seat, xsurface->surface, false); - transaction_commit_dirty(); - seat_pointer_notify_button(seat, time_msec, button, state); - return; - } + struct wlr_xwayland_surface *xsurface; + if (surface && + (xsurface = wlr_xwayland_surface_try_from_wlr_surface(surface)) && + xsurface->override_redirect && + wlr_xwayland_or_surface_wants_focus(xsurface)) { + struct wlr_xwayland *xwayland = server.xwayland.wlr_xwayland; + wlr_xwayland_set_seat(xwayland, seat->wlr_seat); + seat_set_focus_surface(seat, xsurface->surface, false); + transaction_commit_dirty(); + seat_pointer_notify_button(seat, time_msec, button, state); } #endif @@ -548,6 +545,21 @@ static void check_focus_follows_mouse(struct sway_seat *seat, if (wlr_output == NULL) { return; } + + struct wlr_surface *surface = NULL; + double sx, sy; + node_at_coords(seat, seat->cursor->cursor->x, seat->cursor->cursor->y, + &surface, &sx, &sy); + + // Focus topmost layer surface + struct wlr_layer_surface_v1 *layer = NULL; + if ((layer = toplevel_layer_surface_from_surface(surface)) && + layer->current.keyboard_interactive) { + seat_set_focus_layer(seat, layer); + transaction_commit_dirty(); + return; + } + struct sway_output *hovered_output = wlr_output->data; if (focus && hovered_output != node_get_output(focus)) { struct sway_workspace *ws = output_get_active_workspace(hovered_output); @@ -653,6 +665,36 @@ static void handle_tablet_tool_motion(struct sway_seat *seat, e->previous_node = node; } +static void handle_touch_down(struct sway_seat *seat, + struct wlr_touch_down_event *event, double lx, double ly) { + struct wlr_surface *surface = NULL; + struct wlr_seat *wlr_seat = seat->wlr_seat; + struct sway_cursor *cursor = seat->cursor; + double sx, sy; + node_at_coords(seat, seat->touch_x, seat->touch_y, &surface, &sx, &sy); + + if (surface && wlr_surface_accepts_touch(wlr_seat, surface)) { + if (seat_is_input_allowed(seat, surface)) { + cursor->simulating_pointer_from_touch = false; + seatop_begin_touch_down(seat, surface, event, sx, sy, lx, ly); + } + } else if (!cursor->simulating_pointer_from_touch && + (!surface || seat_is_input_allowed(seat, surface))) { + // Fallback to cursor simulation. + // The pointer_touch_id state is needed, so drags are not aborted when over + // a surface supporting touch and multi touch events don't interfere. + cursor->simulating_pointer_from_touch = true; + cursor->pointer_touch_id = seat->touch_id; + double dx, dy; + dx = seat->touch_x - cursor->cursor->x; + dy = seat->touch_y - cursor->cursor->y; + 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); + } +} + /*----------------------------------------\ * Functions used by handle_pointer_axis / *--------------------------------------*/ @@ -726,7 +768,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 / WLR_POINTER_AXIS_DISCRETE_STEP); + roundf(scroll_factor * event->delta_discrete / WLR_POINTER_AXIS_DISCRETE_STEP); if (desired < 0) { desired = 0; } else if (desired >= siblings->length) { @@ -761,7 +803,7 @@ static void handle_pointer_axis(struct sway_seat *seat, if (!handled) { wlr_seat_pointer_notify_axis(cursor->seat->wlr_seat, event->time_msec, event->orientation, scroll_factor * event->delta, - round(scroll_factor * event->delta_discrete), event->source); + roundf(scroll_factor * event->delta_discrete), event->source); } } @@ -907,7 +949,7 @@ static void handle_hold_begin(struct sway_seat *seat, // ... otherwise forward to client struct sway_cursor *cursor = seat->cursor; wlr_pointer_gestures_v1_send_hold_begin( - cursor->pointer_gestures, cursor->seat->wlr_seat, + server.input->pointer_gestures, cursor->seat->wlr_seat, event->time_msec, event->fingers); } } @@ -919,7 +961,7 @@ static void handle_hold_end(struct sway_seat *seat, 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, + server.input->pointer_gestures, cursor->seat->wlr_seat, event->time_msec, event->cancelled); return; } @@ -952,7 +994,7 @@ static void handle_pinch_begin(struct sway_seat *seat, // ... otherwise forward to client struct sway_cursor *cursor = seat->cursor; wlr_pointer_gestures_v1_send_pinch_begin( - cursor->pointer_gestures, cursor->seat->wlr_seat, + server.input->pointer_gestures, cursor->seat->wlr_seat, event->time_msec, event->fingers); } } @@ -968,7 +1010,7 @@ static void handle_pinch_update(struct sway_seat *seat, // ... otherwise forward to client struct sway_cursor *cursor = seat->cursor; wlr_pointer_gestures_v1_send_pinch_update( - cursor->pointer_gestures, + server.input->pointer_gestures, cursor->seat->wlr_seat, event->time_msec, event->dx, event->dy, event->scale, event->rotation); @@ -982,7 +1024,7 @@ static void handle_pinch_end(struct sway_seat *seat, 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, + server.input->pointer_gestures, cursor->seat->wlr_seat, event->time_msec, event->cancelled); return; } @@ -1015,7 +1057,7 @@ static void handle_swipe_begin(struct sway_seat *seat, // ... otherwise forward to client struct sway_cursor *cursor = seat->cursor; wlr_pointer_gestures_v1_send_swipe_begin( - cursor->pointer_gestures, cursor->seat->wlr_seat, + server.input->pointer_gestures, cursor->seat->wlr_seat, event->time_msec, event->fingers); } } @@ -1032,7 +1074,7 @@ static void handle_swipe_update(struct sway_seat *seat, // ... otherwise forward to client struct sway_cursor *cursor = seat->cursor; wlr_pointer_gestures_v1_send_swipe_update( - cursor->pointer_gestures, cursor->seat->wlr_seat, + server.input->pointer_gestures, cursor->seat->wlr_seat, event->time_msec, event->dx, event->dy); } } @@ -1043,7 +1085,7 @@ static void handle_swipe_end(struct sway_seat *seat, 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, + wlr_pointer_gestures_v1_send_swipe_end(server.input->pointer_gestures, cursor->seat->wlr_seat, event->time_msec, event->cancelled); return; } @@ -1100,6 +1142,7 @@ static const struct sway_seatop_impl seatop_impl = { .swipe_begin = handle_swipe_begin, .swipe_update = handle_swipe_update, .swipe_end = handle_swipe_end, + .touch_down = handle_touch_down, .rebase = handle_rebase, .allow_set_cursor = true, }; diff --git a/sway/input/seatop_down.c b/sway/input/seatop_down.c index b40773d0..36f9bb60 100644 --- a/sway/input/seatop_down.c +++ b/sway/input/seatop_down.c @@ -2,12 +2,20 @@ #include #include #include +#include #include "sway/input/cursor.h" #include "sway/input/seat.h" #include "sway/tree/view.h" #include "sway/desktop/transaction.h" #include "log.h" +struct seatop_touch_point_event { + double ref_lx, ref_ly; // touch's x/y at start of op + double ref_con_lx, ref_con_ly; // container's x/y at start of op + int32_t touch_id; + struct wl_list link; +}; + struct seatop_down_event { struct sway_container *con; struct sway_seat *seat; @@ -15,8 +23,109 @@ struct seatop_down_event { struct wlr_surface *surface; double ref_lx, ref_ly; // cursor's x/y at start of op double ref_con_lx, ref_con_ly; // container's x/y at start of op + struct wl_list point_events; // seatop_touch_point_event::link }; +static void handle_touch_motion(struct sway_seat *seat, + struct wlr_touch_motion_event *event, double lx, double ly) { + struct seatop_down_event *e = seat->seatop_data; + + struct seatop_touch_point_event *point_event; + bool found = false; + wl_list_for_each(point_event, &e->point_events, link) { + if (point_event->touch_id == event->touch_id) { + found = true; + break; + } + } + if (!found) { + return; // Probably not a point_event from this seatop_down + } + + double moved_x = lx - point_event->ref_lx; + double moved_y = ly - point_event->ref_ly; + double sx = point_event->ref_con_lx + moved_x; + double sy = point_event->ref_con_ly + moved_y; + + wlr_seat_touch_notify_motion(seat->wlr_seat, event->time_msec, + event->touch_id, sx, sy); +} + +static void handle_touch_up(struct sway_seat *seat, + struct wlr_touch_up_event *event) { + struct seatop_down_event *e = seat->seatop_data; + struct seatop_touch_point_event *point_event, *tmp; + + wl_list_for_each_safe(point_event, tmp, &e->point_events, link) { + if (point_event->touch_id == event->touch_id) { + wl_list_remove(&point_event->link); + free(point_event); + break; + } + } + + wlr_seat_touch_notify_up(seat->wlr_seat, event->time_msec, event->touch_id); + + if (wl_list_empty(&e->point_events)) { + seatop_begin_default(seat); + } +} + +static void handle_touch_down(struct sway_seat *seat, + struct wlr_touch_down_event *event, double lx, double ly) { + struct seatop_down_event *e = seat->seatop_data; + double sx, sy; + struct wlr_surface *surface = NULL; + struct sway_node *focused_node = node_at_coords(seat, seat->touch_x, + seat->touch_y, &surface, &sx, &sy); + + if (!surface || surface != e->surface) { // Must start from the initial surface + return; + } + + struct seatop_touch_point_event *point_event = + calloc(1, sizeof(struct seatop_touch_point_event)); + if (!sway_assert(point_event, "Unable to allocate point_event")) { + return; + } + point_event->touch_id = event->touch_id; + point_event->ref_lx = lx; + point_event->ref_ly = ly; + point_event->ref_con_lx = sx; + point_event->ref_con_ly = sy; + + wl_list_insert(&e->point_events, &point_event->link); + + wlr_seat_touch_notify_down(seat->wlr_seat, surface, event->time_msec, + event->touch_id, sx, sy); + + if (focused_node) { + seat_set_focus(seat, focused_node); + } +} + +static void handle_touch_cancel(struct sway_seat *seat, + struct wlr_touch_cancel_event *event) { + struct seatop_down_event *e = seat->seatop_data; + struct seatop_touch_point_event *point_event, *tmp; + + wl_list_for_each_safe(point_event, tmp, &e->point_events, link) { + if (point_event->touch_id == event->touch_id) { + wl_list_remove(&point_event->link); + free(point_event); + break; + } + } + + if (e->surface) { + wlr_seat_touch_notify_cancel(seat->wlr_seat, e->surface); + } + + if (wl_list_empty(&e->point_events)) { + seatop_begin_default(seat); + } +} + static void handle_pointer_axis(struct sway_seat *seat, struct wlr_pointer_axis_event *event) { struct sway_input_device *input_device = @@ -28,7 +137,7 @@ static void handle_pointer_axis(struct sway_seat *seat, wlr_seat_pointer_notify_axis(seat->wlr_seat, event->time_msec, event->orientation, scroll_factor * event->delta, - round(scroll_factor * event->delta_discrete), event->source); + roundf(scroll_factor * event->delta_discrete), event->source); } static void handle_button(struct sway_seat *seat, uint32_t time_msec, @@ -99,14 +208,18 @@ 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, + .touch_motion = handle_touch_motion, + .touch_up = handle_touch_up, + .touch_down = handle_touch_down, + .touch_cancel = handle_touch_cancel, .unref = handle_unref, .end = handle_end, .allow_set_cursor = true, }; void seatop_begin_down(struct sway_seat *seat, struct sway_container *con, - uint32_t time_msec, double sx, double sy) { - seatop_begin_down_on_surface(seat, con->view->surface, time_msec, sx, sy); + double sx, double sy) { + seatop_begin_down_on_surface(seat, con->view->surface, sx, sy); struct seatop_down_event *e = seat->seatop_data; e->con = con; @@ -114,13 +227,20 @@ void seatop_begin_down(struct sway_seat *seat, struct sway_container *con, transaction_commit_dirty(); } +void seatop_begin_touch_down(struct sway_seat *seat, + struct wlr_surface *surface, struct wlr_touch_down_event *event, + double sx, double sy, double lx, double ly) { + seatop_begin_down_on_surface(seat, surface, sx, sy); + handle_touch_down(seat, event, lx, ly); +} + void seatop_begin_down_on_surface(struct sway_seat *seat, - struct wlr_surface *surface, uint32_t time_msec, double sx, double sy) { + struct wlr_surface *surface, double sx, double sy) { seatop_end(seat); struct seatop_down_event *e = calloc(1, sizeof(struct seatop_down_event)); - if (!e) { + if (!sway_assert(e, "Unable to allocate e")) { return; } e->con = NULL; @@ -132,6 +252,7 @@ void seatop_begin_down_on_surface(struct sway_seat *seat, e->ref_ly = seat->cursor->cursor->y; e->ref_con_lx = sx; e->ref_con_ly = sy; + wl_list_init(&e->point_events); seat->seatop_impl = &seatop_impl; seat->seatop_data = e; diff --git a/sway/input/seatop_move_tiling.c b/sway/input/seatop_move_tiling.c index 4e4db18c..bbed01a8 100644 --- a/sway/input/seatop_move_tiling.c +++ b/sway/input/seatop_move_tiling.c @@ -31,20 +31,19 @@ struct seatop_move_tiling_event { bool insert_after_target; }; -static void handle_render(struct sway_seat *seat, - struct sway_output *output, pixman_region32_t *damage) { +static void handle_render(struct sway_seat *seat, struct fx_render_context *ctx) { struct seatop_move_tiling_event *e = seat->seatop_data; if (!e->threshold_reached) { return; } - if (e->target_node && node_get_output(e->target_node) == output) { + if (e->target_node && node_get_output(e->target_node) == ctx->output) { float color[4]; memcpy(&color, config->border_colors.focused.indicator, sizeof(float) * 4); premultiply_alpha(color, 0.5); struct wlr_box box; memcpy(&box, &e->drop_box, sizeof(struct wlr_box)); - scale_box(&box, output->wlr_output->scale); + scale_box(&box, ctx->output->wlr_output->scale); // Render blur pixman_region32_t opaque_region; @@ -52,11 +51,11 @@ static void handle_render(struct sway_seat *seat, struct decoration_data deco_data = get_undecorated_decoration_data(); deco_data.blur = e->con->blur_enabled; deco_data.corner_radius = e->con->corner_radius; - render_blur(false, output, damage, &box, &opaque_region, &deco_data, NULL); + struct wlr_fbox src_box = {0}; + render_blur(ctx, NULL, &src_box, &box, false, &opaque_region, deco_data); pixman_region32_fini(&opaque_region); - render_rounded_rect(output, damage, &box, color, - e->con->corner_radius * output->wlr_output->scale, ALL); + render_rounded_rect(ctx, &box, color, e->con->corner_radius * ctx->output->wlr_output->scale, ALL); } } diff --git a/sway/input/switch.c b/sway/input/switch.c index fc7dfaff..7a539801 100644 --- a/sway/input/switch.c +++ b/sway/input/switch.c @@ -1,6 +1,5 @@ #include "sway/config.h" #include "sway/input/switch.h" -#include #include "log.h" struct sway_switch *sway_switch_create(struct sway_seat *seat, diff --git a/sway/input/tablet.c b/sway/input/tablet.c index 884eba74..902cb7ed 100644 --- a/sway/input/tablet.c +++ b/sway/input/tablet.c @@ -1,6 +1,6 @@ #define _POSIX_C_SOURCE 200809L #include -#include +#include #include #include #include @@ -9,6 +9,10 @@ #include "sway/input/seat.h" #include "sway/input/tablet.h" +#if WLR_HAS_LIBINPUT_BACKEND +#include +#endif + static void handle_pad_tablet_destroy(struct wl_listener *listener, void *data) { struct sway_tablet_pad *pad = wl_container_of(listener, pad, tablet_destroy); @@ -54,15 +58,14 @@ void sway_configure_tablet(struct sway_tablet *tablet) { tablet->seat_device->input_device->wlr_device; struct sway_seat *seat = tablet->seat_device->sway_seat; - if ((seat->wlr_seat->capabilities & WL_SEAT_CAPABILITY_POINTER) == 0) { - seat_configure_xcursor(seat); - } + seat_configure_xcursor(seat); if (!tablet->tablet_v2) { tablet->tablet_v2 = wlr_tablet_create(server.tablet_v2, seat->wlr_seat, device); } +#if WLR_HAS_LIBINPUT_BACKEND /* Search for a sibling tablet pad */ if (!wlr_input_device_is_libinput(device)) { /* We can only do this on libinput devices */ @@ -87,6 +90,7 @@ void sway_configure_tablet(struct sway_tablet *tablet) { break; } } +#endif } void sway_tablet_destroy(struct sway_tablet *tablet) { @@ -287,6 +291,7 @@ void sway_configure_tablet_pad(struct sway_tablet_pad *tablet_pad) { tablet_pad->ring.notify = handle_tablet_pad_ring; wl_signal_add(&tablet_pad->wlr->events.ring, &tablet_pad->ring); +#if WLR_HAS_LIBINPUT_BACKEND /* Search for a sibling tablet */ if (!wlr_input_device_is_libinput(wlr_device)) { /* We can only do this on libinput devices */ @@ -311,6 +316,7 @@ void sway_configure_tablet_pad(struct sway_tablet_pad *tablet_pad) { break; } } +#endif } void sway_tablet_pad_destroy(struct sway_tablet_pad *tablet_pad) { diff --git a/sway/ipc-json.c b/sway/ipc-json.c index eea4e186..d5866ef6 100644 --- a/sway/ipc-json.c +++ b/sway/ipc-json.c @@ -3,7 +3,8 @@ #include #include #include -#include +#include +#include #include #include #include "config.h" @@ -21,6 +22,10 @@ #include "wlr-layer-shell-unstable-v1-protocol.h" #include "sway/desktop/idle_inhibit_v1.h" +#if WLR_HAS_LIBINPUT_BACKEND +#include +#endif + static const int i3_output_id = INT32_MAX; static const int i3_scratch_id = INT32_MAX - 1; @@ -202,6 +207,20 @@ static const char *ipc_json_user_idle_inhibitor_description(enum sway_idle_inhib return NULL; } +static const char *ipc_json_content_type_description(enum wp_content_type_v1_type type) { + switch (type) { + case WP_CONTENT_TYPE_V1_TYPE_NONE: + return "none"; + case WP_CONTENT_TYPE_V1_TYPE_PHOTO: + return "photo"; + case WP_CONTENT_TYPE_V1_TYPE_VIDEO: + return "video"; + case WP_CONTENT_TYPE_V1_TYPE_GAME: + return "game"; + } + return NULL; +} + json_object *ipc_json_get_version(void) { int major = 0, minor = 0, patch = 0; json_object *version = json_object_new_object(); @@ -666,6 +685,16 @@ static void ipc_json_describe_view(struct sway_container *c, json_object *object json_object_object_add(object, "idle_inhibitors", idle_inhibitors); + enum wp_content_type_v1_type content_type = WP_CONTENT_TYPE_V1_TYPE_NONE; + if (c->view->surface != NULL) { + content_type = wlr_surface_get_content_type_v1(server.content_type_manager_v1, + c->view->surface); + } + if (content_type != WP_CONTENT_TYPE_V1_TYPE_NONE) { + json_object_object_add(object, "content_type", + json_object_new_string(ipc_json_content_type_description(content_type))); + } + #if HAVE_XWAYLAND if (c->view->type == SWAY_VIEW_XWAYLAND) { json_object_object_add(object, "window", @@ -886,6 +915,7 @@ json_object *ipc_json_describe_node_recursive(struct sway_node *node) { return object; } +#if WLR_HAS_LIBINPUT_BACKEND static json_object *describe_libinput_device(struct libinput_device *device) { json_object *object = json_object_new_object(); @@ -1053,6 +1083,17 @@ static json_object *describe_libinput_device(struct libinput_device *device) { uint32_t button = libinput_device_config_scroll_get_button(device); json_object_object_add(object, "scroll_button", json_object_new_int(button)); + const char *lock = "unknown"; + switch (libinput_device_config_scroll_get_button_lock(device)) { + case LIBINPUT_CONFIG_SCROLL_BUTTON_LOCK_ENABLED: + lock = "enabled"; + break; + case LIBINPUT_CONFIG_SCROLL_BUTTON_LOCK_DISABLED: + lock = "disabled"; + break; + } + json_object_object_add(object, "scroll_button_lock", + json_object_new_string(lock)); } } @@ -1096,6 +1137,7 @@ static json_object *describe_libinput_device(struct libinput_device *device) { return object; } +#endif json_object *ipc_json_describe_input(struct sway_input_device *device) { if (!(sway_assert(device, "Device must not be null"))) { @@ -1159,12 +1201,14 @@ json_object *ipc_json_describe_input(struct sway_input_device *device) { json_object_new_double(scroll_factor)); } +#if WLR_HAS_LIBINPUT_BACKEND if (wlr_input_device_is_libinput(device->wlr_device)) { struct libinput_device *libinput_dev; libinput_dev = wlr_libinput_get_device_handle(device->wlr_device); json_object_object_add(object, "libinput", describe_libinput_device(libinput_dev)); } +#endif return object; } diff --git a/sway/ipc-server.c b/sway/ipc-server.c index 00b01d7d..9692a77f 100644 --- a/sway/ipc-server.c +++ b/sway/ipc-server.c @@ -56,7 +56,6 @@ struct ipc_client { enum ipc_command_type pending_type; }; -struct sockaddr_un *ipc_user_sockaddr(void); int ipc_handle_connection(int fd, uint32_t mask, void *data); int ipc_client_handle_readable(int client_fd, uint32_t mask, void *data); int ipc_client_handle_writable(int client_fd, uint32_t mask, void *data); @@ -509,6 +508,20 @@ void ipc_event_input(const char *change, struct sway_input_device *device) { json_object_put(json); } +void ipc_event_output(void) { + if (!ipc_has_event_listeners(IPC_EVENT_OUTPUT)) { + return; + } + sway_log(SWAY_DEBUG, "Sending output event"); + + json_object *json = json_object_new_object(); + json_object_object_add(json, "change", json_object_new_string("unspecified")); + + const char *json_string = json_object_to_json_string(json); + ipc_send_event(json_string, IPC_EVENT_OUTPUT); + json_object_put(json); +} + int ipc_client_handle_writable(int client_fd, uint32_t mask, void *data) { struct ipc_client *client = data; @@ -726,6 +739,8 @@ void ipc_client_handle_command(struct ipc_client *client, uint32_t payload_lengt const char *event_type = json_object_get_string(json_object_array_get_idx(request, i)); if (strcmp(event_type, "workspace") == 0) { client->subscribed_events |= event_mask(IPC_EVENT_WORKSPACE); + } else if (strcmp(event_type, "output") == 0) { + client->subscribed_events |= event_mask(IPC_EVENT_OUTPUT); } else if (strcmp(event_type, "barconfig_update") == 0) { client->subscribed_events |= event_mask(IPC_EVENT_BARCONFIG_UPDATE); } else if (strcmp(event_type, "bar_state_update") == 0) { @@ -910,7 +925,6 @@ void ipc_client_handle_command(struct ipc_client *client, uint32_t payload_lengt exit_cleanup: free(buf); - return; } bool ipc_send_reply(struct ipc_client *client, enum ipc_command_type payload_type, diff --git a/sway/lock.c b/sway/lock.c index e893622f..199624fc 100644 --- a/sway/lock.c +++ b/sway/lock.c @@ -1,10 +1,12 @@ #define _POSIX_C_SOURCE 200809L #include #include "log.h" +#include "sway/input/cursor.h" #include "sway/input/keyboard.h" #include "sway/input/seat.h" #include "sway/output.h" #include "sway/server.h" +#include "sway/surface.h" struct sway_session_lock_surface { struct wlr_session_lock_surface_v1 *lock_surface; @@ -13,7 +15,6 @@ struct sway_session_lock_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; }; @@ -32,7 +33,8 @@ static void handle_surface_map(struct wl_listener *listener, void *data) { if (server.session_lock.focused == NULL) { set_lock_focused_surface(surf->surface); } - wlr_surface_send_enter(surf->surface, surf->output->wlr_output); + cursor_rebase_all(); + surface_enter_output(surf->surface, surf->output); output_damage_whole(surf->output); } @@ -41,16 +43,10 @@ static void handle_surface_commit(struct wl_listener *listener, void *data) { 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 & ( + if (event->state->committed & ( WLR_OUTPUT_STATE_MODE | WLR_OUTPUT_STATE_SCALE | WLR_OUTPUT_STATE_TRANSFORM)) { @@ -66,7 +62,7 @@ static void destroy_lock_surface(struct sway_session_lock_surface *surf) { 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) { + if (other != surf->lock_surface && other->surface->mapped) { next_focus = other->surface; break; } @@ -77,7 +73,6 @@ static void destroy_lock_surface(struct sway_session_lock_surface *surf) { 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); @@ -111,13 +106,11 @@ static void handle_new_surface(struct wl_listener *listener, void *data) { surf->surface = lock_surface->surface; surf->output = output; surf->map.notify = handle_surface_map; - wl_signal_add(&lock_surface->events.map, &surf->map); + wl_signal_add(&lock_surface->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; diff --git a/sway/meson.build b/sway/meson.build index f0f26346..40c7382e 100644 --- a/sway/meson.build +++ b/sway/meson.build @@ -15,11 +15,6 @@ sway_sources = files( 'xdg_decoration.c', 'desktop/desktop.c', - 'desktop/fx_renderer/fx_framebuffer.c', - 'desktop/fx_renderer/fx_renderer.c', - 'desktop/fx_renderer/fx_stencilbuffer.c', - 'desktop/fx_renderer/fx_texture.c', - 'desktop/fx_renderer/matrix.c', 'desktop/idle_inhibit_v1.c', 'desktop/layer_shell.c', 'desktop/output.c', @@ -32,7 +27,6 @@ sway_sources = files( 'input/input-manager.c', 'input/cursor.c', 'input/keyboard.c', - 'input/libinput.c', 'input/seat.c', 'input/seatop_default.c', 'input/seatop_down.c', @@ -107,6 +101,7 @@ sway_sources = files( 'commands/nop.c', 'commands/output.c', 'commands/popup_during_fullscreen.c', + 'commands/primary_selection.c', 'commands/reload.c', 'commands/rename.c', 'commands/resize.c', @@ -196,9 +191,11 @@ sway_sources = files( 'commands/input/middle_emulation.c', 'commands/input/natural_scroll.c', 'commands/input/pointer_accel.c', + 'commands/input/rotation_angle.c', 'commands/input/repeat_delay.c', 'commands/input/repeat_rate.c', 'commands/input/scroll_button.c', + 'commands/input/scroll_button_lock.c', 'commands/input/scroll_factor.c', 'commands/input/scroll_method.c', 'commands/input/tap.c', @@ -240,8 +237,6 @@ sway_sources = files( 'tree/output.c', ) -subdir('desktop/fx_renderer/shaders') - sway_deps = [ cairo, drm, @@ -252,19 +247,23 @@ sway_deps = [ math, pango, pcre2, - glesv2, pixman, + scenefx, threads, wayland_server, wlroots, xkbcommon, + xcb, xcb_icccm, egl, ] if have_xwayland sway_sources += 'desktop/xwayland.c' - sway_deps += xcb +endif + +if wlroots_features['libinput_backend'] + sway_sources += 'input/libinput.c' endif executable( diff --git a/sway/server.c b/sway/server.c index 262b7788..2b5b8d0f 100644 --- a/sway/server.c +++ b/sway/server.c @@ -1,5 +1,6 @@ #define _POSIX_C_SOURCE 200809L #include +#include #include #include #include @@ -7,17 +8,17 @@ #include #include #include -#include #include #include #include #include +#include +#include #include -#include #include #include +#include #include -#include #include #include #include @@ -25,8 +26,9 @@ #include #include #include -#include +#include #include +#include #include #include #include @@ -41,16 +43,26 @@ #include "list.h" #include "log.h" #include "sway/config.h" -#include "sway/desktop/fx_renderer/fx_renderer.h" #include "sway/desktop/idle_inhibit_v1.h" #include "sway/input/input-manager.h" #include "sway/output.h" #include "sway/server.h" +#include "sway/input/cursor.h" #include "sway/tree/root.h" + #if HAVE_XWAYLAND +#include #include "sway/xwayland.h" #endif +#if WLR_HAS_DRM_BACKEND +#include +#endif + +#define SWAY_XDG_SHELL_VERSION 2 +#define SWAY_LAYER_SHELL_VERSION 4 + +#if WLR_HAS_DRM_BACKEND 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. */ @@ -62,15 +74,67 @@ static void handle_drm_lease_request(struct wl_listener *listener, void *data) { wlr_drm_lease_request_v1_reject(req); } } +#endif -#define SWAY_XDG_SHELL_VERSION 2 +static bool is_privileged(const struct wl_global *global) { +#if WLR_HAS_DRM_BACKEND + if (server.drm_lease_manager != NULL) { + struct wlr_drm_lease_device_v1 *drm_lease_dev; + wl_list_for_each(drm_lease_dev, &server.drm_lease_manager->devices, link) { + if (drm_lease_dev->global == global) { + return true; + } + } + } +#endif + + return + global == server.output_manager_v1->global || + global == server.output_power_manager_v1->global || + global == server.input_method->global || + global == server.foreign_toplevel_manager->global || + global == server.data_control_manager_v1->global || + global == server.screencopy_manager_v1->global || + global == server.export_dmabuf_manager_v1->global || + global == server.security_context_manager_v1->global || + global == server.gamma_control_manager_v1->global || + global == server.layer_shell->global || + global == server.session_lock.manager->global || + global == server.input->inhibit->global || + global == server.input->keyboard_shortcuts_inhibit->global || + global == server.input->virtual_keyboard->global || + global == server.input->virtual_pointer->global; +} + +static bool filter_global(const struct wl_client *client, + const struct wl_global *global, void *data) { +#if HAVE_XWAYLAND + struct wlr_xwayland *xwayland = server.xwayland.wlr_xwayland; + if (xwayland && global == xwayland->shell_v1->global) { + return xwayland->server != NULL && client == xwayland->server->client; + } +#endif + + // Restrict usage of privileged protocols to unsandboxed clients + // TODO: add a way for users to configure an allow-list + const struct wlr_security_context_v1_state *security_context = + wlr_security_context_manager_v1_lookup_client( + server.security_context_manager_v1, (struct wl_client *)client); + if (is_privileged(global)) { + return security_context == NULL; + } + + return true; +} 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); + wl_display_set_global_filter(server->wl_display, filter_global, NULL); + + server->backend = wlr_backend_autocreate(server->wl_display, &server->session); if (!server->backend) { sway_log(SWAY_ERROR, "Unable to create backend"); return false; @@ -81,29 +145,29 @@ bool server_init(struct sway_server *server) { return false; } - server->wlr_renderer = wlr_renderer_autocreate(server->backend); - if (!server->wlr_renderer) { - sway_log(SWAY_ERROR, "Failed to create wlr_renderer"); + server->renderer = fx_renderer_create(server->backend); + if (!server->renderer) { + sway_log(SWAY_ERROR, "Failed to create fx_renderer"); return false; } - wlr_renderer_init_wl_shm(server->wlr_renderer, server->wl_display); + wlr_renderer_init_wl_shm(server->renderer, server->wl_display); - if (wlr_renderer_get_dmabuf_texture_formats(server->wlr_renderer) != NULL) { - wlr_drm_create(server->wl_display, server->wlr_renderer); - server->linux_dmabuf_v1 = - wlr_linux_dmabuf_v1_create(server->wl_display, server->wlr_renderer); + if (wlr_renderer_get_dmabuf_texture_formats(server->renderer) != NULL) { + wlr_drm_create(server->wl_display, server->renderer); + server->linux_dmabuf_v1 = wlr_linux_dmabuf_v1_create_with_renderer( + server->wl_display, 4, server->renderer); } server->allocator = wlr_allocator_autocreate(server->backend, - server->wlr_renderer); + server->renderer); if (!server->allocator) { sway_log(SWAY_ERROR, "Failed to create allocator"); return false; } - server->compositor = wlr_compositor_create(server->wl_display, - server->wlr_renderer); + server->compositor = wlr_compositor_create(server->wl_display, 6, + server->renderer); server->compositor_new_surface.notify = handle_compositor_new_surface; wl_signal_add(&server->compositor->events.new_surface, &server->compositor_new_surface); @@ -113,7 +177,11 @@ bool server_init(struct sway_server *server) { server->data_device_manager = wlr_data_device_manager_create(server->wl_display); - wlr_gamma_control_manager_v1_create(server->wl_display); + server->gamma_control_manager_v1 = + wlr_gamma_control_manager_v1_create(server->wl_display); + server->gamma_control_set_gamma.notify = handle_gamma_control_set_gamma; + wl_signal_add(&server->gamma_control_manager_v1->events.set_gamma, + &server->gamma_control_set_gamma); server->new_output.notify = handle_new_output; wl_signal_add(&server->backend->events.new_output, &server->new_output); @@ -123,12 +191,11 @@ 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); + sway_idle_inhibit_manager_v1_init(); - server->layer_shell = wlr_layer_shell_v1_create(server->wl_display); + server->layer_shell = wlr_layer_shell_v1_create(server->wl_display, + SWAY_LAYER_SHELL_VERSION); wl_signal_add(&server->layer_shell->events.new_surface, &server->layer_shell_surface); server->layer_shell_surface.notify = handle_layer_shell_surface; @@ -193,6 +260,7 @@ bool server_init(struct sway_server *server) { sway_session_lock_init(); +#if WLR_HAS_DRM_BACKEND server->drm_lease_manager= wlr_drm_lease_v1_manager_create(server->wl_display, server->backend); if (server->drm_lease_manager) { @@ -203,13 +271,17 @@ bool server_init(struct sway_server *server) { sway_log(SWAY_DEBUG, "Failed to create wlr_drm_lease_device_v1"); sway_log(SWAY_INFO, "VR will not be available"); } +#endif - wlr_export_dmabuf_manager_v1_create(server->wl_display); - wlr_screencopy_manager_v1_create(server->wl_display); - wlr_data_control_manager_v1_create(server->wl_display); - wlr_primary_selection_v1_device_manager_create(server->wl_display); + server->export_dmabuf_manager_v1 = wlr_export_dmabuf_manager_v1_create(server->wl_display); + server->screencopy_manager_v1 = wlr_screencopy_manager_v1_create(server->wl_display); + server->data_control_manager_v1 = wlr_data_control_manager_v1_create(server->wl_display); + server->security_context_manager_v1 = wlr_security_context_manager_v1_create(server->wl_display); wlr_viewporter_create(server->wl_display); wlr_single_pixel_buffer_manager_v1_create(server->wl_display); + server->content_type_manager_v1 = + wlr_content_type_manager_v1_create(server->wl_display, 1); + wlr_fractional_scale_manager_v1_create(server->wl_display, 1); struct wlr_xdg_foreign_registry *foreign_registry = wlr_xdg_foreign_registry_create(server->wl_display); @@ -221,6 +293,15 @@ bool server_init(struct sway_server *server) { xdg_activation_v1_handle_request_activate; wl_signal_add(&server->xdg_activation_v1->events.request_activate, &server->xdg_activation_v1_request_activate); + server->xdg_activation_v1_new_token.notify = + xdg_activation_v1_handle_new_token; + wl_signal_add(&server->xdg_activation_v1->events.new_token, + &server->xdg_activation_v1_new_token); + + struct wlr_cursor_shape_manager_v1 *cursor_shape_manager = + wlr_cursor_shape_manager_v1_create(server->wl_display, 1); + server->request_set_cursor_shape.notify = handle_request_set_cursor_shape; + wl_signal_add(&cursor_shape_manager->events.request_set_shape, &server->request_set_cursor_shape); wl_list_init(&server->pending_launcher_ctxs); @@ -303,6 +384,10 @@ bool server_start(struct sway_server *server) { } #endif + if (config->primary_selection) { + wlr_primary_selection_v1_device_manager_create(server->wl_display); + } + sway_log(SWAY_INFO, "Starting backend on wayland display '%s'", server->socket); if (!wlr_backend_start(server->backend)) { diff --git a/sway/sway-input.5.scd b/sway/sway-input.5.scd index e073c45d..442311bb 100644 --- a/sway/sway-input.5.scd +++ b/sway/sway-input.5.scd @@ -121,11 +121,16 @@ The following commands may only be used in the configuration file. *input* map_from_region Ignores inputs from this device that do not occur within the specified - region. Can be in millimeters (e.g. 10x20mm 20x40mm) or in terms of 0..1 - (e.g. 0.5x0.5 0.7x0.7). Not all devices support millimeters. Only meaningful - if the device is not a keyboard and provides events in absolute terms (such - as a drawing tablet or touch screen - most pointers provide events relative - to the previous frame). + region. Can be in millimeters (e.g. 10x20mm 20x40mm) or the fraction of the + full available space in terms of 0..1 (e.g. 0.5x0.5 0.7x0.7). Not all + devices support millimeters. Only meaningful if the device is not a + keyboard and provides events in absolute terms (such as a drawing tablet + or touch screen - most pointers provide events relative to the previous + frame). + + Commonly used to maintain the aspect ratio of the input device and screen. + Cropping a 16:10 input region to match a 16:9 display can use 0x0 1x0.9 as + the argument. ## LIBINPUT CONFIGURATION @@ -175,12 +180,19 @@ The following commands may only be used in the configuration file. *input* pointer_accel [<-1|1>] Changes the pointer acceleration for the specified input device. +*input* rotation_angle + Sets the rotation angle of the device to the given clockwise angle in + degrees. The angle must be between 0.0 (inclusive) and 360.0 (exclusive). + *input* scroll_button disable|button[1-3,8,9]| Sets the button used for scroll_method on_button_down. The button can be given as an event name or code, which can be obtained from *libinput debug-events*, or as a x11 mouse button (button[1-3,8,9]). If set to _disable_, it disables the scroll_method on_button_down. +*input* scroll_button_lock enabled|disabled + Enables or disables scroll button lock for specified input device. + *input* scroll_factor Changes the scroll factor for the specified input device. Scroll speed will be scaled by the given value, which must be non-negative. @@ -224,6 +236,8 @@ correct seat. absolute coordinates (with respect to the global coordinate space). Specifying either value as 0 will not update that coordinate. + Deprecated: use the virtual-pointer Wayland protocol instead. + *seat* cursor press|release button[1-9]| Simulate pressing (or releasing) the specified mouse button on the specified seat. The button can either be provided as a button event name or @@ -232,6 +246,8 @@ correct seat. event will be simulated, however _press_ and _release_ will be ignored and both will occur. + Deprecated: use the virtual-pointer Wayland protocol instead. + *seat* fallback true|false Set this seat as the fallback seat. A fallback seat will attach any device not explicitly attached to another seat (similar to a "default" seat). @@ -247,18 +263,16 @@ correct seat. If _when-typing_ is enabled, then the cursor will be hidden whenever a key is pressed. + Be aware that this setting can interfere with input handling in games and + certain types of software (Gimp, Blender etc) that rely on simultaneous + input from mouse and keyboard. + *seat* idle_inhibit Sets the set of input event sources which can prevent the seat from becoming idle, as a space separated list of source names. Valid names are "keyboard", "pointer", "touchpad", "touch", "tablet_pad", "tablet_tool", and "switch". The default behavior is to prevent idle on any event. -*seat* idle_wake - Sets the set of input event sources which can wake the seat from - its idle state, as a space separated list of source names. Valid names are - "keyboard", "pointer", "touchpad", "touch", "tablet_pad", "tablet_tool", - and "switch". The default behavior is to wake from idle on any event. - *seat* keyboard_grouping none|smart Set how the keyboards in the seat are grouped together. Currently, there are two options. _none_ will disable all keyboard grouping. This will make diff --git a/sway/sway-ipc.7.scd b/sway/sway-ipc.7.scd index 2607c15c..bcfbccf8 100644 --- a/sway/sway-ipc.7.scd +++ b/sway/sway-ipc.7.scd @@ -1229,6 +1229,9 @@ following properties will be included for devices that support them: : int : The scroll button to use when _scroll_method_ is _on_button_down_. This will be given as an input event code +|- scroll_button_lock +: string +: Whether scroll button lock is enabled. It can be _enabled_ or _disabled_ |- dwt : string : Whether disable-while-typing is enabled. It can be _enabled_ or _disabled_ @@ -1480,6 +1483,9 @@ available: : workspace :[ Sent whenever an event involving a workspace occurs such as initialization of a new workspace or a different workspace gains focus +|- 0x80000001 +: output +: Sent when outputs are updated |- 0x80000002 : mode : Sent whenever the binding mode changes @@ -1600,6 +1606,20 @@ The following change types are currently available: } ``` +## 0x80000001. OUTPUT + +Sent whenever an output is added, removed, or its configuration is changed. +The event is a single object with the property _change_, which is a string +containing the reason for the change. Currently, the only value for _change_ is +_unspecified_. + +*Example Event:* +``` +{ + "change": "unspecified" +} +``` + ## 0x80000002. MODE Sent whenever the binding mode changes. The event consists of a single object diff --git a/sway/sway.5.scd b/sway/sway.5.scd index b0f583f2..10ae1e0a 100644 --- a/sway/sway.5.scd +++ b/sway/sway.5.scd @@ -393,8 +393,8 @@ runtime. for_window move container to output *bindsym* [--whole-window] [--border] [--exclude-titlebar] [--release] [--locked] \ -[--to-code] [--input-device=] [--no-warn] [--no-repeat] [Group<1-4>+] \ - +[--to-code] [--input-device=] [--no-warn] [--no-repeat] [--inhibited] \ +[Group<1-4>+] Binds _key combo_ to execute the sway command _command_ when pressed. You may use XKB key names here (*wev*(1) is a good tool for discovering these). With the flag _--release_, the command is executed when the key combo is @@ -458,7 +458,8 @@ runtime. ``` *bindcode* [--whole-window] [--border] [--exclude-titlebar] [--release] \ -[--locked] [--input-device=] [--no-warn] [Group<1-4>+] +[--locked] [--input-device=] [--no-warn] [--no-repeat] [--inhibited] \ +[Group<1-4>+] is also available for binding with key/button codes instead of key/button names. *bindswitch* [--locked] [--no-warn] [--reload] : @@ -724,7 +725,8 @@ The default colors are: while 1.1 (110%) is the default value. *default_border* normal|none|pixel [] - Set default border style for new tiled windows. + Set default border style for new tiled windows. Config reload won't affect + existing windows, only newly created ones after the reload. *default_floating_border* normal|none|pixel [] Set default border style for new floating windows. This only applies to @@ -908,6 +910,10 @@ The default colors are: dialog will not be rendered. If _leave_fullscreen_, the view will exit fullscreen mode and the dialog will be rendered. +*primary_selection* enabled|disabled + Enable or disable the primary selection clipboard. May only be configured + at launch. Default is _enabled_. + *set* $ Sets variable $_name_ to _value_. You can use the new variable in the arguments of future commands. When the variable is used, it can be escaped @@ -1063,6 +1069,9 @@ properties in practice for your applications. The following attributes may be matched with: +*all* + Matches all windows. + *app_id* Compare value against the app id. Can be a regular expression. If value is \_\_focused\_\_, then the app id must be the same as that of the currently diff --git a/sway/swaynag.c b/sway/swaynag.c index 4a0a6d30..6031174d 100644 --- a/sway/swaynag.c +++ b/sway/swaynag.c @@ -145,22 +145,16 @@ void swaynag_log(const char *swaynag_command, struct swaynag_instance *swaynag, va_list args; va_start(args, fmt); - size_t length = vsnprintf(NULL, 0, fmt, args) + 1; + char *str = vformat_str(fmt, args); va_end(args); - - char *temp = malloc(length + 1); - if (!temp) { + if (!str) { sway_log(SWAY_ERROR, "Failed to allocate buffer for swaynag log entry."); return; } - va_start(args, fmt); - vsnprintf(temp, length, fmt, args); - va_end(args); + write(swaynag->fd[1], str, strlen(str)); - write(swaynag->fd[1], temp, length); - - free(temp); + free(str); } void swaynag_show(struct swaynag_instance *swaynag) { diff --git a/sway/tree/arrange.c b/sway/tree/arrange.c index 9c1a11e5..af925d05 100644 --- a/sway/tree/arrange.c +++ b/sway/tree/arrange.c @@ -264,6 +264,9 @@ void arrange_workspace(struct sway_workspace *workspace) { area->width, area->height, area->x, area->y); bool first_arrange = workspace->width == 0 && workspace->height == 0; + struct wlr_box prev_box; + workspace_get_box(workspace, &prev_box); + double prev_x = workspace->x - workspace->current_gaps.left; double prev_y = workspace->y - workspace->current_gaps.top; workspace->width = area->width; @@ -277,13 +280,14 @@ void arrange_workspace(struct sway_workspace *workspace) { if (!first_arrange && (diff_x != 0 || diff_y != 0)) { for (int i = 0; i < workspace->floating->length; ++i) { struct sway_container *floater = workspace->floating->items[i]; - container_floating_translate(floater, diff_x, diff_y); - double center_x = floater->pending.x + floater->pending.width / 2; - double center_y = floater->pending.y + floater->pending.height / 2; struct wlr_box workspace_box; workspace_get_box(workspace, &workspace_box); - if (!wlr_box_contains_point(&workspace_box, center_x, center_y)) { - container_floating_move_to_center(floater); + floating_fix_coordinates(floater, &prev_box, &workspace_box); + // Set transformation for scratchpad windows. + if (floater->scratchpad) { + struct wlr_box output_box; + output_get_box(output, &output_box); + floater->transform = output_box; } } } diff --git a/sway/tree/container.c b/sway/tree/container.c index 2e61ff5c..9b90b774 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -3,14 +3,10 @@ #include #include #include -#include -#include -#include #include #include #include #include -#include #include "linux-dmabuf-unstable-v1-protocol.h" #include "cairo_util.h" #include "pango.h" @@ -22,6 +18,7 @@ #include "sway/ipc-server.h" #include "sway/output.h" #include "sway/server.h" +#include "sway/surface.h" #include "sway/tree/arrange.h" #include "sway/tree/view.h" #include "sway/tree/workspace.h" @@ -392,17 +389,17 @@ struct sway_container *tiling_container_at(struct sway_node *parent, } static bool surface_is_popup(struct wlr_surface *surface) { - while (!wlr_surface_is_xdg_surface(surface)) { - if (!wlr_surface_is_subsurface(surface)) { + while (wlr_xdg_surface_try_from_wlr_surface(surface) == NULL) { + struct wlr_subsurface *subsurface = + wlr_subsurface_try_from_wlr_surface(surface); + if (subsurface == NULL) { return false; } - struct wlr_subsurface *subsurface = - wlr_subsurface_from_wlr_surface(surface); surface = subsurface->parent; } struct wlr_xdg_surface *xdg_surface = - wlr_xdg_surface_from_wlr_surface(surface); - return xdg_surface->role == WLR_XDG_SURFACE_ROLE_POPUP; + wlr_xdg_surface_try_from_wlr_surface(surface); + return xdg_surface->role == WLR_XDG_SURFACE_ROLE_POPUP && xdg_surface->popup != NULL; } struct sway_container *container_at(struct sway_workspace *workspace, @@ -516,7 +513,6 @@ static void render_titlebar_text_texture(struct sway_output *output, cairo_t *c = cairo_create(dummy_surface); cairo_set_antialias(c, CAIRO_ANTIALIAS_BEST); cairo_font_options_t *fo = cairo_font_options_create(); - cairo_font_options_set_hint_style(fo, CAIRO_HINT_STYLE_FULL); if (output->wlr_output->subpixel == WL_OUTPUT_SUBPIXEL_NONE) { cairo_font_options_set_antialias(fo, CAIRO_ANTIALIAS_GRAY); } else { @@ -721,6 +717,21 @@ void floating_calculate_constraints(int *min_width, int *max_width, } +void floating_fix_coordinates(struct sway_container *con, struct wlr_box *old, struct wlr_box *new) { + if (!old->width || !old->height) { + // Fall back to centering on the workspace. + container_floating_move_to_center(con); + } else { + int rel_x = con->pending.x - old->x + (con->pending.width / 2); + int rel_y = con->pending.y - old->y + (con->pending.height / 2); + + con->pending.x = new->x + (double)(rel_x * new->width) / old->width - (con->pending.width / 2); + con->pending.y = new->y + (double)(rel_y * new->height) / old->height - (con->pending.height / 2); + + sway_log(SWAY_DEBUG, "Transformed container %p to coords (%f, %f)", con, con->pending.x, con->pending.y); + } +} + static void floating_natural_resize(struct sway_container *con) { int min_width, max_width, min_height, max_height; floating_calculate_constraints(&min_width, &max_width, @@ -1034,6 +1045,13 @@ void container_floating_move_to(struct sway_container *con, workspace_add_floating(new_workspace, con); arrange_workspace(old_workspace); arrange_workspace(new_workspace); + // If the moved container was a visible scratchpad container, then + // update its transform. + if (con->scratchpad) { + struct wlr_box output_box; + output_get_box(new_output, &output_box); + con->transform = output_box; + } workspace_detect_urgent(old_workspace); workspace_detect_urgent(new_workspace); } @@ -1065,16 +1083,6 @@ void container_end_mouse_operation(struct sway_container *container) { } } -static bool devid_from_fd(int fd, dev_t *devid) { - struct stat stat; - if (fstat(fd, &stat) != 0) { - sway_log_errno(SWAY_ERROR, "fstat failed"); - return false; - } - *devid = stat.st_rdev; - return true; -} - static void set_fullscreen(struct sway_container *con, bool enable) { if (!con->view) { return; @@ -1101,60 +1109,19 @@ static void set_fullscreen(struct sway_container *con, bool enable) { } struct sway_output *output = con->pending.workspace->output; - struct wlr_output *wlr_output = output->wlr_output; - - // TODO: add wlroots helpers for all of this stuff - - const struct wlr_drm_format_set *renderer_formats = - wlr_renderer_get_dmabuf_texture_formats(server.wlr_renderer); - assert(renderer_formats); - - int renderer_drm_fd = wlr_renderer_get_drm_fd(server.wlr_renderer); - int backend_drm_fd = wlr_backend_get_drm_fd(wlr_output->backend); - if (renderer_drm_fd < 0 || backend_drm_fd < 0) { - return; - } - - dev_t render_dev, scanout_dev; - if (!devid_from_fd(renderer_drm_fd, &render_dev) || - !devid_from_fd(backend_drm_fd, &scanout_dev)) { - return; - } - - const struct wlr_drm_format_set *output_formats = - wlr_output_get_primary_formats(output->wlr_output, - WLR_BUFFER_CAP_DMABUF); - if (!output_formats) { - return; - } - - struct wlr_drm_format_set scanout_formats = {0}; - if (!wlr_drm_format_set_intersect(&scanout_formats, - output_formats, renderer_formats)) { - return; - } - - struct wlr_linux_dmabuf_feedback_v1_tranche tranches[] = { - { - .target_device = scanout_dev, - .flags = ZWP_LINUX_DMABUF_FEEDBACK_V1_TRANCHE_FLAGS_SCANOUT, - .formats = &scanout_formats, - }, - { - .target_device = render_dev, - .formats = renderer_formats, - }, + const struct wlr_linux_dmabuf_feedback_v1_init_options options = { + .main_renderer = server.renderer, + .scanout_primary_output = output->wlr_output, }; + struct wlr_linux_dmabuf_feedback_v1 feedback = {0}; + if (!wlr_linux_dmabuf_feedback_v1_init_with_options(&feedback, &options)) { + return; + } - const struct wlr_linux_dmabuf_feedback_v1 feedback = { - .main_device = render_dev, - .tranches = tranches, - .tranches_len = sizeof(tranches) / sizeof(tranches[0]), - }; wlr_linux_dmabuf_v1_set_surface_feedback(server.linux_dmabuf_v1, con->view->surface, &feedback); - wlr_drm_format_set_finish(&scanout_formats); + wlr_linux_dmabuf_feedback_v1_finish(&feedback); } static void container_fullscreen_workspace(struct sway_container *con) { @@ -1326,14 +1293,14 @@ bool container_is_fullscreen_or_child(struct sway_container *container) { static void surface_send_enter_iterator(struct wlr_surface *surface, int x, int y, void *data) { - struct wlr_output *wlr_output = data; - wlr_surface_send_enter(surface, wlr_output); + struct sway_output *output = data; + surface_enter_output(surface, output); } static void surface_send_leave_iterator(struct wlr_surface *surface, int x, int y, void *data) { - struct wlr_output *wlr_output = data; - wlr_surface_send_leave(surface, wlr_output); + struct sway_output *output = data; + surface_leave_output(surface, output); } void container_discover_outputs(struct sway_container *con) { @@ -1359,7 +1326,7 @@ void container_discover_outputs(struct sway_container *con) { sway_log(SWAY_DEBUG, "Container %p entered output %p", con, output); if (con->view) { view_for_each_surface(con->view, - surface_send_enter_iterator, output->wlr_output); + surface_send_enter_iterator, output); if (con->view->foreign_toplevel) { wlr_foreign_toplevel_handle_v1_output_enter( con->view->foreign_toplevel, output->wlr_output); @@ -1371,7 +1338,7 @@ void container_discover_outputs(struct sway_container *con) { sway_log(SWAY_DEBUG, "Container %p left output %p", con, output); if (con->view) { view_for_each_surface(con->view, - surface_send_leave_iterator, output->wlr_output); + surface_send_leave_iterator, output); if (con->view->foreign_toplevel) { wlr_foreign_toplevel_handle_v1_output_leave( con->view->foreign_toplevel, output->wlr_output); @@ -1412,9 +1379,6 @@ list_t *container_get_siblings(struct sway_container *container) { if (container->pending.parent) { return container->pending.parent->pending.children; } - if (container_is_scratchpad_hidden(container)) { - return NULL; - } if (!container->pending.workspace) { return NULL; } @@ -1827,3 +1791,177 @@ int container_squash(struct sway_container *con) { } return change; } + +static void swap_places(struct sway_container *con1, + struct sway_container *con2) { + struct sway_container *temp = malloc(sizeof(struct sway_container)); + temp->pending.x = con1->pending.x; + temp->pending.y = con1->pending.y; + temp->pending.width = con1->pending.width; + temp->pending.height = con1->pending.height; + temp->width_fraction = con1->width_fraction; + temp->height_fraction = con1->height_fraction; + temp->pending.parent = con1->pending.parent; + temp->pending.workspace = con1->pending.workspace; + bool temp_floating = container_is_floating(con1); + + con1->pending.x = con2->pending.x; + con1->pending.y = con2->pending.y; + con1->pending.width = con2->pending.width; + con1->pending.height = con2->pending.height; + con1->width_fraction = con2->width_fraction; + con1->height_fraction = con2->height_fraction; + + con2->pending.x = temp->pending.x; + con2->pending.y = temp->pending.y; + con2->pending.width = temp->pending.width; + con2->pending.height = temp->pending.height; + con2->width_fraction = temp->width_fraction; + con2->height_fraction = temp->height_fraction; + + int temp_index = container_sibling_index(con1); + if (con2->pending.parent) { + container_insert_child(con2->pending.parent, con1, + container_sibling_index(con2)); + } else if (container_is_floating(con2)) { + workspace_add_floating(con2->pending.workspace, con1); + } else { + workspace_insert_tiling(con2->pending.workspace, con1, + container_sibling_index(con2)); + } + if (temp->pending.parent) { + container_insert_child(temp->pending.parent, con2, temp_index); + } else if (temp_floating) { + workspace_add_floating(temp->pending.workspace, con2); + } else { + workspace_insert_tiling(temp->pending.workspace, con2, temp_index); + } + + free(temp); +} + +static void swap_focus(struct sway_container *con1, + struct sway_container *con2, struct sway_seat *seat, + struct sway_container *focus) { + if (focus == con1 || focus == con2) { + struct sway_workspace *ws1 = con1->pending.workspace; + struct sway_workspace *ws2 = con2->pending.workspace; + enum sway_container_layout layout1 = container_parent_layout(con1); + enum sway_container_layout layout2 = container_parent_layout(con2); + if (focus == con1 && (layout2 == L_TABBED || layout2 == L_STACKED)) { + if (workspace_is_visible(ws2)) { + seat_set_focus(seat, &con2->node); + } + seat_set_focus_container(seat, ws1 != ws2 ? con2 : con1); + } else if (focus == con2 && (layout1 == L_TABBED + || layout1 == L_STACKED)) { + if (workspace_is_visible(ws1)) { + seat_set_focus(seat, &con1->node); + } + seat_set_focus_container(seat, ws1 != ws2 ? con1 : con2); + } else if (ws1 != ws2) { + seat_set_focus_container(seat, focus == con1 ? con2 : con1); + } else { + seat_set_focus_container(seat, focus); + } + } else { + seat_set_focus_container(seat, focus); + } + + if (root->fullscreen_global) { + seat_set_focus(seat, + seat_get_focus_inactive(seat, &root->fullscreen_global->node)); + } +} + +void container_swap(struct sway_container *con1, struct sway_container *con2) { + if (!sway_assert(con1 && con2, "Cannot swap with nothing")) { + return; + } + if (!sway_assert(!container_has_ancestor(con1, con2) + && !container_has_ancestor(con2, con1), + "Cannot swap ancestor and descendant")) { + return; + } + + sway_log(SWAY_DEBUG, "Swapping containers %zu and %zu", + con1->node.id, con2->node.id); + + bool scratch1 = con1->scratchpad; + bool hidden1 = container_is_scratchpad_hidden(con1); + bool scratch2 = con2->scratchpad; + bool hidden2 = container_is_scratchpad_hidden(con2); + if (scratch1) { + if (hidden1) { + root_scratchpad_show(con1); + } + root_scratchpad_remove_container(con1); + } + if (scratch2) { + if (hidden2) { + root_scratchpad_show(con2); + } + root_scratchpad_remove_container(con2); + } + + enum sway_fullscreen_mode fs1 = con1->pending.fullscreen_mode; + if (fs1) { + container_fullscreen_disable(con1); + } + enum sway_fullscreen_mode fs2 = con2->pending.fullscreen_mode; + if (fs2) { + container_fullscreen_disable(con2); + } + + struct sway_seat *seat = input_manager_current_seat(); + struct sway_container *focus = seat_get_focused_container(seat); + struct sway_workspace *vis1 = + output_get_active_workspace(con1->pending.workspace->output); + struct sway_workspace *vis2 = + output_get_active_workspace(con2->pending.workspace->output); + if (!sway_assert(vis1 && vis2, "con1 or con2 are on an output without a" + "workspace. This should not happen")) { + return; + } + + char *stored_prev_name = NULL; + if (seat->prev_workspace_name) { + stored_prev_name = strdup(seat->prev_workspace_name); + } + + swap_places(con1, con2); + + if (!workspace_is_visible(vis1)) { + seat_set_focus(seat, seat_get_focus_inactive(seat, &vis1->node)); + } + if (!workspace_is_visible(vis2)) { + seat_set_focus(seat, seat_get_focus_inactive(seat, &vis2->node)); + } + + swap_focus(con1, con2, seat, focus); + + if (stored_prev_name) { + free(seat->prev_workspace_name); + seat->prev_workspace_name = stored_prev_name; + } + + if (scratch1) { + root_scratchpad_add_container(con2, NULL); + if (!hidden1) { + root_scratchpad_show(con2); + } + } + if (scratch2) { + root_scratchpad_add_container(con1, NULL); + if (!hidden2) { + root_scratchpad_show(con1); + } + } + + if (fs1) { + container_set_fullscreen(con2, fs1); + } + if (fs2) { + container_set_fullscreen(con1, fs2); + } +} diff --git a/sway/tree/output.c b/sway/tree/output.c index eccab2f7..4aa3a7fe 100644 --- a/sway/tree/output.c +++ b/sway/tree/output.c @@ -271,14 +271,13 @@ void output_disable(struct sway_output *output) { list_del(root->outputs, index); output->enabled = false; - output->current_mode = NULL; arrange_root(); // Reconfigure all devices, since devices with map_to_output directives for // an output that goes offline should stop sending events as long as the // output remains offline. - input_manager_configure_all_inputs(); + input_manager_configure_all_input_mappings(); } void output_begin_destroy(struct sway_output *output) { diff --git a/sway/tree/root.c b/sway/tree/root.c index 9df6f002..381e83de 100644 --- a/sway/tree/root.c +++ b/sway/tree/root.c @@ -50,6 +50,7 @@ struct sway_root *root_create(void) { void root_destroy(struct sway_root *root) { wl_list_remove(&root->output_layout_change.link); list_free(root->scratchpad); + list_free(root->non_desktop_outputs); list_free(root->outputs); wlr_output_layout_destroy(root->output_layout); free(root); @@ -59,9 +60,8 @@ void root_destroy(struct sway_root *root) { static void root_scratchpad_set_minimize(struct sway_container *con, bool minimize) { if (con->view) { #if HAVE_XWAYLAND - if (wlr_surface_is_xwayland_surface(con->view->surface)) { - struct wlr_xwayland_surface *xsurface - = wlr_xwayland_surface_from_wlr_surface(con->view->surface); + struct wlr_xwayland_surface *xsurface; + if ((xsurface = wlr_xwayland_surface_try_from_wlr_surface(con->view->surface))) { wlr_xwayland_surface_set_minimized(xsurface, minimize); return; } @@ -73,6 +73,16 @@ static void root_scratchpad_set_minimize(struct sway_container *con, bool minimi } } +static void set_container_transform(struct sway_workspace *ws, + struct sway_container *con) { + struct sway_output *output = ws->output; + struct wlr_box box = {0}; + if (output) { + output_get_box(output, &box); + } + con->transform = box; +} + void root_scratchpad_add_container(struct sway_container *con, struct sway_workspace *ws) { if (!sway_assert(!con->scratchpad, "Container is already in scratchpad")) { return; @@ -81,6 +91,8 @@ void root_scratchpad_add_container(struct sway_container *con, struct sway_works struct sway_container *parent = con->pending.parent; struct sway_workspace *workspace = con->pending.workspace; + set_container_transform(workspace, con); + // Clear the fullscreen mode when sending to the scratchpad if (con->pending.fullscreen_mode != FULLSCREEN_NONE) { container_fullscreen_disable(con); @@ -155,7 +167,10 @@ void root_scratchpad_show(struct sway_container *con) { // Show the container if (old_ws) { container_detach(con); - workspace_consider_destroy(old_ws); + // Make sure the last inactive container on the old workspace is above + // the workspace itself in the focus stack. + struct sway_node *node = seat_get_focus_inactive(seat, &old_ws->node); + seat_set_raw_focus(seat, node); } else { // Act on the ancestor of scratchpad hidden split containers while (con->pending.parent) { @@ -169,18 +184,18 @@ void root_scratchpad_show(struct sway_container *con) { root_scratchpad_set_minimize(con, false); } - // Make sure the container's center point overlaps this workspace - double center_lx = con->pending.x + con->pending.width / 2; - double center_ly = con->pending.y + con->pending.height / 2; - - struct wlr_box workspace_box; - workspace_get_box(new_ws, &workspace_box); - if (!wlr_box_contains_point(&workspace_box, center_lx, center_ly)) { - container_floating_resize_and_center(con); + if (new_ws->output) { + struct wlr_box output_box; + output_get_box(new_ws->output, &output_box); + floating_fix_coordinates(con, &con->transform, &output_box); } + set_container_transform(new_ws, con); arrange_workspace(new_ws); seat_set_focus(seat, seat_get_focus_inactive(seat, &con->node)); + if (old_ws) { + workspace_consider_destroy(old_ws); + } } static void disable_fullscreen(struct sway_container *con, void *data) { @@ -205,6 +220,8 @@ void root_scratchpad_hide(struct sway_container *con) { root_scratchpad_set_minimize(con, true); } + set_container_transform(con->pending.workspace, con); + disable_fullscreen(con, NULL); container_for_each_child(con, disable_fullscreen, NULL); container_detach(con); diff --git a/sway/tree/view.c b/sway/tree/view.c index 272967f4..bd2268fd 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -25,6 +25,7 @@ #include "sway/output.h" #include "sway/input/seat.h" #include "sway/server.h" +#include "sway/surface.h" #include "sway/tree/arrange.h" #include "sway/tree/container.h" #include "sway/tree/view.h" @@ -366,16 +367,15 @@ void view_set_activated(struct sway_view *view, bool activated) { } } -void view_request_activate(struct sway_view *view) { +void view_request_activate(struct sway_view *view, struct sway_seat *seat) { struct sway_workspace *ws = view->container->pending.workspace; - if (!ws) { // hidden scratchpad container - return; + if (!seat) { + seat = input_manager_current_seat(); } - struct sway_seat *seat = input_manager_current_seat(); switch (config->focus_on_window_activation) { case FOWA_SMART: - if (workspace_is_visible(ws)) { + if (ws && workspace_is_visible(ws)) { seat_set_focus_container(seat, view->container); container_raise_floating(view->container); } else { @@ -386,8 +386,12 @@ void view_request_activate(struct sway_view *view) { view_set_urgent(view, true); break; case FOWA_FOCUS: - seat_set_focus_container(seat, view->container); - container_raise_floating(view->container); + if (container_is_scratchpad_hidden_or_child(view->container)) { + root_scratchpad_show(view->container); + } else { + seat_set_focus_container(seat, view->container); + container_raise_floating(view->container); + } break; case FOWA_NONE: break; @@ -395,6 +399,12 @@ void view_request_activate(struct sway_view *view) { transaction_commit_dirty(); } +void view_request_urgent(struct sway_view *view) { + if (config->focus_on_window_activation != FOWA_NONE) { + view_set_urgent(view, true); + } +} + void view_set_csd_from_server(struct sway_view *view, bool enabled) { sway_log(SWAY_DEBUG, "Telling view %p to set CSD to %i", view, enabled); if (view->xdg_decoration) { @@ -526,7 +536,7 @@ static void view_populate_pid(struct sway_view *view) { #if HAVE_XWAYLAND case SWAY_VIEW_XWAYLAND:; struct wlr_xwayland_surface *surf = - wlr_xwayland_surface_from_wlr_surface(view->surface); + wlr_xwayland_surface_try_from_wlr_surface(view->surface); pid = surf->pid; break; #endif @@ -885,9 +895,8 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface, bool set_focus = should_focus(view); #if HAVE_XWAYLAND - if (wlr_surface_is_xwayland_surface(wlr_surface)) { - struct wlr_xwayland_surface *xsurface = - wlr_xwayland_surface_from_wlr_surface(wlr_surface); + struct wlr_xwayland_surface *xsurface; + if ((xsurface = wlr_xwayland_surface_try_from_wlr_surface(wlr_surface))) { set_focus &= wlr_xwayland_icccm_input_model(xsurface) != WLR_ICCCM_INPUT_MODEL_NONE; } @@ -911,6 +920,8 @@ void view_unmap(struct sway_view *view) { wl_list_remove(&view->surface_new_subsurface.link); + view->executed_criteria->length = 0; + if (view->urgent_timer) { wl_event_source_remove(view->urgent_timer); view->urgent_timer = NULL; @@ -984,7 +995,7 @@ static void subsurface_get_view_coords(struct sway_view_child *child, *sx = *sy = 0; } struct wlr_subsurface *subsurface = - wlr_subsurface_from_wlr_surface(surface); + wlr_subsurface_try_from_wlr_surface(surface); *sx += subsurface->current.x; *sy += subsurface->current.y; } @@ -1179,7 +1190,7 @@ void view_child_init(struct sway_view_child *child, if (container != NULL) { struct sway_workspace *workspace = container->pending.workspace; if (workspace) { - wlr_surface_send_enter(child->surface, workspace->output->wlr_output); + surface_enter_output(child->surface, workspace->output); } } @@ -1220,33 +1231,21 @@ void view_child_destroy(struct sway_view_child *child) { } struct sway_view *view_from_wlr_surface(struct wlr_surface *wlr_surface) { - if (wlr_surface_is_xdg_surface(wlr_surface)) { - struct wlr_xdg_surface *xdg_surface = - wlr_xdg_surface_from_wlr_surface(wlr_surface); - if (xdg_surface == NULL) { - return NULL; - } + struct wlr_xdg_surface *xdg_surface; + if ((xdg_surface = wlr_xdg_surface_try_from_wlr_surface(wlr_surface))) { return view_from_wlr_xdg_surface(xdg_surface); } #if HAVE_XWAYLAND - if (wlr_surface_is_xwayland_surface(wlr_surface)) { - struct wlr_xwayland_surface *xsurface = - wlr_xwayland_surface_from_wlr_surface(wlr_surface); - if (xsurface == NULL) { - return NULL; - } + struct wlr_xwayland_surface *xsurface; + if ((xsurface = wlr_xwayland_surface_try_from_wlr_surface(wlr_surface))) { return view_from_wlr_xwayland_surface(xsurface); } #endif - if (wlr_surface_is_subsurface(wlr_surface)) { - struct wlr_subsurface *subsurface = - wlr_subsurface_from_wlr_surface(wlr_surface); - if (subsurface == NULL) { - return NULL; - } + struct wlr_subsurface *subsurface; + if ((subsurface = wlr_subsurface_try_from_wlr_surface(wlr_surface))) { return view_from_wlr_surface(subsurface->parent); } - if (wlr_surface_is_layer_surface(wlr_surface)) { + if (wlr_layer_surface_v1_try_from_wlr_surface(wlr_surface) != NULL) { return NULL; } @@ -1463,7 +1462,7 @@ static void view_save_buffer_iterator(struct wlr_surface *surface, int sx, int sy, void *data) { struct sway_view *view = data; - if (surface && wlr_surface_has_buffer(surface)) { + if (surface && surface->buffer) { wlr_buffer_lock(&surface->buffer->base); struct sway_saved_buffer *saved_buffer = calloc(1, sizeof(struct sway_saved_buffer)); saved_buffer->buffer = surface->buffer; diff --git a/sway/tree/workspace.c b/sway/tree/workspace.c index b2305341..98a177fa 100644 --- a/sway/tree/workspace.c +++ b/sway/tree/workspace.c @@ -178,22 +178,16 @@ void workspace_consider_destroy(struct sway_workspace *ws) { static bool workspace_valid_on_output(const char *output_name, const char *ws_name) { struct workspace_config *wsc = workspace_find_config(ws_name); - char identifier[128]; struct sway_output *output = output_by_name_or_id(output_name); if (!output) { return false; } - output_name = output->wlr_output->name; - output_get_identifier(identifier, sizeof(identifier), output); - if (!wsc) { return true; } for (int i = 0; i < wsc->outputs->length; i++) { - if (strcmp(wsc->outputs->items[i], "*") == 0 || - strcmp(wsc->outputs->items[i], output_name) == 0 || - strcmp(wsc->outputs->items[i], identifier) == 0) { + if (output_match_name_or_id(output, wsc->outputs->items[i])) { return true; } } @@ -288,13 +282,10 @@ char *workspace_next_name(const char *output_name) { // assignments primarily, falling back to bindings and numbers. struct sway_mode *mode = config->current_mode; - char identifier[128]; struct sway_output *output = output_by_name_or_id(output_name); if (!output) { return NULL; } - output_name = output->wlr_output->name; - output_get_identifier(identifier, sizeof(identifier), output); int order = INT_MAX; char *target = NULL; @@ -314,9 +305,7 @@ char *workspace_next_name(const char *output_name) { } bool found = false; for (int j = 0; j < wsc->outputs->length; ++j) { - if (strcmp(wsc->outputs->items[j], "*") == 0 || - strcmp(wsc->outputs->items[j], output_name) == 0 || - strcmp(wsc->outputs->items[j], identifier) == 0) { + if (output_match_name_or_id(output, wsc->outputs->items[j])) { found = true; free(target); target = strdup(wsc->workspace); @@ -656,15 +645,9 @@ void workspace_output_add_priority(struct sway_workspace *workspace, struct sway_output *workspace_output_get_highest_available( struct sway_workspace *ws, struct sway_output *exclude) { - char exclude_id[128] = {'\0'}; - if (exclude) { - output_get_identifier(exclude_id, sizeof(exclude_id), exclude); - } - for (int i = 0; i < ws->output_priority->length; i++) { - char *name = ws->output_priority->items[i]; - if (exclude && (strcmp(name, exclude->wlr_output->name) == 0 - || strcmp(name, exclude_id) == 0)) { + const char *name = ws->output_priority->items[i]; + if (exclude && output_match_name_or_id(exclude, name)) { continue; } diff --git a/sway/xdg_activation_v1.c b/sway/xdg_activation_v1.c index 2b94136c..3a035972 100644 --- a/sway/xdg_activation_v1.c +++ b/sway/xdg_activation_v1.c @@ -1,17 +1,14 @@ #include #include "sway/desktop/launcher.h" #include "sway/tree/view.h" +#include "sway/tree/workspace.h" void xdg_activation_v1_handle_request_activate(struct wl_listener *listener, void *data) { const struct wlr_xdg_activation_v1_request_activate_event *event = data; - if (!wlr_surface_is_xdg_surface(event->surface)) { - return; - } - struct wlr_xdg_surface *xdg_surface = - wlr_xdg_surface_from_wlr_surface(event->surface); + wlr_xdg_surface_try_from_wlr_surface(event->surface); if (xdg_surface == NULL) { return; } @@ -20,11 +17,15 @@ void xdg_activation_v1_handle_request_activate(struct wl_listener *listener, return; } - if (!xdg_surface->mapped) { + struct launcher_ctx *ctx = event->token->data; + if (ctx == NULL) { + return; + } + + if (!xdg_surface->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) { + if (ctx->activated) { // This ctx has already been activated and cannot be used again // for a startup notification. It will be destroyed return; @@ -35,5 +36,29 @@ void xdg_activation_v1_handle_request_activate(struct wl_listener *listener, return; } - view_request_activate(view); + // This is an activation request. If this context is internal we have ctx->seat. + struct sway_seat *seat = ctx->seat; + if (!seat) { + // Otherwise, use the seat indicated by the launcher client in set_serial + seat = ctx->token->seat ? ctx->token->seat->data : NULL; + } + + if (seat && ctx->had_focused_surface) { + view_request_activate(view, seat); + } else { + // The token is valid, but cannot be used to activate a window + view_request_urgent(view); + } +} + +void xdg_activation_v1_handle_new_token(struct wl_listener *listener, void *data) { + struct wlr_xdg_activation_token_v1 *token = data; + struct sway_seat *seat = token->seat ? token->seat->data : + input_manager_current_seat(); + + struct sway_workspace *ws = seat_get_focused_workspace(seat); + if (ws) { + launcher_ctx_create(token, &ws->node); + return; + } } diff --git a/sway/xdg_decoration.c b/sway/xdg_decoration.c index ec9e8d68..f7f5f5ed 100644 --- a/sway/xdg_decoration.c +++ b/sway/xdg_decoration.c @@ -53,7 +53,7 @@ static void xdg_decoration_handle_request_mode(struct wl_listener *listener, void handle_xdg_decoration(struct wl_listener *listener, void *data) { struct wlr_xdg_toplevel_decoration_v1 *wlr_deco = data; - struct sway_xdg_shell_view *xdg_shell_view = wlr_deco->surface->data; + struct sway_xdg_shell_view *xdg_shell_view = wlr_deco->toplevel->base->data; struct sway_xdg_decoration *deco = calloc(1, sizeof(*deco)); if (deco == NULL) { @@ -79,7 +79,7 @@ struct sway_xdg_decoration *xdg_decoration_from_surface( struct wlr_surface *surface) { struct sway_xdg_decoration *deco; wl_list_for_each(deco, &server.xdg_decorations, link) { - if (deco->wlr_xdg_decoration->surface->surface == surface) { + if (deco->wlr_xdg_decoration->toplevel->base->surface == surface) { return deco; } } diff --git a/swaybar/bar.c b/swaybar/bar.c index 5e4ebd97..021fc3bd 100644 --- a/swaybar/bar.c +++ b/swaybar/bar.c @@ -362,6 +362,9 @@ static void handle_global(void *data, struct wl_registry *registry, } else if (strcmp(interface, zxdg_output_manager_v1_interface.name) == 0) { bar->xdg_output_manager = wl_registry_bind(registry, name, &zxdg_output_manager_v1_interface, 2); + } else if (strcmp(interface, wp_cursor_shape_manager_v1_interface.name) == 0) { + bar->cursor_shape_manager = wl_registry_bind(registry, name, + &wp_cursor_shape_manager_v1_interface, 1); } } @@ -425,15 +428,17 @@ bool bar_setup(struct swaybar *bar, const char *socket_path) { // Second roundtrip for xdg-output wl_display_roundtrip(bar->display); - struct swaybar_seat *seat; - wl_list_for_each(seat, &bar->seats, link) { - struct swaybar_pointer *pointer = &seat->pointer; - if (!pointer) { - continue; + if (!bar->cursor_shape_manager) { + struct swaybar_seat *seat; + wl_list_for_each(seat, &bar->seats, link) { + struct swaybar_pointer *pointer = &seat->pointer; + if (!pointer) { + continue; + } + pointer->cursor_surface = + wl_compositor_create_surface(bar->compositor); + assert(pointer->cursor_surface); } - pointer->cursor_surface = - wl_compositor_create_surface(bar->compositor); - assert(pointer->cursor_surface); } if (bar->config->status_command) { diff --git a/swaybar/input.c b/swaybar/input.c index 8eccf542..ada4bc86 100644 --- a/swaybar/input.c +++ b/swaybar/input.c @@ -81,8 +81,16 @@ void update_cursor(struct swaybar_seat *seat) { int scale = pointer->current ? pointer->current->scale : 1; pointer->cursor_theme = wl_cursor_theme_load( cursor_theme, cursor_size * scale, seat->bar->shm); + if (!pointer->cursor_theme) { + sway_log(SWAY_ERROR, "Failed to load cursor theme"); + return; + } struct wl_cursor *cursor; - cursor = wl_cursor_theme_get_cursor(pointer->cursor_theme, "left_ptr"); + cursor = wl_cursor_theme_get_cursor(pointer->cursor_theme, "default"); + if (!cursor) { + sway_log(SWAY_ERROR, "Failed to get default cursor from theme"); + return; + } pointer->cursor_image = cursor->images[0]; wl_surface_set_buffer_scale(pointer->cursor_surface, scale); wl_surface_attach(pointer->cursor_surface, @@ -103,7 +111,7 @@ static void wl_pointer_enter(void *data, struct wl_pointer *wl_pointer, struct swaybar_pointer *pointer = &seat->pointer; seat->pointer.x = wl_fixed_to_double(surface_x); seat->pointer.y = wl_fixed_to_double(surface_y); - pointer->serial = serial; + struct swaybar_output *output; wl_list_for_each(output, &seat->bar->outputs, link) { if (output->surface == surface) { @@ -111,7 +119,18 @@ static void wl_pointer_enter(void *data, struct wl_pointer *wl_pointer, break; } } - update_cursor(seat); + + if (seat->bar->cursor_shape_manager) { + struct wp_cursor_shape_device_v1 *device = + wp_cursor_shape_manager_v1_get_pointer( + seat->bar->cursor_shape_manager, wl_pointer); + wp_cursor_shape_device_v1_set_shape(device, serial, + WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_DEFAULT); + wp_cursor_shape_device_v1_destroy(device); + } else { + pointer->serial = serial; + update_cursor(seat); + } } static void wl_pointer_leave(void *data, struct wl_pointer *wl_pointer, @@ -207,7 +226,7 @@ static void workspace_next(struct swaybar *bar, struct swaybar_output *output, } } - if (new) { + if (new && new != active) { ipc_send_workspace_command(bar, new->name); // Since we're asking Sway to switch to 'new', it should become visible. diff --git a/swaybar/ipc.c b/swaybar/ipc.c index 9d81a9fb..33ae6544 100644 --- a/swaybar/ipc.c +++ b/swaybar/ipc.c @@ -426,12 +426,9 @@ bool ipc_initialize(struct swaybar *bar) { } free(res); - struct swaybar_config *config = bar->config; - char subscribe[128]; // suitably large buffer - len = snprintf(subscribe, 128, - "[ \"barconfig_update\" , \"bar_state_update\" %s %s ]", - config->binding_mode_indicator ? ", \"mode\"" : "", - config->workspace_buttons ? ", \"workspace\"" : ""); + char *subscribe = + "[ \"barconfig_update\", \"bar_state_update\", \"mode\", \"workspace\" ]"; + len = strlen(subscribe); free(ipc_single_command(bar->ipc_event_socketfd, IPC_SUBSCRIBE, subscribe, &len)); return true; diff --git a/swaybar/render.c b/swaybar/render.c index ccf36563..1113ca44 100644 --- a/swaybar/render.c +++ b/swaybar/render.c @@ -693,15 +693,6 @@ static uint32_t render_to_cairo(struct render_context *ctx) { struct swaybar_output *output = ctx->output; struct swaybar *bar = output->bar; struct swaybar_config *config = bar->config; - cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE); - if (output->focused) { - ctx->background_color = config->colors.focused_background; - } else { - ctx->background_color = config->colors.background; - } - - cairo_set_source_u32(cairo, ctx->background_color); - cairo_paint(cairo); int th; get_text_size(cairo, config->font_description, NULL, &th, NULL, 1, false, ""); @@ -763,8 +754,17 @@ void render_frame(struct swaybar_output *output) { free_hotspots(&output->hotspots); + uint32_t background_color; + if (output->focused) { + background_color = output->bar->config->colors.focused_background; + } else { + background_color = output->bar->config->colors.background; + } + struct render_context ctx = { 0 }; ctx.output = output; + // initial background color used for deciding the best way to antialias text + ctx.background_color = background_color; cairo_surface_t *recorder = cairo_recording_surface_create( CAIRO_CONTENT_COLOR_ALPHA, NULL); @@ -774,24 +774,23 @@ void render_frame(struct swaybar_output *output) { ctx.cairo = cairo; cairo_font_options_t *fo = cairo_font_options_create(); - cairo_font_options_set_hint_style(fo, CAIRO_HINT_STYLE_FULL); cairo_font_options_set_antialias(fo, CAIRO_ANTIALIAS_GRAY); ctx.textaa_safe = fo; if (output->subpixel == WL_OUTPUT_SUBPIXEL_NONE) { ctx.textaa_sharp = ctx.textaa_safe; } else { fo = cairo_font_options_create(); - cairo_font_options_set_hint_style(fo, CAIRO_HINT_STYLE_FULL); cairo_font_options_set_antialias(fo, CAIRO_ANTIALIAS_SUBPIXEL); cairo_font_options_set_subpixel_order(fo, to_cairo_subpixel_order(output->subpixel)); ctx.textaa_sharp = fo; } - cairo_save(cairo); - cairo_set_operator(cairo, CAIRO_OPERATOR_CLEAR); + + cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE); + cairo_set_source_u32(cairo, background_color); cairo_paint(cairo); - cairo_restore(cairo); + uint32_t height = render_to_cairo(&ctx); int config_height = output->bar->config->height; if (config_height > 0) { @@ -836,13 +835,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; + uint32_t bg_alpha = 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); + } else { + wl_surface_set_opaque_region(output->surface, NULL); } struct wl_callback *frame_callback = wl_surface_frame(output->surface); diff --git a/swaybar/tray/host.c b/swaybar/tray/host.c index ddf2416d..eea2caa5 100644 --- a/swaybar/tray/host.c +++ b/swaybar/tray/host.c @@ -10,6 +10,7 @@ #include "swaybar/tray/tray.h" #include "list.h" #include "log.h" +#include "stringop.h" static const char *watcher_path = "/StatusNotifierWatcher"; @@ -138,12 +139,10 @@ static int handle_new_watcher(sd_bus_message *msg, bool init_host(struct swaybar_host *host, char *protocol, struct swaybar_tray *tray) { - size_t len = snprintf(NULL, 0, "org.%s.StatusNotifierWatcher", protocol) + 1; - host->watcher_interface = malloc(len); + host->watcher_interface = format_str("org.%s.StatusNotifierWatcher", protocol); if (!host->watcher_interface) { return false; } - snprintf(host->watcher_interface, len, "org.%s.StatusNotifierWatcher", protocol); sd_bus_slot *reg_slot = NULL, *unreg_slot = NULL, *watcher_slot = NULL; int ret = sd_bus_match_signal(tray->bus, ®_slot, host->watcher_interface, @@ -173,13 +172,10 @@ bool init_host(struct swaybar_host *host, char *protocol, } pid_t pid = getpid(); - size_t service_len = snprintf(NULL, 0, "org.%s.StatusNotifierHost-%d", - protocol, pid) + 1; - host->service = malloc(service_len); + host->service = format_str("org.%s.StatusNotifierHost-%d", protocol, pid); if (!host->service) { goto error; } - snprintf(host->service, service_len, "org.%s.StatusNotifierHost-%d", protocol, pid); ret = sd_bus_request_name(tray->bus, host->service, 0); if (ret < 0) { sway_log(SWAY_DEBUG, "Failed to acquire service name: %s", strerror(-ret)); diff --git a/swaybar/tray/icon.c b/swaybar/tray/icon.c index c426c3d4..b513dca5 100644 --- a/swaybar/tray/icon.c +++ b/swaybar/tray/icon.c @@ -40,9 +40,7 @@ static list_t *get_basedirs(void) { data_dirs = strdup(data_dirs); char *dir = strtok(data_dirs, ":"); do { - size_t path_len = snprintf(NULL, 0, "%s/icons", dir) + 1; - char *path = malloc(path_len); - snprintf(path, path_len, "%s/icons", dir); + char *path = format_str("%s/icons", dir); list_add(basedirs, path); } while ((dir = strtok(NULL, ":"))); free(data_dirs); @@ -206,13 +204,7 @@ static const char *entry_handler(char *group, char *key, char *value, */ static struct icon_theme *read_theme_file(char *basedir, char *theme_name) { // look for index.theme file - size_t path_len = snprintf(NULL, 0, "%s/%s/index.theme", basedir, - theme_name) + 1; - char *path = malloc(path_len); - if (!path) { - return NULL; - } - snprintf(path, path_len, "%s/%s/index.theme", basedir, theme_name); + char *path = format_str("%s/%s/index.theme", basedir, theme_name); FILE *theme_file = fopen(path, "r"); free(path); if (!theme_file) { @@ -416,26 +408,20 @@ static char *find_icon_in_subdir(char *name, char *basedir, char *theme, #endif }; - size_t path_len = snprintf(NULL, 0, "%s/%s/%s/%s.EXT", basedir, theme, - subdir, name) + 1; - char *path = malloc(path_len); - for (size_t i = 0; i < sizeof(extensions) / sizeof(*extensions); ++i) { - snprintf(path, path_len, "%s/%s/%s/%s.%s", basedir, theme, subdir, - name, extensions[i]); + char *path = format_str("%s/%s/%s/%s.%s", + basedir, theme, subdir, name, extensions[i]); if (access(path, R_OK) == 0) { return path; } + free(path); } - free(path); return NULL; } static bool theme_exists_in_basedir(char *theme, char *basedir) { - size_t path_len = snprintf(NULL, 0, "%s/%s", basedir, theme) + 1; - char *path = malloc(path_len); - snprintf(path, path_len, "%s/%s", basedir, theme); + char *path = format_str("%s/%s", basedir, theme); bool ret = dir_exists(path); free(path); return ret; diff --git a/swaybar/tray/watcher.c b/swaybar/tray/watcher.c index 16afc27c..2458a8c2 100644 --- a/swaybar/tray/watcher.c +++ b/swaybar/tray/watcher.c @@ -6,6 +6,7 @@ #include #include "list.h" #include "log.h" +#include "stringop.h" #include "swaybar/tray/watcher.h" static const char *obj_path = "/StatusNotifierWatcher"; @@ -76,9 +77,7 @@ static int register_sni(sd_bus_message *msg, void *data, sd_bus_error *error) { service = service_or_path; path = "/StatusNotifierItem"; } - size_t id_len = snprintf(NULL, 0, "%s%s", service, path) + 1; - id = malloc(id_len); - snprintf(id, id_len, "%s%s", service, path); + id = format_str("%s%s", service, path); } if (list_seq_find(watcher->items, cmp_id, id) == -1) { @@ -107,7 +106,7 @@ static int register_host(sd_bus_message *msg, void *data, sd_bus_error *error) { sway_log(SWAY_DEBUG, "Registering Status Notifier Host '%s'", service); list_add(watcher->hosts, strdup(service)); sd_bus_emit_signal(watcher->bus, obj_path, watcher->interface, - "StatusNotifierHostRegistered", "s", service); + "StatusNotifierHostRegistered", ""); } else { sway_log(SWAY_DEBUG, "Status Notifier Host '%s' already registered", service); } @@ -159,9 +158,7 @@ struct swaybar_watcher *create_watcher(char *protocol, sd_bus *bus) { return NULL; } - size_t len = snprintf(NULL, 0, "org.%s.StatusNotifierWatcher", protocol) + 1; - watcher->interface = malloc(len); - snprintf(watcher->interface, len, "org.%s.StatusNotifierWatcher", protocol); + watcher->interface = format_str("org.%s.StatusNotifierWatcher", protocol); sd_bus_slot *signal_slot = NULL, *vtable_slot = NULL; int ret = sd_bus_add_object_vtable(bus, &vtable_slot, obj_path, diff --git a/swaymsg/main.c b/swaymsg/main.c index 237a4a71..f408307e 100644 --- a/swaymsg/main.c +++ b/swaymsg/main.c @@ -60,7 +60,7 @@ static void pretty_print_cmd(json_object *r) { if (!success_object(r)) { json_object *error; if (!json_object_object_get_ex(r, "error", &error)) { - printf("An unknkown error occurred"); + printf("An unknown error occurred"); } else { printf("Error: %s\n", json_object_get_string(error)); } @@ -185,11 +185,12 @@ 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, *non_desktop; + json_object *name, *rect, *focused, *active, *power, *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, "power", &power); 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, @@ -226,6 +227,7 @@ static void pretty_print_output(json_object *o) { printf( "Output %s '%s %s %s'%s\n" " Current mode: %dx%d @ %.3f Hz\n" + " Power: %s\n" " Position: %d,%d\n" " Scale factor: %f\n" " Scale filter: %s\n" @@ -240,6 +242,7 @@ static void pretty_print_output(json_object *o) { json_object_get_int(width), json_object_get_int(height), (double)json_object_get_int(refresh) / 1000, + json_object_get_boolean(power) ? "on" : "off", json_object_get_int(x), json_object_get_int(y), json_object_get_double(scale), json_object_get_string(scale_filter), @@ -256,7 +259,7 @@ static void pretty_print_output(json_object *o) { json_object_get_string(adaptive_sync_status)); } else { printf( - "Output %s '%s %s %s' (inactive)\n", + "Output %s '%s %s %s' (disabled)\n", json_object_get_string(name), json_object_get_string(make), json_object_get_string(model), diff --git a/swaymsg/swaymsg.1.scd b/swaymsg/swaymsg.1.scd index 24a9d6c9..abee1bb9 100644 --- a/swaymsg/swaymsg.1.scd +++ b/swaymsg/swaymsg.1.scd @@ -107,6 +107,8 @@ _swaymsg_ [options...] [message] Subscribe to a list of event types. The argument for this type should be provided in the form of a valid JSON array. If any of the types are invalid or if a valid JSON array is not provided, this will result in a failure. + For a list of valid event types and the data returned with them refer to + *sway-ipc*(7). # RETURN CODES diff --git a/swaynag/config.c b/swaynag/config.c index db86c1b8..b355b153 100644 --- a/swaynag/config.c +++ b/swaynag/config.c @@ -166,7 +166,7 @@ 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 = calloc(sizeof(struct swaynag_button), 1); + struct swaynag_button *button = calloc(1, sizeof(struct swaynag_button)); if (!button) { perror("calloc"); return EXIT_FAILURE; @@ -226,10 +226,8 @@ int swaynag_parse_options(int argc, char **argv, struct swaynag *swaynag, break; 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); + type->font_description = pango_font_description_from_string(optarg); } break; case 'l': // Detailed Message @@ -245,8 +243,8 @@ int swaynag_parse_options(int argc, char **argv, struct swaynag *swaynag, break; case 'L': // Detailed Button Text if (swaynag) { - free(swaynag->details.button_details.text); - swaynag->details.button_details.text = strdup(optarg); + free(swaynag->details.details_text); + swaynag->details.details_text = strdup(optarg); } break; case 'm': // Message diff --git a/swaynag/main.c b/swaynag/main.c index 2ce37831..20390207 100644 --- a/swaynag/main.c +++ b/swaynag/main.c @@ -29,10 +29,12 @@ int main(int argc, char **argv) { wl_list_init(&swaynag.outputs); wl_list_init(&swaynag.seats); - struct swaynag_button button_close = { 0 }; - button_close.text = strdup("X"); - button_close.type = SWAYNAG_ACTION_DISMISS; - list_add(swaynag.buttons, &button_close); + struct swaynag_button *button_close = calloc(1, sizeof(struct swaynag_button)); + button_close->text = strdup("X"); + button_close->type = SWAYNAG_ACTION_DISMISS; + list_add(swaynag.buttons, button_close); + + swaynag.details.details_text = strdup("Toggle details"); char *config_path = NULL; bool debug = false; @@ -54,8 +56,6 @@ int main(int argc, char **argv) { } } - 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(""); @@ -88,17 +88,20 @@ int main(int argc, char **argv) { swaynag_type_merge(type, swaynag_type_get(types, "")); swaynag.type = type; - swaynag_types_free(types); - if (swaynag.details.message) { - list_add(swaynag.buttons, &swaynag.details.button_details); + swaynag.details.button_details = calloc(1, sizeof(struct swaynag_button)); + swaynag.details.button_details->text = strdup(swaynag.details.details_text); + swaynag.details.button_details->type = SWAYNAG_ACTION_EXPAND; + list_add(swaynag.buttons, swaynag.details.button_details); } sway_log(SWAY_DEBUG, "Output: %s", swaynag.type->output); sway_log(SWAY_DEBUG, "Anchors: %" PRIu32, swaynag.type->anchors); sway_log(SWAY_DEBUG, "Type: %s", swaynag.type->name); sway_log(SWAY_DEBUG, "Message: %s", swaynag.message); - sway_log(SWAY_DEBUG, "Font: %s", swaynag.type->font); + char *font = pango_font_description_to_string(swaynag.type->font_description); + sway_log(SWAY_DEBUG, "Font: %s", font); + free(font); sway_log(SWAY_DEBUG, "Buttons"); for (int i = 0; i < swaynag.buttons->length; i++) { struct swaynag_button *button = swaynag.buttons->items[i]; @@ -109,11 +112,9 @@ int main(int argc, char **argv) { swaynag_setup(&swaynag); swaynag_run(&swaynag); - return status; cleanup: swaynag_types_free(types); - free(swaynag.details.button_details.text); swaynag_destroy(&swaynag); return status; } diff --git a/swaynag/swaynag.c b/swaynag/swaynag.c index 5620155d..6ea739e3 100644 --- a/swaynag/swaynag.c +++ b/swaynag/swaynag.c @@ -153,8 +153,15 @@ static void update_cursor(struct swaynag_seat *seat) { } pointer->cursor_theme = wl_cursor_theme_load( cursor_theme, cursor_size * swaynag->scale, swaynag->shm); - struct wl_cursor *cursor = - wl_cursor_theme_get_cursor(pointer->cursor_theme, "left_ptr"); + if (!pointer->cursor_theme) { + sway_log(SWAY_ERROR, "Failed to load cursor theme"); + return; + } + struct wl_cursor *cursor = wl_cursor_theme_get_cursor(pointer->cursor_theme, "default"); + if (!cursor) { + sway_log(SWAY_ERROR, "Failed to get default cursor from theme"); + return; + } pointer->cursor_image = cursor->images[0]; wl_surface_set_buffer_scale(pointer->cursor_surface, swaynag->scale); @@ -182,11 +189,22 @@ static void wl_pointer_enter(void *data, struct wl_pointer *wl_pointer, uint32_t serial, struct wl_surface *surface, wl_fixed_t surface_x, wl_fixed_t surface_y) { struct swaynag_seat *seat = data; + struct swaynag_pointer *pointer = &seat->pointer; pointer->x = wl_fixed_to_int(surface_x); pointer->y = wl_fixed_to_int(surface_y); - pointer->serial = serial; - update_cursor(seat); + + if (seat->swaynag->cursor_shape_manager) { + struct wp_cursor_shape_device_v1 *device = + wp_cursor_shape_manager_v1_get_pointer( + seat->swaynag->cursor_shape_manager, wl_pointer); + wp_cursor_shape_device_v1_set_shape(device, serial, + WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_DEFAULT); + wp_cursor_shape_device_v1_destroy(device); + } else { + pointer->serial = serial; + update_cursor(seat); + } } static void wl_pointer_motion(void *data, struct wl_pointer *wl_pointer, @@ -378,6 +396,9 @@ static void handle_global(void *data, struct wl_registry *registry, } else if (strcmp(interface, zwlr_layer_shell_v1_interface.name) == 0) { swaynag->layer_shell = wl_registry_bind( registry, name, &zwlr_layer_shell_v1_interface, 1); + } else if (strcmp(interface, wp_cursor_shape_manager_v1_interface.name) == 0) { + swaynag->cursor_shape_manager = wl_registry_bind( + registry, name, &wp_cursor_shape_manager_v1_interface, 1); } } @@ -456,7 +477,9 @@ void swaynag_setup(struct swaynag *swaynag) { exit(EXIT_FAILURE); } - swaynag_setup_cursors(swaynag); + if (!swaynag->cursor_shape_manager) { + swaynag_setup_cursors(swaynag); + } swaynag->surface = wl_compositor_create_surface(swaynag->compositor); assert(swaynag->surface); @@ -483,10 +506,6 @@ void swaynag_run(struct swaynag *swaynag) { && wl_display_dispatch(swaynag->display) != -1) { // This is intentionally left blank } - - if (swaynag->display) { - wl_display_disconnect(swaynag->display); - } } void swaynag_destroy(struct swaynag *swaynag) { @@ -501,6 +520,7 @@ void swaynag_destroy(struct swaynag *swaynag) { } list_free(swaynag->buttons); free(swaynag->details.message); + free(swaynag->details.details_text); free(swaynag->details.button_up.text); free(swaynag->details.button_down.text); @@ -541,4 +561,8 @@ void swaynag_destroy(struct swaynag *swaynag) { if (swaynag->shm) { wl_shm_destroy(swaynag->shm); } + + if (swaynag->display) { + wl_display_disconnect(swaynag->display); + } } diff --git a/swaynag/types.c b/swaynag/types.c index a46aacd5..409cc668 100644 --- a/swaynag/types.c +++ b/swaynag/types.c @@ -32,9 +32,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); + pango_font_description_from_string("pango:Monospace 10"); type_defaults->anchors = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT; @@ -92,10 +91,6 @@ void swaynag_type_merge(struct swaynag_type *dest, struct swaynag_type *src) { return; } - if (src->font) { - dest->font = strdup(src->font); - } - if (src->font_description) { dest->font_description = pango_font_description_copy(src->font_description); } @@ -178,7 +173,6 @@ 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);