Merge branch 'swaywm:master' into remove_stuff

This commit is contained in:
iguanajuice 2025-01-09 00:43:57 -05:00 committed by GitHub
commit 5d51cfc948
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
82 changed files with 1260 additions and 818 deletions

View file

@ -27,7 +27,7 @@ packages:
- x11/libX11
- x11/pixman
- x11/xcb-util-wm
- x11-servers/xwayland-devel
- x11-servers/xwayland
- misc/hwdata
sources:
- https://github.com/swaywm/sway

View file

@ -1,7 +1,5 @@
# sway
[English][en] - **[Česky][cs]** - [Deutsch][de] - [Dansk][dk] - [Español][es] - [Français][fr] - [Svenska][sv] - [Ελληνικά][gr] - [हिन्दी][hi] - [Magyar][hu] - [فارسی][ir] - [Italiano][it] - [日本語][ja] - [한국어][ko] - [Nederlands][nl] - [Polski][pl] - [Português][pt] - [Română][ro] - [Русский][ru] - [Türkçe][tr] - [Українська][uk] - [中文-简体][zh-CN] - [中文-繁體][zh-TW]
sway je s [i3] kompatibilní [Wayland] kompozitor. Přečtěte si [FAQ]. Připojte se na
[IRC kanál][IRC channel] \(#sway na irc.libera.chat).
@ -32,10 +30,11 @@ Nainstalujte závislosti:
* pango
* cairo
* gdk-pixbuf2 (volitelné: oznamovací oblast)
* [swaybg] (volitelné: tapeta)
* [scdoc] (volitelné: manuálové stránky) \*
* git (volitelné: informace o verzi) \*
_\* Závislost pouze pro sestavení_
_\* Závislost pouze pro kompilaci_
Spusťte tyto příkazy:
@ -56,12 +55,13 @@ Spusťte `sway` z TTY. Některé správce zobrazení mohou fungovat, ale nejsou
podporovány sway (je známo, že gdm funguje docela dobře).
[en]: https://github.com/swaywm/sway#readme
[ar]: README.ar.md
[cs]: README.cs.md
[de]: README.de.md
[dk]: README.dk.md
[es]: README.es.md
[fr]: README.fr.md
[sv]: README.sv.md
[ge]: README.ge.md
[gr]: README.gr.md
[hi]: README.hi.md
[hu]: README.hu.md
@ -70,10 +70,12 @@ podporovány sway (je známo, že gdm funguje docela dobře).
[ja]: README.ja.md
[ko]: README.ko.md
[nl]: README.nl.md
[no]: README.no.md
[pl]: README.pl.md
[pt]: README.pt.md
[ro]: README.ro.md
[ru]: README.ru.md
[sv]: README.sv.md
[tr]: README.tr.md
[uk]: README.uk.md
[zh-CN]: README.zh-CN.md
@ -86,4 +88,5 @@ podporovány sway (je známo, že gdm funguje docela dobře).
[GitHub releases]: https://github.com/swaywm/sway/releases
[Development setup]: https://github.com/swaywm/sway/wiki/Development-Setup
[wlroots]: https://gitlab.freedesktop.org/wlroots/wlroots
[swaybg]: https://github.com/swaywm/swaybg/
[scdoc]: https://git.sr.ht/~sircmpwn/scdoc

View file

@ -1,10 +1,10 @@
# sway
A Sway egy [i3]-kompatibilis [Wayland] kompozitor. Olvasd el a [Gyarkan Ismételt Kérdéseket][FAQ]. Csatlakozz az [IRC csatornához][IRC channel] \(`#sway` az `irc.libera.chat`-en).
A Sway egy [i3]-kompatibilis [Wayland]-kompozitor. Olvasd el a [Gyarkan Ismételt Kérdéseket][FAQ]. Csatlakozz az [IRC-csatornához][IRC channel] \(`#sway` az `irc.libera.chat`-en).
## Csomagaláírások
A kiadott csomagok az [E88F5E48] kulccsal vannak aláírva és [GitHub-on][GitHub releases] publikálva.
A kiadott csomagok az [E88F5E48] kulccsal vannak aláírva, és [GitHubon][GitHub releases] publikálva.
## Telepítés
@ -13,7 +13,7 @@ A kiadott csomagok az [E88F5E48] kulccsal vannak aláírva és [GitHub-on][GitHu
A Sway sok disztribúció csomagkezelőjéből elérhető, próbáld meg a "sway"
csomagot telepíteni az általad használt eszközzel.
Ha szeretnél csomagot készíteni a saját disztribúciódhoz, ugorj be az IRC
Ha szeretnél csomagot készíteni a saját disztribúciódhoz, ugorj be az IRC-
csatornára, vagy küldj levelet a sir@cmpwn.com címre tanácsokért.
### Fordítás forráskódból
@ -46,7 +46,7 @@ Futtasd ezeket a parancsokat:
## Konfiguráció
Ha előzőleg i3-mat használtál, akkor átmásolhatod az i3 beállításaidat a
Ha előzőleg i3-at használtál, akkor átmásolhatod az i3-beállításaidat a
`~/.config/sway/config` file-ba és ugyanúgy működni fognak. Egyéb esetben másold
le kiindulási alapnak a mintát, ami általában az `etc/sway/config` elérési
útvonalon található.
@ -55,7 +55,7 @@ kapcsolatban.
## Futtatás
Futtasd a `sway` parancsot egy TTY felületről. Néhány bejelentkezéskezelő
Futtasd a `sway` parancsot egy TTY-felületről. Néhány bejelentkezéskezelő
(display manager) működhet, de alapvetően nem támogatottak a sway által. (A
gdm-ről ismeretes, hogy egész jól működik.)

View file

@ -1,7 +1,5 @@
# sway
[English][en] - [Deutsch][de] - [Dansk][dk] - [Español][es] - [Français][fr] - **[Svenska][sv]** - [Ελληνικά][gr] - [Magyar][hu] - [فارسی][ir] - [Italiano][it] - [日本語][ja] - [한국어][ko] - [Nederlands][nl] - [Polski][pl] - [Português][pt] - [Română][ro] - [Русский][ru] - [Türkçe][tr] - [Українська][uk] - [中文-简体][zh-CN] - [中文-繁體][zh-TW]
sway är en [i3]-kompatibel [Wayland] compositor. Läs våran [FAQ]-sida. Gå med i vår
[IRC-kanal] \(#sway på irc.libera.chat).

View file

@ -360,3 +360,7 @@ char *format_str(const char *fmt, ...) {
va_end(args);
return str;
}
bool has_prefix(const char *str, const char *prefix) {
return strncmp(str, prefix, strlen(prefix)) == 0;
}

View file

@ -16,9 +16,7 @@ set $right l
# Your preferred terminal emulator
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 | wmenu | xargs swaymsg exec --
set $menu wmenu-run
### Output configuration
#
@ -195,6 +193,19 @@ mode "resize" {
bindsym Escape mode "default"
}
bindsym $mod+r mode "resize"
#
# Utilities:
#
# Special keys to adjust volume via PulseAudio
bindsym --locked XF86AudioMute exec pactl set-sink-mute \@DEFAULT_SINK@ toggle
bindsym --locked XF86AudioLowerVolume exec pactl set-sink-volume \@DEFAULT_SINK@ -5%
bindsym --locked XF86AudioRaiseVolume exec pactl set-sink-volume \@DEFAULT_SINK@ +5%
bindsym --locked XF86AudioMicMute exec pactl set-source-mute \@DEFAULT_SOURCE@ toggle
# Special keys to adjust brightness via brightnessctl
bindsym --locked XF86MonBrightnessDown exec brightnessctl set 5%-
bindsym --locked XF86MonBrightnessUp exec brightnessctl set 5%+
# Special key to take a screenshot with grim
bindsym Print exec grim
#
# Status Bar:

View file

@ -40,4 +40,6 @@ 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);
bool has_prefix(const char *str, const char *prefix);
#endif

View file

@ -104,6 +104,7 @@ struct sway_container *container_find_resize_parent(struct sway_container *con,
sway_cmd cmd_exec_validate;
sway_cmd cmd_exec_process;
sway_cmd cmd_allow_tearing;
sway_cmd cmd_assign;
sway_cmd cmd_bar;
sway_cmd cmd_bindcode;
@ -283,6 +284,7 @@ sway_cmd input_cmd_xkb_switch_layout;
sway_cmd input_cmd_xkb_variant;
sway_cmd output_cmd_adaptive_sync;
sway_cmd output_cmd_allow_tearing;
sway_cmd output_cmd_background;
sway_cmd output_cmd_color_profile;
sway_cmd output_cmd_disable;

View file

@ -262,6 +262,7 @@ enum scale_filter_mode {
enum render_bit_depth {
RENDER_BIT_DEPTH_DEFAULT, // the default is currently 8
RENDER_BIT_DEPTH_6,
RENDER_BIT_DEPTH_8,
RENDER_BIT_DEPTH_10,
};
@ -289,20 +290,13 @@ struct output_config {
enum render_bit_depth render_bit_depth;
bool set_color_transform;
struct wlr_color_transform *color_transform;
int allow_tearing;
char *background;
char *background_option;
char *background_fallback;
};
/**
* An output config pre-matched to an output
*/
struct matched_output_config {
struct sway_output *output;
struct output_config *config;
};
/**
* Stores size of gaps for each side
*/
@ -692,13 +686,10 @@ const char *sway_output_scale_filter_to_string(enum scale_filter_mode scale_filt
struct output_config *new_output_config(const char *name);
bool apply_output_configs(struct matched_output_config *configs,
size_t configs_len, bool test_only, bool degrade_to_off);
bool apply_output_configs(struct output_config **ocs, size_t ocs_len,
bool test_only, bool degrade_to_off);
void apply_all_output_configs(void);
void sort_output_configs_by_priority(struct matched_output_config *configs,
size_t configs_len);
void apply_stored_output_configs(void);
/**
* store_output_config stores a new output config. An output may be matched by
@ -713,6 +704,10 @@ struct output_config *find_output_config(struct sway_output *output);
void free_output_config(struct output_config *oc);
void request_modeset(void);
void force_modeset(void);
bool modeset_is_pending(void);
bool spawn_swaybg(void);
int workspace_output_cmp_workspace(const void *a, const void *b);

View file

@ -9,11 +9,14 @@ struct sway_input_popup {
struct wlr_scene_tree *scene_tree;
struct sway_popup_desc desc;
struct wlr_input_popup_surface_v2 *popup_surface;
struct wlr_output *fixed_output;
struct wl_list link;
struct wl_listener popup_destroy;
struct wl_listener popup_surface_commit;
struct wl_listener popup_surface_map;
struct wl_listener popup_surface_unmap;
struct wl_listener focused_surface_unmap;
};

6
include/sway/lock.h Normal file
View file

@ -0,0 +1,6 @@
#ifndef _SWAY_LOCK_H
#define _SWAY_LOCK_H
void arrange_locks(void);
#endif

View file

@ -57,7 +57,6 @@ struct sway_output {
struct wl_listener layout_destroy;
struct wl_listener destroy;
struct wl_listener commit;
struct wl_listener present;
struct wl_listener frame;
struct wl_listener request_state;
@ -72,7 +71,7 @@ struct sway_output {
uint32_t refresh_nsec;
int max_render_time; // In milliseconds
struct wl_event_source *repaint_timer;
bool gamma_lut_changed;
bool allow_tearing;
};
struct sway_output_non_desktop {
@ -135,10 +134,6 @@ enum sway_container_layout output_get_default_layout(
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);
@ -148,4 +143,6 @@ void handle_output_power_manager_set_mode(struct wl_listener *listener,
struct sway_output_non_desktop *output_non_desktop_create(struct wlr_output *wlr_output);
void update_output_manager_config(struct sway_server *server);
#endif

View file

@ -45,7 +45,6 @@ struct sway_server {
struct sway_input_manager *input;
struct wl_listener new_output;
struct wl_listener output_layout_change;
struct wl_listener renderer_lost;
struct wlr_idle_notifier_v1 *idle_notifier_v1;
@ -116,6 +115,10 @@ struct sway_server {
struct wl_listener request_set_cursor_shape;
struct wlr_tearing_control_manager_v1 *tearing_control_v1;
struct wl_listener tearing_control_new_object;
struct wl_list tearing_controllers; // sway_tearing_controller::link
struct wl_list pending_launcher_ctxs; // launcher_ctx::link
// The timeout for transactions, after which a transaction is applied
@ -182,4 +185,6 @@ void xdg_activation_v1_handle_new_token(struct wl_listener *listener,
void set_rr_scheduling(void);
void handle_new_tearing_hint(struct wl_listener *listener, void *data);
#endif

View file

@ -103,6 +103,8 @@ struct sway_container {
char *formatted_title; // The title displayed in the title bar
int title_width;
char *title_format;
enum sway_container_layout prev_split_layout;
// Whether stickiness has been enabled on this container. Use
@ -183,6 +185,8 @@ void container_update_title_bar(struct sway_container *container);
void container_update_marks(struct sway_container *container);
size_t parse_title_format(struct sway_container *container, char *buffer);
size_t container_build_representation(enum sway_container_layout layout,
list_t *children, char *buffer);

View file

@ -16,8 +16,6 @@ struct sway_root {
struct sway_node node;
struct wlr_output_layout *output_layout;
struct wl_listener output_layout_change;
// scene node layout:
// - root
// - staging

View file

@ -4,6 +4,7 @@
#include <wlr/config.h>
#include <wlr/types/wlr_compositor.h>
#include <wlr/types/wlr_scene.h>
#include <wlr/types/wlr_tearing_control_v1.h>
#include "sway/config.h"
#if WLR_HAS_XWAYLAND
#include <wlr/xwayland.h>
@ -34,6 +35,12 @@ enum sway_view_prop {
#endif
};
enum sway_view_tearing_mode {
TEARING_OVERRIDE_FALSE,
TEARING_OVERRIDE_TRUE,
TEARING_WINDOW_HINT,
};
struct sway_view_impl {
void (*get_constraints)(struct sway_view *view, double *min_width,
double *max_width, double *min_height, double *max_height);
@ -73,8 +80,6 @@ struct sway_view {
// Used when changing a view from tiled to floating.
int natural_width, natural_height;
char *title_format;
bool using_csd;
struct timespec urgent;
@ -111,6 +116,9 @@ struct sway_view {
int max_render_time; // In milliseconds
enum seat_config_shortcuts_inhibit shortcuts_inhibit;
enum sway_view_tearing_mode tearing_mode;
enum wp_tearing_control_v1_presentation_hint tearing_hint;
};
struct sway_xdg_shell_view {
@ -335,4 +343,6 @@ void view_assign_ctx(struct sway_view *view, struct launcher_ctx *ctx);
void view_send_frame_done(struct sway_view *view);
bool view_can_tear(struct sway_view *view);
#endif

View file

@ -3,7 +3,7 @@ project(
'c',
version: '1.10-dev',
license: 'MIT',
meson_version: '>=0.60.0',
meson_version: '>=1.3',
default_options: [
'c_std=c11',
'warning_level=2',
@ -109,11 +109,9 @@ conf_data.set10('HAVE_LIBSYSTEMD', sdbus.found() and sdbus.name() == 'libsystemd
conf_data.set10('HAVE_LIBELOGIND', sdbus.found() and sdbus.name() == 'libelogind')
conf_data.set10('HAVE_BASU', sdbus.found() and sdbus.name() == 'basu')
conf_data.set10('HAVE_TRAY', have_tray)
conf_data.set10('HAVE_LIBINPUT_CONFIG_ACCEL_PROFILE_CUSTOM', cc.has_header_symbol(
'libinput.h',
'LIBINPUT_CONFIG_ACCEL_PROFILE_CUSTOM',
dependencies: libinput,
))
foreach sym : ['LIBINPUT_CONFIG_ACCEL_PROFILE_CUSTOM', 'LIBINPUT_CONFIG_DRAG_LOCK_ENABLED_STICKY']
conf_data.set10('HAVE_' + sym, cc.has_header_symbol('libinput.h', sym, dependencies: libinput))
endforeach
scdoc = dependency('scdoc', version: '>=1.9.2', native: true, required: get_option('man-pages'))
if scdoc.found()
@ -160,8 +158,8 @@ add_project_arguments('-DSYSCONFDIR="/@0@"'.format(join_paths(prefix, sysconfdir
version = '"@0@"'.format(meson.project_version())
git = find_program('git', native: true, required: false)
if git.found()
git_commit = run_command([git, 'rev-parse', '--short', 'HEAD'], check: false)
git_branch = run_command([git, 'rev-parse', '--abbrev-ref', 'HEAD'], check: false)
git_commit = run_command([git, '--git-dir=.git', 'rev-parse', '--short', 'HEAD'], check: false)
git_branch = run_command([git, '--git-dir=.git', 'rev-parse', '--abbrev-ref', 'HEAD'], check: false)
if git_commit.returncode() == 0 and git_branch.returncode() == 0
version = '"@0@-@1@ (" __DATE__ ", branch \'@2@\')"'.format(
meson.project_version(),
@ -172,31 +170,10 @@ if git.found()
endif
add_project_arguments('-DSWAY_VERSION=@0@'.format(version), language: 'c')
# Compute the relative path used by compiler invocations.
source_root = meson.current_source_dir().split('/')
build_root = meson.global_build_root().split('/')
relative_dir_parts = []
i = 0
in_prefix = true
foreach p : build_root
if i >= source_root.length() or not in_prefix or p != source_root[i]
in_prefix = false
relative_dir_parts += '..'
endif
i += 1
endforeach
i = 0
in_prefix = true
foreach p : source_root
if i >= build_root.length() or not in_prefix or build_root[i] != p
in_prefix = false
relative_dir_parts += p
endif
i += 1
endforeach
relative_dir = join_paths(relative_dir_parts) + '/'
fs = import('fs')
# Strip relative path prefixes from the code if possible, otherwise hide them.
relative_dir = fs.relative_to(meson.current_source_dir(), meson.global_build_root()) + '/'
if cc.has_argument('-fmacro-prefix-map=/prefix/to/hide=')
add_project_arguments(
'-fmacro-prefix-map=@0@='.format(relative_dir),

View file

@ -14,6 +14,7 @@ protocols = [
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',
wl_protocol_dir / 'staging/tearing-control/tearing-control-v1.xml',
'wlr-layer-shell-unstable-v1.xml',
'idle.xml',
'wlr-output-power-management-unstable-v1.xml',

View file

@ -1,7 +1,7 @@
#!/bin/sh -eu
prev=$(git describe --tags --abbrev=0)
next=$(meson rewrite kwargs info project / 2>&1 >/dev/null | jq -r '.kwargs["project#/"].version')
next=$(meson rewrite kwargs info project / | jq -r '.kwargs["project#/"].version')
case "$next" in
*-dev)
@ -28,4 +28,5 @@ archive=$prefix.tar.gz
git archive --prefix="$prefix/" -o "$archive" "$next"
gpg --output "$archive".sig --detach-sig "$archive"
git push --follow-tags
gh release create "sway $next" -t "$next" -n "" -d "$archive" "$archive.sig"

View file

@ -112,6 +112,7 @@ static const struct cmd_handler config_handlers[] = {
/* Runtime-only commands. Keep alphabetized */
static const struct cmd_handler command_handlers[] = {
{ "allow_tearing", cmd_allow_tearing },
{ "border", cmd_border },
{ "create_output", cmd_create_output },
{ "exit", cmd_exit },

View file

@ -0,0 +1,24 @@
#include <sway/commands.h>
#include "sway/config.h"
#include "sway/tree/view.h"
#include "util.h"
struct cmd_results *cmd_allow_tearing(int argc, char **argv) {
struct cmd_results *error = NULL;
if ((error = checkarg(argc, "allow_tearing", EXPECTED_AT_LEAST, 1))) {
return error;
}
struct sway_container *container = config->handler_context.container;
if (!container || !container->view) {
return cmd_results_new(CMD_INVALID, "Tearing can only be allowed on views");
}
bool wants_tearing = parse_boolean(argv[0], true);
struct sway_view *view = container->view;
view->tearing_mode = wants_tearing ? TEARING_OVERRIDE_TRUE :
TEARING_OVERRIDE_FALSE;
return cmd_results_new(CMD_SUCCESS, NULL);
}

View file

@ -23,7 +23,7 @@ struct cmd_results *cmd_assign(int argc, char **argv) {
--argc; ++argv;
if (strncmp(*argv, "", strlen("")) == 0) {
if (has_prefix(*argv, "")) {
if (argc < 2) {
free(criteria);
return cmd_results_new(CMD_INVALID, "Missing workspace");

View file

@ -11,7 +11,7 @@ struct cmd_results *bar_cmd_font(int argc, char **argv) {
char *font = join_args(argv, argc);
free(config->current_bar->font);
if (strncmp(font, "pango:", 6) == 0) {
if (has_prefix(font, "pango:")) {
if (config->current_bar->pango_markup == PANGO_MARKUP_DEFAULT) {
config->current_bar->pango_markup = true;
}

View file

@ -367,8 +367,7 @@ static struct cmd_results *cmd_bindsym_or_bindcode(int argc, char **argv,
}
} else if (strcmp("--exclude-titlebar", argv[0]) == 0) {
exclude_titlebar = true;
} else if (strncmp("--input-device=", argv[0],
strlen("--input-device=")) == 0) {
} else if (has_prefix("--input-device=", argv[0])) {
free(binding->input);
binding->input = strdup(argv[0] + strlen("--input-device="));
strip_quotes(binding->input);
@ -399,7 +398,7 @@ static struct cmd_results *cmd_bindsym_or_bindcode(int argc, char **argv,
list_t *split = split_string(argv[0], "+");
for (int i = 0; i < split->length; ++i) {
// Check for group
if (strncmp(split->items[i], "Group", strlen("Group")) == 0) {
if (has_prefix(split->items[i], "Group")) {
if (binding->group != XKB_LAYOUT_INVALID) {
free_sway_binding(binding);
list_free_items_and_destroy(split);

View file

@ -13,9 +13,9 @@ struct cmd_results *cmd_font(int argc, char **argv) {
char *font = join_args(argv, argc);
free(config->font);
if (strncmp(font, "pango:", 6) == 0) {
if (has_prefix(font, "pango:")) {
config->pango_markup = true;
config->font = strdup(font + 6);
config->font = strdup(font + strlen("pango:"));
free(font);
} else {
config->pango_markup = false;

View file

@ -215,15 +215,13 @@ struct cmd_results *cmd_gaps(int argc, char **argv) {
return error;
}
bool config_loading = !config->active || config->reloading;
if (argc == 2) {
return gaps_set_defaults(argc, argv);
}
if (argc == 4 && !config_loading) {
if (argc == 4 && !config->reading) {
return gaps_set_runtime(argc, argv);
}
if (config_loading) {
if (config->reading) {
return cmd_results_new(CMD_INVALID, "Expected %s", expected_defaults);
}
return cmd_results_new(CMD_INVALID, "Expected %s or %s",

View file

@ -121,8 +121,7 @@ static struct cmd_results *cmd_bind_or_unbind_gesture(int argc, char **argv, boo
binding->flags |= BINDING_EXACT;
} else if (strcmp("--no-warn", argv[0]) == 0) {
warn = false;
} else if (strncmp("--input-device=", argv[0],
strlen("--input-device=")) == 0) {
} else if (has_prefix("--input-device=", argv[0])) {
free(binding->input);
binding->input = strdup(argv[0] + strlen("--input-device="));
} else {

View file

@ -3,12 +3,12 @@
struct cmd_results *cmd_include(int argc, char **argv) {
struct cmd_results *error = NULL;
if ((error = checkarg(argc, "include", EXPECTED_EQUAL_TO, 1))) {
if ((error = checkarg(argc, "include", EXPECTED_AT_LEAST, 1))) {
return error;
}
char *files = join_args(argv, argc);
// We don't care if the included config(s) fails to load.
load_include_configs(argv[0], config, &config->swaynag_config_errors);
load_include_configs(files, config, &config->swaynag_config_errors);
return cmd_results_new(CMD_SUCCESS, NULL);
}

View file

@ -94,7 +94,7 @@ struct cmd_results *cmd_input(int argc, char **argv) {
return res;
}
if (!config->reloading) {
if (!config->reading) {
input_manager_apply_input_config(ic);
}
} else {

View file

@ -15,6 +15,11 @@ struct cmd_results *input_cmd_drag_lock(int argc, char **argv) {
return cmd_results_new(CMD_FAILURE, "No input device defined.");
}
#if HAVE_LIBINPUT_CONFIG_DRAG_LOCK_ENABLED_STICKY
if (strcmp(argv[0], "enabled_sticky") == 0) {
ic->drag_lock = LIBINPUT_CONFIG_DRAG_LOCK_ENABLED_STICKY;
} else
#endif
if (parse_boolean(argv[0], true)) {
ic->drag_lock = LIBINPUT_CONFIG_DRAG_LOCK_ENABLED;
} else {

View file

@ -86,7 +86,7 @@ static void toggle_select_send_events_for_device(struct input_config *ic,
static void toggle_send_events(int argc, char **argv) {
struct input_config *ic = config->handler_context.input_config;
bool wildcard = strcmp(ic->identifier, "*") == 0;
const char *type = strncmp(ic->identifier, "type:", strlen("type:")) == 0
const char *type = has_prefix(ic->identifier, "type:")
? ic->identifier + strlen("type:") : NULL;
struct sway_input_device *device = NULL;
wl_list_for_each(device, &server.input->devices, link) {
@ -146,8 +146,7 @@ struct cmd_results *input_cmd_events(int argc, char **argv) {
toggle_send_events(argc - 1, argv + 1);
if (strcmp(ic->identifier, "*") == 0 ||
strncmp(ic->identifier, "type:", strlen("type:")) == 0) {
if (strcmp(ic->identifier, "*") == 0 || has_prefix(ic->identifier, "type:")) {
// Update the device input configs and then reset the type/wildcard
// config send events mode so that is does not override the device
// ones. The device ones will be applied when attempting to apply

View file

@ -95,10 +95,18 @@ struct cmd_results *input_cmd_xkb_switch_layout(int argc, char **argv) {
continue;
}
struct wlr_keyboard *keyboard =
wlr_keyboard_from_input_device(dev->wlr_device);
if (keyboard->keymap == NULL && dev->is_virtual) {
// The `sway_keyboard_set_layout` function is by default skipped
// when configuring virtual keyboards.
continue;
}
struct xkb_switch_layout_action *action =
&actions[actions_len++];
action->keyboard = keyboard;
action->keyboard = wlr_keyboard_from_input_device(dev->wlr_device);
if (relative) {
action->layout = get_layout_relative(action->keyboard, relative);
} else {

View file

@ -23,7 +23,7 @@ struct cmd_results *cmd_mark(int argc, char **argv) {
}
bool add = false, toggle = false;
while (argc > 0 && strncmp(*argv, "--", 2) == 0) {
while (argc > 0 && has_prefix(*argv, "--")) {
if (strcmp(*argv, "--add") == 0) {
add = true;
} else if (strcmp(*argv, "--replace") == 0) {

View file

@ -240,7 +240,6 @@ static void container_move_to_workspace(struct sway_container *container,
static void container_move_to_container(struct sway_container *container,
struct sway_container *destination) {
if (container == destination
|| container_has_ancestor(container, destination)
|| container_has_ancestor(destination, container)) {
return;
}
@ -510,6 +509,7 @@ static struct cmd_results *cmd_move_container(bool no_auto_back_and_forth,
}
}
ws = workspace_create(NULL, ws_name);
arrange_workspace(ws);
}
free(ws_name);
struct sway_container *dst = seat_get_focus_inactive_tiling(seat, ws);

View file

@ -8,6 +8,7 @@
// must be in order for the bsearch
static const struct cmd_handler output_handlers[] = {
{ "adaptive_sync", output_cmd_adaptive_sync },
{ "allow_tearing", output_cmd_allow_tearing },
{ "background", output_cmd_background },
{ "bg", output_cmd_background },
{ "color_profile", output_cmd_color_profile },
@ -106,18 +107,17 @@ struct cmd_results *cmd_output(int argc, char **argv) {
store_output_config(output);
// If reloading, the output configs will be applied after reading the
// entire config and before the deferred commands so that an auto generated
// workspace name is not given to re-enabled outputs.
if (!config->reloading && !config->validating) {
apply_all_output_configs();
if (background) {
if (!spawn_swaybg()) {
if (config->reading) {
// When reading the config file, we wait till the end to do a single
// modeset and swaybg spawn.
return cmd_results_new(CMD_SUCCESS, NULL);
}
request_modeset();
if (background && !spawn_swaybg()) {
return cmd_results_new(CMD_FAILURE,
"Failed to apply background configuration");
}
}
}
return cmd_results_new(CMD_SUCCESS, NULL);

View file

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

View file

@ -0,0 +1,23 @@
#include "sway/commands.h"
#include "sway/config.h"
#include "util.h"
struct cmd_results *output_cmd_allow_tearing(int argc, char **argv) {
if (!config->handler_context.output_config) {
return cmd_results_new(CMD_FAILURE, "Missing output config");
}
if (argc == 0) {
return cmd_results_new(CMD_INVALID, "Missing allow_tearing argument");
}
if (parse_boolean(argv[0],
(config->handler_context.output_config->allow_tearing == 1))) {
config->handler_context.output_config->allow_tearing = 1;
} else {
config->handler_context.output_config->allow_tearing = 0;
}
config->handler_context.leftovers.argc = argc - 1;
config->handler_context.leftovers.argv = argv + 1;
return NULL;
}

View file

@ -5,6 +5,7 @@
#include <wlr/render/color.h>
#include "sway/commands.h"
#include "sway/config.h"
#include "stringop.h"
static bool read_file_into_buf(const char *path, void **buf, size_t *size) {
/* Why not use fopen/fread directly? glibc will succesfully open directories,
@ -70,12 +71,23 @@ struct cmd_results *output_cmd_color_profile(int argc, char **argv) {
return cmd_results_new(CMD_INVALID,
"Invalid color profile specification: icc type requires a file");
}
char *icc_path = strdup(argv[1]);
if (!expand_path(&icc_path)) {
struct cmd_results *cmd_res = cmd_results_new(CMD_INVALID,
"Invalid color profile specification: invalid file path");
free(icc_path);
return cmd_res;
}
void *data = NULL;
size_t size = 0;
if (!read_file_into_buf(argv[1], &data, &size)) {
if (!read_file_into_buf(icc_path, &data, &size)) {
free(icc_path);
return cmd_results_new(CMD_FAILURE,
"Failed to load color profile: could not read ICC file");
}
free(icc_path);
struct wlr_color_transform *tmp =
wlr_color_transform_init_linear_to_icc(data, size);

View file

@ -11,7 +11,10 @@ struct cmd_results *output_cmd_render_bit_depth(int argc, char **argv) {
return cmd_results_new(CMD_INVALID, "Missing bit depth argument.");
}
if (strcmp(*argv, "8") == 0) {
if (strcmp(*argv, "6") == 0) {
config->handler_context.output_config->render_bit_depth =
RENDER_BIT_DEPTH_6;
} else if (strcmp(*argv, "8") == 0) {
config->handler_context.output_config->render_bit_depth =
RENDER_BIT_DEPTH_8;
} else if (strcmp(*argv, "10") == 0) {
@ -19,7 +22,7 @@ struct cmd_results *output_cmd_render_bit_depth(int argc, char **argv) {
RENDER_BIT_DEPTH_10;
} else {
return cmd_results_new(CMD_INVALID,
"Invalid bit depth. Must be a value in (8|10).");
"Invalid bit depth. Must be a value in (6|8|10).");
}
config->handler_context.leftovers.argc = argc - 1;

View file

@ -1,3 +1,4 @@
#define _POSIX_C_SOURCE 200809L
#include <string.h>
#include "sway/commands.h"
#include "sway/config.h"
@ -11,16 +12,19 @@ struct cmd_results *cmd_title_format(int argc, char **argv) {
return error;
}
struct sway_container *container = config->handler_context.container;
if (!container || !container->view) {
if (!container) {
return cmd_results_new(CMD_INVALID,
"Only views can have a title_format");
"Only valid containers can have a title_format");
}
struct sway_view *view = container->view;
char *format = join_args(argv, argc);
if (view->title_format) {
free(view->title_format);
if (container->title_format) {
free(container->title_format);
}
container->title_format = format;
if (container->view) {
view_update_title(container->view, true);
} else {
container_update_representation(container);
}
view->title_format = format;
view_update_title(view, true);
return cmd_results_new(CMD_SUCCESS, NULL);
}

View file

@ -516,7 +516,7 @@ bool load_main_config(const char *file, bool is_active, bool validating) {
// Only really necessary if not explicitly `font` is set in the config.
config_update_font_height();
if (is_active && !validating) {
if (!validating) {
input_manager_verify_fallback_seat();
for (int i = 0; i < config->input_configs->length; i++) {
@ -533,14 +533,16 @@ bool load_main_config(const char *file, bool is_active, bool validating) {
}
sway_switch_retrigger_bindings_for_all();
apply_all_output_configs();
spawn_swaybg();
config->reloading = false;
if (is_active) {
request_modeset();
if (config->swaynag_config_errors.client != NULL) {
swaynag_show(&config->swaynag_config_errors);
}
}
}
if (old_config) {
destroy_removed_seats(old_config, config);
@ -923,8 +925,8 @@ char *do_var_replacement(char *str) {
// Find matching variable
for (i = 0; i < config->symbols->length; ++i) {
struct sway_variable *var = config->symbols->items[i];
if (has_prefix(find, var->name)) {
int vnlen = strlen(var->name);
if (strncmp(find, var->name, vnlen) == 0) {
int vvlen = strlen(var->value);
char *newstr = malloc(strlen(str) - vnlen + vvlen + 1);
if (!newstr) {

View file

@ -300,7 +300,7 @@ struct input_config *store_input_config(struct input_config *ic,
return NULL;
}
bool type = strncmp(ic->identifier, "type:", strlen("type:")) == 0;
bool type = has_prefix(ic->identifier, "type:");
if (type && error && !validate_type_on_existing(ic, error)) {
return NULL;
}

View file

@ -6,14 +6,20 @@
#include <sys/wait.h>
#include <unistd.h>
#include <wlr/config.h>
#include <wlr/render/allocator.h>
#include <wlr/types/wlr_cursor.h>
#include <wlr/types/wlr_output_layout.h>
#include <wlr/types/wlr_output.h>
#include <wlr/types/wlr_output_swapchain_manager.h>
#include <xf86drm.h>
#include "sway/config.h"
#include "sway/desktop/transaction.h"
#include "sway/input/cursor.h"
#include "sway/layers.h"
#include "sway/lock.h"
#include "sway/output.h"
#include "sway/server.h"
#include "sway/tree/arrange.h"
#include "sway/tree/root.h"
#include "log.h"
#include "util.h"
@ -22,13 +28,6 @@
#include <wlr/backend/drm.h>
#endif
int output_name_cmp(const void *item, const void *data) {
const struct output_config *output = item;
const char *name = data;
return strcmp(output->name, name);
}
void output_get_identifier(char *identifier, size_t len,
struct sway_output *output) {
struct wlr_output *wlr_output = output->wlr_output;
@ -79,6 +78,7 @@ struct output_config *new_output_config(const char *name) {
oc->set_color_transform = false;
oc->color_transform = NULL;
oc->power = -1;
oc->allow_tearing = -1;
return oc;
}
@ -216,6 +216,9 @@ static void merge_output_config(struct output_config *dst, struct output_config
if (src->power != -1) {
dst->power = src->power;
}
if (src->allow_tearing != -1) {
dst->allow_tearing = src->allow_tearing;
}
}
void store_output_config(struct output_config *oc) {
@ -258,11 +261,11 @@ void store_output_config(struct output_config *oc) {
sway_log(SWAY_DEBUG, "Config stored for output %s (enabled: %d) (%dx%d@%fHz "
"position %d,%d scale %f subpixel %s transform %d) (bg %s %s) (power %d) "
"(max render time: %d)",
"(max render time: %d) (allow tearing: %d)",
oc->name, oc->enabled, oc->width, oc->height, oc->refresh_rate,
oc->x, oc->y, oc->scale, sway_wl_output_subpixel_to_string(oc->subpixel),
oc->transform, oc->background, oc->background_option, oc->power,
oc->max_render_time);
oc->max_render_time, oc->allow_tearing);
// If the configuration was not merged into an existing configuration, add
// it to the list. Otherwise we're done with it and can free it.
@ -283,7 +286,6 @@ static void set_mode(struct wlr_output *output, struct wlr_output_state *pending
mhz = mhz <= 0 ? INT_MAX : mhz;
if (wl_list_empty(&output->modes) || custom) {
sway_log(SWAY_DEBUG, "Assigning custom mode to %s", output->name);
wlr_output_state_set_custom_mode(pending, width, height,
refresh_rate > 0 ? mhz : 0);
return;
@ -303,10 +305,7 @@ static void set_mode(struct wlr_output *output, struct wlr_output_state *pending
}
}
}
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 {
if (!best) {
best = wlr_output_preferred_mode(output);
sway_log(SWAY_INFO, "Configured mode (%dx%d@%.3fHz) not available, "
"applying preferred mode (%dx%d@%.3fHz)",
@ -323,7 +322,6 @@ static void set_modeline(struct wlr_output *output,
sway_log(SWAY_ERROR, "Modeline can only be set to DRM output");
return;
}
sway_log(SWAY_DEBUG, "Assigning custom modeline to %s", output->name);
struct wlr_output_mode *mode = wlr_drm_connector_add_mode(output, drm_mode);
if (mode) {
wlr_output_state_set_mode(pending, mode);
@ -389,7 +387,6 @@ static int compute_default_scale(struct wlr_output *output,
double dpi_x = (double) width / (output->phys_width / MM_PER_INCH);
double dpi_y = (double) height / (output->phys_height / MM_PER_INCH);
sway_log(SWAY_DEBUG, "Output DPI: %fx%f", dpi_x, dpi_y);
if (dpi_x <= HIDPI_DPI_LIMIT || dpi_y <= HIDPI_DPI_LIMIT) {
return 1;
}
@ -397,9 +394,15 @@ static int compute_default_scale(struct wlr_output *output,
return 2;
}
static bool render_format_is_10bit(uint32_t render_format) {
return render_format == DRM_FORMAT_XRGB2101010 ||
render_format == DRM_FORMAT_XBGR2101010;
static enum render_bit_depth bit_depth_from_format(uint32_t render_format) {
if (render_format == DRM_FORMAT_XRGB2101010 || render_format == DRM_FORMAT_XBGR2101010) {
return RENDER_BIT_DEPTH_10;
} else if (render_format == DRM_FORMAT_XRGB8888 || render_format == DRM_FORMAT_ARGB8888) {
return RENDER_BIT_DEPTH_8;
} else if (render_format == DRM_FORMAT_RGB565) {
return RENDER_BIT_DEPTH_6;
}
return RENDER_BIT_DEPTH_DEFAULT;
}
static bool render_format_is_bgr(uint32_t fmt) {
@ -419,90 +422,73 @@ static void queue_output_config(struct output_config *oc,
struct wlr_output *wlr_output = output->wlr_output;
if (output_config_is_disabling(oc)) {
sway_log(SWAY_DEBUG, "Turning off output %s", wlr_output->name);
wlr_output_state_set_enabled(pending, false);
return;
}
sway_log(SWAY_DEBUG, "Turning on output %s", wlr_output->name);
wlr_output_state_set_enabled(pending, true);
if (oc && oc->drm_mode.type != 0 && oc->drm_mode.type != (uint32_t) -1) {
sway_log(SWAY_DEBUG, "Set %s modeline",
wlr_output->name);
set_modeline(wlr_output, pending, &oc->drm_mode);
} else if (oc && oc->width > 0 && oc->height > 0) {
sway_log(SWAY_DEBUG, "Set %s mode to %dx%d (%f Hz)",
wlr_output->name, oc->width, oc->height, oc->refresh_rate);
set_mode(wlr_output, pending, oc->width, oc->height,
oc->refresh_rate, oc->custom_mode == 1);
} else if (!wl_list_empty(&wlr_output->modes)) {
sway_log(SWAY_DEBUG, "Set preferred mode");
struct wlr_output_mode *preferred_mode =
wlr_output_preferred_mode(wlr_output);
wlr_output_state_set_mode(pending, preferred_mode);
}
if (oc && (oc->subpixel != WL_OUTPUT_SUBPIXEL_UNKNOWN || config->reloading)) {
sway_log(SWAY_DEBUG, "Set %s subpixel to %s", oc->name,
sway_wl_output_subpixel_to_string(oc->subpixel));
if (oc && oc->subpixel != WL_OUTPUT_SUBPIXEL_UNKNOWN) {
wlr_output_state_set_subpixel(pending, oc->subpixel);
} else {
wlr_output_state_set_subpixel(pending, output->detected_subpixel);
}
enum wl_output_transform tr = WL_OUTPUT_TRANSFORM_NORMAL;
if (oc && oc->transform >= 0) {
tr = oc->transform;
wlr_output_state_set_transform(pending, 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);
wlr_output_state_set_transform(pending,
wlr_drm_connector_get_panel_orientation(wlr_output));
#endif
}
if (wlr_output->transform != tr) {
sway_log(SWAY_DEBUG, "Set %s transform to %d", wlr_output->name, tr);
wlr_output_state_set_transform(pending, tr);
} else {
wlr_output_state_set_transform(pending, WL_OUTPUT_TRANSFORM_NORMAL);
}
// Apply the scale last before the commit, because the scale auto-detection
// reads the pending output size
float scale;
// Apply the scale after sorting out the mode, because the scale
// auto-detection reads the pending output size
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;
}
wlr_output_state_set_scale(pending, round(oc->scale * 120) / 120);
} else {
scale = compute_default_scale(wlr_output, pending);
sway_log(SWAY_DEBUG, "Auto-detected output scale: %f", scale);
}
if (scale != wlr_output->scale) {
sway_log(SWAY_DEBUG, "Set %s scale to %f", wlr_output->name, scale);
wlr_output_state_set_scale(pending, scale);
wlr_output_state_set_scale(pending,
compute_default_scale(wlr_output, pending));
}
if (wlr_output->adaptive_sync_supported) {
if (oc && oc->adaptive_sync != -1) {
sway_log(SWAY_DEBUG, "Set %s adaptive sync to %d", wlr_output->name,
oc->adaptive_sync);
wlr_output_state_set_adaptive_sync_enabled(pending, oc->adaptive_sync == 1);
} else {
wlr_output_state_set_adaptive_sync_enabled(pending, false);
}
}
if (oc && oc->render_bit_depth != RENDER_BIT_DEPTH_DEFAULT) {
if (oc->render_bit_depth == RENDER_BIT_DEPTH_10 &&
render_format_is_10bit(output->wlr_output->render_format)) {
bit_depth_from_format(output->wlr_output->render_format) == oc->render_bit_depth) {
// 10-bit was set successfully before, try to save some tests by reusing the format
wlr_output_state_set_render_format(pending, output->wlr_output->render_format);
} else if (oc->render_bit_depth == RENDER_BIT_DEPTH_10) {
wlr_output_state_set_render_format(pending, DRM_FORMAT_XRGB2101010);
} else if (oc->render_bit_depth == RENDER_BIT_DEPTH_6){
wlr_output_state_set_render_format(pending, DRM_FORMAT_RGB565);
} else {
wlr_output_state_set_render_format(pending, DRM_FORMAT_XRGB8888);
}
} else {
wlr_output_state_set_render_format(pending, DRM_FORMAT_XRGB8888);
}
}
@ -521,9 +507,9 @@ static bool finalize_output_config(struct output_config *oc, struct sway_output
return true;
}
if (oc) {
enum scale_filter_mode scale_filter_old = output->scale_filter;
switch (oc->scale_filter) {
enum scale_filter_mode scale_filter_new = oc ? oc->scale_filter : SCALE_FILTER_DEFAULT;
switch (scale_filter_new) {
case SCALE_FILTER_DEFAULT:
case SCALE_FILTER_SMART:
output->scale_filter = ceilf(wlr_output->scale) == wlr_output->scale ?
@ -531,7 +517,7 @@ static bool finalize_output_config(struct output_config *oc, struct sway_output
break;
case SCALE_FILTER_LINEAR:
case SCALE_FILTER_NEAREST:
output->scale_filter = oc->scale_filter;
output->scale_filter = scale_filter_new;
break;
}
if (scale_filter_old != output->scale_filter) {
@ -539,7 +525,6 @@ static bool finalize_output_config(struct output_config *oc, struct sway_output
sway_output_scale_filter_to_string(output->scale_filter));
wlr_damage_ring_add_whole(&output->scene_output->damage_ring);
}
}
// Find position for it
if (oc && (oc->x != -1 || oc->y != -1)) {
@ -549,99 +534,76 @@ static bool finalize_output_config(struct output_config *oc, struct sway_output
wlr_output_layout_add_auto(root->output_layout, wlr_output);
}
// Update output->{lx, ly, width, height}
struct wlr_box output_box;
wlr_output_layout_get_box(root->output_layout, wlr_output, &output_box);
output->lx = output_box.x;
output->ly = output_box.y;
output->width = output_box.width;
output->height = output_box.height;
if (!output->enabled) {
output_enable(output);
}
if (oc && oc->max_render_time >= 0) {
sway_log(SWAY_DEBUG, "Set %s max render time to %d",
oc->name, oc->max_render_time);
output->max_render_time = oc->max_render_time;
}
if (oc && oc->set_color_transform) {
if (oc->color_transform) {
wlr_color_transform_ref(oc->color_transform);
}
wlr_color_transform_unref(output->color_transform);
output->color_transform = oc->color_transform;
} else {
wlr_color_transform_unref(output->color_transform);
output->color_transform = NULL;
}
output->max_render_time = oc && oc->max_render_time > 0 ? oc->max_render_time : 0;
output->allow_tearing = oc && oc->allow_tearing > 0;
return true;
}
static void default_output_config(struct output_config *oc,
struct wlr_output *wlr_output) {
oc->enabled = 1;
oc->power = 1;
struct wlr_output_mode *mode = wlr_output_preferred_mode(wlr_output);
if (mode != NULL) {
oc->width = mode->width;
oc->height = mode->height;
oc->refresh_rate = mode->refresh / 1000.f;
}
oc->x = oc->y = -1;
oc->scale = 0; // auto
oc->scale_filter = SCALE_FILTER_DEFAULT;
struct sway_output *output = wlr_output->data;
oc->subpixel = output->detected_subpixel;
oc->transform = WL_OUTPUT_TRANSFORM_NORMAL;
oc->max_render_time = 0;
static void output_update_position(struct sway_output *output) {
struct wlr_box output_box;
wlr_output_layout_get_box(root->output_layout, output->wlr_output, &output_box);
output->lx = output_box.x;
output->ly = output_box.y;
output->width = output_box.width;
output->height = output_box.height;
}
// find_output_config returns a merged output_config containing all stored
// configuration that applies to the specified output.
struct output_config *find_output_config(struct sway_output *sway_output) {
// find_output_config_from_list returns a merged output_config containing all
// stored configuration that applies to the specified output.
static struct output_config *find_output_config_from_list(
struct output_config **configs, size_t configs_len,
struct sway_output *sway_output) {
const char *name = sway_output->wlr_output->name;
struct output_config *oc = NULL;
struct output_config *result = new_output_config(name);
if (config->reloading) {
default_output_config(result, sway_output->wlr_output);
if (result == NULL) {
return NULL;
}
char id[128];
output_get_identifier(id, sizeof(id), sway_output);
int i;
bool match = false;
if ((i = list_seq_find(config->output_configs, output_name_cmp, "*")) >= 0) {
match = true;
oc = config->output_configs->items[i];
// We take a new config and merge on top, in order, the wildcard config,
// output config by name, and output config by identifier to form the final
// config. If there are multiple matches, they are merged in order.
struct output_config *oc = NULL;
const char *names[] = {"*", name, id, NULL};
for (const char **name = &names[0]; *name; name++) {
for (size_t idx = 0; idx < configs_len; idx++) {
oc = configs[idx];
if (strcmp(oc->name, *name) == 0) {
merge_output_config(result, oc);
}
if ((i = list_seq_find(config->output_configs, output_name_cmp, name)) >= 0) {
match = true;
oc = config->output_configs->items[i];
merge_output_config(result, oc);
}
if ((i = list_seq_find(config->output_configs, output_name_cmp, id)) >= 0) {
match = true;
oc = config->output_configs->items[i];
merge_output_config(result, oc);
}
if (!match && !config->reloading) {
// No name, identifier, or wildcard config. Since we are not
// reloading with defaults, the output config will be empty, so
// just return NULL
free_output_config(result);
return NULL;
}
return result;
}
static bool config_has_auto_mode(struct output_config *oc) {
struct output_config *find_output_config(struct sway_output *sway_output) {
return find_output_config_from_list(
(struct output_config **)config->output_configs->items,
config->output_configs->length, sway_output);
}
static bool config_has_manual_mode(struct output_config *oc) {
if (!oc) {
return true;
return false;
}
if (oc->drm_mode.type != 0 && oc->drm_mode.type != (uint32_t)-1) {
return true;
@ -651,6 +613,14 @@ static bool config_has_auto_mode(struct output_config *oc) {
return false;
}
/**
* An output config pre-matched to an output
*/
struct matched_output_config {
struct sway_output *output;
struct output_config *config;
};
struct search_context {
struct wlr_output_swapchain_manager *swapchain_mgr;
struct wlr_backend_output_state *states;
@ -665,7 +635,9 @@ static void dump_output_state(struct wlr_output *wlr_output, struct wlr_output_s
sway_log(SWAY_DEBUG, " enabled: %s", state->enabled ? "yes" : "no");
}
if (state->committed & WLR_OUTPUT_STATE_RENDER_FORMAT) {
sway_log(SWAY_DEBUG, " render_format: %d", state->render_format);
char *format_name = drmGetFormatName(state->render_format);
sway_log(SWAY_DEBUG, " render_format: %s", format_name);
free(format_name);
}
if (state->committed & WLR_OUTPUT_STATE_MODE) {
if (state->mode_type == WLR_OUTPUT_STATE_MODE_CUSTOM) {
@ -681,6 +653,13 @@ static void dump_output_state(struct wlr_output *wlr_output, struct wlr_output_s
sway_log(SWAY_DEBUG, " adaptive_sync: %s",
state->adaptive_sync_enabled ? "enabled": "disabled");
}
if (state->committed & WLR_OUTPUT_STATE_SCALE) {
sway_log(SWAY_DEBUG, " scale: %f", state->scale);
}
if (state->committed & WLR_OUTPUT_STATE_SUBPIXEL) {
sway_log(SWAY_DEBUG, " subpixel: %s",
sway_wl_output_subpixel_to_string(state->subpixel));
}
}
static bool search_valid_config(struct search_context *ctx, size_t output_idx);
@ -742,7 +721,8 @@ static bool search_mode(struct search_context *ctx, size_t output_idx) {
struct wlr_output_state *state = &backend_state->base;
struct wlr_output *wlr_output = backend_state->output;
if (!config_has_auto_mode(cfg->config)) {
// We only search for mode if one is not explicitly specified in the config
if (config_has_manual_mode(cfg->config)) {
return search_adaptive_sync(ctx, output_idx);
}
@ -783,6 +763,8 @@ static bool search_render_format(struct search_context *ctx, size_t output_idx)
DRM_FORMAT_XRGB2101010,
DRM_FORMAT_XBGR2101010,
DRM_FORMAT_XRGB8888,
DRM_FORMAT_ARGB8888,
DRM_FORMAT_RGB565,
DRM_FORMAT_INVALID,
};
if (render_format_is_bgr(wlr_output->render_format)) {
@ -792,13 +774,18 @@ static bool search_render_format(struct search_context *ctx, size_t output_idx)
}
const struct wlr_drm_format_set *primary_formats =
wlr_output_get_primary_formats(wlr_output, WLR_BUFFER_CAP_DMABUF);
bool need_10bit = cfg->config && cfg->config->render_bit_depth == RENDER_BIT_DEPTH_10;
wlr_output_get_primary_formats(wlr_output, server.allocator->buffer_caps);
enum render_bit_depth needed_bits = RENDER_BIT_DEPTH_8;
if (cfg->config && cfg->config->render_bit_depth != RENDER_BIT_DEPTH_DEFAULT) {
needed_bits = cfg->config->render_bit_depth;
}
for (size_t idx = 0; fmts[idx] != DRM_FORMAT_INVALID; idx++) {
if (!need_10bit && render_format_is_10bit(fmts[idx])) {
enum render_bit_depth format_bits = bit_depth_from_format(fmts[idx]);
if (needed_bits < format_bits) {
continue;
}
if (!wlr_drm_format_set_get(primary_formats, fmts[idx])) {
// If primary_formats is NULL, all formats are supported
if (primary_formats && !wlr_drm_format_set_get(primary_formats, fmts[idx])) {
// This is not a supported format for this output
continue;
}
@ -868,12 +855,12 @@ static int compare_matched_output_config_priority(const void *a, const void *b)
return 0;
}
void sort_output_configs_by_priority(struct matched_output_config *configs,
size_t configs_len) {
static void sort_output_configs_by_priority(
struct matched_output_config *configs, size_t configs_len) {
qsort(configs, configs_len, sizeof(*configs), compare_matched_output_config_priority);
}
bool apply_output_configs(struct matched_output_config *configs,
static bool apply_resolved_output_configs(struct matched_output_config *configs,
size_t configs_len, bool test_only, bool degrade_to_off) {
struct wlr_backend_output_state *states = calloc(configs_len, sizeof(*states));
if (!states) {
@ -888,9 +875,8 @@ bool apply_output_configs(struct matched_output_config *configs,
backend_state->output = cfg->output->wlr_output;
wlr_output_state_init(&backend_state->base);
sway_log(SWAY_DEBUG, "Preparing config for %s",
cfg->output->wlr_output->name);
queue_output_config(cfg->config, cfg->output, &backend_state->base);
dump_output_state(cfg->output->wlr_output, &backend_state->base);
}
struct wlr_output_swapchain_manager swapchain_mgr;
@ -952,6 +938,19 @@ bool apply_output_configs(struct matched_output_config *configs,
finalize_output_config(cfg->config, cfg->output);
}
// Output layout being applied in finalize_output_config can shift outputs
// around, so we do a second pass to update positions and arrange.
for (size_t idx = 0; idx < configs_len; idx++) {
struct matched_output_config *cfg = &configs[idx];
output_update_position(cfg->output);
arrange_layers(cfg->output);
}
arrange_root();
arrange_locks();
update_output_manager_config(&server);
transaction_commit_dirty();
out:
wlr_output_swapchain_manager_finish(&swapchain_mgr);
for (size_t idx = 0; idx < configs_len; idx++) {
@ -976,11 +975,12 @@ out:
return ok;
}
void apply_all_output_configs(void) {
bool apply_output_configs(struct output_config **ocs, size_t ocs_len,
bool test_only, bool degrade_to_off) {
size_t configs_len = wl_list_length(&root->all_outputs);
struct matched_output_config *configs = calloc(configs_len, sizeof(*configs));
if (!configs) {
return;
return false;
}
int config_idx = 0;
@ -993,16 +993,22 @@ void apply_all_output_configs(void) {
struct matched_output_config *config = &configs[config_idx++];
config->output = sway_output;
config->config = find_output_config(sway_output);
config->config = find_output_config_from_list(ocs, ocs_len, sway_output);
}
sort_output_configs_by_priority(configs, configs_len);
apply_output_configs(configs, configs_len, false, true);
bool ok = apply_resolved_output_configs(configs, configs_len, test_only, degrade_to_off);
for (size_t idx = 0; idx < configs_len; idx++) {
struct matched_output_config *cfg = &configs[idx];
free_output_config(cfg->config);
}
free(configs);
return ok;
}
void apply_stored_output_configs(void) {
apply_output_configs((struct output_config **)config->output_configs->items,
config->output_configs->length, false, true);
}
void free_output_config(struct output_config *oc) {

View file

@ -54,7 +54,7 @@ struct wlr_layer_surface_v1 *toplevel_layer_surface_from_surface(
}
static void arrange_surface(struct sway_output *output, const struct wlr_box *full_area,
struct wlr_box *usable_area, struct wlr_scene_tree *tree) {
struct wlr_box *usable_area, struct wlr_scene_tree *tree, bool exclusive) {
struct wlr_scene_node *node;
wl_list_for_each(node, &tree->children, link) {
struct sway_layer_surface *surface = scene_descriptor_try_get(node,
@ -68,6 +68,10 @@ static void arrange_surface(struct sway_output *output, const struct wlr_box *fu
continue;
}
if ((surface->scene->layer_surface->current.exclusive_zone > 0) != exclusive) {
continue;
}
wlr_scene_layer_surface_v1_configure(surface->scene, full_area, usable_area);
}
}
@ -78,10 +82,15 @@ void arrange_layers(struct sway_output *output) {
&usable_area.width, &usable_area.height);
const struct wlr_box full_area = usable_area;
arrange_surface(output, &full_area, &usable_area, output->layers.shell_background);
arrange_surface(output, &full_area, &usable_area, output->layers.shell_bottom);
arrange_surface(output, &full_area, &usable_area, output->layers.shell_top);
arrange_surface(output, &full_area, &usable_area, output->layers.shell_overlay);
arrange_surface(output, &full_area, &usable_area, output->layers.shell_overlay, true);
arrange_surface(output, &full_area, &usable_area, output->layers.shell_top, true);
arrange_surface(output, &full_area, &usable_area, output->layers.shell_bottom, true);
arrange_surface(output, &full_area, &usable_area, output->layers.shell_background, true);
arrange_surface(output, &full_area, &usable_area, output->layers.shell_overlay, false);
arrange_surface(output, &full_area, &usable_area, output->layers.shell_top, false);
arrange_surface(output, &full_area, &usable_area, output->layers.shell_bottom, false);
arrange_surface(output, &full_area, &usable_area, output->layers.shell_background, false);
if (!wlr_box_equal(&usable_area, &output->usable_area)) {
sway_log(SWAY_DEBUG, "Usable area changed, rearranging output");
@ -90,6 +99,43 @@ void arrange_layers(struct sway_output *output) {
} else {
arrange_popups(root->layers.popup);
}
// Find topmost keyboard interactive layer, if such a layer exists
struct wlr_scene_tree *layers_above_shell[] = {
output->layers.shell_overlay,
output->layers.shell_top,
};
size_t nlayers = sizeof(layers_above_shell) / sizeof(layers_above_shell[0]);
struct wlr_scene_node *node;
struct sway_layer_surface *topmost = NULL;
for (size_t i = 0; i < nlayers; ++i) {
wl_list_for_each_reverse(node,
&layers_above_shell[i]->children, link) {
struct sway_layer_surface *surface = scene_descriptor_try_get(node,
SWAY_SCENE_DESC_LAYER_SHELL);
if (surface && surface->layer_surface->current.keyboard_interactive
== ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_EXCLUSIVE &&
surface->layer_surface->surface->mapped) {
topmost = surface;
break;
}
}
if (topmost != NULL) {
break;
}
}
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
!= ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_EXCLUSIVE) {
seat_set_focus_layer(seat, NULL);
}
}
}
static struct wlr_scene_tree *sway_layer_get_scene(struct sway_output *output,

View file

@ -8,6 +8,7 @@
#include <wlr/render/swapchain.h>
#include <wlr/render/wlr_renderer.h>
#include <wlr/types/wlr_buffer.h>
#include <wlr/types/wlr_alpha_modifier_v1.h>
#include <wlr/types/wlr_gamma_control_v1.h>
#include <wlr/types/wlr_matrix.h>
#include <wlr/types/wlr_output_layout.h>
@ -187,8 +188,8 @@ static enum wlr_scale_filter_mode get_scale_filter(struct sway_output *output,
struct wlr_scene_buffer *buffer) {
// if we are scaling down, we should always choose linear
if (buffer->dst_width > 0 && buffer->dst_height > 0 && (
buffer->dst_width < buffer->buffer_width ||
buffer->dst_height < buffer->buffer_height)) {
buffer->dst_width < buffer->WLR_PRIVATE.buffer_width ||
buffer->dst_height < buffer->WLR_PRIVATE.buffer_height)) {
return WLR_SCALE_FILTER_BILINEAR;
}
@ -216,6 +217,15 @@ static void output_configure_scene(struct sway_output *output,
if (node->type == WLR_SCENE_NODE_BUFFER) {
struct wlr_scene_buffer *buffer = wlr_scene_buffer_from_node(node);
struct wlr_scene_surface *surface = wlr_scene_surface_try_from_buffer(buffer);
if (surface) {
const struct wlr_alpha_modifier_surface_v1_state *alpha_modifier_state =
wlr_alpha_modifier_v1_get_surface_state(surface->surface);
if (alpha_modifier_state != NULL) {
opacity *= (float)alpha_modifier_state->multiplier;
}
}
// hack: don't call the scene setter because that will damage all outputs
// We don't want to damage outputs that aren't our current output that
@ -232,40 +242,56 @@ static void output_configure_scene(struct sway_output *output,
}
}
static bool output_can_tear(struct sway_output *output) {
struct sway_workspace *workspace = output->current.active_workspace;
if (!workspace) {
return false;
}
struct sway_container *fullscreen_con = root->fullscreen_global;
if (!fullscreen_con) {
fullscreen_con = workspace->current.fullscreen;
}
if (fullscreen_con && fullscreen_con->view) {
return (output->allow_tearing && view_can_tear(fullscreen_con->view));
}
return false;
}
static int output_repaint_timer_handler(void *data) {
struct sway_output *output = data;
output->wlr_output->frame_pending = false;
if (!output->enabled) {
return 0;
}
output->wlr_output->frame_pending = false;
output_configure_scene(output, &root->root_scene->tree.node, 1.0f);
struct wlr_scene_output_state_options opts = {
.color_transform = output->color_transform,
};
struct wlr_output_state pending;
wlr_output_state_init(&pending);
if (!wlr_scene_output_build_state(output->scene_output, &pending, &opts)) {
struct wlr_scene_output *scene_output = output->scene_output;
if (!wlr_scene_output_needs_frame(scene_output)) {
return 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, output->wlr_output);
if (!wlr_gamma_control_v1_apply(gamma_control, &pending)) {
struct wlr_output_state pending;
wlr_output_state_init(&pending);
if (!wlr_scene_output_build_state(output->scene_output, &pending, &opts)) {
wlr_output_state_finish(&pending);
return 0;
}
if (output_can_tear(output)) {
pending.tearing_page_flip = true;
if (!wlr_output_test_state(output->wlr_output, &pending)) {
wlr_gamma_control_v1_send_failed_and_destroy(gamma_control);
wlr_output_state_set_gamma_lut(&pending, 0, NULL, NULL, NULL);
sway_log(SWAY_DEBUG, "Output test failed on '%s', retrying without tearing page-flip",
output->wlr_output->name);
pending.tearing_page_flip = false;
}
}
@ -340,7 +366,7 @@ static void handle_frame(struct wl_listener *listener, void *user_data) {
wlr_scene_output_for_each_buffer(output->scene_output, send_frame_done_iterator, &data);
}
static void update_output_manager_config(struct sway_server *server) {
void update_output_manager_config(struct sway_server *server) {
struct wlr_output_configuration_v1 *config =
wlr_output_configuration_v1_create();
@ -370,24 +396,31 @@ static int timer_modeset_handle(void *data) {
wl_event_source_remove(server->delayed_modeset);
server->delayed_modeset = NULL;
apply_all_output_configs();
transaction_commit_dirty();
update_output_manager_config(server);
apply_stored_output_configs();
return 0;
}
static void request_modeset(struct sway_server *server) {
if (server->delayed_modeset == NULL) {
server->delayed_modeset = wl_event_loop_add_timer(server->wl_event_loop,
timer_modeset_handle, server);
wl_event_source_timer_update(server->delayed_modeset, 10);
void request_modeset(void) {
if (server.delayed_modeset == NULL) {
server.delayed_modeset = wl_event_loop_add_timer(server.wl_event_loop,
timer_modeset_handle, &server);
wl_event_source_timer_update(server.delayed_modeset, 10);
}
}
bool modeset_is_pending(void) {
return server.delayed_modeset != NULL;
}
void force_modeset(void) {
if (server.delayed_modeset != NULL) {
wl_event_source_remove(server.delayed_modeset);
server.delayed_modeset = NULL;
}
apply_stored_output_configs();
}
static void begin_destroy(struct sway_output *output) {
struct sway_server *server = output->server;
if (output->enabled) {
output_disable(output);
}
@ -398,7 +431,6 @@ static void begin_destroy(struct sway_output *output) {
wl_list_remove(&output->layout_destroy.link);
wl_list_remove(&output->destroy.link);
wl_list_remove(&output->commit.link);
wl_list_remove(&output->present.link);
wl_list_remove(&output->frame.link);
wl_list_remove(&output->request_state.link);
@ -408,7 +440,10 @@ static void begin_destroy(struct sway_output *output) {
output->wlr_output->data = NULL;
output->wlr_output = NULL;
request_modeset(server);
wl_event_source_remove(output->repaint_timer);
output->repaint_timer = NULL;
request_modeset();
}
static void handle_destroy(struct wl_listener *listener, void *data) {
@ -421,31 +456,6 @@ static void handle_layout_destroy(struct wl_listener *listener, void *data) {
begin_destroy(output);
}
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;
if (!output->enabled) {
return;
}
if (event->state->committed & (
WLR_OUTPUT_STATE_MODE |
WLR_OUTPUT_STATE_TRANSFORM |
WLR_OUTPUT_STATE_SCALE)) {
arrange_layers(output);
arrange_output(output);
transaction_commit_dirty();
update_output_manager_config(output->server);
}
// 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) {
struct sway_output *output = wl_container_of(listener, output, present);
struct wlr_output_event_present *output_event = data;
@ -454,7 +464,7 @@ static void handle_present(struct wl_listener *listener, void *data) {
return;
}
output->last_presentation = *output_event->when;
output->last_presentation = output_event->when;
output->refresh_nsec = output_event->refresh;
}
@ -462,7 +472,44 @@ 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);
const struct wlr_output_state *state = event->state;
// Store the requested changes so that the active configuration is
// consistent with the current state, and to avoid duplicate logic to apply
// the changes.
struct output_config *oc = new_output_config(output->wlr_output->name);
if (!oc) {
sway_log(SWAY_ERROR, "Allocation failed");
return;
}
int committed = state->committed;
if (committed & WLR_OUTPUT_STATE_MODE) {
if (state->mode != NULL) {
oc->width = state->mode->width;
oc->height = state->mode->height;
oc->refresh_rate = state->mode->refresh / 1000.f;
} else {
oc->width = state->custom_mode.width;
oc->height = state->custom_mode.height;
oc->refresh_rate = state->custom_mode.refresh / 1000.f;
}
committed &= ~WLR_OUTPUT_STATE_MODE;
}
if (committed & WLR_OUTPUT_STATE_SCALE) {
oc->scale = state->scale;
committed &= ~WLR_OUTPUT_STATE_SCALE;
}
if (committed & WLR_OUTPUT_STATE_TRANSFORM) {
oc->transform = state->transform;
committed &= ~WLR_OUTPUT_STATE_TRANSFORM;
}
// We do not expect or support any other changes here
assert(committed == 0);
store_output_config(oc);
force_modeset();
}
static unsigned int last_headless_num = 0;
@ -526,8 +573,6 @@ void handle_new_output(struct wl_listener *listener, void *data) {
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.present, &output->present);
output->present.notify = handle_present;
wl_signal_add(&wlr_output->events.frame, &output->frame);
@ -542,35 +587,16 @@ void handle_new_output(struct wl_listener *listener, void *data) {
sway_session_lock_add_output(server->session_lock.lock, output);
}
request_modeset(server);
}
void handle_output_layout_change(struct wl_listener *listener,
void *data) {
struct sway_server *server =
wl_container_of(listener, server, output_layout_change);
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);
request_modeset();
}
static struct output_config *output_config_for_config_head(
struct wlr_output_configuration_head_v1 *config_head,
struct sway_output *output) {
struct output_config *oc = new_output_config(output->wlr_output->name);
struct wlr_output_configuration_head_v1 *config_head) {
struct output_config *oc = new_output_config(config_head->state.output->name);
if (!oc) {
return NULL;
}
oc->enabled = config_head->state.enabled;
if (!oc->enabled) {
return oc;
@ -596,71 +622,59 @@ static struct output_config *output_config_for_config_head(
}
static void output_manager_apply(struct sway_server *server,
struct wlr_output_configuration_v1 *config, bool test_only) {
size_t configs_len = wl_list_length(&root->all_outputs);
struct matched_output_config *configs = calloc(configs_len, sizeof(*configs));
struct wlr_output_configuration_v1 *cfg, bool test_only) {
bool ok = false;
size_t configs_len = config->output_configs->length + wl_list_length(&cfg->heads);
struct output_config **configs = calloc(configs_len, sizeof(*configs));
if (!configs) {
return;
sway_log(SWAY_ERROR, "Allocation failed");
goto error;
}
size_t start_new_configs = config->output_configs->length;
for (size_t idx = 0; idx < start_new_configs; idx++) {
configs[idx] = config->output_configs->items[idx];
}
int config_idx = 0;
struct sway_output *sway_output;
wl_list_for_each(sway_output, &root->all_outputs, link) {
if (sway_output == root->fallback_output) {
configs_len--;
continue;
}
struct matched_output_config *cfg = &configs[config_idx++];
cfg->output = sway_output;
size_t config_idx = start_new_configs;
struct wlr_output_configuration_head_v1 *config_head;
wl_list_for_each(config_head, &config->heads, link) {
if (config_head->state.output == sway_output->wlr_output) {
cfg->config = output_config_for_config_head(config_head, sway_output);
break;
}
}
if (!cfg->config) {
cfg->config = find_output_config(sway_output);
wl_list_for_each(config_head, &cfg->heads, link) {
// Generate the configuration and store it as a temporary
// config. We keep a record of it so we can remove it later.
struct output_config *oc = output_config_for_config_head(config_head);
if (!oc) {
sway_log(SWAY_ERROR, "Allocation failed");
goto error_config;
}
configs[config_idx++] = oc;
}
sort_output_configs_by_priority(configs, configs_len);
bool ok = apply_output_configs(configs, configs_len, test_only, false);
for (size_t idx = 0; idx < configs_len; idx++) {
struct matched_output_config *cfg = &configs[idx];
// Try to commit without degrade to off enabled. Note that this will fail
// if any output configured for enablement fails to be enabled, even if it
// was not part of the config heads we were asked to configure.
ok = apply_output_configs(configs, configs_len, test_only, false);
// Only store new configs for successful non-test commits. Old configs,
// test-only and failed commits just get freed.
bool store_config = false;
error_config:
for (size_t idx = start_new_configs; idx < configs_len; idx++) {
struct output_config *cfg = configs[idx];
if (!test_only && ok) {
struct wlr_output_configuration_head_v1 *config_head;
wl_list_for_each(config_head, &config->heads, link) {
if (config_head->state.output == cfg->output->wlr_output) {
store_config = true;
break;
}
}
}
if (store_config) {
store_output_config(cfg->config);
store_output_config(cfg);
} else {
free_output_config(cfg->config);
free_output_config(cfg);
}
}
free(configs);
error:
if (ok) {
wlr_output_configuration_v1_send_succeeded(config);
wlr_output_configuration_v1_send_succeeded(cfg);
if (server->delayed_modeset != NULL) {
wl_event_source_remove(server->delayed_modeset);
server->delayed_modeset = NULL;
}
} else {
wlr_output_configuration_v1_send_failed(config);
}
wlr_output_configuration_v1_destroy(config);
if (!test_only) {
update_output_manager_config(server);
wlr_output_configuration_v1_send_failed(cfg);
}
wlr_output_configuration_v1_destroy(cfg);
}
void handle_output_manager_apply(struct wl_listener *listener, void *data) {
@ -685,6 +699,11 @@ void handle_output_power_manager_set_mode(struct wl_listener *listener,
struct sway_output *output = event->output->data;
struct output_config *oc = new_output_config(output->wlr_output->name);
if (!oc) {
sway_log(SWAY_ERROR, "Allocation failed");
return;
}
switch (event->mode) {
case ZWLR_OUTPUT_POWER_V1_MODE_OFF:
oc->power = 0;
@ -694,5 +713,5 @@ void handle_output_power_manager_set_mode(struct wl_listener *listener,
break;
}
store_output_config(oc);
request_modeset(output->server);
request_modeset();
}

64
sway/desktop/tearing.c Normal file
View file

@ -0,0 +1,64 @@
#include <wayland-server-core.h>
#include <wlr/types/wlr_tearing_control_v1.h>
#include "sway/server.h"
#include "sway/tree/view.h"
#include "sway/output.h"
#include "log.h"
struct sway_tearing_controller {
struct wlr_tearing_control_v1 *tearing_control;
struct wl_listener set_hint;
struct wl_listener destroy;
struct wl_list link; // sway_server::tearing_controllers
};
static void handle_tearing_controller_set_hint(struct wl_listener *listener,
void *data) {
struct sway_tearing_controller *controller =
wl_container_of(listener, controller, set_hint);
struct sway_view *view = view_from_wlr_surface(
controller->tearing_control->surface);
if (view) {
view->tearing_hint = controller->tearing_control->current;
}
}
static void handle_tearing_controller_destroy(struct wl_listener *listener,
void *data) {
struct sway_tearing_controller *controller =
wl_container_of(listener, controller, destroy);
wl_list_remove(&controller->set_hint.link);
wl_list_remove(&controller->destroy.link);
wl_list_remove(&controller->link);
free(controller);
}
void handle_new_tearing_hint(struct wl_listener *listener,
void *data) {
struct sway_server *server =
wl_container_of(listener, server, tearing_control_new_object);
struct wlr_tearing_control_v1 *tearing_control = data;
enum wp_tearing_control_v1_presentation_hint hint =
wlr_tearing_control_manager_v1_surface_hint_from_surface(
server->tearing_control_v1, tearing_control->surface);
sway_log(SWAY_DEBUG, "New presentation hint %d received for surface %p",
hint, tearing_control->surface);
struct sway_tearing_controller *controller =
calloc(1, sizeof(struct sway_tearing_controller));
if (!controller) {
return;
}
controller->tearing_control = tearing_control;
controller->set_hint.notify = handle_tearing_controller_set_hint;
wl_signal_add(&tearing_control->events.set_hint, &controller->set_hint);
controller->destroy.notify = handle_tearing_controller_destroy;
wl_signal_add(&tearing_control->events.destroy, &controller->destroy);
wl_list_init(&controller->link);
wl_list_insert(&server->tearing_controllers, &controller->link);
}

View file

@ -424,13 +424,14 @@ static void arrange_container(struct sway_container *con,
int border_bottom = con->current.border_bottom ? border_width : 0;
int border_left = con->current.border_left ? border_width : 0;
int border_right = con->current.border_right ? border_width : 0;
int vert_border_height = MAX(0, height - border_top - border_bottom);
wlr_scene_rect_set_size(con->border.top, width, border_top);
wlr_scene_rect_set_size(con->border.bottom, width, border_bottom);
wlr_scene_rect_set_size(con->border.left,
border_left, height - border_top - border_bottom);
border_left, vert_border_height);
wlr_scene_rect_set_size(con->border.right,
border_right, height - border_top - border_bottom);
border_right, vert_border_height);
wlr_scene_node_set_position(&con->border.top->node, 0, 0);
wlr_scene_node_set_position(&con->border.bottom->node,
@ -559,7 +560,7 @@ static void arrange_output(struct sway_output *output, int width, int height) {
for (int i = 0; i < output->current.workspaces->length; i++) {
struct sway_workspace *child = output->current.workspaces->items[i];
bool activated = output->current.active_workspace == child;
bool activated = output->current.active_workspace == child && output->wlr_output->enabled;
wlr_scene_node_reparent(&child->layers.tiling->node, output->layers.tiling);
wlr_scene_node_reparent(&child->layers.fullscreen->node, output->layers.fullscreen);
@ -612,11 +613,13 @@ void arrange_popups(struct wlr_scene_tree *popups) {
struct sway_popup_desc *popup = scene_descriptor_try_get(node,
SWAY_SCENE_DESC_POPUP);
if (popup) {
int lx, ly;
wlr_scene_node_coords(popup->relative, &lx, &ly);
wlr_scene_node_set_position(node, lx, ly);
}
}
}
static void arrange_root(struct sway_root *root) {
struct sway_container *fs = root->fullscreen_global;
@ -632,6 +635,15 @@ static void arrange_root(struct sway_root *root) {
for (int i = 0; i < root->scratchpad->length; i++) {
struct sway_container *con = root->scratchpad->items[i];
// When a container is moved to a scratchpad, it's possible that it
// was moved into a floating container as part of the same transaction.
// In this case, we need to make sure we reparent all the container's
// children so that disabling the container will disable all descendants.
if (!con->view) for (int ii = 0; ii < con->current.children->length; ii++) {
struct sway_container *child = con->current.children->items[ii];
wlr_scene_node_reparent(&child->scene_tree->node, con->content_tree);
}
wlr_scene_node_set_enabled(&con->scene_tree->node, false);
}

View file

@ -299,18 +299,17 @@ static void handle_commit(struct wl_listener *listener, void *data) {
return;
}
struct wlr_box new_geo;
wlr_xdg_surface_get_geometry(xdg_surface, &new_geo);
bool new_size = new_geo.width != view->geometry.width ||
new_geo.height != view->geometry.height ||
new_geo.x != view->geometry.x ||
new_geo.y != view->geometry.y;
struct wlr_box *new_geo = &xdg_surface->geometry;
bool new_size = new_geo->width != view->geometry.width ||
new_geo->height != view->geometry.height ||
new_geo->x != view->geometry.x ||
new_geo->y != view->geometry.y;
if (new_size) {
// The client changed its surface size in this commit. For floating
// containers, we resize the container to match. For tiling containers,
// we only recenter the surface.
memcpy(&view->geometry, &new_geo, sizeof(struct wlr_box));
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.
@ -463,12 +462,8 @@ static void handle_map(struct wl_listener *listener, void *data) {
struct sway_view *view = &xdg_shell_view->view;
struct wlr_xdg_toplevel *toplevel = view->wlr_xdg_toplevel;
view->natural_width = toplevel->base->current.geometry.width;
view->natural_height = toplevel->base->current.geometry.height;
if (!view->natural_width && !view->natural_height) {
view->natural_width = toplevel->base->surface->current.width;
view->natural_height = toplevel->base->surface->current.height;
}
view->natural_width = toplevel->base->geometry.width;
view->natural_height = toplevel->base->geometry.height;
bool csd = false;

View file

@ -71,7 +71,7 @@ static void unmanaged_handle_map(struct wl_listener *listener, void *data) {
surface->set_geometry.notify = unmanaged_handle_set_geometry;
}
if (wlr_xwayland_or_surface_wants_focus(xsurface)) {
if (wlr_xwayland_surface_override_redirect_wants_focus(xsurface)) {
struct sway_seat *seat = input_manager_current_seat();
struct wlr_xwayland *xwayland = server.xwayland.wlr_xwayland;
wlr_xwayland_set_seat(xwayland, seat->wlr_seat);
@ -96,7 +96,7 @@ static void unmanaged_handle_unmap(struct wl_listener *listener, void *data) {
// This simply returns focus to the parent surface if there's one available.
// This seems to handle JetBrains issues.
if (xsurface->parent && xsurface->parent->surface
&& wlr_xwayland_or_surface_wants_focus(xsurface->parent)) {
&& wlr_xwayland_surface_override_redirect_wants_focus(xsurface->parent)) {
seat_set_focus_surface(seat, xsurface->parent->surface, false);
return;
}
@ -289,7 +289,6 @@ static void set_activated(struct sway_view *view, bool activated) {
}
wlr_xwayland_surface_activate(surface, activated);
wlr_xwayland_surface_restack(surface, NULL, XCB_STACK_MODE_ABOVE);
}
static void set_tiled(struct sway_view *view, bool tiled) {
@ -297,7 +296,7 @@ static void set_tiled(struct sway_view *view, bool tiled) {
return;
}
struct wlr_xwayland_surface *surface = view->wlr_xwayland_surface;
wlr_xwayland_surface_set_maximized(surface, tiled);
wlr_xwayland_surface_set_maximized(surface, tiled, tiled);
}
static void set_fullscreen(struct sway_view *view, bool fullscreen) {

View file

@ -560,7 +560,7 @@ static void handle_tablet_tool_position(struct sway_cursor *cursor,
// tablet events until the drag is released, even if we are now over a
// non-tablet surface.
if (!cursor->simulating_pointer_from_tool_tip &&
((surface && wlr_surface_accepts_tablet_v2(tablet->tablet_v2, surface)) ||
((surface && wlr_surface_accepts_tablet_v2(surface, tablet->tablet_v2)) ||
wlr_tablet_tool_v2_has_implicit_grab(tool->tablet_v2_tool))) {
seatop_tablet_tool_motion(seat, tool, time_msec);
} else {
@ -646,7 +646,7 @@ static void handle_tool_tip(struct wl_listener *listener, void *data) {
dispatch_cursor_button(cursor, &event->tablet->base, event->time_msec,
BTN_LEFT, WL_POINTER_BUTTON_STATE_RELEASED);
wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat);
} else if (!surface || !wlr_surface_accepts_tablet_v2(tablet_v2, surface)) {
} else if (!surface || !wlr_surface_accepts_tablet_v2(surface, tablet_v2)) {
// If we started holding the tool tip down on a surface that accepts
// tablet v2, we should notify that surface if it gets released over a
// surface that doesn't support v2.
@ -731,7 +731,7 @@ static void handle_tool_button(struct wl_listener *listener, void *data) {
bool mod_pressed = modifiers & config->floating_mod;
bool surface_supports_tablet_events =
surface && wlr_surface_accepts_tablet_v2(tablet_v2, surface);
surface && wlr_surface_accepts_tablet_v2(surface, tablet_v2);
// Simulate pointer when:
// 1. The modifier key is pressed, OR
@ -1194,7 +1194,7 @@ uint32_t get_mouse_bindsym(const char *name, char **error) {
SWAY_SCROLL_UP, SWAY_SCROLL_DOWN, SWAY_SCROLL_LEFT,
SWAY_SCROLL_RIGHT, BTN_SIDE, BTN_EXTRA};
return buttons[number - 1];
} else if (strncmp(name, "BTN_", strlen("BTN_")) == 0) {
} else if (has_prefix(name, "BTN_")) {
// Get event code from name
int code = libevdev_event_code_from_name(EV_KEY, name);
if (code == -1) {
@ -1219,7 +1219,7 @@ uint32_t get_mouse_bindcode(const char *name, char **error) {
return 0;
}
const char *event = libevdev_event_code_get_name(EV_KEY, code);
if (!event || strncmp(event, "BTN_", strlen("BTN_")) != 0) {
if (!event || !has_prefix(event, "BTN_")) {
*error = format_str("Event code %d (%s) is not a button",
code, event ? event : "(null)");
return 0;

View file

@ -4,7 +4,6 @@
#include <math.h>
#include <assert.h>
#include <wlr/config.h>
#include <wlr/backend/libinput.h>
#include <wlr/types/wlr_cursor.h>
#include <wlr/types/wlr_keyboard_group.h>
#include <wlr/types/wlr_virtual_keyboard_v1.h>
@ -579,7 +578,7 @@ void input_manager_configure_all_input_mappings(void) {
void input_manager_apply_input_config(struct input_config *input_config) {
struct sway_input_device *input_device = NULL;
bool wildcard = strcmp(input_config->identifier, "*") == 0;
bool type_wildcard = strncmp(input_config->identifier, "type:", 5) == 0;
bool type_wildcard = has_prefix(input_config->identifier, "type:");
wl_list_for_each(input_device, &server.input->devices, link) {
bool type_matches = type_wildcard &&
strcmp(input_device_get_type(input_device), input_config->identifier + 5) == 0;

View file

@ -4,6 +4,7 @@
#include <wlr/config.h>
#include <wlr/backend/multi.h>
#include <wlr/interfaces/wlr_keyboard.h>
#include <wlr/types/wlr_cursor.h>
#include <wlr/types/wlr_keyboard.h>
#include <wlr/types/wlr_keyboard_group.h>
#include <xkbcommon/xkbcommon-names.h>
@ -267,6 +268,7 @@ static bool keyboard_execute_compositor_binding(struct sway_keyboard *keyboard,
const xkb_keysym_t *pressed_keysyms, uint32_t modifiers, size_t keysyms_len) {
for (size_t i = 0; i < keysyms_len; ++i) {
xkb_keysym_t keysym = pressed_keysyms[i];
if (keysym >= XKB_KEY_XF86Switch_VT_1 &&
keysym <= XKB_KEY_XF86Switch_VT_12) {
#if WLR_HAS_SESSION
@ -282,6 +284,36 @@ static bool keyboard_execute_compositor_binding(struct sway_keyboard *keyboard,
return false;
}
static bool keyboard_execute_pointer_keysyms(struct sway_keyboard *keyboard,
uint32_t time, const xkb_keysym_t *pressed_keysyms, size_t keysyms_len,
enum wl_keyboard_key_state state) {
struct sway_cursor *cursor = keyboard->seat_device->sway_seat->cursor;
for (size_t i = 0; i < keysyms_len; ++i) {
xkb_keysym_t keysym = pressed_keysyms[i];
uint32_t button = wlr_keyboard_keysym_to_pointer_button(keysym);
if (button != 0) {
dispatch_cursor_button(cursor, &keyboard->wlr->base, time, button,
(enum wl_pointer_button_state)state);
wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat);
return true;
}
int dx, dy;
wlr_keyboard_keysym_to_pointer_motion(keysym, &dx, &dy);
if (state == WL_KEYBOARD_KEY_STATE_PRESSED && (dx != 0 || dy != 0)) {
dx *= 10;
dy *= 10;
pointer_motion(cursor, time, &keyboard->wlr->base, dx, dy, dx, dy);
wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat);
return true;
}
}
return false;
}
/**
* Get keysyms and modifiers from the keyboard as xkb sees them.
*
@ -507,6 +539,11 @@ static void handle_key_event(struct sway_keyboard *keyboard,
keyboard, keyinfo.raw_keysyms, keyinfo.raw_modifiers,
keyinfo.raw_keysyms_len);
}
if (!handled) {
handled = keyboard_execute_pointer_keysyms(keyboard, event->time_msec,
keyinfo.translated_keysyms, keyinfo.translated_keysyms_len,
event->state);
}
if (event->state == WL_KEYBOARD_KEY_STATE_RELEASED) {
// If the pressed event was sent to a client and we have a focused
@ -1021,13 +1058,6 @@ static void sway_keyboard_set_layout(struct sway_keyboard *keyboard,
}
}
// If the seat has no active keyboard, set this one
struct wlr_seat *seat = keyboard->seat_device->sway_seat->wlr_seat;
struct wlr_keyboard *current_keyboard = seat->keyboard_state.keyboard;
if (current_keyboard == NULL) {
wlr_seat_set_keyboard(seat, keyboard->wlr);
}
if (keymap_changed) {
ipc_event_input("xkb_keymap",
keyboard->seat_device->input_device);
@ -1071,6 +1101,13 @@ void sway_keyboard_configure(struct sway_keyboard *keyboard) {
sway_keyboard_set_layout(keyboard, input_config);
}
// If the seat has no active keyboard, set this one
struct wlr_seat *seat = keyboard->seat_device->sway_seat->wlr_seat;
struct wlr_keyboard *current_keyboard = seat->keyboard_state.keyboard;
if (current_keyboard == NULL) {
wlr_seat_set_keyboard(seat, keyboard->wlr);
}
wl_list_remove(&keyboard->keyboard_key.link);
wl_signal_add(&keyboard->wlr->events.key, &keyboard->keyboard_key);
keyboard->keyboard_key.notify = handle_keyboard_key;

View file

@ -272,6 +272,10 @@ bool sway_input_configure_libinput_device(struct sway_input_device *input_device
}
if (ic->drag_lock != INT_MIN) {
changed |= set_tap_drag_lock(device, ic->drag_lock);
} else {
#if HAVE_LIBINPUT_CONFIG_DRAG_LOCK_ENABLED_STICKY
changed |= set_tap_drag_lock(device, LIBINPUT_CONFIG_DRAG_LOCK_ENABLED_STICKY);
#endif
}
if (ic->pointer_accel != FLT_MIN) {
changed |= set_accel_speed(device, ic->pointer_accel);
@ -354,8 +358,12 @@ void sway_input_reset_libinput_device(struct sway_input_device *input_device) {
libinput_device_config_tap_get_default_button_map(device));
changed |= set_tap_drag(device,
libinput_device_config_tap_get_default_drag_enabled(device));
#if HAVE_LIBINPUT_CONFIG_DRAG_LOCK_ENABLED_STICKY
changed |= set_tap_drag_lock(device, LIBINPUT_CONFIG_DRAG_LOCK_ENABLED_STICKY);
#else
changed |= set_tap_drag_lock(device,
libinput_device_config_tap_get_default_drag_lock_enabled(device));
#endif
changed |= set_accel_speed(device,
libinput_device_config_accel_get_default_speed(device));
changed |= set_rotation_angle(device,
@ -409,13 +417,9 @@ bool sway_libinput_device_is_builtin(struct sway_input_device *sway_device) {
return false;
}
const char prefix_platform[] = "platform-";
if (strncmp(id_path, prefix_platform, strlen(prefix_platform)) != 0) {
return false;
if (has_prefix(id_path, "platform-")) {
return true;
}
const char prefix_pci[] = "pci-";
const char infix_platform[] = "-platform-";
return (strncmp(id_path, prefix_pci, strlen(prefix_pci)) == 0) &&
strstr(id_path, infix_platform);
return has_prefix(id_path, "pci-") && strstr(id_path, "-platform-");
}

View file

@ -647,10 +647,6 @@ static void seat_reset_input_config(struct sway_seat *seat,
sway_device->input_device->wlr_device, NULL);
}
static bool has_prefix(const char *str, const char *prefix) {
return strncmp(str, prefix, strlen(prefix)) == 0;
}
/**
* Get the name of the built-in output, if any. Returns NULL if there isn't
* exactly one built-in output.
@ -1090,6 +1086,7 @@ static void seat_send_unfocus(struct sway_node *node, struct sway_seat *seat) {
static int handle_urgent_timeout(void *data) {
struct sway_view *view = data;
view_set_urgent(view, false);
container_update_itself_and_parents(view->container);
return 0;
}

View file

@ -273,7 +273,7 @@ static void handle_tablet_tool_tip(struct sway_seat *seat,
// Handle tapping on an xwayland unmanaged view
else if ((xsurface = wlr_xwayland_surface_try_from_wlr_surface(surface)) &&
xsurface->override_redirect &&
wlr_xwayland_or_surface_wants_focus(xsurface)) {
wlr_xwayland_surface_override_redirect_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);
@ -355,6 +355,13 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec,
struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(seat->wlr_seat);
uint32_t modifiers = keyboard ? wlr_keyboard_get_modifiers(keyboard) : 0;
bool mod_pressed = modifiers & config->floating_mod;
uint32_t mod_move_btn = config->floating_mod_inverse ? BTN_RIGHT : BTN_LEFT;
uint32_t mod_resize_btn = config->floating_mod_inverse ? BTN_LEFT : BTN_RIGHT;
bool mod_move_btn_pressed = mod_pressed && button == mod_move_btn;
bool mod_resize_btn_pressed = mod_pressed && button == mod_resize_btn;
bool titlebar_left_btn_pressed = on_titlebar && button == BTN_LEFT;
// Handle mouse bindings
if (trigger_pointer_button_binding(seat, device, button, state, modifiers,
on_titlebar, on_border, on_contents, on_workspace)) {
@ -403,12 +410,8 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec,
}
// Handle tiling resize via mod
bool mod_pressed = modifiers & config->floating_mod;
if (cont && !is_floating_or_child && mod_pressed &&
if (cont && !is_floating_or_child && mod_pressed && mod_resize_btn_pressed &&
state == WL_POINTER_BUTTON_STATE_PRESSED) {
uint32_t btn_resize = config->floating_mod_inverse ?
BTN_LEFT : BTN_RIGHT;
if (button == btn_resize) {
edge = 0;
edge |= cursor->cursor->x > cont->pending.x + cont->pending.width / 2 ?
WLR_EDGE_RIGHT : WLR_EDGE_LEFT;
@ -430,7 +433,6 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec,
seatop_begin_resize_tiling(seat, cont, edge);
return;
}
}
// Handle changing focus when clicking on a container
if (cont && state == WL_POINTER_BUTTON_STATE_PRESSED) {
@ -454,13 +456,11 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec,
// Handle beginning floating move
if (cont && is_floating_or_child && !is_fullscreen_or_child &&
state == WL_POINTER_BUTTON_STATE_PRESSED) {
uint32_t btn_move = config->floating_mod_inverse ? BTN_RIGHT : BTN_LEFT;
if (button == btn_move && (mod_pressed || on_titlebar)) {
state == WL_POINTER_BUTTON_STATE_PRESSED &&
(mod_move_btn_pressed || titlebar_left_btn_pressed)) {
seatop_begin_move_floating(seat, container_toplevel_ancestor(cont));
return;
}
}
// Handle beginning floating resize
if (cont && is_floating_or_child && !is_fullscreen_or_child &&
@ -473,9 +473,7 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec,
}
// Via mod+click
uint32_t btn_resize = config->floating_mod_inverse ?
BTN_LEFT : BTN_RIGHT;
if (mod_pressed && button == btn_resize) {
if (mod_resize_btn_pressed) {
struct sway_container *floater = container_toplevel_ancestor(cont);
edge = 0;
edge |= cursor->cursor->x > floater->pending.x + floater->pending.width / 2 ?
@ -489,7 +487,7 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec,
}
// Handle moving a tiling container
if (config->tiling_drag && (mod_pressed || on_titlebar) &&
if (config->tiling_drag && (mod_move_btn_pressed || titlebar_left_btn_pressed) &&
state == WL_POINTER_BUTTON_STATE_PRESSED && !is_floating_or_child &&
cont && cont->pending.fullscreen_mode == FULLSCREEN_NONE) {
// If moving a container by its title bar, use a threshold for the drag
@ -498,7 +496,6 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec,
} else {
seatop_begin_move_tiling(seat, cont);
}
return;
}
@ -521,7 +518,7 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec,
if (surface &&
(xsurface = wlr_xwayland_surface_try_from_wlr_surface(surface)) &&
xsurface->override_redirect &&
wlr_xwayland_or_surface_wants_focus(xsurface)) {
wlr_xwayland_surface_override_redirect_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);
@ -667,7 +664,7 @@ static void handle_touch_down(struct sway_seat *seat,
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 (surface && wlr_surface_accepts_touch(surface, wlr_seat)) {
if (seat_is_input_allowed(seat, surface)) {
cursor->simulating_pointer_from_touch = false;
seatop_begin_touch_down(seat, surface, event, sx, sy, lx, ly);

View file

@ -363,7 +363,7 @@ void sway_tablet_pad_set_focus(struct sway_tablet_pad *tablet_pad,
}
if (surface == NULL ||
!wlr_surface_accepts_tablet_v2(tablet_pad->tablet->tablet_v2, surface)) {
!wlr_surface_accepts_tablet_v2(surface, tablet_pad->tablet->tablet_v2)) {
return;
}

View file

@ -11,8 +11,6 @@
#include "sway/layers.h"
#include "sway/server.h"
static void input_popup_update(struct sway_input_popup *popup);
static struct sway_text_input *relay_get_focusable_text_input(
struct sway_input_method_relay *relay) {
struct sway_text_input *text_input = NULL;
@ -128,6 +126,89 @@ static void handle_im_destroy(struct wl_listener *listener, void *data) {
}
}
static void constrain_popup(struct sway_input_popup *popup) {
struct sway_text_input *text_input =
relay_get_focused_text_input(popup->relay);
if (!popup->desc.relative) {
return;
}
struct wlr_box parent = {0};
wlr_scene_node_coords(&popup->desc.relative->parent->node, &parent.x, &parent.y);
struct wlr_box geo = {0};
struct wlr_output *output;
if (popup->desc.view) {
struct sway_view *view = popup->desc.view;
output = wlr_output_layout_output_at(root->output_layout,
view->container->pending.content_x + view->geometry.x,
view->container->pending.content_y + view->geometry.y);
parent.width = view->geometry.width;
parent.height = view->geometry.height;
geo = view->geometry;
} else {
output = popup->fixed_output;
}
struct wlr_box output_box;
wlr_output_layout_get_box(root->output_layout, output, &output_box);
bool cursor_rect = text_input->input->current.features &
WLR_TEXT_INPUT_V3_FEATURE_CURSOR_RECTANGLE;
struct wlr_box cursor_area;
if (cursor_rect) {
cursor_area = text_input->input->current.cursor_rectangle;
} else {
cursor_area = (struct wlr_box) {
.width = parent.width,
.height = parent.height,
};
}
int popup_width = popup->popup_surface->surface->current.width;
int popup_height = popup->popup_surface->surface->current.height;
int x1 = parent.x + cursor_area.x;
int x2 = parent.x + cursor_area.x + cursor_area.width;
int y1 = parent.y + cursor_area.y;
int y2 = parent.y + cursor_area.y + cursor_area.height;
int x = x1;
int y = y2;
int available_right = output_box.x + output_box.width - x1;
int available_left = x2 - output_box.x;
if (available_right < popup_width && available_left > available_right) {
x = x2 - popup_width;
}
int available_down = output_box.y + output_box.height - y2;
int available_up = y1 - output_box.y;
if (available_down < popup_height && available_up > available_down) {
y = y1 - popup_height;
}
wlr_scene_node_set_position(popup->desc.relative, x - parent.x - geo.x, y - parent.y - geo.y);
if (cursor_rect) {
struct wlr_box box = {
.x = x1 - x,
.y = y1 - y,
.width = cursor_area.width,
.height = cursor_area.height,
};
wlr_input_popup_surface_v2_send_text_input_rectangle(
popup->popup_surface, &box);
}
if (popup->scene_tree) {
wlr_scene_node_set_position(&popup->scene_tree->node, x - geo.x, y - geo.y);
}
}
static void input_popup_set_focus(struct sway_input_popup *popup,
struct wlr_surface *surface);
static void relay_send_im_state(struct sway_input_method_relay *relay,
struct wlr_text_input_v3 *input) {
struct wlr_input_method_v2 *input_method = relay->input_method;
@ -148,10 +229,16 @@ static void relay_send_im_state(struct sway_input_method_relay *relay,
input->current.content_type.hint,
input->current.content_type.purpose);
}
struct sway_text_input *text_input = relay_get_focused_text_input(relay);
struct sway_input_popup *popup;
wl_list_for_each(popup, &relay->input_popups, link) {
// send_text_input_rectangle is called in this function
input_popup_update(popup);
if (text_input != NULL) {
input_popup_set_focus(popup, text_input->input->focused_surface);
} else {
input_popup_set_focus(popup, NULL);
}
}
wlr_input_method_v2_send_done(input_method);
// TODO: pass intent, display popup size
@ -275,72 +362,52 @@ static void relay_handle_text_input(struct wl_listener *listener,
sway_text_input_create(relay, wlr_text_input);
}
static void input_popup_update(struct sway_input_popup *popup) {
struct sway_text_input *text_input =
relay_get_focused_text_input(popup->relay);
static void input_popup_set_focus(struct sway_input_popup *popup,
struct wlr_surface *surface) {
wl_list_remove(&popup->focused_surface_unmap.link);
if (text_input == NULL || text_input->input->focused_surface == NULL) {
if (!popup->scene_tree) {
wl_list_init(&popup->focused_surface_unmap.link);
return;
}
if (popup->scene_tree != NULL) {
wlr_scene_node_destroy(&popup->scene_tree->node);
popup->scene_tree = NULL;
}
if (popup->desc.relative != NULL) {
if (popup->desc.relative) {
scene_descriptor_destroy(&popup->scene_tree->node, SWAY_SCENE_DESC_POPUP);
wlr_scene_node_destroy(popup->desc.relative);
popup->desc.relative = NULL;
}
popup->desc.view = NULL;
if (!popup->popup_surface->surface->mapped) {
if (surface == NULL) {
wl_list_init(&popup->focused_surface_unmap.link);
wlr_scene_node_set_enabled(&popup->scene_tree->node, false);
return;
}
bool cursor_rect = text_input->input->current.features
& WLR_TEXT_INPUT_V3_FEATURE_CURSOR_RECTANGLE;
struct wlr_surface *focused_surface = text_input->input->focused_surface;
struct wlr_box cursor_area = text_input->input->current.cursor_rectangle;
struct wlr_box output_box;
struct wlr_box parent = {0};
struct wlr_layer_surface_v1 *layer_surface =
wlr_layer_surface_v1_try_from_wlr_surface(focused_surface);
wlr_layer_surface_v1_try_from_wlr_surface(surface);
struct wlr_scene_tree *relative_parent;
if (layer_surface) {
wl_signal_add(&layer_surface->surface->events.unmap,
&popup->focused_surface_unmap);
struct wlr_box geo = {0};
popup->scene_tree = wlr_scene_subsurface_tree_create(root->layers.popup, popup->popup_surface->surface);
if (layer_surface != NULL) {
struct sway_layer_surface *layer =
layer_surface->data;
struct sway_layer_surface *layer = layer_surface->data;
if (layer == NULL) {
return;
}
relative_parent = layer->scene->tree;
struct wlr_output *output = layer->layer_surface->output;
wlr_output_layout_get_box(root->output_layout, output, &output_box);
int lx, ly;
wlr_scene_node_coords(&layer->tree->node, &lx, &ly);
parent.x = lx;
parent.y = ly;
popup->desc.view = NULL;
} else {
struct sway_view *view = view_from_wlr_surface(focused_surface);
relative_parent = view->scene_tree;
geo = view->geometry;
int lx, ly;
wlr_scene_node_coords(&view->scene_tree->node, &lx, &ly);
struct wlr_output *output = wlr_output_layout_output_at(root->output_layout,
view->container->pending.content_x + view->geometry.x,
view->container->pending.content_y + view->geometry.y);
wlr_output_layout_get_box(root->output_layout, output, &output_box);
parent.x = lx;
parent.y = ly;
parent.width = view->geometry.width;
parent.height = view->geometry.height;
// we don't need to add an event here to NULL out this field because
// this field will only be initialized if the popup is part of a layer
// surface. Layer surfaces get destroyed as part of the output being
// destroyed, thus also trickling down to popups.
popup->fixed_output = layer->layer_surface->output;
} else {
struct sway_view *view = view_from_wlr_surface(surface);
wl_signal_add(&view->events.unmap, &popup->focused_surface_unmap);
relative_parent = view->scene_tree;
popup->desc.view = view;
}
@ -354,93 +421,59 @@ static void input_popup_update(struct sway_input_popup *popup) {
return;
}
if (!cursor_rect) {
cursor_area.x = 0;
cursor_area.y = 0;
cursor_area.width = parent.width;
cursor_area.height = parent.height;
}
int popup_width = popup->popup_surface->surface->current.width;
int popup_height = popup->popup_surface->surface->current.height;
int x1 = parent.x + cursor_area.x;
int x2 = parent.x + cursor_area.x + cursor_area.width;
int y1 = parent.y + cursor_area.y;
int y2 = parent.y + cursor_area.y + cursor_area.height;
int x = x1;
int y = y2;
int available_right = output_box.x + output_box.width - x1;
int available_left = x2 - output_box.x;
if (available_right < popup_width && available_left > available_right) {
x = x2 - popup_width;
}
int available_down = output_box.y + output_box.height - y2;
int available_up = y1 - output_box.y;
if (available_down < popup_height && available_up > available_down) {
y = y1 - popup_height;
}
wlr_scene_node_set_position(&relative->node, x - parent.x - geo.x, y - parent.y - geo.y);
if (cursor_rect) {
struct wlr_box box = {
.x = x1 - x,
.y = y1 - y,
.width = cursor_area.width,
.height = cursor_area.height,
};
wlr_input_popup_surface_v2_send_text_input_rectangle(
popup->popup_surface, &box);
}
wlr_scene_node_set_position(&popup->scene_tree->node, x - geo.x, y - geo.y);
}
static void input_popup_set_focus(struct sway_input_popup *popup,
struct wlr_surface *surface) {
wl_list_remove(&popup->focused_surface_unmap.link);
if (surface == NULL) {
wl_list_init(&popup->focused_surface_unmap.link);
input_popup_update(popup);
return;
}
struct wlr_layer_surface_v1 *layer_surface =
wlr_layer_surface_v1_try_from_wlr_surface(surface);
if (layer_surface != NULL) {
wl_signal_add(
&layer_surface->surface->events.unmap, &popup->focused_surface_unmap);
input_popup_update(popup);
return;
}
struct sway_view *view = view_from_wlr_surface(surface);
wl_signal_add(&view->events.unmap, &popup->focused_surface_unmap);
constrain_popup(popup);
wlr_scene_node_set_enabled(&popup->scene_tree->node, true);
}
static void handle_im_popup_destroy(struct wl_listener *listener, void *data) {
struct sway_input_popup *popup =
wl_container_of(listener, popup, popup_destroy);
wlr_scene_node_destroy(&popup->scene_tree->node);
wl_list_remove(&popup->focused_surface_unmap.link);
wl_list_remove(&popup->popup_surface_commit.link);
wl_list_remove(&popup->popup_surface_map.link);
wl_list_remove(&popup->popup_surface_unmap.link);
wl_list_remove(&popup->popup_destroy.link);
wl_list_remove(&popup->link);
free(popup);
}
static void handle_im_popup_surface_commit(struct wl_listener *listener,
void *data) {
static void handle_im_popup_surface_map(struct wl_listener *listener, void *data) {
struct sway_input_popup *popup =
wl_container_of(listener, popup, popup_surface_map);
struct sway_text_input *text_input = relay_get_focused_text_input(popup->relay);
if (text_input != NULL) {
input_popup_set_focus(popup, text_input->input->focused_surface);
} else {
input_popup_set_focus(popup, NULL);
}
}
static void handle_im_popup_surface_unmap(struct wl_listener *listener, void *data) {
struct sway_input_popup *popup =
wl_container_of(listener, popup, popup_surface_unmap);
scene_descriptor_destroy(&popup->scene_tree->node, SWAY_SCENE_DESC_POPUP);
// relative should already be freed as it should be a child of the just unmapped scene
popup->desc.relative = NULL;
input_popup_set_focus(popup, NULL);
}
static void handle_im_popup_surface_commit(struct wl_listener *listener, void *data) {
struct sway_input_popup *popup =
wl_container_of(listener, popup, popup_surface_commit);
input_popup_update(popup);
constrain_popup(popup);
}
static void handle_im_focused_surface_unmap(
struct wl_listener *listener, void *data) {
struct sway_input_popup *popup =
wl_container_of(listener, popup, focused_surface_unmap);
input_popup_update(popup);
input_popup_set_focus(popup, NULL);
}
static void handle_im_new_popup_surface(struct wl_listener *listener,
@ -448,16 +481,38 @@ static void handle_im_new_popup_surface(struct wl_listener *listener,
struct sway_input_method_relay *relay = wl_container_of(listener, relay,
input_method_new_popup_surface);
struct sway_input_popup *popup = calloc(1, sizeof(*popup));
if (!popup) {
sway_log(SWAY_ERROR, "Failed to allocate an input method popup");
return;
}
popup->relay = relay;
popup->popup_surface = data;
popup->popup_surface->data = popup;
wl_signal_add(
&popup->popup_surface->events.destroy, &popup->popup_destroy);
popup->scene_tree = wlr_scene_tree_create(root->layers.popup);
if (!popup->scene_tree) {
sway_log(SWAY_ERROR, "Failed to allocate scene tree");
free(popup);
return;
}
if (!wlr_scene_subsurface_tree_create(popup->scene_tree,
popup->popup_surface->surface)) {
sway_log(SWAY_ERROR, "Failed to allocate subsurface tree");
wlr_scene_node_destroy(&popup->scene_tree->node);
free(popup);
return;
}
wl_signal_add(&popup->popup_surface->events.destroy, &popup->popup_destroy);
popup->popup_destroy.notify = handle_im_popup_destroy;
wl_signal_add(&popup->popup_surface->surface->events.commit,
&popup->popup_surface_commit);
wl_signal_add(&popup->popup_surface->surface->events.commit, &popup->popup_surface_commit);
popup->popup_surface_commit.notify = handle_im_popup_surface_commit;
wl_signal_add(&popup->popup_surface->surface->events.map, &popup->popup_surface_map);
popup->popup_surface_map.notify = handle_im_popup_surface_map;
wl_signal_add(&popup->popup_surface->surface->events.unmap, &popup->popup_surface_unmap);
popup->popup_surface_unmap.notify = handle_im_popup_surface_unmap;
wl_list_init(&popup->focused_surface_unmap.link);
popup->focused_surface_unmap.notify = handle_im_focused_surface_unmap;

View file

@ -399,6 +399,8 @@ static void ipc_json_describe_enabled_output(struct sway_output *output,
}
json_object_object_add(object, "max_render_time", json_object_new_int(output->max_render_time));
json_object_object_add(object, "allow_tearing", json_object_new_boolean(output->allow_tearing));
}
json_object *ipc_json_describe_disabled_output(struct sway_output *output) {
@ -593,6 +595,8 @@ static void ipc_json_describe_view(struct sway_container *c, json_object *object
json_object_object_add(object, "max_render_time", json_object_new_int(c->view->max_render_time));
json_object_object_add(object, "allow_tearing", json_object_new_boolean(view_can_tear(c->view)));
json_object_object_add(object, "shell", json_object_new_string(view_get_shell(c->view)));
json_object_object_add(object, "inhibit_idle",
@ -927,6 +931,11 @@ static json_object *describe_libinput_device(struct libinput_device *device) {
case LIBINPUT_CONFIG_DRAG_LOCK_DISABLED:
drag_lock = "disabled";
break;
#if HAVE_LIBINPUT_CONFIG_DRAG_LOCK_ENABLED_STICKY
case LIBINPUT_CONFIG_DRAG_LOCK_ENABLED_STICKY:
drag_lock = "enabled_sticky";
break;
#endif
}
json_object_object_add(object, "tap_drag_lock",
json_object_new_string(drag_lock));
@ -1129,7 +1138,9 @@ json_object *ipc_json_describe_input(struct sway_input_device *device) {
json_object *layouts_arr = json_object_new_array();
json_object_object_add(object, "xkb_layout_names", layouts_arr);
xkb_layout_index_t num_layouts = xkb_keymap_num_layouts(keymap);
xkb_layout_index_t num_layouts =
keymap ? xkb_keymap_num_layouts(keymap) : 0;
// Virtual keyboards might have null keymap
xkb_layout_index_t layout_idx;
for (layout_idx = 0; layout_idx < num_layouts; layout_idx++) {
const char *layout = xkb_keymap_layout_get_name(keymap, layout_idx);

View file

@ -648,6 +648,12 @@ void ipc_client_handle_command(struct ipc_client *client, uint32_t payload_lengt
}
list_t *res_list = execute_command(buf, NULL, NULL);
if (modeset_is_pending()) {
// IPC expects commands to have taken immediate effect, so we need
// to force a modeset after output commands. We do a single modeset
// here to avoid modesetting for every output command in sequence.
force_modeset();
}
transaction_commit_dirty();
char *json = cmd_results_to_json(res_list);
int length = strlen(json);

View file

@ -8,6 +8,7 @@
#include "sway/layers.h"
#include "sway/output.h"
#include "sway/server.h"
#include "sway/lock.h"
struct sway_session_lock_output {
struct wlr_scene_tree *tree;
@ -19,7 +20,6 @@ struct sway_session_lock_output {
struct wl_list link; // sway_session_lock::outputs
struct wl_listener destroy;
struct wl_listener commit;
struct wlr_session_lock_surface_v1 *surface;
@ -89,6 +89,17 @@ static void lock_output_reconfigure(struct sway_session_lock_output *output) {
}
}
void arrange_locks(void) {
if (server.session_lock.lock == NULL) {
return;
}
struct sway_session_lock_output *lock_output;
wl_list_for_each(lock_output, &server.session_lock.lock->outputs, link) {
lock_output_reconfigure(lock_output);
}
}
static void handle_new_surface(struct wl_listener *listener, void *data) {
struct sway_session_lock *lock = wl_container_of(listener, lock, new_surface);
struct wlr_session_lock_surface_v1 *lock_surface = data;
@ -125,7 +136,6 @@ static void sway_session_lock_output_destroy(struct sway_session_lock_output *ou
wl_list_remove(&output->surface_map.link);
}
wl_list_remove(&output->commit.link);
wl_list_remove(&output->destroy.link);
wl_list_remove(&output->link);
@ -138,18 +148,6 @@ static void lock_node_handle_destroy(struct wl_listener *listener, void *data) {
sway_session_lock_output_destroy(output);
}
static void lock_output_handle_commit(struct wl_listener *listener, void *data) {
struct wlr_output_event_commit *event = data;
struct sway_session_lock_output *output =
wl_container_of(listener, output, commit);
if (event->state->committed & (
WLR_OUTPUT_STATE_MODE |
WLR_OUTPUT_STATE_SCALE |
WLR_OUTPUT_STATE_TRANSFORM)) {
lock_output_reconfigure(output);
}
}
static struct sway_session_lock_output *session_lock_output_create(
struct sway_session_lock *lock, struct sway_output *output) {
struct sway_session_lock_output *lock_output = calloc(1, sizeof(*lock_output));
@ -186,9 +184,6 @@ static struct sway_session_lock_output *session_lock_output_create(
lock_output->destroy.notify = lock_node_handle_destroy;
wl_signal_add(&tree->node.events.destroy, &lock_output->destroy);
lock_output->commit.notify = lock_output_handle_commit;
wl_signal_add(&output->wlr_output->events.commit, &lock_output->commit);
lock_output_reconfigure(lock_output);
wl_list_insert(&lock->outputs, &lock_output->link);

View file

@ -159,8 +159,8 @@ void enable_debug_flag(const char *flag) {
debug.txn_wait = true;
} else if (strcmp(flag, "txn-timings") == 0) {
debug.txn_timings = true;
} else if (strncmp(flag, "txn-timeout=", 12) == 0) {
server.txn_timeout_ms = atoi(&flag[12]);
} else if (has_prefix(flag, "txn-timeout=")) {
server.txn_timeout_ms = atoi(&flag[strlen("txn-timeout=")]);
} else if (strcmp(flag, "legacy-wl-drm") == 0) {
debug.legacy_wl_drm = true;
} else {
@ -361,6 +361,7 @@ int main(int argc, char **argv) {
}
config->active = true;
force_modeset();
load_swaybars();
run_deferred_commands();
run_deferred_bindings();

View file

@ -18,6 +18,7 @@ sway_sources = files(
'desktop/idle_inhibit_v1.c',
'desktop/layer_shell.c',
'desktop/output.c',
'desktop/tearing.c',
'desktop/transaction.c',
'desktop/xdg_shell.c',
'desktop/launcher.c',
@ -41,6 +42,7 @@ sway_sources = files(
'config/seat.c',
'config/input.c',
'commands/allow_tearing.c',
'commands/assign.c',
'commands/bar.c',
'commands/bind.c',
@ -188,6 +190,7 @@ sway_sources = files(
'commands/input/xkb_variant.c',
'commands/output/adaptive_sync.c',
'commands/output/allow_tearing.c',
'commands/output/background.c',
'commands/output/disable.c',
'commands/output/dpms.c',

View file

@ -39,6 +39,9 @@ void *scene_descriptor_try_get(struct wlr_scene_node *node,
void scene_descriptor_destroy(struct wlr_scene_node *node,
enum sway_scene_descriptor_type type) {
struct scene_descriptor *desc = scene_node_get_descriptor(node, type);
if (!desc) {
return;
}
descriptor_destroy(desc);
}

View file

@ -9,6 +9,7 @@
#include <wlr/config.h>
#include <wlr/render/allocator.h>
#include <wlr/render/wlr_renderer.h>
#include <wlr/types/wlr_alpha_modifier_v1.h>
#include <wlr/types/wlr_compositor.h>
#include <wlr/types/wlr_content_type_v1.h>
#include <wlr/types/wlr_cursor_shape_v1.h>
@ -23,6 +24,7 @@
#include <wlr/types/wlr_idle_notify_v1.h>
#include <wlr/types/wlr_layer_shell_v1.h>
#include <wlr/types/wlr_linux_dmabuf_v1.h>
#include <wlr/types/wlr_linux_drm_syncobj_v1.h>
#include <wlr/types/wlr_output_management_v1.h>
#include <wlr/types/wlr_output_power_management_v1.h>
#include <wlr/types/wlr_pointer_constraints_v1.h>
@ -68,6 +70,7 @@
#define SWAY_XDG_SHELL_VERSION 5
#define SWAY_LAYER_SHELL_VERSION 4
#define SWAY_FOREIGN_TOPLEVEL_LIST_VERSION 1
#define SWAY_PRESENTATION_VERSION 2
bool allow_unsupported_gpu = false;
@ -203,8 +206,8 @@ static void handle_renderer_lost(struct wl_listener *listener, void *data) {
wlr_compositor_set_renderer(server->compositor, renderer);
for (int i = 0; i < root->outputs->length; ++i) {
struct sway_output *output = root->outputs->items[i];
struct sway_output *output;
wl_list_for_each(output, &root->all_outputs, link) {
wlr_output_init_render(output->wlr_output,
server->allocator, server->renderer);
}
@ -248,6 +251,12 @@ bool server_init(struct sway_server *server) {
wlr_drm_create(server->wl_display, server->renderer);
}
}
if (wlr_renderer_get_drm_fd(server->renderer) >= 0 &&
server->renderer->features.timeline &&
server->backend->features.timeline) {
wlr_linux_drm_syncobj_manager_v1_create(server->wl_display, 1,
wlr_renderer_get_drm_fd(server->renderer));
}
server->allocator = wlr_allocator_autocreate(server->backend,
server->renderer);
@ -266,15 +275,11 @@ bool server_init(struct sway_server *server) {
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);
wlr_scene_set_gamma_control_manager_v1(root->root_scene,
server->gamma_control_manager_v1);
server->new_output.notify = handle_new_output;
wl_signal_add(&server->backend->events.new_output, &server->new_output);
server->output_layout_change.notify = handle_output_layout_change;
wl_signal_add(&root->output_layout->events.change,
&server->output_layout_change);
server->xdg_output_manager_v1 =
wlr_xdg_output_manager_v1_create(server->wl_display, root->output_layout);
@ -323,7 +328,8 @@ bool server_init(struct sway_server *server) {
wl_signal_add(&server->pointer_constraints->events.new_constraint,
&server->pointer_constraint);
wlr_presentation_create(server->wl_display, server->backend);
wlr_presentation_create(server->wl_display, server->backend, SWAY_PRESENTATION_VERSION);
wlr_alpha_modifier_v1_create(server->wl_display);
server->output_manager_v1 =
wlr_output_manager_v1_create(server->wl_display);
@ -372,6 +378,13 @@ bool server_init(struct sway_server *server) {
wlr_content_type_manager_v1_create(server->wl_display, 1);
wlr_fractional_scale_manager_v1_create(server->wl_display, 1);
server->tearing_control_v1 =
wlr_tearing_control_manager_v1_create(server->wl_display, 1);
server->tearing_control_new_object.notify = handle_new_tearing_hint;
wl_signal_add(&server->tearing_control_v1->events.new_object,
&server->tearing_control_new_object);
wl_list_init(&server->tearing_controllers);
struct wlr_xdg_foreign_registry *foreign_registry =
wlr_xdg_foreign_registry_create(server->wl_display);
wlr_xdg_foreign_v1_create(server->wl_display, foreign_registry);

View file

@ -152,8 +152,9 @@ The following commands may only be used in the configuration file.
*input* <identifier> drag enabled|disabled
Enables or disables tap-and-drag for specified input device.
*input* <identifier> drag_lock enabled|disabled
Enables or disables drag lock for specified input device.
*input* <identifier> drag_lock enabled|disabled|enabled_sticky
Enables or disables drag lock for specified input device. The default is
_enabled_sticky_.
*input* <identifier> dwt enabled|disabled
Enables or disables disable-while-typing for the specified input device.

View file

@ -1174,7 +1174,8 @@ following properties will be included for devices that support them:
: Whether tap-and-drag is enabled. It can be _enabled_ or _disabled_
|- tap_drag_lock
: string
: Whether drag-lock is enabled. It can be _enabled_ or _disabled_
: Whether drag-lock is enabled. It can be _enabled_, _disabled_ or
_enabled_sticky_
|- accel_speed
: double
: The pointer-acceleration in use

View file

@ -154,7 +154,7 @@ must be separated by one space. For example:
This setting only has an effect on Wayland and DRM backends, as support for
presentation timestamps and predicted output refresh rate is required.
*output* <name> adaptive_sync on|off
*output* <name> adaptive_sync on|off|toggle
Enables or disables adaptive synchronization (often referred to as Variable
Refresh Rate, or by the vendor-specific names FreeSync/G-Sync).
@ -163,9 +163,9 @@ must be separated by one space. For example:
adaptive sync can improve latency, but can cause flickering on some
hardware.
*output* <name> render_bit_depth 8|10
Controls the color channel bit depth at which frames are rendered; the
default is currently 8 bits per channel.
*output* <name> render_bit_depth 6|8|10
Controls the maximum color channel bit depth at which frames are
rendered; the default is currently 8 bits per channel.
Setting higher values will not have an effect if hardware and software lack
support for such bit depths. Successfully increasing the render bit depth
@ -190,6 +190,26 @@ must be separated by one space. For example:
may have no effect or produce unexpected output when used together with future
HDR support features.
*output* <name> allow_tearing yes|no
Allows or disallows screen tearing as a result of immediate page flips,
and an immediate presentation mode from a client. The default is that no
screen tearing is allowed.
With immediate page flips, frames from the client are presented as soon
as possible instead of synchronizing with the monitor's vblank interval
(VSync).
It is recommended to set *max_render_time* to *off*. In that case a page flip
happens as soon as a client updates. Otherwise, tearing will only happen if
rendering takes longer than the configured milliseconds before the next
display refresh.
To adjust whether tearing is allowed for specific applications, see
*allow_tearing* in *sway*(5). Note that tearing will only be enabled
when it's allowed for both the output and the application.
This setting only has effect when a window is fullscreen on the output.
# SEE ALSO
*sway*(5) *sway-input*(5)

View file

@ -57,6 +57,7 @@ which you may only select one. *[...]* is used for optional arguments, and
This section only lists general commands. For input and output commands, refer
to *sway-input*(5) and *sway-output*(5).
## Config only commands
The following commands may only be used in the configuration file.
*bar* [<bar-id>] <bar-subcommands...>
@ -65,8 +66,8 @@ The following commands may only be used in the configuration file.
*default_orientation* horizontal|vertical|auto
Sets the default container layout for tiled containers.
*include* <path>
Includes another file from _path_. _path_ can be either a full path or a
*include* <paths...>
Include files from _paths_. _paths_ can include either a full path or a
path relative to the parent config, and expands shell syntax (see
*wordexp*(3) for details). The same include file can only be included once;
subsequent attempts will be ignored.
@ -98,15 +99,16 @@ The following commands may only be used in the configuration file.
machines, it may be desirable to have Xwayland started immediately by
using _force_ instead of _enable_.
## Runtime only commands
The following commands cannot be used directly in the configuration file.
They are expected to be used with *bindsym* or at runtime through *swaymsg*(1).
*border* none|normal|csd|pixel [<n>]
Set border style for focused window. _normal_ includes a border of
thickness _n_ and a title bar. _pixel_ is a border without title bar _n_
pixels thick. Default is _normal_ with border thickness 2. _csd_ is short
for client-side-decorations, which allows the client to draw its own
decorations.
pixels thick. The title bar always shows in stacking or tabbed layouts.
_csd_ is short for client-side-decorations, which allows the client to draw
its own decorations. Default is _normal_ with border thickness 2.
*border* toggle
Cycles through the available border styles.
@ -215,6 +217,20 @@ set|plus|minus|toggle <amount>
effect on the output the window is currently on. See *sway-output*(5) for
further details.
*allow_tearing* yes|no
Allows or disallows screen tearing as a result of immediate page flips
for a fullscreen application.
When this option is not set, the tearing hints provided by the application
determine whether tearing is allowed. When _yes_ is specified,
the application allows tearing regardless of the tearing hints.
When _no_ is specified, tearing will never be allowed on the application,
regardless of the tearing hints.
This setting only has an effect if tearing is allowed on the output through
the per-output *allow_tearing* setting. See *sway-output*(5)
for further details.
*move* left|right|up|down [<px> px]
Moves the focused container in the direction specified. The optional _px_
argument specifies how many pixels to move the container. If unspecified,
@ -371,6 +387,7 @@ set|plus|minus|toggle <amount>
The default format is "%title".
## Config or runtime commands
The following commands may be used either in the configuration file or at
runtime.

View file

@ -314,14 +314,9 @@ void arrange_output(struct sway_output *output) {
if (config->reloading) {
return;
}
struct wlr_box output_box;
wlr_output_layout_get_box(root->output_layout,
output->wlr_output, &output_box);
output->lx = output_box.x;
output->ly = output_box.y;
output->width = output_box.width;
output->height = output_box.height;
if (!output->wlr_output->enabled) {
return;
}
for (int i = 0; i < output->workspaces->length; ++i) {
struct sway_workspace *workspace = output->workspaces->items[i];
arrange_workspace(workspace);

View file

@ -22,6 +22,7 @@
#include "sway/tree/workspace.h"
#include "sway/xdg_decoration.h"
#include "list.h"
#include "pango.h"
#include "log.h"
#include "stringop.h"
@ -348,7 +349,7 @@ void container_arrange_title_bar(struct sway_container *con) {
h_padding = width - config->titlebar_h_padding - marks_buffer_width;
}
h_padding = MAX(h_padding, 0);
h_padding = MAX(h_padding, config->titlebar_h_padding);
int alloc_width = MIN((int)node->width,
width - h_padding - config->titlebar_h_padding);
@ -374,7 +375,7 @@ void container_arrange_title_bar(struct sway_container *con) {
h_padding = config->titlebar_h_padding;
}
h_padding = MAX(h_padding, 0);
h_padding = MAX(h_padding, config->titlebar_h_padding);
int alloc_width = MIN((int) node->width,
width - h_padding - config->titlebar_h_padding);
@ -499,6 +500,7 @@ void container_destroy(struct sway_container *con) {
}
free(con->title);
free(con->formatted_title);
free(con->title_format);
list_free(con->pending.children);
list_free(con->current.children);
@ -506,6 +508,8 @@ void container_destroy(struct sway_container *con) {
if (con->view && con->view->container == con) {
con->view->container = NULL;
wl_list_remove(&con->output_enter.link);
wl_list_remove(&con->output_leave.link);
wlr_scene_node_destroy(&con->output_handler->node);
if (con->view->destroying) {
view_destroy(con->view);
@ -645,6 +649,91 @@ bool container_has_ancestor(struct sway_container *descendant,
return false;
}
static char *escape_pango_markup(const char *buffer) {
size_t length = escape_markup_text(buffer, NULL);
char *escaped_title = calloc(length + 1, sizeof(char));
escape_markup_text(buffer, escaped_title);
return escaped_title;
}
static size_t append_prop(char *buffer, const char *value) {
if (!value) {
return 0;
}
// If using pango_markup in font, we need to escape all markup chars
// from values to make sure tags are not inserted by clients
if (config->pango_markup) {
char *escaped_value = escape_pango_markup(value);
lenient_strcat(buffer, escaped_value);
size_t len = strlen(escaped_value);
free(escaped_value);
return len;
} else {
lenient_strcat(buffer, value);
return strlen(value);
}
}
/**
* Calculate and return the length of the formatted title.
* If buffer is not NULL, also populate the buffer with the formatted title.
*/
size_t parse_title_format(struct sway_container *container, char *buffer) {
if (!container->title_format || strcmp(container->title_format, "%title") == 0) {
if (container->view) {
return append_prop(buffer, view_get_title(container->view));
} else {
return container_build_representation(container->pending.layout, container->pending.children, buffer);
}
}
size_t len = 0;
char *format = container->title_format;
char *next = strchr(format, '%');
while (next) {
// Copy everything up to the %
lenient_strncat(buffer, format, next - format);
len += next - format;
format = next;
if (has_prefix(next, "%title")) {
if (container->view) {
len += append_prop(buffer, view_get_title(container->view));
} else {
len += container_build_representation(container->pending.layout, container->pending.children, buffer);
}
format += strlen("%title");
} else if (container->view) {
if (has_prefix(next, "%app_id")) {
len += append_prop(buffer, view_get_app_id(container->view));
format += strlen("%app_id");
} else if (has_prefix(next, "%class")) {
len += append_prop(buffer, view_get_class(container->view));
format += strlen("%class");
} else if (has_prefix(next, "%instance")) {
len += append_prop(buffer, view_get_instance(container->view));
format += strlen("%instance");
} else if (has_prefix(next, "%shell")) {
len += append_prop(buffer, view_get_shell(container->view));
format += strlen("%shell");
} else {
lenient_strcat(buffer, "%");
++format;
++len;
}
} else {
lenient_strcat(buffer, "%");
++format;
++len;
}
next = strchr(format, '%');
}
lenient_strcat(buffer, format);
len += strlen(format);
return len;
}
/**
* Calculate and return the length of the tree representation.
* An example tree representation is: V[Terminal, Firefox]
@ -689,7 +778,7 @@ size_t container_build_representation(enum sway_container_layout layout,
len += strlen(identifier);
lenient_strcat(buffer, identifier);
} else {
len += 6;
len += strlen("(null)");
lenient_strcat(buffer, "(null)");
}
}
@ -700,16 +789,14 @@ size_t container_build_representation(enum sway_container_layout layout,
void container_update_representation(struct sway_container *con) {
if (!con->view) {
size_t len = container_build_representation(con->pending.layout,
con->pending.children, NULL);
size_t len = parse_title_format(con, NULL);
free(con->formatted_title);
con->formatted_title = calloc(len + 1, sizeof(char));
if (!sway_assert(con->formatted_title,
"Unable to allocate title string")) {
return;
}
container_build_representation(con->pending.layout, con->pending.children,
con->formatted_title);
parse_title_format(con, con->formatted_title);
if (con->title_bar.title_text) {
sway_text_node_set_text(con->title_bar.title_text, con->formatted_title);
@ -773,11 +860,11 @@ void floating_fix_coordinates(struct sway_container *con, struct wlr_box *old, s
// 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);
double rel_x = con->pending.x - old->x + (con->pending.width / 2);
double 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);
con->pending.x = new->x + (rel_x * new->width) / old->width - (con->pending.width / 2);
con->pending.y = new->y + (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);
}

View file

@ -180,12 +180,7 @@ void output_enable(struct sway_output *output) {
ws->layout = output_get_default_layout(output);
}
input_manager_configure_xcursor();
wl_signal_emit_mutable(&root->events.new_node, &output->node);
arrange_layers(output);
arrange_root();
}
static void evacuate_sticky(struct sway_workspace *old_ws,
@ -278,7 +273,6 @@ void output_destroy(struct sway_output *output) {
destroy_scene_layers(output);
list_free(output->workspaces);
list_free(output->current.workspaces);
wl_event_source_remove(output->repaint_timer);
wlr_color_transform_unref(output->color_transform);
free(output);
}
@ -300,13 +294,6 @@ void output_disable(struct sway_output *output) {
list_del(root->outputs, index);
output->enabled = false;
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_input_mappings();
}
void output_begin_destroy(struct sway_output *output) {

View file

@ -19,12 +19,6 @@
struct sway_root *root;
static void output_layout_handle_change(struct wl_listener *listener,
void *data) {
arrange_root();
transaction_commit_dirty();
}
struct sway_root *root_create(struct wl_display *wl_display) {
struct sway_root *root = calloc(1, sizeof(struct sway_root));
if (!root) {
@ -81,14 +75,10 @@ struct sway_root *root_create(struct wl_display *wl_display) {
root->non_desktop_outputs = create_list();
root->scratchpad = create_list();
root->output_layout_change.notify = output_layout_handle_change;
wl_signal_add(&root->output_layout->events.change,
&root->output_layout_change);
return root;
}
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);

View file

@ -34,7 +34,6 @@
#include "sway/tree/workspace.h"
#include "sway/config.h"
#include "sway/xdg_decoration.h"
#include "pango.h"
#include "stringop.h"
bool view_init(struct sway_view *view, enum sway_view_type type,
@ -58,6 +57,7 @@ bool view_init(struct sway_view *view, enum sway_view_type type,
view->executed_criteria = create_list();
view->allow_request_urgent = true;
view->shortcuts_inhibit = SHORTCUTS_INHIBIT_DEFAULT;
view->tearing_mode = TEARING_WINDOW_HINT;
wl_signal_init(&view->events.unmap);
return true;
}
@ -80,8 +80,6 @@ void view_destroy(struct sway_view *view) {
view_assign_ctx(view, NULL);
wlr_scene_node_destroy(&view->scene_tree->node);
free(view->title_format);
if (view->impl->destroy) {
view->impl->destroy(view);
} else {
@ -850,7 +848,7 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface,
#if WLR_HAS_XWAYLAND
struct wlr_xwayland_surface *xsurface;
if ((xsurface = wlr_xwayland_surface_try_from_wlr_surface(wlr_surface))) {
set_focus &= wlr_xwayland_icccm_input_model(xsurface) !=
set_focus &= wlr_xwayland_surface_icccm_input_model(xsurface) !=
WLR_ICCCM_INPUT_MODEL_NONE;
}
#endif
@ -990,77 +988,6 @@ struct sway_view *view_from_wlr_surface(struct wlr_surface *wlr_surface) {
return NULL;
}
static char *escape_pango_markup(const char *buffer) {
size_t length = escape_markup_text(buffer, NULL);
char *escaped_title = calloc(length + 1, sizeof(char));
escape_markup_text(buffer, escaped_title);
return escaped_title;
}
static size_t append_prop(char *buffer, const char *value) {
if (!value) {
return 0;
}
// If using pango_markup in font, we need to escape all markup chars
// from values to make sure tags are not inserted by clients
if (config->pango_markup) {
char *escaped_value = escape_pango_markup(value);
lenient_strcat(buffer, escaped_value);
size_t len = strlen(escaped_value);
free(escaped_value);
return len;
} else {
lenient_strcat(buffer, value);
return strlen(value);
}
}
/**
* Calculate and return the length of the formatted title.
* If buffer is not NULL, also populate the buffer with the formatted title.
*/
static size_t parse_title_format(struct sway_view *view, char *buffer) {
if (!view->title_format || strcmp(view->title_format, "%title") == 0) {
return append_prop(buffer, view_get_title(view));
}
size_t len = 0;
char *format = view->title_format;
char *next = strchr(format, '%');
while (next) {
// Copy everything up to the %
lenient_strncat(buffer, format, next - format);
len += next - format;
format = next;
if (strncmp(next, "%title", 6) == 0) {
len += append_prop(buffer, view_get_title(view));
format += 6;
} else if (strncmp(next, "%app_id", 7) == 0) {
len += append_prop(buffer, view_get_app_id(view));
format += 7;
} else if (strncmp(next, "%class", 6) == 0) {
len += append_prop(buffer, view_get_class(view));
format += 6;
} else if (strncmp(next, "%instance", 9) == 0) {
len += append_prop(buffer, view_get_instance(view));
format += 9;
} else if (strncmp(next, "%shell", 6) == 0) {
len += append_prop(buffer, view_get_shell(view));
format += 6;
} else {
lenient_strcat(buffer, "%");
++format;
++len;
}
next = strchr(format, '%');
}
lenient_strcat(buffer, format);
len += strlen(format);
return len;
}
void view_update_app_id(struct sway_view *view) {
const char *app_id = view_get_app_id(view);
@ -1089,7 +1016,7 @@ void view_update_title(struct sway_view *view, bool force) {
free(view->container->title);
free(view->container->formatted_title);
size_t len = parse_title_format(view, NULL);
size_t len = parse_title_format(view->container, NULL);
if (len) {
char *buffer = calloc(len + 1, sizeof(char));
@ -1097,7 +1024,7 @@ void view_update_title(struct sway_view *view, bool force) {
return;
}
parse_title_format(view, buffer);
parse_title_format(view->container, buffer);
view->container->formatted_title = buffer;
} else {
view->container->formatted_title = NULL;
@ -1260,6 +1187,19 @@ bool view_is_transient_for(struct sway_view *child,
child->impl->is_transient_for(child, ancestor);
}
bool view_can_tear(struct sway_view *view) {
switch (view->tearing_mode) {
case TEARING_OVERRIDE_FALSE:
return false;
case TEARING_OVERRIDE_TRUE:
return true;
case TEARING_WINDOW_HINT:
return view->tearing_hint ==
WP_TEARING_CONTROL_V1_PRESENTATION_HINT_ASYNC;
}
return false;
}
static void send_frame_done_iterator(struct wlr_scene_buffer *scene_buffer,
int x, int y, void *data) {
struct timespec *when = data;

View file

@ -246,7 +246,7 @@ static void workspace_name_from_binding(const struct sway_binding * binding,
}
// If the command is workspace number <name>, isolate the name
if (strncmp(_target, "number ", strlen("number ")) == 0) {
if (has_prefix(_target, "number ")) {
size_t length = strlen(_target) - strlen("number ") + 1;
char *temp = malloc(length);
strncpy(temp, _target + strlen("number "), length - 1);

View file

@ -508,7 +508,7 @@ void bar_run(struct swaybar *bar) {
}
#if HAVE_TRAY
if (bar->tray) {
loop_add_fd(bar->eventloop, bar->tray->fd, POLLIN, tray_in, bar->tray->bus);
loop_add_fd(bar->eventloop, bar->tray->fd, POLLIN, tray_in, bar);
}
#endif
while (bar->running) {

View file

@ -15,6 +15,7 @@
#include "list.h"
#include "log.h"
#include "loop.h"
#include "stringop.h"
#include "util.h"
void ipc_send_workspace_command(struct swaybar *bar, const char *ws) {
@ -45,8 +46,8 @@ void ipc_send_workspace_command(struct swaybar *bar, const char *ws) {
char *parse_font(const char *font) {
char *new_font = NULL;
if (strncmp("pango:", font, 6) == 0) {
font += 6;
if (has_prefix("pango:", font)) {
font += strlen("pango:");
}
new_font = strdup(font);
return new_font;
@ -518,8 +519,7 @@ static bool handle_barconfig_update(struct swaybar *bar, const char *payload,
#if HAVE_TRAY
if (oldcfg->tray_hidden && !newcfg->tray_hidden) {
bar->tray = create_tray(bar);
loop_add_fd(bar->eventloop, bar->tray->fd, POLLIN, tray_in,
bar->tray->bus);
loop_add_fd(bar->eventloop, bar->tray->fd, POLLIN, tray_in, bar);
} else if (bar->tray && newcfg->tray_hidden) {
loop_remove_fd(bar->eventloop, bar->tray->fd);
destroy_tray(bar->tray);

View file

@ -29,6 +29,7 @@ struct render_context {
cairo_font_options_t *textaa_sharp;
cairo_font_options_t *textaa_safe;
uint32_t background_color;
bool has_transparency;
};
static void choose_text_aa_mode(struct render_context *ctx, uint32_t fontcolor) {
@ -265,6 +266,7 @@ static uint32_t render_status_block(struct render_context *ctx,
uint32_t bg_color = block->urgent
? config->colors.urgent_workspace.background : block->background;
ctx->has_transparency |= (bg_color & 0xFF) != 0xFF;
if (bg_color) {
render_sharp_rectangle(cairo, bg_color, x_pos, y_pos,
block_width, render_height);
@ -291,11 +293,11 @@ static uint32_t render_status_block(struct render_context *ctx,
}
double offset = 0;
if (strncmp(block->align, "left", 4) == 0) {
if (has_prefix(block->align, "left")) {
offset = x_pos;
} else if (strncmp(block->align, "right", 5) == 0) {
} else if (has_prefix(block->align, "right")) {
offset = x_pos + width - text_width;
} else if (strncmp(block->align, "center", 6) == 0) {
} else if (has_prefix(block->align, "center")) {
offset = x_pos + (width - text_width) / 2;
}
double text_y = height / 2.0 - text_height / 2.0;
@ -574,6 +576,7 @@ static uint32_t render_binding_mode_indicator(struct render_context *ctx,
cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE);
cairo_set_source_u32(cairo, config->colors.binding_mode.background);
ctx->background_color = config->colors.binding_mode.background;
ctx->has_transparency |= (config->colors.binding_mode.background & 0xFF) != 0xFF;
cairo_rectangle(cairo, x, 0, width, height);
cairo_fill(cairo);
@ -653,6 +656,7 @@ static uint32_t render_workspace_button(struct render_context *ctx,
cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE);
cairo_set_source_u32(cairo, box_colors.background);
ctx->background_color = box_colors.background;
ctx->has_transparency |= (box_colors.background & 0xFF) != 0xFF;
cairo_rectangle(cairo, *x, 0, width, height);
cairo_fill(cairo);
@ -760,10 +764,12 @@ void render_frame(struct swaybar_output *output) {
background_color = output->bar->config->colors.background;
}
struct render_context ctx = { 0 };
ctx.output = output;
struct render_context ctx = {
.output = output,
// initial background color used for deciding the best way to antialias text
ctx.background_color = background_color;
.background_color = background_color,
.has_transparency = (background_color & 0xFF) != 0xFF,
};
cairo_surface_t *recorder = cairo_recording_surface_create(
CAIRO_CONTENT_COLOR_ALPHA, NULL);
@ -834,8 +840,7 @@ void render_frame(struct swaybar_output *output) {
wl_surface_damage(output->surface, 0, 0,
output->width, output->height);
uint32_t bg_alpha = background_color & 0xFF;
if (bg_alpha == 0xFF) {
if (!ctx.has_transparency) {
struct wl_region *region =
wl_compositor_create_region(output->bar->compositor);
wl_region_add(region, 0, 0, INT32_MAX, INT32_MAX);

View file

@ -15,6 +15,7 @@
#include "cairo_util.h"
#include "list.h"
#include "log.h"
#include "stringop.h"
#include "wlr-layer-shell-unstable-v1-client-protocol.h"
// TODO menu
@ -161,7 +162,7 @@ static int get_property_callback(sd_bus_message *msg, void *data,
}
if (strcmp(prop, "Status") == 0 || (sni->status && (sni->status[0] == 'N' ?
prop[0] == 'A' : strncmp(prop, "Icon", 4) == 0))) {
prop[0] == 'A' : has_prefix(prop, "Icon")))) {
set_sni_dirty(sni);
}
cleanup:
@ -364,7 +365,7 @@ static void handle_click(struct swaybar_sni *sni, int x, int y,
method = "ContextMenu";
}
if (strncmp(method, "Scroll", strlen("Scroll")) == 0) {
if (has_prefix(method, "Scroll")) {
char dir = method[strlen("Scroll")];
char *orientation = (dir == 'U' || dir == 'D') ? "vertical" : "horizontal";
int sign = (dir == 'U' || dir == 'L') ? -1 : 1;

View file

@ -1,4 +1,5 @@
#include <cairo.h>
#include <poll.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
@ -90,9 +91,16 @@ void destroy_tray(struct swaybar_tray *tray) {
}
void tray_in(int fd, short mask, void *data) {
sd_bus *bus = data;
struct swaybar *bar = data;
int ret;
while ((ret = sd_bus_process(bus, NULL)) > 0) {
if (mask & (POLLHUP | POLLERR)) {
sway_log(SWAY_ERROR, "D-Bus connection closed unexpectedly");
bar->running = false;
return;
}
while ((ret = sd_bus_process(bar->tray->bus, NULL)) > 0) {
// This space intentionally left blank
}
if (ret < 0) {

View file

@ -31,13 +31,15 @@ static int handle_lost_service(sd_bus_message *msg,
struct swaybar_watcher *watcher = data;
for (int idx = 0; idx < watcher->items->length; ++idx) {
char *id = watcher->items->items[idx];
int cmp_res = using_standard_protocol(watcher) ?
cmp_id(id, service) : strncmp(id, service, strlen(service));
if (cmp_res == 0) {
bool cmp_res = using_standard_protocol(watcher) ?
cmp_id(id, service) == 0 : has_prefix(id, service);
if (cmp_res) {
sway_log(SWAY_DEBUG, "Unregistering Status Notifier Item '%s'", id);
list_del(watcher->items, idx--);
sd_bus_emit_signal(watcher->bus, obj_path, watcher->interface,
"StatusNotifierItemUnregistered", "s", id);
sd_bus_emit_properties_changed(watcher->bus, obj_path, watcher->interface,
"RegisteredStatusNotifierItems", NULL);
free(id);
if (using_standard_protocol(watcher)) {
break;
@ -50,6 +52,10 @@ static int handle_lost_service(sd_bus_message *msg,
sway_log(SWAY_DEBUG, "Unregistering Status Notifier Host '%s'", service);
free(watcher->hosts->items[idx]);
list_del(watcher->hosts, idx);
if (watcher->hosts->length == 0) {
sd_bus_emit_properties_changed(watcher->bus, obj_path, watcher->interface,
"IsStatusNotifierHostRegistered", NULL);
}
}
}
@ -82,6 +88,8 @@ static int register_sni(sd_bus_message *msg, void *data, sd_bus_error *error) {
if (list_seq_find(watcher->items, cmp_id, id) == -1) {
sway_log(SWAY_DEBUG, "Registering Status Notifier Item '%s'", id);
list_add(watcher->items, id);
sd_bus_emit_properties_changed(watcher->bus, obj_path, watcher->interface,
"RegisteredStatusNotifierItems", NULL);
sd_bus_emit_signal(watcher->bus, obj_path, watcher->interface,
"StatusNotifierItemRegistered", "s", id);
} else {
@ -104,6 +112,10 @@ static int register_host(sd_bus_message *msg, void *data, sd_bus_error *error) {
if (list_seq_find(watcher->hosts, cmp_id, service) == -1) {
sway_log(SWAY_DEBUG, "Registering Status Notifier Host '%s'", service);
list_add(watcher->hosts, strdup(service));
if (watcher->hosts->length == 1) {
sd_bus_emit_properties_changed(watcher->bus, obj_path, watcher->interface,
"IsStatusNotifierHostRegistered", NULL);
}
sd_bus_emit_signal(watcher->bus, obj_path, watcher->interface,
"StatusNotifierHostRegistered", "");
} else {

View file

@ -193,7 +193,7 @@ static void pretty_print_output(json_object *o) {
json_object_object_get_ex(o, "current_workspace", &ws);
json_object_object_get_ex(o, "non_desktop", &non_desktop);
json_object *make, *model, *serial, *scale, *scale_filter, *subpixel,
*transform, *max_render_time, *adaptive_sync_status;
*transform, *max_render_time, *adaptive_sync_status, *allow_tearing;
json_object_object_get_ex(o, "make", &make);
json_object_object_get_ex(o, "model", &model);
json_object_object_get_ex(o, "serial", &serial);
@ -203,6 +203,7 @@ static void pretty_print_output(json_object *o) {
json_object_object_get_ex(o, "transform", &transform);
json_object_object_get_ex(o, "max_render_time", &max_render_time);
json_object_object_get_ex(o, "adaptive_sync_status", &adaptive_sync_status);
json_object_object_get_ex(o, "allow_tearing", &allow_tearing);
json_object *x, *y;
json_object_object_get_ex(rect, "x", &x);
json_object_object_get_ex(rect, "y", &y);
@ -256,6 +257,9 @@ static void pretty_print_output(json_object *o) {
printf(" Adaptive sync: %s\n",
json_object_get_string(adaptive_sync_status));
printf(" Allow tearing: %s\n",
json_object_get_boolean(allow_tearing) ? "yes" : "no");
} else {
printf(
"Output %s '%s %s %s' (disabled)\n",

View file

@ -324,7 +324,9 @@ static void output_scale(void *data, struct wl_output *output,
swaynag_output->scale = factor;
if (swaynag_output->swaynag->output == swaynag_output) {
swaynag_output->swaynag->scale = swaynag_output->scale;
if (!swaynag_output->swaynag->cursor_shape_manager) {
update_all_cursors(swaynag_output->swaynag);
}
render_frame(swaynag_output->swaynag);
}
}