aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--common/background-image.c8
-rw-r--r--common/cairo.c6
-rw-r--r--common/util.c12
-rw-r--r--config.in2
-rw-r--r--include/cairo.h12
-rw-r--r--include/stringop.h7
-rw-r--r--include/sway/commands.h1
-rw-r--r--include/sway/config.h25
-rw-r--r--include/sway/criteria.h2
-rw-r--r--include/sway/output.h2
-rw-r--r--include/sway/server.h6
-rw-r--r--include/sway/tree/container.h21
-rw-r--r--include/sway/tree/root.h2
-rw-r--r--include/sway/tree/view.h16
-rw-r--r--include/sway/tree/workspace.h4
-rw-r--r--include/util.h6
-rw-r--r--meson.build40
-rw-r--r--sway/commands.c61
-rw-r--r--sway/commands/bar/binding_mode_indicator.c12
-rw-r--r--sway/commands/bar/pango_markup.c13
-rw-r--r--sway/commands/bar/workspace_buttons.c12
-rw-r--r--sway/commands/bar/wrap_scroll.c14
-rw-r--r--sway/commands/border.c2
-rw-r--r--sway/commands/create_output.c4
-rw-r--r--sway/commands/floating.c14
-rw-r--r--sway/commands/focus_follows_mouse.c11
-rw-r--r--sway/commands/gaps.c151
-rw-r--r--sway/commands/input.c1
-rw-r--r--sway/commands/input/pointer_accel.c9
-rw-r--r--sway/commands/input/scroll_factor.c32
-rw-r--r--sway/commands/input/xkb_capslock.c10
-rw-r--r--sway/commands/input/xkb_numlock.c10
-rw-r--r--sway/commands/resize.c111
-rw-r--r--sway/commands/seat/fallback.c13
-rw-r--r--sway/commands/smart_gaps.c10
-rw-r--r--sway/commands/sticky.c17
-rw-r--r--sway/commands/swap.c25
-rw-r--r--sway/commands/workspace.c137
-rw-r--r--sway/commands/ws_auto_back_and_forth.c4
-rw-r--r--sway/config.c8
-rw-r--r--sway/config/bar.c1
-rw-r--r--sway/config/input.c4
-rw-r--r--sway/config/output.c1
-rw-r--r--sway/criteria.c27
-rw-r--r--sway/desktop/desktop.c4
-rw-r--r--sway/desktop/output.c39
-rw-r--r--sway/desktop/render.c57
-rw-r--r--sway/desktop/transaction.c50
-rw-r--r--sway/desktop/xdg_shell.c10
-rw-r--r--sway/desktop/xdg_shell_v6.c10
-rw-r--r--sway/desktop/xwayland.c18
-rw-r--r--sway/input/cursor.c52
-rw-r--r--sway/input/seat.c2
-rw-r--r--sway/ipc-json.c32
-rw-r--r--sway/meson.build1
-rw-r--r--sway/server.c6
-rw-r--r--sway/sway-input.5.scd10
-rw-r--r--sway/sway.5.scd54
-rw-r--r--sway/tree/container.c80
-rw-r--r--sway/tree/output.c7
-rw-r--r--sway/tree/root.c2
-rw-r--r--sway/tree/view.c61
-rw-r--r--sway/tree/workspace.c126
-rw-r--r--swaybar/config.c1
-rw-r--r--swaybar/status_line.c3
-rw-r--r--swayidle/main.c8
-rw-r--r--swayidle/meson.build24
-rw-r--r--swaymsg/main.c11
-rw-r--r--swaynag/swaynag.c1
69 files changed, 963 insertions, 592 deletions
diff --git a/common/background-image.c b/common/background-image.c
index 5ede55e3..72f39a79 100644
--- a/common/background-image.c
+++ b/common/background-image.c
@@ -24,7 +24,7 @@ enum background_mode parse_background_mode(const char *mode) {
24 24
25cairo_surface_t *load_background_image(const char *path) { 25cairo_surface_t *load_background_image(const char *path) {
26 cairo_surface_t *image; 26 cairo_surface_t *image;
27#ifdef HAVE_GDK_PIXBUF 27#if HAVE_GDK_PIXBUF
28 GError *err = NULL; 28 GError *err = NULL;
29 GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file(path, &err); 29 GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file(path, &err);
30 if (!pixbuf) { 30 if (!pixbuf) {
@@ -36,17 +36,17 @@ cairo_surface_t *load_background_image(const char *path) {
36 g_object_unref(pixbuf); 36 g_object_unref(pixbuf);
37#else 37#else
38 image = cairo_image_surface_create_from_png(path); 38 image = cairo_image_surface_create_from_png(path);
39#endif //HAVE_GDK_PIXBUF 39#endif // HAVE_GDK_PIXBUF
40 if (!image) { 40 if (!image) {
41 wlr_log(WLR_ERROR, "Failed to read background image."); 41 wlr_log(WLR_ERROR, "Failed to read background image.");
42 return NULL; 42 return NULL;
43 } 43 }
44 if (cairo_surface_status(image) != CAIRO_STATUS_SUCCESS) { 44 if (cairo_surface_status(image) != CAIRO_STATUS_SUCCESS) {
45 wlr_log(WLR_ERROR, "Failed to read background image: %s." 45 wlr_log(WLR_ERROR, "Failed to read background image: %s."
46#ifndef HAVE_GDK_PIXBUF 46#if !HAVE_GDK_PIXBUF
47 "\nSway was compiled without gdk_pixbuf support, so only" 47 "\nSway was compiled without gdk_pixbuf support, so only"
48 "\nPNG images can be loaded. This is the likely cause." 48 "\nPNG images can be loaded. This is the likely cause."
49#endif //HAVE_GDK_PIXBUF 49#endif // !HAVE_GDK_PIXBUF
50 , cairo_status_to_string(cairo_surface_status(image))); 50 , cairo_status_to_string(cairo_surface_status(image)));
51 return NULL; 51 return NULL;
52 } 52 }
diff --git a/common/cairo.c b/common/cairo.c
index e8231484..f2ad54c1 100644
--- a/common/cairo.c
+++ b/common/cairo.c
@@ -1,7 +1,7 @@
1#include <stdint.h> 1#include <stdint.h>
2#include <cairo/cairo.h> 2#include <cairo/cairo.h>
3#include "cairo.h" 3#include "cairo.h"
4#ifdef HAVE_GDK_PIXBUF 4#if HAVE_GDK_PIXBUF
5#include <gdk-pixbuf/gdk-pixbuf.h> 5#include <gdk-pixbuf/gdk-pixbuf.h>
6#endif 6#endif
7 7
@@ -46,7 +46,7 @@ cairo_surface_t *cairo_image_surface_scale(cairo_surface_t *image,
46 return new; 46 return new;
47} 47}
48 48
49#ifdef HAVE_GDK_PIXBUF 49#if HAVE_GDK_PIXBUF
50cairo_surface_t* gdk_cairo_image_surface_create_from_pixbuf(const GdkPixbuf *gdkbuf) { 50cairo_surface_t* gdk_cairo_image_surface_create_from_pixbuf(const GdkPixbuf *gdkbuf) {
51 int chan = gdk_pixbuf_get_n_channels(gdkbuf); 51 int chan = gdk_pixbuf_get_n_channels(gdkbuf);
52 if (chan < 3) { 52 if (chan < 3) {
@@ -140,4 +140,4 @@ cairo_surface_t* gdk_cairo_image_surface_create_from_pixbuf(const GdkPixbuf *gdk
140 cairo_surface_mark_dirty(cs); 140 cairo_surface_mark_dirty(cs);
141 return cs; 141 return cs;
142} 142}
143#endif //HAVE_GDK_PIXBUF 143#endif // HAVE_GDK_PIXBUF
diff --git a/common/util.c b/common/util.c
index abaca17f..40c64230 100644
--- a/common/util.c
+++ b/common/util.c
@@ -3,6 +3,7 @@
3#include <sys/types.h> 3#include <sys/types.h>
4#include <sys/stat.h> 4#include <sys/stat.h>
5#include <unistd.h> 5#include <unistd.h>
6#include <float.h>
6#include <math.h> 7#include <math.h>
7#include <stdint.h> 8#include <stdint.h>
8#include <stdio.h> 9#include <stdio.h>
@@ -141,6 +142,17 @@ bool parse_boolean(const char *boolean, bool current) {
141 return false; 142 return false;
142} 143}
143 144
145float parse_float(const char *value) {
146 errno = 0;
147 char *end;
148 float flt = strtof(value, &end);
149 if (*end || errno) {
150 wlr_log(WLR_DEBUG, "Invalid float value '%s', defaulting to NAN", value);
151 return NAN;
152 }
153 return flt;
154}
155
144enum wlr_direction opposite_direction(enum wlr_direction d) { 156enum wlr_direction opposite_direction(enum wlr_direction d) {
145 switch (d) { 157 switch (d) {
146 case WLR_DIRECTION_UP: 158 case WLR_DIRECTION_UP:
diff --git a/config.in b/config.in
index 72523816..9c751179 100644
--- a/config.in
+++ b/config.in
@@ -21,7 +21,7 @@ set $menu dmenu_path | dmenu | xargs swaymsg exec
21 21
22### Output configuration 22### Output configuration
23# 23#
24# Default wallpaper (more resolutions are available in __DATADIR__/backgrounds/sway/) 24# Default wallpaper (more resolutions are available in @datadir@/backgrounds/sway/)
25output * bg @datadir@/backgrounds/sway/Sway_Wallpaper_Blue_1920x1080.png fill 25output * bg @datadir@/backgrounds/sway/Sway_Wallpaper_Blue_1920x1080.png fill
26# 26#
27# Example configuration: 27# Example configuration:
diff --git a/include/cairo.h b/include/cairo.h
index 86530b60..f28c072f 100644
--- a/include/cairo.h
+++ b/include/cairo.h
@@ -1,8 +1,13 @@
1#ifndef _SWAY_CAIRO_H 1#ifndef _SWAY_CAIRO_H
2#define _SWAY_CAIRO_H 2#define _SWAY_CAIRO_H
3
4#include "config.h"
3#include <stdint.h> 5#include <stdint.h>
4#include <cairo/cairo.h> 6#include <cairo/cairo.h>
5#include <wlr/types/wlr_output.h> 7#include <wlr/types/wlr_output.h>
8#if HAVE_GDK_PIXBUF
9#include <gdk-pixbuf/gdk-pixbuf.h>
10#endif
6 11
7void cairo_set_source_u32(cairo_t *cairo, uint32_t color); 12void cairo_set_source_u32(cairo_t *cairo, uint32_t color);
8cairo_subpixel_order_t to_cairo_subpixel_order(enum wl_output_subpixel subpixel); 13cairo_subpixel_order_t to_cairo_subpixel_order(enum wl_output_subpixel subpixel);
@@ -10,12 +15,11 @@ cairo_subpixel_order_t to_cairo_subpixel_order(enum wl_output_subpixel subpixel)
10cairo_surface_t *cairo_image_surface_scale(cairo_surface_t *image, 15cairo_surface_t *cairo_image_surface_scale(cairo_surface_t *image,
11 int width, int height); 16 int width, int height);
12 17
13#include "config.h" 18#if HAVE_GDK_PIXBUF
14#ifdef HAVE_GDK_PIXBUF
15#include <gdk-pixbuf/gdk-pixbuf.h>
16 19
17cairo_surface_t* gdk_cairo_image_surface_create_from_pixbuf( 20cairo_surface_t* gdk_cairo_image_surface_create_from_pixbuf(
18 const GdkPixbuf *gdkbuf); 21 const GdkPixbuf *gdkbuf);
19#endif //WITH_GDK_PIXBUF 22
23#endif // HAVE_GDK_PIXBUF
20 24
21#endif 25#endif
diff --git a/include/stringop.h b/include/stringop.h
index 01bbdaa9..919e605c 100644
--- a/include/stringop.h
+++ b/include/stringop.h
@@ -1,12 +1,7 @@
1#ifndef _SWAY_STRINGOP_H 1#ifndef _SWAY_STRINGOP_H
2#define _SWAY_STRINGOP_H 2#define _SWAY_STRINGOP_H
3#include <stdlib.h>
4#include "list.h"
5 3
6#if !HAVE_DECL_SETENV 4#include "list.h"
7// Not sure why we need to provide this
8extern int setenv(const char *, const char *, int);
9#endif
10 5
11// array of whitespace characters to use for delims 6// array of whitespace characters to use for delims
12extern const char whitespace[]; 7extern const char whitespace[];
diff --git a/include/sway/commands.h b/include/sway/commands.h
index 45f9addd..b0339313 100644
--- a/include/sway/commands.h
+++ b/include/sway/commands.h
@@ -230,6 +230,7 @@ sway_cmd input_cmd_map_to_output;
230sway_cmd input_cmd_middle_emulation; 230sway_cmd input_cmd_middle_emulation;
231sway_cmd input_cmd_natural_scroll; 231sway_cmd input_cmd_natural_scroll;
232sway_cmd input_cmd_pointer_accel; 232sway_cmd input_cmd_pointer_accel;
233sway_cmd input_cmd_scroll_factor;
233sway_cmd input_cmd_repeat_delay; 234sway_cmd input_cmd_repeat_delay;
234sway_cmd input_cmd_repeat_rate; 235sway_cmd input_cmd_repeat_rate;
235sway_cmd input_cmd_scroll_button; 236sway_cmd input_cmd_scroll_button;
diff --git a/include/sway/config.h b/include/sway/config.h
index 5a355139..4927b8e0 100644
--- a/include/sway/config.h
+++ b/include/sway/config.h
@@ -100,6 +100,7 @@ struct input_config {
100 int middle_emulation; 100 int middle_emulation;
101 int natural_scroll; 101 int natural_scroll;
102 float pointer_accel; 102 float pointer_accel;
103 float scroll_factor;
103 int repeat_delay; 104 int repeat_delay;
104 int repeat_rate; 105 int repeat_rate;
105 int scroll_button; 106 int scroll_button;
@@ -168,14 +169,24 @@ struct output_config {
168}; 169};
169 170
170/** 171/**
172 * Stores size of gaps for each side
173 */
174struct side_gaps {
175 int top;
176 int right;
177 int bottom;
178 int left;
179};
180
181/**
171 * Stores configuration for a workspace, regardless of whether the workspace 182 * Stores configuration for a workspace, regardless of whether the workspace
172 * exists. 183 * exists.
173 */ 184 */
174struct workspace_config { 185struct workspace_config {
175 char *workspace; 186 char *workspace;
176 char *output; 187 list_t *outputs;
177 int gaps_inner; 188 int gaps_inner;
178 int gaps_outer; 189 struct side_gaps gaps_outer;
179}; 190};
180 191
181struct bar_config { 192struct bar_config {
@@ -328,6 +339,12 @@ struct ipc_policy {
328 uint32_t features; 339 uint32_t features;
329}; 340};
330 341
342enum focus_follows_mouse_mode {
343 FOLLOWS_NO,
344 FOLLOWS_YES,
345 FOLLOWS_ALWAYS
346};
347
331enum focus_wrapping_mode { 348enum focus_wrapping_mode {
332 WRAP_NO, 349 WRAP_NO,
333 WRAP_YES, 350 WRAP_YES,
@@ -379,7 +396,7 @@ struct sway_config {
379 enum sway_popup_during_fullscreen popup_during_fullscreen; 396 enum sway_popup_during_fullscreen popup_during_fullscreen;
380 397
381 // Flags 398 // Flags
382 bool focus_follows_mouse; 399 enum focus_follows_mouse_mode focus_follows_mouse;
383 enum mouse_warping_mode mouse_warping; 400 enum mouse_warping_mode mouse_warping;
384 enum focus_wrapping_mode focus_wrapping; 401 enum focus_wrapping_mode focus_wrapping;
385 bool active; 402 bool active;
@@ -393,7 +410,7 @@ struct sway_config {
393 410
394 bool smart_gaps; 411 bool smart_gaps;
395 int gaps_inner; 412 int gaps_inner;
396 int gaps_outer; 413 struct side_gaps gaps_outer;
397 414
398 list_t *config_chain; 415 list_t *config_chain;
399 const char *current_config_path; 416 const char *current_config_path;
diff --git a/include/sway/criteria.h b/include/sway/criteria.h
index 323ba01d..3eb583d5 100644
--- a/include/sway/criteria.h
+++ b/include/sway/criteria.h
@@ -25,7 +25,7 @@ struct criteria {
25 pcre *app_id; 25 pcre *app_id;
26 pcre *con_mark; 26 pcre *con_mark;
27 uint32_t con_id; // internal ID 27 uint32_t con_id; // internal ID
28#ifdef HAVE_XWAYLAND 28#if HAVE_XWAYLAND
29 pcre *class; 29 pcre *class;
30 uint32_t id; // X11 window ID 30 uint32_t id; // X11 window ID
31 pcre *instance; 31 pcre *instance;
diff --git a/include/sway/output.h b/include/sway/output.h
index 5efe1660..43c1ab96 100644
--- a/include/sway/output.h
+++ b/include/sway/output.h
@@ -119,7 +119,7 @@ void output_layer_for_each_surface(struct sway_output *output,
119 struct wl_list *layer_surfaces, sway_surface_iterator_func_t iterator, 119 struct wl_list *layer_surfaces, sway_surface_iterator_func_t iterator,
120 void *user_data); 120 void *user_data);
121 121
122#ifdef HAVE_XWAYLAND 122#if HAVE_XWAYLAND
123void output_unmanaged_for_each_surface(struct sway_output *output, 123void output_unmanaged_for_each_surface(struct sway_output *output,
124 struct wl_list *unmanaged, sway_surface_iterator_func_t iterator, 124 struct wl_list *unmanaged, sway_surface_iterator_func_t iterator,
125 void *user_data); 125 void *user_data);
diff --git a/include/sway/server.h b/include/sway/server.h
index 5fced224..a3233d66 100644
--- a/include/sway/server.h
+++ b/include/sway/server.h
@@ -14,7 +14,7 @@
14#include <wlr/types/wlr_xdg_shell.h> 14#include <wlr/types/wlr_xdg_shell.h>
15#include "config.h" 15#include "config.h"
16#include "list.h" 16#include "list.h"
17#ifdef HAVE_XWAYLAND 17#if HAVE_XWAYLAND
18#include "sway/xwayland.h" 18#include "sway/xwayland.h"
19#endif 19#endif
20 20
@@ -44,7 +44,7 @@ struct sway_server {
44 struct wlr_xdg_shell *xdg_shell; 44 struct wlr_xdg_shell *xdg_shell;
45 struct wl_listener xdg_shell_surface; 45 struct wl_listener xdg_shell_surface;
46 46
47#ifdef HAVE_XWAYLAND 47#if HAVE_XWAYLAND
48 struct sway_xwayland xwayland; 48 struct sway_xwayland xwayland;
49 struct wl_listener xwayland_surface; 49 struct wl_listener xwayland_surface;
50 struct wl_listener xwayland_ready; 50 struct wl_listener xwayland_ready;
@@ -80,7 +80,7 @@ void handle_idle_inhibitor_v1(struct wl_listener *listener, void *data);
80void handle_layer_shell_surface(struct wl_listener *listener, void *data); 80void handle_layer_shell_surface(struct wl_listener *listener, void *data);
81void handle_xdg_shell_v6_surface(struct wl_listener *listener, void *data); 81void handle_xdg_shell_v6_surface(struct wl_listener *listener, void *data);
82void handle_xdg_shell_surface(struct wl_listener *listener, void *data); 82void handle_xdg_shell_surface(struct wl_listener *listener, void *data);
83#ifdef HAVE_XWAYLAND 83#if HAVE_XWAYLAND
84void handle_xwayland_surface(struct wl_listener *listener, void *data); 84void handle_xwayland_surface(struct wl_listener *listener, void *data);
85#endif 85#endif
86void handle_server_decoration(struct wl_listener *listener, void *data); 86void handle_server_decoration(struct wl_listener *listener, void *data);
diff --git a/include/sway/tree/container.h b/include/sway/tree/container.h
index 4366a010..f907aad2 100644
--- a/include/sway/tree/container.h
+++ b/include/sway/tree/container.h
@@ -41,8 +41,8 @@ enum wlr_direction;
41struct sway_container_state { 41struct sway_container_state {
42 // Container properties 42 // Container properties
43 enum sway_container_layout layout; 43 enum sway_container_layout layout;
44 double con_x, con_y; 44 double x, y;
45 double con_width, con_height; 45 double width, height;
46 46
47 bool is_fullscreen; 47 bool is_fullscreen;
48 48
@@ -60,9 +60,8 @@ struct sway_container_state {
60 bool border_left; 60 bool border_left;
61 bool border_right; 61 bool border_right;
62 62
63 // View properties 63 double content_x, content_y;
64 double view_x, view_y; 64 double content_width, content_height;
65 double view_width, view_height;
66}; 65};
67 66
68struct sway_container { 67struct sway_container {
@@ -89,6 +88,9 @@ struct sway_container {
89 double saved_x, saved_y; 88 double saved_x, saved_y;
90 double saved_width, saved_height; 89 double saved_width, saved_height;
91 90
91 double content_x, content_y;
92 int content_width, content_height;
93
92 bool is_fullscreen; 94 bool is_fullscreen;
93 95
94 enum sway_container_border border; 96 enum sway_container_border border;
@@ -104,7 +106,12 @@ struct sway_container {
104 bool border_right; 106 bool border_right;
105 107
106 // The gaps currently applied to the container. 108 // The gaps currently applied to the container.
107 double current_gaps; 109 struct {
110 int top;
111 int right;
112 int bottom;
113 int left;
114 } current_gaps;
108 115
109 struct sway_workspace *workspace; // NULL when hidden in the scratchpad 116 struct sway_workspace *workspace; // NULL when hidden in the scratchpad
110 struct sway_container *parent; // NULL if container in root of workspace 117 struct sway_container *parent; // NULL if container in root of workspace
@@ -205,7 +212,7 @@ void container_init_floating(struct sway_container *container);
205 212
206void container_set_floating(struct sway_container *container, bool enable); 213void container_set_floating(struct sway_container *container, bool enable);
207 214
208void container_set_geometry_from_floating_view(struct sway_container *con); 215void container_set_geometry_from_content(struct sway_container *con);
209 216
210/** 217/**
211 * Determine if the given container is itself floating. 218 * Determine if the given container is itself floating.
diff --git a/include/sway/tree/root.h b/include/sway/tree/root.h
index a2d464f9..ceccc920 100644
--- a/include/sway/tree/root.h
+++ b/include/sway/tree/root.h
@@ -16,7 +16,7 @@ struct sway_root {
16 struct wlr_output_layout *output_layout; 16 struct wlr_output_layout *output_layout;
17 17
18 struct wl_listener output_layout_change; 18 struct wl_listener output_layout_change;
19#ifdef HAVE_XWAYLAND 19#if HAVE_XWAYLAND
20 struct wl_list xwayland_unmanaged; // sway_xwayland_unmanaged::link 20 struct wl_list xwayland_unmanaged; // sway_xwayland_unmanaged::link
21#endif 21#endif
22 struct wl_list drag_icons; // sway_drag_icon::link 22 struct wl_list drag_icons; // sway_drag_icon::link
diff --git a/include/sway/tree/view.h b/include/sway/tree/view.h
index 4a8c3cb1..4716c688 100644
--- a/include/sway/tree/view.h
+++ b/include/sway/tree/view.h
@@ -4,7 +4,7 @@
4#include <wlr/types/wlr_surface.h> 4#include <wlr/types/wlr_surface.h>
5#include <wlr/types/wlr_xdg_shell_v6.h> 5#include <wlr/types/wlr_xdg_shell_v6.h>
6#include "config.h" 6#include "config.h"
7#ifdef HAVE_XWAYLAND 7#if HAVE_XWAYLAND
8#include <wlr/xwayland.h> 8#include <wlr/xwayland.h>
9#endif 9#endif
10#include "sway/input/input-manager.h" 10#include "sway/input/input-manager.h"
@@ -16,7 +16,7 @@ struct sway_xdg_decoration;
16enum sway_view_type { 16enum sway_view_type {
17 SWAY_VIEW_XDG_SHELL_V6, 17 SWAY_VIEW_XDG_SHELL_V6,
18 SWAY_VIEW_XDG_SHELL, 18 SWAY_VIEW_XDG_SHELL,
19#ifdef HAVE_XWAYLAND 19#if HAVE_XWAYLAND
20 SWAY_VIEW_XWAYLAND, 20 SWAY_VIEW_XWAYLAND,
21#endif 21#endif
22}; 22};
@@ -28,7 +28,7 @@ enum sway_view_prop {
28 VIEW_PROP_INSTANCE, 28 VIEW_PROP_INSTANCE,
29 VIEW_PROP_WINDOW_TYPE, 29 VIEW_PROP_WINDOW_TYPE,
30 VIEW_PROP_WINDOW_ROLE, 30 VIEW_PROP_WINDOW_ROLE,
31#ifdef HAVE_XWAYLAND 31#if HAVE_XWAYLAND
32 VIEW_PROP_X11_WINDOW_ID, 32 VIEW_PROP_X11_WINDOW_ID,
33 VIEW_PROP_X11_PARENT_ID, 33 VIEW_PROP_X11_PARENT_ID,
34#endif 34#endif
@@ -67,10 +67,6 @@ struct sway_view {
67 67
68 pid_t pid; 68 pid_t pid;
69 69
70 // Geometry of the view itself (excludes borders) in layout coordinates
71 double x, y;
72 int width, height;
73
74 double saved_x, saved_y; 70 double saved_x, saved_y;
75 int saved_width, saved_height; 71 int saved_width, saved_height;
76 72
@@ -104,7 +100,7 @@ struct sway_view {
104 union { 100 union {
105 struct wlr_xdg_surface_v6 *wlr_xdg_surface_v6; 101 struct wlr_xdg_surface_v6 *wlr_xdg_surface_v6;
106 struct wlr_xdg_surface *wlr_xdg_surface; 102 struct wlr_xdg_surface *wlr_xdg_surface;
107#ifdef HAVE_XWAYLAND 103#if HAVE_XWAYLAND
108 struct wlr_xwayland_surface *wlr_xwayland_surface; 104 struct wlr_xwayland_surface *wlr_xwayland_surface;
109#endif 105#endif
110 struct wlr_wl_shell_surface *wlr_wl_shell_surface; 106 struct wlr_wl_shell_surface *wlr_wl_shell_surface;
@@ -148,7 +144,7 @@ struct sway_xdg_shell_view {
148 struct wl_listener unmap; 144 struct wl_listener unmap;
149 struct wl_listener destroy; 145 struct wl_listener destroy;
150}; 146};
151#ifdef HAVE_XWAYLAND 147#if HAVE_XWAYLAND
152struct sway_xwayland_view { 148struct sway_xwayland_view {
153 struct sway_view view; 149 struct sway_view view;
154 150
@@ -327,7 +323,7 @@ struct sway_view *view_from_wlr_xdg_surface(
327 struct wlr_xdg_surface *xdg_surface); 323 struct wlr_xdg_surface *xdg_surface);
328struct sway_view *view_from_wlr_xdg_surface_v6( 324struct sway_view *view_from_wlr_xdg_surface_v6(
329 struct wlr_xdg_surface_v6 *xdg_surface_v6); 325 struct wlr_xdg_surface_v6 *xdg_surface_v6);
330#ifdef HAVE_XWAYLAND 326#if HAVE_XWAYLAND
331struct sway_view *view_from_wlr_xwayland_surface( 327struct sway_view *view_from_wlr_xwayland_surface(
332 struct wlr_xwayland_surface *xsurface); 328 struct wlr_xwayland_surface *xsurface);
333#endif 329#endif
diff --git a/include/sway/tree/workspace.h b/include/sway/tree/workspace.h
index b5ae92f3..7abfbff1 100644
--- a/include/sway/tree/workspace.h
+++ b/include/sway/tree/workspace.h
@@ -32,9 +32,9 @@ struct sway_workspace {
32 enum sway_container_layout layout; 32 enum sway_container_layout layout;
33 enum sway_container_layout prev_split_layout; 33 enum sway_container_layout prev_split_layout;
34 34
35 int current_gaps; 35 struct side_gaps current_gaps;
36 int gaps_inner; 36 int gaps_inner;
37 int gaps_outer; 37 struct side_gaps gaps_outer;
38 38
39 struct sway_output *output; // NULL if no outputs are connected 39 struct sway_output *output; // NULL if no outputs are connected
40 list_t *floating; // struct sway_container 40 list_t *floating; // struct sway_container
diff --git a/include/util.h b/include/util.h
index f143d0c0..84318fe7 100644
--- a/include/util.h
+++ b/include/util.h
@@ -59,6 +59,12 @@ uint32_t parse_color(const char *color);
59 */ 59 */
60bool parse_boolean(const char *boolean, bool current); 60bool parse_boolean(const char *boolean, bool current);
61 61
62/**
63 * Given a string that represents a floating point value, return a float.
64 * Returns NAN on error.
65 */
66float parse_float(const char *value);
67
62enum wlr_direction opposite_direction(enum wlr_direction d); 68enum wlr_direction opposite_direction(enum wlr_direction d);
63 69
64#endif 70#endif
diff --git a/meson.build b/meson.build
index 6b23b4e3..bb60bc89 100644
--- a/meson.build
+++ b/meson.build
@@ -9,11 +9,17 @@ project(
9 ], 9 ],
10) 10)
11 11
12add_project_arguments('-Wno-unused-parameter', language: 'c') 12add_project_arguments(
13add_project_arguments('-Wno-unused-function', language: 'c') 13 [
14add_project_arguments('-Wno-unused-result', language: 'c') 14 '-DWL_HIDE_DEPRECATED',
15add_project_arguments('-DWL_HIDE_DEPRECATED', language: 'c') 15 '-DWLR_USE_UNSTABLE',
16add_project_arguments('-DWLR_USE_UNSTABLE', language: 'c') 16
17 '-Wno-unused-parameter',
18 '-Wno-unused-result',
19 '-Wundef',
20 ],
21 language: 'c',
22)
17 23
18cc = meson.get_compiler('c') 24cc = meson.get_compiler('c')
19 25
@@ -53,30 +59,18 @@ git = find_program('git', required: false)
53 59
54conf_data = configuration_data() 60conf_data = configuration_data()
55 61
62conf_data.set10('HAVE_XWAYLAND', get_option('enable-xwayland'))
56if get_option('enable-xwayland') 63if get_option('enable-xwayland')
57 conf_data.set('HAVE_XWAYLAND', true)
58 xcb = dependency('xcb') 64 xcb = dependency('xcb')
59else
60 conf_data.set('HAVE_XWAYLAND', false)
61endif
62
63if gdk_pixbuf.found()
64 conf_data.set('HAVE_GDK_PIXBUF', true)
65endif
66
67if systemd.found()
68 conf_data.set('SWAY_IDLE_HAS_SYSTEMD', true)
69 swayidle_deps += systemd
70endif 65endif
71 66
72if elogind.found() 67conf_data.set10('HAVE_GDK_PIXBUF', gdk_pixbuf.found())
73 conf_data.set('SWAY_IDLE_HAS_ELOGIND', true) 68conf_data.set10('HAVE_SYSTEMD', systemd.found())
74 swayidle_deps += elogind 69conf_data.set10('HAVE_ELOGIND', elogind.found())
75endif
76 70
77if not systemd.found() and not elogind.found() 71if not systemd.found() and not elogind.found()
78 warning('The sway binary must be setuid when compiled without (e)logind') 72 warning('The sway binary must be setuid when compiled without (e)logind')
79 warning('You must do this manually post-install: chmod a+s /path/to/sway') 73 warning('You must do this manually post-install: chmod a+s /path/to/sway')
80endif 74endif
81 75
82scdoc = find_program('scdoc', required: false) 76scdoc = find_program('scdoc', required: false)
diff --git a/sway/commands.c b/sway/commands.c
index 37c7169a..4b86c2fa 100644
--- a/sway/commands.c
+++ b/sway/commands.c
@@ -353,12 +353,14 @@ struct cmd_results *config_command(char *exec) {
353 struct cmd_results *results = NULL; 353 struct cmd_results *results = NULL;
354 int argc; 354 int argc;
355 char **argv = split_args(exec, &argc); 355 char **argv = split_args(exec, &argc);
356
357 // Check for empty lines
356 if (!argc) { 358 if (!argc) {
357 results = cmd_results_new(CMD_SUCCESS, NULL, NULL); 359 results = cmd_results_new(CMD_SUCCESS, NULL, NULL);
358 goto cleanup; 360 goto cleanup;
359 } 361 }
360 362
361 // Start block 363 // Check for the start of a block
362 if (argc > 1 && strcmp(argv[argc - 1], "{") == 0) { 364 if (argc > 1 && strcmp(argv[argc - 1], "{") == 0) {
363 char *block = join_args(argv, argc - 1); 365 char *block = join_args(argv, argc - 1);
364 results = cmd_results_new(CMD_BLOCK, block, NULL); 366 results = cmd_results_new(CMD_BLOCK, block, NULL);
@@ -366,22 +368,54 @@ struct cmd_results *config_command(char *exec) {
366 goto cleanup; 368 goto cleanup;
367 } 369 }
368 370
369 // Endblock 371 // Check for the end of a block
370 if (strcmp(argv[argc - 1], "}") == 0) { 372 if (strcmp(argv[argc - 1], "}") == 0) {
371 results = cmd_results_new(CMD_BLOCK_END, NULL, NULL); 373 results = cmd_results_new(CMD_BLOCK_END, NULL, NULL);
372 goto cleanup; 374 goto cleanup;
373 } 375 }
374 wlr_log(WLR_INFO, "handling config command '%s'", exec); 376
377 // Make sure the command is not stored in a variable
378 if (*argv[0] == '$') {
379 argv[0] = do_var_replacement(argv[0]);
380 char *temp = join_args(argv, argc);
381 free_argv(argc, argv);
382 argv = split_args(temp, &argc);
383 free(temp);
384 if (!argc) {
385 results = cmd_results_new(CMD_SUCCESS, NULL, NULL);
386 goto cleanup;
387 }
388 }
389
390 // Determine the command handler
391 wlr_log(WLR_INFO, "Config command: %s", exec);
375 struct cmd_handler *handler = find_handler(argv[0], NULL, 0); 392 struct cmd_handler *handler = find_handler(argv[0], NULL, 0);
376 if (!handler) { 393 if (!handler || !handler->handle) {
377 char *input = argv[0] ? argv[0] : "(empty)"; 394 char *input = argv[0] ? argv[0] : "(empty)";
378 results = cmd_results_new(CMD_INVALID, input, "Unknown/invalid command"); 395 char *error = handler
396 ? "This command is shimmed, but unimplemented"
397 : "Unknown/invalid command";
398 results = cmd_results_new(CMD_INVALID, input, error);
379 goto cleanup; 399 goto cleanup;
380 } 400 }
381 int i; 401
382 // Var replacement, for all but first argument of set 402 // Do variable replacement
383 // TODO commands 403 if (handler->handle == cmd_set && argc > 1 && *argv[1] == '$') {
384 for (i = handler->handle == cmd_set ? 2 : 1; i < argc; ++i) { 404 // Escape the variable name so it does not get replaced by one shorter
405 char *temp = calloc(1, strlen(argv[1]) + 2);
406 temp[0] = '$';
407 strcpy(&temp[1], argv[1]);
408 free(argv[1]);
409 argv[1] = temp;
410 }
411 char *command = do_var_replacement(join_args(argv, argc));
412 wlr_log(WLR_INFO, "After replacement: %s", command);
413 free_argv(argc, argv);
414 argv = split_args(command, &argc);
415 free(command);
416
417 // Strip quotes and unescape the string
418 for (int i = handler->handle == cmd_set ? 2 : 1; i < argc; ++i) {
385 if (handler->handle != cmd_exec && handler->handle != cmd_exec_always 419 if (handler->handle != cmd_exec && handler->handle != cmd_exec_always
386 && handler->handle != cmd_bindsym 420 && handler->handle != cmd_bindsym
387 && handler->handle != cmd_bindcode 421 && handler->handle != cmd_bindcode
@@ -389,14 +423,11 @@ struct cmd_results *config_command(char *exec) {
389 && (*argv[i] == '\"' || *argv[i] == '\'')) { 423 && (*argv[i] == '\"' || *argv[i] == '\'')) {
390 strip_quotes(argv[i]); 424 strip_quotes(argv[i]);
391 } 425 }
392 argv[i] = do_var_replacement(argv[i]);
393 unescape_string(argv[i]); 426 unescape_string(argv[i]);
394 } 427 }
395 if (handler->handle) { 428
396 results = handler->handle(argc-1, argv+1); 429 // Run command
397 } else { 430 results = handler->handle(argc - 1, argv + 1);
398 results = cmd_results_new(CMD_INVALID, argv[0], "This command is shimmed, but unimplemented");
399 }
400 431
401cleanup: 432cleanup:
402 free_argv(argc, argv); 433 free_argv(argc, argv);
diff --git a/sway/commands/bar/binding_mode_indicator.c b/sway/commands/bar/binding_mode_indicator.c
index f18b8d7c..b048b7b9 100644
--- a/sway/commands/bar/binding_mode_indicator.c
+++ b/sway/commands/bar/binding_mode_indicator.c
@@ -2,6 +2,7 @@
2#include <strings.h> 2#include <strings.h>
3#include "sway/commands.h" 3#include "sway/commands.h"
4#include "log.h" 4#include "log.h"
5#include "util.h"
5 6
6struct cmd_results *bar_cmd_binding_mode_indicator(int argc, char **argv) { 7struct cmd_results *bar_cmd_binding_mode_indicator(int argc, char **argv) {
7 struct cmd_results *error = NULL; 8 struct cmd_results *error = NULL;
@@ -13,17 +14,14 @@ struct cmd_results *bar_cmd_binding_mode_indicator(int argc, char **argv) {
13 return cmd_results_new(CMD_FAILURE, 14 return cmd_results_new(CMD_FAILURE,
14 "binding_mode_indicator", "No bar defined."); 15 "binding_mode_indicator", "No bar defined.");
15 } 16 }
16 if (strcasecmp("yes", argv[0]) == 0) { 17 config->current_bar->binding_mode_indicator =
17 config->current_bar->binding_mode_indicator = true; 18 parse_boolean(argv[0], config->current_bar->binding_mode_indicator);
19 if (config->current_bar->binding_mode_indicator) {
18 wlr_log(WLR_DEBUG, "Enabling binding mode indicator on bar: %s", 20 wlr_log(WLR_DEBUG, "Enabling binding mode indicator on bar: %s",
19 config->current_bar->id); 21 config->current_bar->id);
20 } else if (strcasecmp("no", argv[0]) == 0) { 22 } else {
21 config->current_bar->binding_mode_indicator = false;
22 wlr_log(WLR_DEBUG, "Disabling binding mode indicator on bar: %s", 23 wlr_log(WLR_DEBUG, "Disabling binding mode indicator on bar: %s",
23 config->current_bar->id); 24 config->current_bar->id);
24 } else {
25 return cmd_results_new(CMD_INVALID, "binding_mode_indicator",
26 "Invalid value %s", argv[0]);
27 } 25 }
28 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 26 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
29} 27}
diff --git a/sway/commands/bar/pango_markup.c b/sway/commands/bar/pango_markup.c
index 857571fb..d57cc45c 100644
--- a/sway/commands/bar/pango_markup.c
+++ b/sway/commands/bar/pango_markup.c
@@ -2,6 +2,7 @@
2#include <strings.h> 2#include <strings.h>
3#include "sway/commands.h" 3#include "sway/commands.h"
4#include "log.h" 4#include "log.h"
5#include "util.h"
5 6
6struct cmd_results *bar_cmd_pango_markup(int argc, char **argv) { 7struct cmd_results *bar_cmd_pango_markup(int argc, char **argv) {
7 struct cmd_results *error = NULL; 8 struct cmd_results *error = NULL;
@@ -11,18 +12,14 @@ struct cmd_results *bar_cmd_pango_markup(int argc, char **argv) {
11 if (!config->current_bar) { 12 if (!config->current_bar) {
12 return cmd_results_new(CMD_FAILURE, "pango_markup", "No bar defined."); 13 return cmd_results_new(CMD_FAILURE, "pango_markup", "No bar defined.");
13 } 14 }
14 if (strcasecmp("enabled", argv[0]) == 0) { 15 config->current_bar->pango_markup
15 config->current_bar->pango_markup = true; 16 = parse_boolean(argv[0], config->current_bar->pango_markup);
17 if (config->current_bar->pango_markup) {
16 wlr_log(WLR_DEBUG, "Enabling pango markup for bar: %s", 18 wlr_log(WLR_DEBUG, "Enabling pango markup for bar: %s",
17 config->current_bar->id); 19 config->current_bar->id);
18 } else if (strcasecmp("disabled", argv[0]) == 0) { 20 } else {
19 config->current_bar->pango_markup = false;
20 wlr_log(WLR_DEBUG, "Disabling pango markup for bar: %s", 21 wlr_log(WLR_DEBUG, "Disabling pango markup for bar: %s",
21 config->current_bar->id); 22 config->current_bar->id);
22 } else {
23 error = cmd_results_new(CMD_INVALID, "pango_markup",
24 "Invalid value %s", argv[0]);
25 return error;
26 } 23 }
27 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 24 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
28} 25}
diff --git a/sway/commands/bar/workspace_buttons.c b/sway/commands/bar/workspace_buttons.c
index a4079b2a..cd001e20 100644
--- a/sway/commands/bar/workspace_buttons.c
+++ b/sway/commands/bar/workspace_buttons.c
@@ -2,6 +2,7 @@
2#include <strings.h> 2#include <strings.h>
3#include "sway/commands.h" 3#include "sway/commands.h"
4#include "log.h" 4#include "log.h"
5#include "util.h"
5 6
6struct cmd_results *bar_cmd_workspace_buttons(int argc, char **argv) { 7struct cmd_results *bar_cmd_workspace_buttons(int argc, char **argv) {
7 struct cmd_results *error = NULL; 8 struct cmd_results *error = NULL;
@@ -12,17 +13,14 @@ struct cmd_results *bar_cmd_workspace_buttons(int argc, char **argv) {
12 return cmd_results_new(CMD_FAILURE, 13 return cmd_results_new(CMD_FAILURE,
13 "workspace_buttons", "No bar defined."); 14 "workspace_buttons", "No bar defined.");
14 } 15 }
15 if (strcasecmp("yes", argv[0]) == 0) { 16 config->current_bar->workspace_buttons =
16 config->current_bar->workspace_buttons = true; 17 parse_boolean(argv[0], config->current_bar->workspace_buttons);
18 if (config->current_bar->workspace_buttons) {
17 wlr_log(WLR_DEBUG, "Enabling workspace buttons on bar: %s", 19 wlr_log(WLR_DEBUG, "Enabling workspace buttons on bar: %s",
18 config->current_bar->id); 20 config->current_bar->id);
19 } else if (strcasecmp("no", argv[0]) == 0) { 21 } else {
20 config->current_bar->workspace_buttons = false;
21 wlr_log(WLR_DEBUG, "Disabling workspace buttons on bar: %s", 22 wlr_log(WLR_DEBUG, "Disabling workspace buttons on bar: %s",
22 config->current_bar->id); 23 config->current_bar->id);
23 } else {
24 return cmd_results_new(CMD_INVALID, "workspace_buttons",
25 "Invalid value %s", argv[0]);
26 } 24 }
27 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 25 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
28} 26}
diff --git a/sway/commands/bar/wrap_scroll.c b/sway/commands/bar/wrap_scroll.c
index 701de00a..04a4e6b8 100644
--- a/sway/commands/bar/wrap_scroll.c
+++ b/sway/commands/bar/wrap_scroll.c
@@ -2,6 +2,7 @@
2#include <strings.h> 2#include <strings.h>
3#include "sway/commands.h" 3#include "sway/commands.h"
4#include "log.h" 4#include "log.h"
5#include "util.h"
5 6
6struct cmd_results *bar_cmd_wrap_scroll(int argc, char **argv) { 7struct cmd_results *bar_cmd_wrap_scroll(int argc, char **argv) {
7 struct cmd_results *error = NULL; 8 struct cmd_results *error = NULL;
@@ -11,17 +12,14 @@ struct cmd_results *bar_cmd_wrap_scroll(int argc, char **argv) {
11 if (!config->current_bar) { 12 if (!config->current_bar) {
12 return cmd_results_new(CMD_FAILURE, "wrap_scroll", "No bar defined."); 13 return cmd_results_new(CMD_FAILURE, "wrap_scroll", "No bar defined.");
13 } 14 }
14 if (strcasecmp("yes", argv[0]) == 0) { 15 config->current_bar->wrap_scroll =
15 config->current_bar->wrap_scroll = true; 16 parse_boolean(argv[0], config->current_bar->wrap_scroll);
17 if (config->current_bar->wrap_scroll) {
16 wlr_log(WLR_DEBUG, "Enabling wrap scroll on bar: %s", 18 wlr_log(WLR_DEBUG, "Enabling wrap scroll on bar: %s",
17 config->current_bar->id); 19 config->current_bar->id);
18 } else if (strcasecmp("no", argv[0]) == 0) { 20 } else {
19 config->current_bar->wrap_scroll = false;
20 wlr_log(WLR_DEBUG, "Disabling wrap scroll on bar: %s", 21 wlr_log(WLR_DEBUG, "Disabling wrap scroll on bar: %s",
21 config->current_bar->id); 22 config->current_bar->id);
22 } else {
23 return cmd_results_new(CMD_INVALID,
24 "wrap_scroll", "Invalid value %s", argv[0]);
25 } 23 }
26 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 24 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
27} 25}
diff --git a/sway/commands/border.c b/sway/commands/border.c
index b6eab550..d51741d2 100644
--- a/sway/commands/border.c
+++ b/sway/commands/border.c
@@ -93,7 +93,7 @@ struct cmd_results *cmd_border(int argc, char **argv) {
93 } 93 }
94 94
95 if (container_is_floating(container)) { 95 if (container_is_floating(container)) {
96 container_set_geometry_from_floating_view(container); 96 container_set_geometry_from_content(container);
97 } 97 }
98 98
99 arrange_container(container); 99 arrange_container(container);
diff --git a/sway/commands/create_output.c b/sway/commands/create_output.c
index 1c2464ea..3f870acb 100644
--- a/sway/commands/create_output.c
+++ b/sway/commands/create_output.c
@@ -1,7 +1,7 @@
1#include <wlr/config.h> 1#include <wlr/config.h>
2#include <wlr/backend/multi.h> 2#include <wlr/backend/multi.h>
3#include <wlr/backend/wayland.h> 3#include <wlr/backend/wayland.h>
4#ifdef WLR_HAS_X11_BACKEND 4#if WLR_HAS_X11_BACKEND
5#include <wlr/backend/x11.h> 5#include <wlr/backend/x11.h>
6#endif 6#endif
7#include "sway/commands.h" 7#include "sway/commands.h"
@@ -18,7 +18,7 @@ static void create_output(struct wlr_backend *backend, void *data) {
18 wlr_wl_output_create(backend); 18 wlr_wl_output_create(backend);
19 *done = true; 19 *done = true;
20 } 20 }
21#ifdef WLR_HAS_X11_BACKEND 21#if WLR_HAS_X11_BACKEND
22 else if (wlr_backend_is_x11(backend)) { 22 else if (wlr_backend_is_x11(backend)) {
23 wlr_x11_output_create(backend); 23 wlr_x11_output_create(backend);
24 *done = true; 24 *done = true;
diff --git a/sway/commands/floating.c b/sway/commands/floating.c
index 81bb86f8..4b82921c 100644
--- a/sway/commands/floating.c
+++ b/sway/commands/floating.c
@@ -9,6 +9,7 @@
9#include "sway/tree/view.h" 9#include "sway/tree/view.h"
10#include "sway/tree/workspace.h" 10#include "sway/tree/workspace.h"
11#include "list.h" 11#include "list.h"
12#include "util.h"
12 13
13struct cmd_results *cmd_floating(int argc, char **argv) { 14struct cmd_results *cmd_floating(int argc, char **argv) {
14 struct cmd_results *error = NULL; 15 struct cmd_results *error = NULL;
@@ -40,17 +41,8 @@ struct cmd_results *cmd_floating(int argc, char **argv) {
40 } 41 }
41 } 42 }
42 43
43 bool wants_floating; 44 bool wants_floating =
44 if (strcasecmp(argv[0], "enable") == 0) { 45 parse_boolean(argv[0], container_is_floating(container));
45 wants_floating = true;
46 } else if (strcasecmp(argv[0], "disable") == 0) {
47 wants_floating = false;
48 } else if (strcasecmp(argv[0], "toggle") == 0) {
49 wants_floating = !container_is_floating(container);
50 } else {
51 return cmd_results_new(CMD_FAILURE, "floating",
52 "Expected 'floating <enable|disable|toggle>'");
53 }
54 46
55 container_set_floating(container, wants_floating); 47 container_set_floating(container, wants_floating);
56 48
diff --git a/sway/commands/focus_follows_mouse.c b/sway/commands/focus_follows_mouse.c
index 0b0e334c..d0d2cb8a 100644
--- a/sway/commands/focus_follows_mouse.c
+++ b/sway/commands/focus_follows_mouse.c
@@ -7,8 +7,15 @@ struct cmd_results *cmd_focus_follows_mouse(int argc, char **argv) {
7 struct cmd_results *error = NULL; 7 struct cmd_results *error = NULL;
8 if ((error = checkarg(argc, "focus_follows_mouse", EXPECTED_EQUAL_TO, 1))) { 8 if ((error = checkarg(argc, "focus_follows_mouse", EXPECTED_EQUAL_TO, 1))) {
9 return error; 9 return error;
10 } else if(strcmp(argv[0], "no") == 0) {
11 config->focus_follows_mouse = FOLLOWS_NO;
12 } else if(strcmp(argv[0], "yes") == 0) {
13 config->focus_follows_mouse = FOLLOWS_YES;
14 } else if(strcmp(argv[0], "always") == 0) {
15 config->focus_follows_mouse = FOLLOWS_ALWAYS;
16 } else {
17 return cmd_results_new(CMD_FAILURE, "focus_follows_mouse",
18 "Expected 'focus_follows_mouse no|yes|always'");
10 } 19 }
11 config->focus_follows_mouse =
12 parse_boolean(argv[0], config->focus_follows_mouse);
13 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 20 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
14} 21}
diff --git a/sway/commands/gaps.c b/sway/commands/gaps.c
index 3f0ef155..faaeab37 100644
--- a/sway/commands/gaps.c
+++ b/sway/commands/gaps.c
@@ -16,73 +16,128 @@ enum gaps_op {
16 16
17struct gaps_data { 17struct gaps_data {
18 bool inner; 18 bool inner;
19 struct {
20 bool top;
21 bool right;
22 bool bottom;
23 bool left;
24 } outer;
19 enum gaps_op operation; 25 enum gaps_op operation;
20 int amount; 26 int amount;
21}; 27};
22 28
23// gaps inner|outer <px> 29// Prevent negative outer gaps from moving windows out of the workspace.
30static void prevent_invalid_outer_gaps(void) {
31 if (config->gaps_outer.top < -config->gaps_inner) {
32 config->gaps_outer.top = -config->gaps_inner;
33 }
34 if (config->gaps_outer.right < -config->gaps_inner) {
35 config->gaps_outer.right = -config->gaps_inner;
36 }
37 if (config->gaps_outer.bottom < -config->gaps_inner) {
38 config->gaps_outer.bottom = -config->gaps_inner;
39 }
40 if (config->gaps_outer.left < -config->gaps_inner) {
41 config->gaps_outer.left = -config->gaps_inner;
42 }
43}
44
45// gaps inner|outer|horizontal|vertical|top|right|bottom|left <px>
46static const char *expected_defaults =
47 "'gaps inner|outer|horizontal|vertical|top|right|bottom|left <px>'";
24static struct cmd_results *gaps_set_defaults(int argc, char **argv) { 48static struct cmd_results *gaps_set_defaults(int argc, char **argv) {
25 struct cmd_results *error = checkarg(argc, "gaps", EXPECTED_EQUAL_TO, 2); 49 struct cmd_results *error = checkarg(argc, "gaps", EXPECTED_EQUAL_TO, 2);
26 if (error) { 50 if (error) {
27 return error; 51 return error;
28 } 52 }
29 53
30 bool inner;
31 if (strcasecmp(argv[0], "inner") == 0) {
32 inner = true;
33 } else if (strcasecmp(argv[0], "outer") == 0) {
34 inner = false;
35 } else {
36 return cmd_results_new(CMD_INVALID, "gaps",
37 "Expected 'gaps inner|outer <px>'");
38 }
39
40 char *end; 54 char *end;
41 int amount = strtol(argv[1], &end, 10); 55 int amount = strtol(argv[1], &end, 10);
42 if (strlen(end) && strcasecmp(end, "px") != 0) { 56 if (strlen(end) && strcasecmp(end, "px") != 0) {
43 return cmd_results_new(CMD_INVALID, "gaps", 57 return cmd_results_new(CMD_INVALID, "gaps",
44 "Expected 'gaps inner|outer <px>'"); 58 "Expected %s", expected_defaults);
45 } 59 }
46 if (inner) { 60
61 bool valid = false;
62 if (!strcasecmp(argv[0], "inner")) {
63 valid = true;
47 config->gaps_inner = (amount >= 0) ? amount : 0; 64 config->gaps_inner = (amount >= 0) ? amount : 0;
48 } else { 65 } else {
49 config->gaps_outer = amount; 66 if (!strcasecmp(argv[0], "outer") || !strcasecmp(argv[0], "vertical")
50 } 67 || !strcasecmp(argv[0], "top")) {
51 68 valid = true;
52 // Prevent negative outer gaps from moving windows out of the workspace. 69 config->gaps_outer.top = amount;
53 if (config->gaps_outer < -config->gaps_inner) { 70 }
54 config->gaps_outer = -config->gaps_inner; 71 if (!strcasecmp(argv[0], "outer") || !strcasecmp(argv[0], "horizontal")
72 || !strcasecmp(argv[0], "right")) {
73 valid = true;
74 config->gaps_outer.right = amount;
75 }
76 if (!strcasecmp(argv[0], "outer") || !strcasecmp(argv[0], "vertical")
77 || !strcasecmp(argv[0], "bottom")) {
78 valid = true;
79 config->gaps_outer.bottom = amount;
80 }
81 if (!strcasecmp(argv[0], "outer") || !strcasecmp(argv[0], "horizontal")
82 || !strcasecmp(argv[0], "left")) {
83 valid = true;
84 config->gaps_outer.left = amount;
85 }
86 }
87 if (!valid) {
88 return cmd_results_new(CMD_INVALID, "gaps",
89 "Expected %s", expected_defaults);
55 } 90 }
56 91
92 prevent_invalid_outer_gaps();
57 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 93 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
58} 94}
59 95
60static void configure_gaps(struct sway_workspace *ws, void *_data) { 96static void apply_gaps_op(int *prop, enum gaps_op op, int amount) {
61 struct gaps_data *data = _data; 97 switch (op) {
62 int *prop = data->inner ? &ws->gaps_inner : &ws->gaps_outer;
63
64 switch (data->operation) {
65 case GAPS_OP_SET: 98 case GAPS_OP_SET:
66 *prop = data->amount; 99 *prop = amount;
67 break; 100 break;
68 case GAPS_OP_ADD: 101 case GAPS_OP_ADD:
69 *prop += data->amount; 102 *prop += amount;
70 break; 103 break;
71 case GAPS_OP_SUBTRACT: 104 case GAPS_OP_SUBTRACT:
72 *prop -= data->amount; 105 *prop -= amount;
73 break; 106 break;
74 } 107 }
108}
109
110static void configure_gaps(struct sway_workspace *ws, void *_data) {
111 // Apply operation to gaps
112 struct gaps_data *data = _data;
113 if (data->inner) {
114 apply_gaps_op(&ws->gaps_inner, data->operation, data->amount);
115 }
116 if (data->outer.top) {
117 apply_gaps_op(&(ws->gaps_outer.top), data->operation, data->amount);
118 }
119 if (data->outer.right) {
120 apply_gaps_op(&(ws->gaps_outer.right), data->operation, data->amount);
121 }
122 if (data->outer.bottom) {
123 apply_gaps_op(&(ws->gaps_outer.bottom), data->operation, data->amount);
124 }
125 if (data->outer.left) {
126 apply_gaps_op(&(ws->gaps_outer.left), data->operation, data->amount);
127 }
128
75 // Prevent invalid gaps configurations. 129 // Prevent invalid gaps configurations.
76 if (ws->gaps_inner < 0) { 130 if (ws->gaps_inner < 0) {
77 ws->gaps_inner = 0; 131 ws->gaps_inner = 0;
78 } 132 }
79 if (ws->gaps_outer < -ws->gaps_inner) { 133 prevent_invalid_outer_gaps();
80 ws->gaps_outer = -ws->gaps_inner;
81 }
82 arrange_workspace(ws); 134 arrange_workspace(ws);
83} 135}
84 136
85// gaps inner|outer current|all set|plus|minus <px> 137// gaps inner|outer|horizontal|vertical|top|right|bottom|left current|all
138// set|plus|minus <px>
139static const char *expected_runtime = "'gaps inner|outer|horizontal|vertical|"
140 "top|right|bottom|left current|all set|plus|minus <px>'";
86static struct cmd_results *gaps_set_runtime(int argc, char **argv) { 141static struct cmd_results *gaps_set_runtime(int argc, char **argv) {
87 struct cmd_results *error = checkarg(argc, "gaps", EXPECTED_EQUAL_TO, 4); 142 struct cmd_results *error = checkarg(argc, "gaps", EXPECTED_EQUAL_TO, 4);
88 if (error) { 143 if (error) {
@@ -93,15 +148,24 @@ static struct cmd_results *gaps_set_runtime(int argc, char **argv) {
93 "Can't run this command while there's no outputs connected."); 148 "Can't run this command while there's no outputs connected.");
94 } 149 }
95 150
96 struct gaps_data data; 151 struct gaps_data data = {0};
97 152
98 if (strcasecmp(argv[0], "inner") == 0) { 153 if (strcasecmp(argv[0], "inner") == 0) {
99 data.inner = true; 154 data.inner = true;
100 } else if (strcasecmp(argv[0], "outer") == 0) {
101 data.inner = false;
102 } else { 155 } else {
156 data.outer.top = !strcasecmp(argv[0], "outer") ||
157 !strcasecmp(argv[0], "vertical") || !strcasecmp(argv[0], "top");
158 data.outer.right = !strcasecmp(argv[0], "outer") ||
159 !strcasecmp(argv[0], "horizontal") || !strcasecmp(argv[0], "right");
160 data.outer.bottom = !strcasecmp(argv[0], "outer") ||
161 !strcasecmp(argv[0], "vertical") || !strcasecmp(argv[0], "bottom");
162 data.outer.left = !strcasecmp(argv[0], "outer") ||
163 !strcasecmp(argv[0], "horizontal") || !strcasecmp(argv[0], "left");
164 }
165 if (!data.inner && !data.outer.top && !data.outer.right &&
166 !data.outer.bottom && !data.outer.left) {
103 return cmd_results_new(CMD_INVALID, "gaps", 167 return cmd_results_new(CMD_INVALID, "gaps",
104 "Expected 'gaps inner|outer current|all set|plus|minus <px>'"); 168 "Expected %s", expected_runtime);
105 } 169 }
106 170
107 bool all; 171 bool all;
@@ -111,7 +175,7 @@ static struct cmd_results *gaps_set_runtime(int argc, char **argv) {
111 all = true; 175 all = true;
112 } else { 176 } else {
113 return cmd_results_new(CMD_INVALID, "gaps", 177 return cmd_results_new(CMD_INVALID, "gaps",
114 "Expected 'gaps inner|outer current|all set|plus|minus <px>'"); 178 "Expected %s", expected_runtime);
115 } 179 }
116 180
117 if (strcasecmp(argv[2], "set") == 0) { 181 if (strcasecmp(argv[2], "set") == 0) {
@@ -122,14 +186,14 @@ static struct cmd_results *gaps_set_runtime(int argc, char **argv) {
122 data.operation = GAPS_OP_SUBTRACT; 186 data.operation = GAPS_OP_SUBTRACT;
123 } else { 187 } else {
124 return cmd_results_new(CMD_INVALID, "gaps", 188 return cmd_results_new(CMD_INVALID, "gaps",
125 "Expected 'gaps inner|outer current|all set|plus|minus <px>'"); 189 "Expected %s", expected_runtime);
126 } 190 }
127 191
128 char *end; 192 char *end;
129 data.amount = strtol(argv[3], &end, 10); 193 data.amount = strtol(argv[3], &end, 10);
130 if (strlen(end) && strcasecmp(end, "px") != 0) { 194 if (strlen(end) && strcasecmp(end, "px") != 0) {
131 return cmd_results_new(CMD_INVALID, "gaps", 195 return cmd_results_new(CMD_INVALID, "gaps",
132 "Expected 'gaps inner|outer current|all set|plus|minus <px>'"); 196 "Expected %s", expected_runtime);
133 } 197 }
134 198
135 if (all) { 199 if (all) {
@@ -141,8 +205,10 @@ static struct cmd_results *gaps_set_runtime(int argc, char **argv) {
141 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 205 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
142} 206}
143 207
144// gaps inner|outer <px> - sets defaults for workspaces 208// gaps inner|outer|<dir>|<side> <px> - sets defaults for workspaces
145// gaps inner|outer current|all set|plus|minus <px> - runtime only 209// gaps inner|outer|<dir>|<side> current|all set|plus|minus <px> - runtime only
210// <dir> = horizontal|vertical
211// <side> = top|right|bottom|left
146struct cmd_results *cmd_gaps(int argc, char **argv) { 212struct cmd_results *cmd_gaps(int argc, char **argv) {
147 struct cmd_results *error = checkarg(argc, "gaps", EXPECTED_AT_LEAST, 2); 213 struct cmd_results *error = checkarg(argc, "gaps", EXPECTED_AT_LEAST, 2);
148 if (error) { 214 if (error) {
@@ -159,9 +225,8 @@ struct cmd_results *cmd_gaps(int argc, char **argv) {
159 } 225 }
160 if (config_loading) { 226 if (config_loading) {
161 return cmd_results_new(CMD_INVALID, "gaps", 227 return cmd_results_new(CMD_INVALID, "gaps",
162 "Expected 'gaps inner|outer <px>'"); 228 "Expected %s", expected_defaults);
163 } 229 }
164 return cmd_results_new(CMD_INVALID, "gaps", 230 return cmd_results_new(CMD_INVALID, "gaps",
165 "Expected 'gaps inner|outer <px>' or " 231 "Expected %s or %s", expected_runtime, expected_defaults);
166 "'gaps inner|outer current|all set|plus|minus <px>'");
167} 232}
diff --git a/sway/commands/input.c b/sway/commands/input.c
index c50926a8..b5765c38 100644
--- a/sway/commands/input.c
+++ b/sway/commands/input.c
@@ -22,6 +22,7 @@ static struct cmd_handler input_handlers[] = {
22 { "repeat_delay", input_cmd_repeat_delay }, 22 { "repeat_delay", input_cmd_repeat_delay },
23 { "repeat_rate", input_cmd_repeat_rate }, 23 { "repeat_rate", input_cmd_repeat_rate },
24 { "scroll_button", input_cmd_scroll_button }, 24 { "scroll_button", input_cmd_scroll_button },
25 { "scroll_factor", input_cmd_scroll_factor },
25 { "scroll_method", input_cmd_scroll_method }, 26 { "scroll_method", input_cmd_scroll_method },
26 { "tap", input_cmd_tap }, 27 { "tap", input_cmd_tap },
27 { "tap_button_map", input_cmd_tap_button_map }, 28 { "tap_button_map", input_cmd_tap_button_map },
diff --git a/sway/commands/input/pointer_accel.c b/sway/commands/input/pointer_accel.c
index df487b1c..efd81ee6 100644
--- a/sway/commands/input/pointer_accel.c
+++ b/sway/commands/input/pointer_accel.c
@@ -1,8 +1,10 @@
1#include <math.h>
1#include <stdlib.h> 2#include <stdlib.h>
2#include <string.h> 3#include <string.h>
3#include "sway/config.h" 4#include "sway/config.h"
4#include "sway/commands.h" 5#include "sway/commands.h"
5#include "sway/input/input-manager.h" 6#include "sway/input/input-manager.h"
7#include "util.h"
6 8
7struct cmd_results *input_cmd_pointer_accel(int argc, char **argv) { 9struct cmd_results *input_cmd_pointer_accel(int argc, char **argv) {
8 struct cmd_results *error = NULL; 10 struct cmd_results *error = NULL;
@@ -15,8 +17,11 @@ struct cmd_results *input_cmd_pointer_accel(int argc, char **argv) {
15 "pointer_accel", "No input device defined."); 17 "pointer_accel", "No input device defined.");
16 } 18 }
17 19
18 float pointer_accel = atof(argv[0]); 20 float pointer_accel = parse_float(argv[0]);
19 if (pointer_accel < -1 || pointer_accel > 1) { 21 if (isnan(pointer_accel)) {
22 return cmd_results_new(CMD_INVALID, "pointer_accel",
23 "Invalid pointer accel; expected float.");
24 } if (pointer_accel < -1 || pointer_accel > 1) {
20 return cmd_results_new(CMD_INVALID, "pointer_accel", 25 return cmd_results_new(CMD_INVALID, "pointer_accel",
21 "Input out of range [-1, 1]"); 26 "Input out of range [-1, 1]");
22 } 27 }
diff --git a/sway/commands/input/scroll_factor.c b/sway/commands/input/scroll_factor.c
new file mode 100644
index 00000000..52d943b0
--- /dev/null
+++ b/sway/commands/input/scroll_factor.c
@@ -0,0 +1,32 @@
1#include <errno.h>
2#include <math.h>
3#include <stdlib.h>
4#include <string.h>
5#include "sway/config.h"
6#include "sway/commands.h"
7#include "sway/input/input-manager.h"
8#include "util.h"
9
10struct cmd_results *input_cmd_scroll_factor(int argc, char **argv) {
11 struct cmd_results *error = NULL;
12 if ((error = checkarg(argc, "scroll_factor", EXPECTED_AT_LEAST, 1))) {
13 return error;
14 }
15 struct input_config *ic = config->handler_context.input_config;
16 if (!ic) {
17 return cmd_results_new(CMD_FAILURE,
18 "scroll_factor", "No input device defined.");
19 }
20
21 float scroll_factor = parse_float(argv[0]);
22 if (isnan(scroll_factor)) {
23 return cmd_results_new(CMD_INVALID, "scroll_factor",
24 "Invalid scroll factor; expected float.");
25 } else if (scroll_factor < 0) {
26 return cmd_results_new(CMD_INVALID, "scroll_factor",
27 "Scroll factor cannot be negative.");
28 }
29 ic->scroll_factor = scroll_factor;
30
31 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
32}
diff --git a/sway/commands/input/xkb_capslock.c b/sway/commands/input/xkb_capslock.c
index 669b4ea9..a939c72f 100644
--- a/sway/commands/input/xkb_capslock.c
+++ b/sway/commands/input/xkb_capslock.c
@@ -3,6 +3,7 @@
3#include "sway/config.h" 3#include "sway/config.h"
4#include "sway/commands.h" 4#include "sway/commands.h"
5#include "sway/input/input-manager.h" 5#include "sway/input/input-manager.h"
6#include "util.h"
6 7
7struct cmd_results *input_cmd_xkb_capslock(int argc, char **argv) { 8struct cmd_results *input_cmd_xkb_capslock(int argc, char **argv) {
8 struct cmd_results *error = NULL; 9 struct cmd_results *error = NULL;
@@ -15,14 +16,7 @@ struct cmd_results *input_cmd_xkb_capslock(int argc, char **argv) {
15 "No input device defined."); 16 "No input device defined.");
16 } 17 }
17 18
18 if (strcasecmp(argv[0], "enabled") == 0) { 19 ic->xkb_capslock = parse_boolean(argv[0], false);
19 ic->xkb_capslock = 1;
20 } else if (strcasecmp(argv[0], "disabled") == 0) {
21 ic->xkb_capslock = 0;
22 } else {
23 return cmd_results_new(CMD_INVALID, "xkb_capslock",
24 "Expected 'xkb_capslock <enabled|disabled>'");
25 }
26 20
27 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 21 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
28} 22}
diff --git a/sway/commands/input/xkb_numlock.c b/sway/commands/input/xkb_numlock.c
index 1367da44..2e962c5b 100644
--- a/sway/commands/input/xkb_numlock.c
+++ b/sway/commands/input/xkb_numlock.c
@@ -3,6 +3,7 @@
3#include "sway/config.h" 3#include "sway/config.h"
4#include "sway/commands.h" 4#include "sway/commands.h"
5#include "sway/input/input-manager.h" 5#include "sway/input/input-manager.h"
6#include "util.h"
6 7
7struct cmd_results *input_cmd_xkb_numlock(int argc, char **argv) { 8struct cmd_results *input_cmd_xkb_numlock(int argc, char **argv) {
8 struct cmd_results *error = NULL; 9 struct cmd_results *error = NULL;
@@ -15,14 +16,7 @@ struct cmd_results *input_cmd_xkb_numlock(int argc, char **argv) {
15 "No input device defined."); 16 "No input device defined.");
16 } 17 }
17 18
18 if (strcasecmp(argv[0], "enabled") == 0) { 19 ic->xkb_numlock = parse_boolean(argv[0], false);
19 ic->xkb_numlock = 1;
20 } else if (strcasecmp(argv[0], "disabled") == 0) {
21 ic->xkb_numlock = 0;
22 } else {
23 return cmd_results_new(CMD_INVALID, "xkb_numlock",
24 "Expected 'xkb_numlock <enabled|disabled>'");
25 }
26 20
27 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 21 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
28} 22}
diff --git a/sway/commands/resize.c b/sway/commands/resize.c
index 8635b309..a90d578e 100644
--- a/sway/commands/resize.c
+++ b/sway/commands/resize.c
@@ -404,13 +404,10 @@ static struct cmd_results *resize_adjust_floating(enum resize_axis axis,
404 con->width += grow_width; 404 con->width += grow_width;
405 con->height += grow_height; 405 con->height += grow_height;
406 406
407 if (con->view) { 407 con->content_x += grow_x;
408 struct sway_view *view = con->view; 408 con->content_y += grow_y;
409 view->x += grow_x; 409 con->content_width += grow_width;
410 view->y += grow_y; 410 con->content_height += grow_height;
411 view->width += grow_width;
412 view->height += grow_height;
413 }
414 411
415 arrange_container(con); 412 arrange_container(con);
416 413
@@ -499,7 +496,7 @@ static struct cmd_results *resize_set_tiled(struct sway_container *con,
499 } 496 }
500 if (height->unit == RESIZE_UNIT_PX) { 497 if (height->unit == RESIZE_UNIT_PX) {
501 resize_tiled(con, height->amount - con->height, 498 resize_tiled(con, height->amount - con->height,
502 RESIZE_AXIS_HORIZONTAL); 499 RESIZE_AXIS_VERTICAL);
503 } 500 }
504 } 501 }
505 502
@@ -511,25 +508,46 @@ static struct cmd_results *resize_set_tiled(struct sway_container *con,
511 */ 508 */
512static struct cmd_results *resize_set_floating(struct sway_container *con, 509static struct cmd_results *resize_set_floating(struct sway_container *con,
513 struct resize_amount *width, struct resize_amount *height) { 510 struct resize_amount *width, struct resize_amount *height) {
514 int min_width, max_width, min_height, max_height; 511 int min_width, max_width, min_height, max_height, grow_width = 0, grow_height = 0;
515 calculate_constraints(&min_width, &max_width, &min_height, &max_height); 512 calculate_constraints(&min_width, &max_width, &min_height, &max_height);
516 width->amount = fmax(min_width, fmin(width->amount, max_width)); 513
517 height->amount = fmax(min_height, fmin(height->amount, max_height)); 514 if (width->amount) {
518 int grow_width = width->amount - con->width; 515 if (width->unit == RESIZE_UNIT_PPT ||
519 int grow_height = height->amount - con->height; 516 width->unit == RESIZE_UNIT_DEFAULT) {
520 con->x -= grow_width / 2; 517 // Convert to px
521 con->y -= grow_height / 2; 518 width->amount = con->workspace->width * width->amount / 100;
522 con->width = width->amount; 519 width->unit = RESIZE_UNIT_PX;
523 con->height = height->amount; 520 }
524 521 if (width->unit == RESIZE_UNIT_PX) {
525 if (con->view) { 522 width->amount = fmax(min_width, fmin(width->amount, max_width));
526 struct sway_view *view = con->view; 523 grow_width = width->amount - con->width;
527 view->x -= grow_width / 2; 524
528 view->y -= grow_height / 2; 525 con->x -= grow_width / 2;
529 view->width += grow_width; 526 con->width = width->amount;
530 view->height += grow_height; 527 }
531 } 528 }
532 529
530 if (height->amount) {
531 if (height->unit == RESIZE_UNIT_PPT ||
532 height->unit == RESIZE_UNIT_DEFAULT) {
533 // Convert to px
534 height->amount = con->workspace->height * height->amount / 100;
535 height->unit = RESIZE_UNIT_PX;
536 }
537 if (height->unit == RESIZE_UNIT_PX) {
538 height->amount = fmax(min_height, fmin(height->amount, max_height));
539 grow_height = height->amount - con->height;
540
541 con->y -= grow_height / 2;
542 con->height = height->amount;
543 }
544 }
545
546 con->content_x -= grow_width / 2;
547 con->content_y -= grow_height / 2;
548 con->content_width += grow_width;
549 con->content_height += grow_height;
550
533 arrange_container(con); 551 arrange_container(con);
534 552
535 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 553 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
@@ -538,34 +556,45 @@ static struct cmd_results *resize_set_floating(struct sway_container *con,
538/** 556/**
539 * resize set <args> 557 * resize set <args>
540 * 558 *
541 * args: <width> [px|ppt] <height> [px|ppt] 559 * args: [width] <width> [px|ppt]
560 * : height <height> [px|ppt]
561 * : [width] <width> [px|ppt] [height] <height> [px|ppt]
542 */ 562 */
543static struct cmd_results *cmd_resize_set(int argc, char **argv) { 563static struct cmd_results *cmd_resize_set(int argc, char **argv) {
544 struct cmd_results *error; 564 struct cmd_results *error;
545 if ((error = checkarg(argc, "resize", EXPECTED_AT_LEAST, 2))) { 565 if ((error = checkarg(argc, "resize", EXPECTED_AT_LEAST, 1))) {
546 return error; 566 return error;
547 } 567 }
548 const char *usage = "Expected 'resize set <width> <height>'"; 568 const char *usage = "Expected 'resize set [width] <width> [px|ppt]' or "
569 "'resize set height <height> [px|ppt]' or "
570 "'resize set [width] <width> [px|ppt] [height] <height> [px|ppt]'";
549 571
550 // Width 572 // Width
551 struct resize_amount width; 573 struct resize_amount width = {0};
552 int num_consumed_args = parse_resize_amount(argc, argv, &width); 574 if (argc >= 2 && !strcmp(argv[0], "width") && strcmp(argv[1], "height")) {
553 argc -= num_consumed_args; 575 argc--; argv++;
554 argv += num_consumed_args;
555 if (width.unit == RESIZE_UNIT_INVALID) {
556 return cmd_results_new(CMD_INVALID, "resize", usage);
557 } 576 }
558 if (!argc) { 577 if (strcmp(argv[0], "height")) {
559 return cmd_results_new(CMD_INVALID, "resize", usage); 578 int num_consumed_args = parse_resize_amount(argc, argv, &width);
579 argc -= num_consumed_args;
580 argv += num_consumed_args;
581 if (width.unit == RESIZE_UNIT_INVALID) {
582 return cmd_results_new(CMD_INVALID, "resize set", usage);
583 }
560 } 584 }
561 585
562 // Height 586 // Height
563 struct resize_amount height; 587 struct resize_amount height = {0};
564 num_consumed_args = parse_resize_amount(argc, argv, &height); 588 if (argc) {
565 argc -= num_consumed_args; 589 if (argc >= 2 && !strcmp(argv[0], "height")) {
566 argv += num_consumed_args; 590 argc--; argv++;
567 if (height.unit == RESIZE_UNIT_INVALID) { 591 }
568 return cmd_results_new(CMD_INVALID, "resize", usage); 592 int num_consumed_args = parse_resize_amount(argc, argv, &height);
593 argc -= num_consumed_args;
594 argv += num_consumed_args;
595 if (width.unit == RESIZE_UNIT_INVALID) {
596 return cmd_results_new(CMD_INVALID, "resize set", usage);
597 }
569 } 598 }
570 599
571 // If 0, don't resize that dimension 600 // If 0, don't resize that dimension
diff --git a/sway/commands/seat/fallback.c b/sway/commands/seat/fallback.c
index 11f5a08c..a0ddf3ef 100644
--- a/sway/commands/seat/fallback.c
+++ b/sway/commands/seat/fallback.c
@@ -3,6 +3,7 @@
3#include "sway/config.h" 3#include "sway/config.h"
4#include "sway/commands.h" 4#include "sway/commands.h"
5#include "sway/input/input-manager.h" 5#include "sway/input/input-manager.h"
6#include "util.h"
6 7
7struct cmd_results *seat_cmd_fallback(int argc, char **argv) { 8struct cmd_results *seat_cmd_fallback(int argc, char **argv) {
8 struct cmd_results *error = NULL; 9 struct cmd_results *error = NULL;
@@ -16,16 +17,8 @@ struct cmd_results *seat_cmd_fallback(int argc, char **argv) {
16 } 17 }
17 struct seat_config *new_config = 18 struct seat_config *new_config =
18 new_seat_config(current_seat_config->name); 19 new_seat_config(current_seat_config->name);
19 20
20 if (strcasecmp(argv[0], "true") == 0) { 21 new_config->fallback = parse_boolean(argv[0], false);
21 new_config->fallback = 1;
22 } else if (strcasecmp(argv[0], "false") == 0) {
23 new_config->fallback = 0;
24 } else {
25 free_seat_config(new_config);
26 return cmd_results_new(CMD_INVALID, "fallback",
27 "Expected 'fallback <true|false>'");
28 }
29 22
30 if (!config->validating) { 23 if (!config->validating) {
31 apply_seat_config(new_config); 24 apply_seat_config(new_config);
diff --git a/sway/commands/smart_gaps.c b/sway/commands/smart_gaps.c
index 273905df..f14b6760 100644
--- a/sway/commands/smart_gaps.c
+++ b/sway/commands/smart_gaps.c
@@ -6,6 +6,7 @@
6#include "sway/tree/container.h" 6#include "sway/tree/container.h"
7#include "log.h" 7#include "log.h"
8#include "stringop.h" 8#include "stringop.h"
9#include "util.h"
9 10
10struct cmd_results *cmd_smart_gaps(int argc, char **argv) { 11struct cmd_results *cmd_smart_gaps(int argc, char **argv) {
11 struct cmd_results *error = checkarg(argc, "smart_gaps", EXPECTED_AT_LEAST, 1); 12 struct cmd_results *error = checkarg(argc, "smart_gaps", EXPECTED_AT_LEAST, 1);
@@ -14,14 +15,7 @@ struct cmd_results *cmd_smart_gaps(int argc, char **argv) {
14 return error; 15 return error;
15 } 16 }
16 17
17 if (strcmp(argv[0], "on") == 0) { 18 config->smart_gaps = parse_boolean(argv[0], config->smart_gaps);
18 config->smart_gaps = true;
19 } else if (strcmp(argv[0], "off") == 0) {
20 config->smart_gaps = false;
21 } else {
22 return cmd_results_new(CMD_INVALID, "smart_gaps",
23 "Expected 'smart_gaps <on|off>' ");
24 }
25 19
26 arrange_root(); 20 arrange_root();
27 21
diff --git a/sway/commands/sticky.c b/sway/commands/sticky.c
index f18322b7..7cd358a4 100644
--- a/sway/commands/sticky.c
+++ b/sway/commands/sticky.c
@@ -9,6 +9,7 @@
9#include "sway/tree/view.h" 9#include "sway/tree/view.h"
10#include "sway/tree/workspace.h" 10#include "sway/tree/workspace.h"
11#include "list.h" 11#include "list.h"
12#include "util.h"
12 13
13struct cmd_results *cmd_sticky(int argc, char **argv) { 14struct cmd_results *cmd_sticky(int argc, char **argv) {
14 struct cmd_results *error = NULL; 15 struct cmd_results *error = NULL;
@@ -26,21 +27,9 @@ struct cmd_results *cmd_sticky(int argc, char **argv) {
26 "Can't set sticky on a tiled container"); 27 "Can't set sticky on a tiled container");
27 } 28 }
28 29
29 bool wants_sticky; 30 container->is_sticky = parse_boolean(argv[0], container->is_sticky);
30 if (strcasecmp(argv[0], "enable") == 0) {
31 wants_sticky = true;
32 } else if (strcasecmp(argv[0], "disable") == 0) {
33 wants_sticky = false;
34 } else if (strcasecmp(argv[0], "toggle") == 0) {
35 wants_sticky = !container->is_sticky;
36 } else {
37 return cmd_results_new(CMD_FAILURE, "sticky",
38 "Expected 'sticky <enable|disable|toggle>'");
39 }
40
41 container->is_sticky = wants_sticky;
42 31
43 if (wants_sticky) { 32 if (container->is_sticky) {
44 // move container to active workspace 33 // move container to active workspace
45 struct sway_workspace *active_workspace = 34 struct sway_workspace *active_workspace =
46 output_get_active_workspace(container->workspace->output); 35 output_get_active_workspace(container->workspace->output);
diff --git a/sway/commands/swap.c b/sway/commands/swap.c
index 23e8d583..08860264 100644
--- a/sway/commands/swap.c
+++ b/sway/commands/swap.c
@@ -144,19 +144,18 @@ static void container_swap(struct sway_container *con1,
144 } 144 }
145} 145}
146 146
147static bool test_con_id(struct sway_container *container, void *con_id) { 147static bool test_con_id(struct sway_container *container, void *data) {
148 return container->node.id == (size_t)con_id; 148 size_t *con_id = data;
149 return container->node.id == *con_id;
149} 150}
150 151
151static bool test_id(struct sway_container *container, void *id) { 152#if HAVE_XWAYLAND
152#ifdef HAVE_XWAYLAND 153static bool test_id(struct sway_container *container, void *data) {
153 xcb_window_t *wid = id; 154 xcb_window_t *wid = data;
154 return (container->view && container->view->type == SWAY_VIEW_XWAYLAND 155 return (container->view && container->view->type == SWAY_VIEW_XWAYLAND
155 && container->view->wlr_xwayland_surface->window_id == *wid); 156 && container->view->wlr_xwayland_surface->window_id == *wid);
156#else
157 return false;
158#endif
159} 157}
158#endif
160 159
161static bool test_mark(struct sway_container *container, void *mark) { 160static bool test_mark(struct sway_container *container, void *mark) {
162 if (container->marks->length) { 161 if (container->marks->length) {
@@ -181,19 +180,19 @@ struct cmd_results *cmd_swap(int argc, char **argv) {
181 } 180 }
182 181
183 struct sway_container *current = config->handler_context.container; 182 struct sway_container *current = config->handler_context.container;
184 struct sway_container *other; 183 struct sway_container *other = NULL;
185 184
186 char *value = join_args(argv + 3, argc - 3); 185 char *value = join_args(argv + 3, argc - 3);
187 if (strcasecmp(argv[2], "id") == 0) { 186 if (strcasecmp(argv[2], "id") == 0) {
188#ifdef HAVE_XWAYLAND 187#if HAVE_XWAYLAND
189 xcb_window_t id = strtol(value, NULL, 0); 188 xcb_window_t id = strtol(value, NULL, 0);
190 other = root_find_container(test_id, (void *)&id); 189 other = root_find_container(test_id, &id);
191#endif 190#endif
192 } else if (strcasecmp(argv[2], "con_id") == 0) { 191 } else if (strcasecmp(argv[2], "con_id") == 0) {
193 size_t con_id = atoi(value); 192 size_t con_id = atoi(value);
194 other = root_find_container(test_con_id, (void *)con_id); 193 other = root_find_container(test_con_id, &con_id);
195 } else if (strcasecmp(argv[2], "mark") == 0) { 194 } else if (strcasecmp(argv[2], "mark") == 0) {
196 other = root_find_container(test_mark, (void *)value); 195 other = root_find_container(test_mark, value);
197 } else { 196 } else {
198 free(value); 197 free(value);
199 return cmd_results_new(CMD_INVALID, "swap", EXPECTED_SYNTAX); 198 return cmd_results_new(CMD_INVALID, "swap", EXPECTED_SYNTAX);
diff --git a/sway/commands/workspace.c b/sway/commands/workspace.c
index 5abbb676..92118ecf 100644
--- a/sway/commands/workspace.c
+++ b/sway/commands/workspace.c
@@ -21,18 +21,105 @@ static struct workspace_config *workspace_config_find_or_create(char *ws_name) {
21 return NULL; 21 return NULL;
22 } 22 }
23 wsc->workspace = strdup(ws_name); 23 wsc->workspace = strdup(ws_name);
24 wsc->outputs = create_list();
24 wsc->gaps_inner = INT_MIN; 25 wsc->gaps_inner = INT_MIN;
25 wsc->gaps_outer = INT_MIN; 26 wsc->gaps_outer.top = INT_MIN;
27 wsc->gaps_outer.right = INT_MIN;
28 wsc->gaps_outer.bottom = INT_MIN;
29 wsc->gaps_outer.left = INT_MIN;
26 list_add(config->workspace_configs, wsc); 30 list_add(config->workspace_configs, wsc);
27 return wsc; 31 return wsc;
28} 32}
29 33
30void free_workspace_config(struct workspace_config *wsc) { 34void free_workspace_config(struct workspace_config *wsc) {
31 free(wsc->workspace); 35 free(wsc->workspace);
32 free(wsc->output); 36 free_flat_list(wsc->outputs);
33 free(wsc); 37 free(wsc);
34} 38}
35 39
40static void prevent_invalid_outer_gaps(struct workspace_config *wsc) {
41 if (wsc->gaps_outer.top != INT_MIN &&
42 wsc->gaps_outer.top < -wsc->gaps_inner) {
43 wsc->gaps_outer.top = -wsc->gaps_inner;
44 }
45 if (wsc->gaps_outer.right != INT_MIN &&
46 wsc->gaps_outer.right < -wsc->gaps_inner) {
47 wsc->gaps_outer.right = -wsc->gaps_inner;
48 }
49 if (wsc->gaps_outer.bottom != INT_MIN &&
50 wsc->gaps_outer.bottom < -wsc->gaps_inner) {
51 wsc->gaps_outer.bottom = -wsc->gaps_inner;
52 }
53 if (wsc->gaps_outer.left != INT_MIN &&
54 wsc->gaps_outer.left < -wsc->gaps_inner) {
55 wsc->gaps_outer.left = -wsc->gaps_inner;
56 }
57}
58
59static struct cmd_results *cmd_workspace_gaps(int argc, char **argv,
60 int gaps_location) {
61 const char *expected = "Expected 'workspace <name> gaps "
62 "inner|outer|horizontal|vertical|top|right|bottom|left <px>'";
63 struct cmd_results *error = NULL;
64 if ((error = checkarg(argc, "workspace", EXPECTED_EQUAL_TO,
65 gaps_location + 3))) {
66 return error;
67 }
68 char *ws_name = join_args(argv, argc - 3);
69 struct workspace_config *wsc = workspace_config_find_or_create(ws_name);
70 free(ws_name);
71 if (!wsc) {
72 return cmd_results_new(CMD_FAILURE, "workspace gaps",
73 "Unable to allocate workspace output");
74 }
75
76 char *end;
77 int amount = strtol(argv[gaps_location + 2], &end, 10);
78 if (strlen(end)) {
79 free(end);
80 return cmd_results_new(CMD_FAILURE, "workspace gaps", expected);
81 }
82
83 bool valid = false;
84 char *type = argv[gaps_location + 1];
85 if (!strcasecmp(type, "inner")) {
86 valid = true;
87 wsc->gaps_inner = (amount >= 0) ? amount : 0;
88 } else {
89 if (!strcasecmp(type, "outer") || !strcasecmp(type, "vertical")
90 || !strcasecmp(type, "top")) {
91 valid = true;
92 wsc->gaps_outer.top = amount;
93 }
94 if (!strcasecmp(type, "outer") || !strcasecmp(type, "horizontal")
95 || !strcasecmp(type, "right")) {
96 valid = true;
97 wsc->gaps_outer.right = amount;
98 }
99 if (!strcasecmp(type, "outer") || !strcasecmp(type, "vertical")
100 || !strcasecmp(type, "bottom")) {
101 valid = true;
102 wsc->gaps_outer.bottom = amount;
103 }
104 if (!strcasecmp(type, "outer") || !strcasecmp(type, "horizontal")
105 || !strcasecmp(type, "left")) {
106 valid = true;
107 wsc->gaps_outer.left = amount;
108 }
109 }
110 if (!valid) {
111 return cmd_results_new(CMD_INVALID, "workspace gaps", expected);
112 }
113
114 // Prevent invalid gaps configurations.
115 if (wsc->gaps_inner != INT_MIN && wsc->gaps_inner < 0) {
116 wsc->gaps_inner = 0;
117 }
118 prevent_invalid_outer_gaps(wsc);
119
120 return error;
121}
122
36struct cmd_results *cmd_workspace(int argc, char **argv) { 123struct cmd_results *cmd_workspace(int argc, char **argv) {
37 struct cmd_results *error = NULL; 124 struct cmd_results *error = NULL;
38 if ((error = checkarg(argc, "workspace", EXPECTED_AT_LEAST, 1))) { 125 if ((error = checkarg(argc, "workspace", EXPECTED_AT_LEAST, 1))) {
@@ -55,56 +142,24 @@ struct cmd_results *cmd_workspace(int argc, char **argv) {
55 } 142 }
56 } 143 }
57 if (output_location >= 0) { 144 if (output_location >= 0) {
58 if ((error = checkarg(argc, "workspace", EXPECTED_EQUAL_TO, output_location + 2))) { 145 if ((error = checkarg(argc, "workspace", EXPECTED_AT_LEAST,
146 output_location + 2))) {
59 return error; 147 return error;
60 } 148 }
61 char *ws_name = join_args(argv, argc - 2); 149 char *ws_name = join_args(argv, output_location);
62 struct workspace_config *wsc = workspace_config_find_or_create(ws_name); 150 struct workspace_config *wsc = workspace_config_find_or_create(ws_name);
63 free(ws_name); 151 free(ws_name);
64 if (!wsc) { 152 if (!wsc) {
65 return cmd_results_new(CMD_FAILURE, "workspace output", 153 return cmd_results_new(CMD_FAILURE, "workspace output",
66 "Unable to allocate workspace output"); 154 "Unable to allocate workspace output");
67 } 155 }
68 free(wsc->output); 156 for (int i = output_location + 1; i < argc; ++i) {
69 wsc->output = strdup(argv[output_location + 1]); 157 list_add(wsc->outputs, strdup(argv[i]));
158 }
70 } else if (gaps_location >= 0) { 159 } else if (gaps_location >= 0) {
71 if ((error = checkarg(argc, "workspace", EXPECTED_EQUAL_TO, gaps_location + 3))) { 160 if ((error = cmd_workspace_gaps(argc, argv, gaps_location))) {
72 return error; 161 return error;
73 } 162 }
74 char *ws_name = join_args(argv, argc - 3);
75 struct workspace_config *wsc = workspace_config_find_or_create(ws_name);
76 free(ws_name);
77 if (!wsc) {
78 return cmd_results_new(CMD_FAILURE, "workspace gaps",
79 "Unable to allocate workspace output");
80 }
81 int *prop = NULL;
82 if (strcasecmp(argv[gaps_location + 1], "inner") == 0) {
83 prop = &wsc->gaps_inner;
84 } else if (strcasecmp(argv[gaps_location + 1], "outer") == 0) {
85 prop = &wsc->gaps_outer;
86 } else {
87 return cmd_results_new(CMD_FAILURE, "workspace gaps",
88 "Expected 'workspace <ws> gaps inner|outer <px>'");
89 }
90 char *end;
91 int val = strtol(argv[gaps_location + 2], &end, 10);
92
93 if (strlen(end)) {
94 free(end);
95 return cmd_results_new(CMD_FAILURE, "workspace gaps",
96 "Expected 'workspace <ws> gaps inner|outer <px>'");
97 }
98 *prop = val;
99
100 // Prevent invalid gaps configurations.
101 if (wsc->gaps_inner < 0) {
102 wsc->gaps_inner = 0;
103 }
104 if (wsc->gaps_outer < -wsc->gaps_inner) {
105 wsc->gaps_outer = -wsc->gaps_inner;
106 }
107
108 } else { 163 } else {
109 if (config->reading || !config->active) { 164 if (config->reading || !config->active) {
110 return cmd_results_new(CMD_DEFER, "workspace", NULL); 165 return cmd_results_new(CMD_DEFER, "workspace", NULL);
diff --git a/sway/commands/ws_auto_back_and_forth.c b/sway/commands/ws_auto_back_and_forth.c
index 2485db35..adb851c2 100644
--- a/sway/commands/ws_auto_back_and_forth.c
+++ b/sway/commands/ws_auto_back_and_forth.c
@@ -1,12 +1,14 @@
1#include <string.h> 1#include <string.h>
2#include <strings.h> 2#include <strings.h>
3#include "sway/commands.h" 3#include "sway/commands.h"
4#include "util.h"
4 5
5struct cmd_results *cmd_ws_auto_back_and_forth(int argc, char **argv) { 6struct cmd_results *cmd_ws_auto_back_and_forth(int argc, char **argv) {
6 struct cmd_results *error = NULL; 7 struct cmd_results *error = NULL;
7 if ((error = checkarg(argc, "workspace_auto_back_and_forth", EXPECTED_EQUAL_TO, 1))) { 8 if ((error = checkarg(argc, "workspace_auto_back_and_forth", EXPECTED_EQUAL_TO, 1))) {
8 return error; 9 return error;
9 } 10 }
10 config->auto_back_and_forth = !strcasecmp(argv[0], "yes"); 11 config->auto_back_and_forth =
12 parse_boolean(argv[0], config->auto_back_and_forth);
11 return cmd_results_new(CMD_SUCCESS, NULL, NULL); 13 return cmd_results_new(CMD_SUCCESS, NULL, NULL);
12} 14}
diff --git a/sway/config.c b/sway/config.c
index 7ef3ef38..c1320acf 100644
--- a/sway/config.c
+++ b/sway/config.c
@@ -220,7 +220,7 @@ static void config_defaults(struct sway_config *config) {
220 config->floating_minimum_height = 50; 220 config->floating_minimum_height = 50;
221 221
222 // Flags 222 // Flags
223 config->focus_follows_mouse = true; 223 config->focus_follows_mouse = FOLLOWS_YES;
224 config->mouse_warping = WARP_OUTPUT; 224 config->mouse_warping = WARP_OUTPUT;
225 config->focus_wrapping = WRAP_YES; 225 config->focus_wrapping = WRAP_YES;
226 config->validating = false; 226 config->validating = false;
@@ -234,7 +234,10 @@ static void config_defaults(struct sway_config *config) {
234 234
235 config->smart_gaps = false; 235 config->smart_gaps = false;
236 config->gaps_inner = 0; 236 config->gaps_inner = 0;
237 config->gaps_outer = 0; 237 config->gaps_outer.top = 0;
238 config->gaps_outer.right = 0;
239 config->gaps_outer.bottom = 0;
240 config->gaps_outer.left = 0;
238 241
239 if (!(config->active_bar_modifiers = create_list())) goto cleanup; 242 if (!(config->active_bar_modifiers = create_list())) goto cleanup;
240 243
@@ -254,7 +257,6 @@ static void config_defaults(struct sway_config *config) {
254 257
255 // border colors 258 // border colors
256 set_color(config->border_colors.focused.border, 0x4C7899); 259 set_color(config->border_colors.focused.border, 0x4C7899);
257 set_color(config->border_colors.focused.border, 0x4C7899);
258 set_color(config->border_colors.focused.background, 0x285577); 260 set_color(config->border_colors.focused.background, 0x285577);
259 set_color(config->border_colors.focused.text, 0xFFFFFFFF); 261 set_color(config->border_colors.focused.text, 0xFFFFFFFF);
260 set_color(config->border_colors.focused.indicator, 0x2E9EF4); 262 set_color(config->border_colors.focused.indicator, 0x2E9EF4);
diff --git a/sway/config/bar.c b/sway/config/bar.c
index 94405a92..7009d0a0 100644
--- a/sway/config/bar.c
+++ b/sway/config/bar.c
@@ -46,6 +46,7 @@ void free_bar_config(struct bar_config *bar) {
46 free(bar->position); 46 free(bar->position);
47 free(bar->hidden_state); 47 free(bar->hidden_state);
48 free(bar->status_command); 48 free(bar->status_command);
49 free(bar->swaybar_command);
49 free(bar->font); 50 free(bar->font);
50 free(bar->separator_symbol); 51 free(bar->separator_symbol);
51 for (int i = 0; i < bar->bindings->length; i++) { 52 for (int i = 0; i < bar->bindings->length; i++) {
diff --git a/sway/config/input.c b/sway/config/input.c
index 794d5194..d5d2d90b 100644
--- a/sway/config/input.c
+++ b/sway/config/input.c
@@ -29,6 +29,7 @@ struct input_config *new_input_config(const char* identifier) {
29 input->natural_scroll = INT_MIN; 29 input->natural_scroll = INT_MIN;
30 input->accel_profile = INT_MIN; 30 input->accel_profile = INT_MIN;
31 input->pointer_accel = FLT_MIN; 31 input->pointer_accel = FLT_MIN;
32 input->scroll_factor = FLT_MIN;
32 input->scroll_button = INT_MIN; 33 input->scroll_button = INT_MIN;
33 input->scroll_method = INT_MIN; 34 input->scroll_method = INT_MIN;
34 input->left_handed = INT_MIN; 35 input->left_handed = INT_MIN;
@@ -68,6 +69,9 @@ void merge_input_config(struct input_config *dst, struct input_config *src) {
68 if (src->pointer_accel != FLT_MIN) { 69 if (src->pointer_accel != FLT_MIN) {
69 dst->pointer_accel = src->pointer_accel; 70 dst->pointer_accel = src->pointer_accel;
70 } 71 }
72 if (src->scroll_factor != FLT_MIN) {
73 dst->scroll_factor = src->scroll_factor;
74 }
71 if (src->repeat_delay != INT_MIN) { 75 if (src->repeat_delay != INT_MIN) {
72 dst->repeat_delay = src->repeat_delay; 76 dst->repeat_delay = src->repeat_delay;
73 } 77 }
diff --git a/sway/config/output.c b/sway/config/output.c
index 2b041353..07543e3c 100644
--- a/sway/config/output.c
+++ b/sway/config/output.c
@@ -326,6 +326,7 @@ void free_output_config(struct output_config *oc) {
326 free(oc->name); 326 free(oc->name);
327 free(oc->background); 327 free(oc->background);
328 free(oc->background_option); 328 free(oc->background_option);
329 free(oc->background_fallback);
329 free(oc); 330 free(oc);
330} 331}
331 332
diff --git a/sway/criteria.c b/sway/criteria.c
index 2f9992e9..3393852c 100644
--- a/sway/criteria.c
+++ b/sway/criteria.c
@@ -21,7 +21,7 @@ bool criteria_is_empty(struct criteria *criteria) {
21 && !criteria->app_id 21 && !criteria->app_id
22 && !criteria->con_mark 22 && !criteria->con_mark
23 && !criteria->con_id 23 && !criteria->con_id
24#ifdef HAVE_XWAYLAND 24#if HAVE_XWAYLAND
25 && !criteria->class 25 && !criteria->class
26 && !criteria->id 26 && !criteria->id
27 && !criteria->instance 27 && !criteria->instance
@@ -35,18 +35,19 @@ bool criteria_is_empty(struct criteria *criteria) {
35} 35}
36 36
37void criteria_destroy(struct criteria *criteria) { 37void criteria_destroy(struct criteria *criteria) {
38 free(criteria->raw);
39 free(criteria->cmdlist);
40 free(criteria->target);
38 pcre_free(criteria->title); 41 pcre_free(criteria->title);
39 pcre_free(criteria->shell); 42 pcre_free(criteria->shell);
40 pcre_free(criteria->app_id); 43 pcre_free(criteria->app_id);
41#ifdef HAVE_XWAYLAND 44 pcre_free(criteria->con_mark);
45#if HAVE_XWAYLAND
42 pcre_free(criteria->class); 46 pcre_free(criteria->class);
43 pcre_free(criteria->instance); 47 pcre_free(criteria->instance);
44 pcre_free(criteria->window_role); 48 pcre_free(criteria->window_role);
45#endif 49#endif
46 pcre_free(criteria->con_mark);
47 free(criteria->workspace); 50 free(criteria->workspace);
48 free(criteria->cmdlist);
49 free(criteria->raw);
50 free(criteria); 51 free(criteria);
51} 52}
52 53
@@ -54,7 +55,7 @@ static int regex_cmp(const char *item, const pcre *regex) {
54 return pcre_exec(regex, NULL, item, strlen(item), 0, 0, NULL, 0); 55 return pcre_exec(regex, NULL, item, strlen(item), 0, 0, NULL, 0);
55} 56}
56 57
57#ifdef HAVE_XWAYLAND 58#if HAVE_XWAYLAND
58static bool view_has_window_type(struct sway_view *view, enum atom_name name) { 59static bool view_has_window_type(struct sway_view *view, enum atom_name name) {
59 if (view->type != SWAY_VIEW_XWAYLAND) { 60 if (view->type != SWAY_VIEW_XWAYLAND) {
60 return false; 61 return false;
@@ -139,7 +140,7 @@ static bool criteria_matches_view(struct criteria *criteria,
139 } 140 }
140 } 141 }
141 142
142#ifdef HAVE_XWAYLAND 143#if HAVE_XWAYLAND
143 if (criteria->id) { // X11 window ID 144 if (criteria->id) { // X11 window ID
144 uint32_t x11_window_id = view_get_x11_window_id(view); 145 uint32_t x11_window_id = view_get_x11_window_id(view);
145 if (!x11_window_id || x11_window_id != criteria->id) { 146 if (!x11_window_id || x11_window_id != criteria->id) {
@@ -275,7 +276,7 @@ static bool generate_regex(pcre **regex, char *value) {
275 return true; 276 return true;
276} 277}
277 278
278#ifdef HAVE_XWAYLAND 279#if HAVE_XWAYLAND
279static enum atom_name parse_window_type(const char *type) { 280static enum atom_name parse_window_type(const char *type) {
280 if (strcasecmp(type, "normal") == 0) { 281 if (strcasecmp(type, "normal") == 0) {
281 return NET_WM_WINDOW_TYPE_NORMAL; 282 return NET_WM_WINDOW_TYPE_NORMAL;
@@ -307,7 +308,7 @@ enum criteria_token {
307 T_CON_ID, 308 T_CON_ID,
308 T_CON_MARK, 309 T_CON_MARK,
309 T_FLOATING, 310 T_FLOATING,
310#ifdef HAVE_XWAYLAND 311#if HAVE_XWAYLAND
311 T_CLASS, 312 T_CLASS,
312 T_ID, 313 T_ID,
313 T_INSTANCE, 314 T_INSTANCE,
@@ -330,7 +331,7 @@ static enum criteria_token token_from_name(char *name) {
330 return T_CON_ID; 331 return T_CON_ID;
331 } else if (strcmp(name, "con_mark") == 0) { 332 } else if (strcmp(name, "con_mark") == 0) {
332 return T_CON_MARK; 333 return T_CON_MARK;
333#ifdef HAVE_XWAYLAND 334#if HAVE_XWAYLAND
334 } else if (strcmp(name, "class") == 0) { 335 } else if (strcmp(name, "class") == 0) {
335 return T_CLASS; 336 return T_CLASS;
336 } else if (strcmp(name, "id") == 0) { 337 } else if (strcmp(name, "id") == 0) {
@@ -401,7 +402,7 @@ static char *get_focused_prop(enum criteria_token token) {
401 snprintf(id_str, id_size, "%zu", id); 402 snprintf(id_str, id_size, "%zu", id);
402 value = id_str; 403 value = id_str;
403 break; 404 break;
404#ifdef HAVE_XWAYLAND 405#if HAVE_XWAYLAND
405 case T_CLASS: 406 case T_CLASS:
406 value = view_get_class(view); 407 value = view_get_class(view);
407 break; 408 break;
@@ -473,7 +474,7 @@ static bool parse_token(struct criteria *criteria, char *name, char *value) {
473 case T_CON_MARK: 474 case T_CON_MARK:
474 generate_regex(&criteria->con_mark, effective_value); 475 generate_regex(&criteria->con_mark, effective_value);
475 break; 476 break;
476#ifdef HAVE_XWAYLAND 477#if HAVE_XWAYLAND
477 case T_CLASS: 478 case T_CLASS:
478 generate_regex(&criteria->class, effective_value); 479 generate_regex(&criteria->class, effective_value);
479 break; 480 break;
@@ -576,7 +577,7 @@ struct criteria *criteria_parse(char *raw, char **error_arg) {
576 ++head; 577 ++head;
577 578
578 struct criteria *criteria = calloc(1, sizeof(struct criteria)); 579 struct criteria *criteria = calloc(1, sizeof(struct criteria));
579#ifdef HAVE_XWAYLAND 580#if HAVE_XWAYLAND
580 criteria->window_type = ATOM_LAST; // default value 581 criteria->window_type = ATOM_LAST; // default value
581#endif 582#endif
582 char *name = NULL, *value = NULL; 583 char *name = NULL, *value = NULL;
diff --git a/sway/desktop/desktop.c b/sway/desktop/desktop.c
index 771b58fe..d8dd0240 100644
--- a/sway/desktop/desktop.c
+++ b/sway/desktop/desktop.c
@@ -28,8 +28,8 @@ void desktop_damage_box(struct wlr_box *box) {
28void desktop_damage_view(struct sway_view *view) { 28void desktop_damage_view(struct sway_view *view) {
29 desktop_damage_whole_container(view->container); 29 desktop_damage_whole_container(view->container);
30 struct wlr_box box = { 30 struct wlr_box box = {
31 .x = view->container->current.view_x - view->geometry.x, 31 .x = view->container->current.content_x - view->geometry.x,
32 .y = view->container->current.view_y - view->geometry.y, 32 .y = view->container->current.content_y - view->geometry.y,
33 .width = view->surface->current.width, 33 .width = view->surface->current.width,
34 .height = view->surface->current.height, 34 .height = view->surface->current.height,
35 }; 35 };
diff --git a/sway/desktop/output.c b/sway/desktop/output.c
index d48ddef3..d649100f 100644
--- a/sway/desktop/output.c
+++ b/sway/desktop/output.c
@@ -160,12 +160,12 @@ void output_view_for_each_surface(struct sway_output *output,
160 .user_iterator = iterator, 160 .user_iterator = iterator,
161 .user_data = user_data, 161 .user_data = user_data,
162 .output = output, 162 .output = output,
163 .ox = view->container->current.view_x - output->wlr_output->lx 163 .ox = view->container->current.content_x - output->wlr_output->lx
164 - view->geometry.x, 164 - view->geometry.x,
165 .oy = view->container->current.view_y - output->wlr_output->ly 165 .oy = view->container->current.content_y - output->wlr_output->ly
166 - view->geometry.y, 166 - view->geometry.y,
167 .width = view->container->current.view_width, 167 .width = view->container->current.content_width,
168 .height = view->container->current.view_height, 168 .height = view->container->current.content_height,
169 .rotation = 0, // TODO 169 .rotation = 0, // TODO
170 }; 170 };
171 171
@@ -179,12 +179,12 @@ void output_view_for_each_popup(struct sway_output *output,
179 .user_iterator = iterator, 179 .user_iterator = iterator,
180 .user_data = user_data, 180 .user_data = user_data,
181 .output = output, 181 .output = output,
182 .ox = view->container->current.view_x - output->wlr_output->lx 182 .ox = view->container->current.content_x - output->wlr_output->lx
183 - view->geometry.x, 183 - view->geometry.x,
184 .oy = view->container->current.view_y - output->wlr_output->ly 184 .oy = view->container->current.content_y - output->wlr_output->ly
185 - view->geometry.y, 185 - view->geometry.y,
186 .width = view->container->current.view_width, 186 .width = view->container->current.content_width,
187 .height = view->container->current.view_height, 187 .height = view->container->current.content_height,
188 .rotation = 0, // TODO 188 .rotation = 0, // TODO
189 }; 189 };
190 190
@@ -204,7 +204,7 @@ void output_layer_for_each_surface(struct sway_output *output,
204 } 204 }
205} 205}
206 206
207#ifdef HAVE_XWAYLAND 207#if HAVE_XWAYLAND
208void output_unmanaged_for_each_surface(struct sway_output *output, 208void output_unmanaged_for_each_surface(struct sway_output *output,
209 struct wl_list *unmanaged, sway_surface_iterator_func_t iterator, 209 struct wl_list *unmanaged, sway_surface_iterator_func_t iterator,
210 void *user_data) { 210 void *user_data) {
@@ -274,7 +274,7 @@ static void output_for_each_surface(struct sway_output *output,
274 for_each_surface_container_iterator(floater, &data); 274 for_each_surface_container_iterator(floater, &data);
275 } 275 }
276 } 276 }
277#ifdef HAVE_XWAYLAND 277#if HAVE_XWAYLAND
278 output_unmanaged_for_each_surface(output, &root->xwayland_unmanaged, 278 output_unmanaged_for_each_surface(output, &root->xwayland_unmanaged,
279 iterator, user_data); 279 iterator, user_data);
280#endif 280#endif
@@ -289,7 +289,7 @@ static void output_for_each_surface(struct sway_output *output,
289 workspace_for_each_container(workspace, 289 workspace_for_each_container(workspace,
290 for_each_surface_container_iterator, &data); 290 for_each_surface_container_iterator, &data);
291 291
292#ifdef HAVE_XWAYLAND 292#if HAVE_XWAYLAND
293 output_unmanaged_for_each_surface(output, &root->xwayland_unmanaged, 293 output_unmanaged_for_each_surface(output, &root->xwayland_unmanaged,
294 iterator, user_data); 294 iterator, user_data);
295#endif 295#endif
@@ -469,23 +469,14 @@ void output_damage_box(struct sway_output *output, struct wlr_box *_box) {
469 wlr_output_damage_add_box(output->damage, &box); 469 wlr_output_damage_add_box(output->damage, &box);
470} 470}
471 471
472static void output_damage_whole_container_iterator(struct sway_container *con,
473 void *data) {
474 if (!sway_assert(con->view, "expected a view")) {
475 return;
476 }
477 struct sway_output *output = data;
478 output_damage_view(output, con->view, true);
479}
480
481void output_damage_whole_container(struct sway_output *output, 472void output_damage_whole_container(struct sway_output *output,
482 struct sway_container *con) { 473 struct sway_container *con) {
483 // Pad the box by 1px, because the width is a double and might be a fraction 474 // Pad the box by 1px, because the width is a double and might be a fraction
484 struct wlr_box box = { 475 struct wlr_box box = {
485 .x = con->current.con_x - output->wlr_output->lx - 1, 476 .x = con->current.x - output->wlr_output->lx - 1,
486 .y = con->current.con_y - output->wlr_output->ly - 1, 477 .y = con->current.y - output->wlr_output->ly - 1,
487 .width = con->current.con_width + 2, 478 .width = con->current.width + 2,
488 .height = con->current.con_height + 2, 479 .height = con->current.height + 2,
489 }; 480 };
490 scale_box(&box, output->wlr_output->scale); 481 scale_box(&box, output->wlr_output->scale);
491 wlr_output_damage_add_box(output->damage, &box); 482 wlr_output_damage_add_box(output->damage, &box);
diff --git a/sway/desktop/render.c b/sway/desktop/render.c
index cf6da682..8d4a701b 100644
--- a/sway/desktop/render.c
+++ b/sway/desktop/render.c
@@ -140,7 +140,7 @@ static void render_layer(struct sway_output *output,
140 render_surface_iterator, &data); 140 render_surface_iterator, &data);
141} 141}
142 142
143#ifdef HAVE_XWAYLAND 143#if HAVE_XWAYLAND
144static void render_unmanaged(struct sway_output *output, 144static void render_unmanaged(struct sway_output *output,
145 pixman_region32_t *damage, struct wl_list *unmanaged) { 145 pixman_region32_t *damage, struct wl_list *unmanaged) {
146 struct render_data data = { 146 struct render_data data = {
@@ -211,9 +211,9 @@ static void render_view_toplevels(struct sway_view *view,
211 .alpha = alpha, 211 .alpha = alpha,
212 }; 212 };
213 // Render all toplevels without descending into popups 213 // Render all toplevels without descending into popups
214 double ox = view->container->current.view_x - 214 double ox = view->container->current.content_x -
215 output->wlr_output->lx - view->geometry.x; 215 output->wlr_output->lx - view->geometry.x;
216 double oy = view->container->current.view_y - 216 double oy = view->container->current.content_y -
217 output->wlr_output->ly - view->geometry.y; 217 output->wlr_output->ly - view->geometry.y;
218 output_surface_for_each_surface(output, view->surface, ox, oy, 218 output_surface_for_each_surface(output, view->surface, ox, oy,
219 render_surface_iterator, &data); 219 render_surface_iterator, &data);
@@ -247,9 +247,9 @@ static void render_saved_view(struct sway_view *view,
247 return; 247 return;
248 } 248 }
249 struct wlr_box box = { 249 struct wlr_box box = {
250 .x = view->container->current.view_x - output->wlr_output->lx - 250 .x = view->container->current.content_x - output->wlr_output->lx -
251 view->saved_geometry.x, 251 view->saved_geometry.x,
252 .y = view->container->current.view_y - output->wlr_output->ly - 252 .y = view->container->current.content_y - output->wlr_output->ly -
253 view->saved_geometry.y, 253 view->saved_geometry.y,
254 .width = view->saved_buffer_width, 254 .width = view->saved_buffer_width,
255 .height = view->saved_buffer_height, 255 .height = view->saved_buffer_height,
@@ -300,10 +300,10 @@ static void render_view(struct sway_output *output, pixman_region32_t *damage,
300 if (state->border_left) { 300 if (state->border_left) {
301 memcpy(&color, colors->child_border, sizeof(float) * 4); 301 memcpy(&color, colors->child_border, sizeof(float) * 4);
302 premultiply_alpha(color, con->alpha); 302 premultiply_alpha(color, con->alpha);
303 box.x = state->con_x; 303 box.x = state->x;
304 box.y = state->view_y; 304 box.y = state->content_y;
305 box.width = state->border_thickness; 305 box.width = state->border_thickness;
306 box.height = state->view_height; 306 box.height = state->content_height;
307 scale_box(&box, output_scale); 307 scale_box(&box, output_scale);
308 render_rect(output->wlr_output, damage, &box, color); 308 render_rect(output->wlr_output, damage, &box, color);
309 } 309 }
@@ -319,10 +319,10 @@ static void render_view(struct sway_output *output, pixman_region32_t *damage,
319 memcpy(&color, colors->child_border, sizeof(float) * 4); 319 memcpy(&color, colors->child_border, sizeof(float) * 4);
320 } 320 }
321 premultiply_alpha(color, con->alpha); 321 premultiply_alpha(color, con->alpha);
322 box.x = state->view_x + state->view_width; 322 box.x = state->content_x + state->content_width;
323 box.y = state->view_y; 323 box.y = state->content_y;
324 box.width = state->border_thickness; 324 box.width = state->border_thickness;
325 box.height = state->view_height; 325 box.height = state->content_height;
326 scale_box(&box, output_scale); 326 scale_box(&box, output_scale);
327 render_rect(output->wlr_output, damage, &box, color); 327 render_rect(output->wlr_output, damage, &box, color);
328 } 328 }
@@ -334,9 +334,9 @@ static void render_view(struct sway_output *output, pixman_region32_t *damage,
334 memcpy(&color, colors->child_border, sizeof(float) * 4); 334 memcpy(&color, colors->child_border, sizeof(float) * 4);
335 } 335 }
336 premultiply_alpha(color, con->alpha); 336 premultiply_alpha(color, con->alpha);
337 box.x = state->con_x; 337 box.x = state->x;
338 box.y = state->view_y + state->view_height; 338 box.y = state->content_y + state->content_height;
339 box.width = state->con_width; 339 box.width = state->width;
340 box.height = state->border_thickness; 340 box.height = state->border_thickness;
341 scale_box(&box, output_scale); 341 scale_box(&box, output_scale);
342 render_rect(output->wlr_output, damage, &box, color); 342 render_rect(output->wlr_output, damage, &box, color);
@@ -585,9 +585,9 @@ static void render_top_border(struct sway_output *output,
585 // Child border - top edge 585 // Child border - top edge
586 memcpy(&color, colors->child_border, sizeof(float) * 4); 586 memcpy(&color, colors->child_border, sizeof(float) * 4);
587 premultiply_alpha(color, con->alpha); 587 premultiply_alpha(color, con->alpha);
588 box.x = state->con_x; 588 box.x = state->x;
589 box.y = state->con_y; 589 box.y = state->y;
590 box.width = state->con_width; 590 box.width = state->width;
591 box.height = state->border_thickness; 591 box.height = state->border_thickness;
592 scale_box(&box, output_scale); 592 scale_box(&box, output_scale);
593 render_rect(output->wlr_output, output_damage, &box, color); 593 render_rect(output->wlr_output, output_damage, &box, color);
@@ -641,8 +641,8 @@ static void render_containers_linear(struct sway_output *output,
641 } 641 }
642 642
643 if (state->border == B_NORMAL) { 643 if (state->border == B_NORMAL) {
644 render_titlebar(output, damage, child, state->con_x, 644 render_titlebar(output, damage, child, state->x,
645 state->con_y, state->con_width, colors, 645 state->y, state->width, colors,
646 title_texture, marks_texture); 646 title_texture, marks_texture);
647 } else if (state->border == B_PIXEL) { 647 } else if (state->border == B_PIXEL) {
648 render_top_border(output, damage, child, colors); 648 render_top_border(output, damage, child, colors);
@@ -696,7 +696,7 @@ static void render_containers_tabbed(struct sway_output *output,
696 marks_texture = child->marks_unfocused; 696 marks_texture = child->marks_unfocused;
697 } 697 }
698 698
699 int x = cstate->con_x + tab_width * i; 699 int x = cstate->x + tab_width * i;
700 700
701 // Make last tab use the remaining width of the parent 701 // Make last tab use the remaining width of the parent
702 if (i == parent->children->length - 1) { 702 if (i == parent->children->length - 1) {
@@ -801,10 +801,10 @@ static void render_container(struct sway_output *output,
801 struct parent_data data = { 801 struct parent_data data = {
802 .layout = con->current.layout, 802 .layout = con->current.layout,
803 .box = { 803 .box = {
804 .x = con->current.con_x, 804 .x = con->current.x,
805 .y = con->current.con_y, 805 .y = con->current.y,
806 .width = con->current.con_width, 806 .width = con->current.width,
807 .height = con->current.con_height, 807 .height = con->current.height,
808 }, 808 },
809 .children = con->current.children, 809 .children = con->current.children,
810 .focused = focused, 810 .focused = focused,
@@ -853,8 +853,8 @@ static void render_floating_container(struct sway_output *soutput,
853 } 853 }
854 854
855 if (con->current.border == B_NORMAL) { 855 if (con->current.border == B_NORMAL) {
856 render_titlebar(soutput, damage, con, con->current.con_x, 856 render_titlebar(soutput, damage, con, con->current.x,
857 con->current.con_y, con->current.con_width, colors, 857 con->current.y, con->current.width, colors,
858 title_texture, marks_texture); 858 title_texture, marks_texture);
859 } else if (con->current.border == B_PIXEL) { 859 } else if (con->current.border == B_PIXEL) {
860 render_top_border(soutput, damage, con, colors); 860 render_top_border(soutput, damage, con, colors);
@@ -966,7 +966,7 @@ void output_render(struct sway_output *output, struct timespec *when,
966 render_floating_container(output, damage, floater); 966 render_floating_container(output, damage, floater);
967 } 967 }
968 } 968 }
969#ifdef HAVE_XWAYLAND 969#if HAVE_XWAYLAND
970 render_unmanaged(output, damage, &root->xwayland_unmanaged); 970 render_unmanaged(output, damage, &root->xwayland_unmanaged);
971#endif 971#endif
972 } else { 972 } else {
@@ -986,7 +986,7 @@ void output_render(struct sway_output *output, struct timespec *when,
986 986
987 render_workspace(output, damage, workspace, workspace->current.focused); 987 render_workspace(output, damage, workspace, workspace->current.focused);
988 render_floating(output, damage); 988 render_floating(output, damage);
989#ifdef HAVE_XWAYLAND 989#if HAVE_XWAYLAND
990 render_unmanaged(output, damage, &root->xwayland_unmanaged); 990 render_unmanaged(output, damage, &root->xwayland_unmanaged);
991#endif 991#endif
992 render_layer(output, damage, 992 render_layer(output, damage,
@@ -1019,6 +1019,7 @@ renderer_end:
1019 } 1019 }
1020 1020
1021 wlr_renderer_scissor(renderer, NULL); 1021 wlr_renderer_scissor(renderer, NULL);
1022 wlr_output_render_software_cursors(wlr_output, damage);
1022 wlr_renderer_end(renderer); 1023 wlr_renderer_end(renderer);
1023 if (!wlr_output_damage_swap_buffers(output->damage, when, damage)) { 1024 if (!wlr_output_damage_swap_buffers(output->damage, when, damage)) {
1024 return; 1025 return;
diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c
index 44156d41..bf0038b4 100644
--- a/sway/desktop/transaction.c
+++ b/sway/desktop/transaction.c
@@ -130,10 +130,10 @@ static void copy_container_state(struct sway_container *container,
130 struct sway_container_state *state = &instruction->container_state; 130 struct sway_container_state *state = &instruction->container_state;
131 131
132 state->layout = container->layout; 132 state->layout = container->layout;
133 state->con_x = container->x; 133 state->x = container->x;
134 state->con_y = container->y; 134 state->y = container->y;
135 state->con_width = container->width; 135 state->width = container->width;
136 state->con_height = container->height; 136 state->height = container->height;
137 state->is_fullscreen = container->is_fullscreen; 137 state->is_fullscreen = container->is_fullscreen;
138 state->parent = container->parent; 138 state->parent = container->parent;
139 state->workspace = container->workspace; 139 state->workspace = container->workspace;
@@ -143,14 +143,12 @@ static void copy_container_state(struct sway_container *container,
143 state->border_left = container->border_left; 143 state->border_left = container->border_left;
144 state->border_right = container->border_right; 144 state->border_right = container->border_right;
145 state->border_bottom = container->border_bottom; 145 state->border_bottom = container->border_bottom;
146 state->content_x = container->content_x;
147 state->content_y = container->content_y;
148 state->content_width = container->content_width;
149 state->content_height = container->content_height;
146 150
147 if (container->view) { 151 if (!container->view) {
148 struct sway_view *view = container->view;
149 state->view_x = view->x;
150 state->view_y = view->y;
151 state->view_width = view->width;
152 state->view_height = view->height;
153 } else {
154 state->children = create_list(); 152 state->children = create_list();
155 list_cat(state->children, container->children); 153 list_cat(state->children, container->children);
156 } 154 }
@@ -217,8 +215,8 @@ static void apply_container_state(struct sway_container *container,
217 desktop_damage_whole_container(container); 215 desktop_damage_whole_container(container);
218 if (view && view->saved_buffer) { 216 if (view && view->saved_buffer) {
219 struct wlr_box box = { 217 struct wlr_box box = {
220 .x = container->current.view_x - view->saved_geometry.x, 218 .x = container->current.content_x - view->saved_geometry.x,
221 .y = container->current.view_y - view->saved_geometry.y, 219 .y = container->current.content_y - view->saved_geometry.y,
222 .width = view->saved_buffer_width, 220 .width = view->saved_buffer_width,
223 .height = view->saved_buffer_height, 221 .height = view->saved_buffer_height,
224 }; 222 };
@@ -245,8 +243,8 @@ static void apply_container_state(struct sway_container *container,
245 if (view && view->surface) { 243 if (view && view->surface) {
246 struct wlr_surface *surface = view->surface; 244 struct wlr_surface *surface = view->surface;
247 struct wlr_box box = { 245 struct wlr_box box = {
248 .x = container->current.view_x - view->geometry.x, 246 .x = container->current.content_x - view->geometry.x,
249 .y = container->current.view_y - view->geometry.y, 247 .y = container->current.content_y - view->geometry.y,
250 .width = surface->current.width, 248 .width = surface->current.width,
251 .height = surface->current.height, 249 .height = surface->current.height,
252 }; 250 };
@@ -382,18 +380,18 @@ static bool should_configure(struct sway_node *node,
382 } 380 }
383 struct sway_container_state *cstate = &node->sway_container->current; 381 struct sway_container_state *cstate = &node->sway_container->current;
384 struct sway_container_state *istate = &instruction->container_state; 382 struct sway_container_state *istate = &instruction->container_state;
385#ifdef HAVE_XWAYLAND 383#if HAVE_XWAYLAND
386 // Xwayland views are position-aware and need to be reconfigured 384 // Xwayland views are position-aware and need to be reconfigured
387 // when their position changes. 385 // when their position changes.
388 if (node->sway_container->view->type == SWAY_VIEW_XWAYLAND) { 386 if (node->sway_container->view->type == SWAY_VIEW_XWAYLAND) {
389 if (cstate->view_x != istate->view_x || 387 if (cstate->content_x != istate->content_x ||
390 cstate->view_y != istate->view_y) { 388 cstate->content_y != istate->content_y) {
391 return true; 389 return true;
392 } 390 }
393 } 391 }
394#endif 392#endif
395 if (cstate->view_width == istate->view_width && 393 if (cstate->content_width == istate->content_width &&
396 cstate->view_height == istate->view_height) { 394 cstate->content_height == istate->content_height) {
397 return false; 395 return false;
398 } 396 }
399 return true; 397 return true;
@@ -409,10 +407,10 @@ static void transaction_commit(struct sway_transaction *transaction) {
409 struct sway_node *node = instruction->node; 407 struct sway_node *node = instruction->node;
410 if (should_configure(node, instruction)) { 408 if (should_configure(node, instruction)) {
411 instruction->serial = view_configure(node->sway_container->view, 409 instruction->serial = view_configure(node->sway_container->view,
412 instruction->container_state.view_x, 410 instruction->container_state.content_x,
413 instruction->container_state.view_y, 411 instruction->container_state.content_y,
414 instruction->container_state.view_width, 412 instruction->container_state.content_width,
415 instruction->container_state.view_height); 413 instruction->container_state.content_height);
416 ++transaction->num_waiting; 414 ++transaction->num_waiting;
417 415
418 // From here on we are rendering a saved buffer of the view, which 416 // From here on we are rendering a saved buffer of the view, which
@@ -504,8 +502,8 @@ void transaction_notify_view_ready_by_size(struct sway_view *view,
504 int width, int height) { 502 int width, int height) {
505 struct sway_transaction_instruction *instruction = 503 struct sway_transaction_instruction *instruction =
506 view->container->node.instruction; 504 view->container->node.instruction;
507 if (instruction->container_state.view_width == width && 505 if (instruction->container_state.content_width == width &&
508 instruction->container_state.view_height == height) { 506 instruction->container_state.content_height == height) {
509 set_instruction_ready(instruction); 507 set_instruction_ready(instruction);
510 } 508 }
511} 509}
diff --git a/sway/desktop/xdg_shell.c b/sway/desktop/xdg_shell.c
index 0b2ebc96..801dcee0 100644
--- a/sway/desktop/xdg_shell.c
+++ b/sway/desktop/xdg_shell.c
@@ -75,8 +75,8 @@ static void popup_unconstrain(struct sway_xdg_popup *popup) {
75 // the output box expressed in the coordinate system of the toplevel parent 75 // the output box expressed in the coordinate system of the toplevel parent
76 // of the popup 76 // of the popup
77 struct wlr_box output_toplevel_sx_box = { 77 struct wlr_box output_toplevel_sx_box = {
78 .x = output->lx - view->x, 78 .x = output->lx - view->container->content_x,
79 .y = output->ly - view->y, 79 .y = output->ly - view->container->content_y,
80 .width = output->width, 80 .width = output->width,
81 .height = output->height, 81 .height = output->height,
82 }; 82 };
@@ -286,9 +286,11 @@ static void handle_commit(struct wl_listener *listener, void *data) {
286 } else { 286 } else {
287 struct wlr_box new_geo; 287 struct wlr_box new_geo;
288 wlr_xdg_surface_get_geometry(xdg_surface, &new_geo); 288 wlr_xdg_surface_get_geometry(xdg_surface, &new_geo);
289 struct sway_container *con = view->container;
289 290
290 if ((new_geo.width != view->width || new_geo.height != view->height) && 291 if ((new_geo.width != con->content_width ||
291 container_is_floating(view->container)) { 292 new_geo.height != con->content_height) &&
293 container_is_floating(con)) {
292 // A floating view has unexpectedly sent a new size 294 // A floating view has unexpectedly sent a new size
293 desktop_damage_view(view); 295 desktop_damage_view(view);
294 view_update_size(view, new_geo.width, new_geo.height); 296 view_update_size(view, new_geo.width, new_geo.height);
diff --git a/sway/desktop/xdg_shell_v6.c b/sway/desktop/xdg_shell_v6.c
index 692cfbf5..4bc83b8e 100644
--- a/sway/desktop/xdg_shell_v6.c
+++ b/sway/desktop/xdg_shell_v6.c
@@ -74,8 +74,8 @@ static void popup_unconstrain(struct sway_xdg_popup_v6 *popup) {
74 // the output box expressed in the coordinate system of the toplevel parent 74 // the output box expressed in the coordinate system of the toplevel parent
75 // of the popup 75 // of the popup
76 struct wlr_box output_toplevel_sx_box = { 76 struct wlr_box output_toplevel_sx_box = {
77 .x = output->lx - view->x, 77 .x = output->lx - view->container->content_x,
78 .y = output->ly - view->y, 78 .y = output->ly - view->container->content_y,
79 .width = output->width, 79 .width = output->width,
80 .height = output->height, 80 .height = output->height,
81 }; 81 };
@@ -283,9 +283,11 @@ static void handle_commit(struct wl_listener *listener, void *data) {
283 } else { 283 } else {
284 struct wlr_box new_geo; 284 struct wlr_box new_geo;
285 wlr_xdg_surface_v6_get_geometry(xdg_surface_v6, &new_geo); 285 wlr_xdg_surface_v6_get_geometry(xdg_surface_v6, &new_geo);
286 struct sway_container *con = view->container;
286 287
287 if ((new_geo.width != view->width || new_geo.height != view->height) && 288 if ((new_geo.width != con->content_width ||
288 container_is_floating(view->container)) { 289 new_geo.height != con->content_height) &&
290 container_is_floating(con)) {
289 // A floating view has unexpectedly sent a new size 291 // A floating view has unexpectedly sent a new size
290 desktop_damage_view(view); 292 desktop_damage_view(view);
291 view_update_size(view, new_geo.width, new_geo.height); 293 view_update_size(view, new_geo.width, new_geo.height);
diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c
index 0c41d960..1838ad32 100644
--- a/sway/desktop/xwayland.c
+++ b/sway/desktop/xwayland.c
@@ -332,9 +332,11 @@ static void handle_commit(struct wl_listener *listener, void *data) {
332 } else { 332 } else {
333 struct wlr_box new_geo; 333 struct wlr_box new_geo;
334 get_geometry(view, &new_geo); 334 get_geometry(view, &new_geo);
335 struct sway_container *con = view->container;
335 336
336 if ((new_geo.width != view->width || new_geo.height != view->height) && 337 if ((new_geo.width != con->content_width ||
337 container_is_floating(view->container)) { 338 new_geo.height != con->content_height) &&
339 container_is_floating(con)) {
338 // A floating view has unexpectedly sent a new size 340 // A floating view has unexpectedly sent a new size
339 // eg. The Firefox "Save As" dialog when downloading a file 341 // eg. The Firefox "Save As" dialog when downloading a file
340 desktop_damage_view(view); 342 desktop_damage_view(view);
@@ -432,13 +434,13 @@ static void handle_request_configure(struct wl_listener *listener, void *data) {
432 return; 434 return;
433 } 435 }
434 if (container_is_floating(view->container)) { 436 if (container_is_floating(view->container)) {
435 configure(view, view->container->current.view_x, 437 configure(view, view->container->current.content_x,
436 view->container->current.view_y, ev->width, ev->height); 438 view->container->current.content_y, ev->width, ev->height);
437 } else { 439 } else {
438 configure(view, view->container->current.view_x, 440 configure(view, view->container->current.content_x,
439 view->container->current.view_y, 441 view->container->current.content_y,
440 view->container->current.view_width, 442 view->container->current.content_width,
441 view->container->current.view_height); 443 view->container->current.content_height);
442 } 444 }
443} 445}
444 446
diff --git a/sway/input/cursor.c b/sway/input/cursor.c
index c539df40..d89f64d8 100644
--- a/sway/input/cursor.c
+++ b/sway/input/cursor.c
@@ -5,6 +5,7 @@
5#elif __FreeBSD__ 5#elif __FreeBSD__
6#include <dev/evdev/input-event-codes.h> 6#include <dev/evdev/input-event-codes.h>
7#endif 7#endif
8#include <float.h>
8#include <limits.h> 9#include <limits.h>
9#include <wlr/types/wlr_cursor.h> 10#include <wlr/types/wlr_cursor.h>
10#include <wlr/types/wlr_xcursor_manager.h> 11#include <wlr/types/wlr_xcursor_manager.h>
@@ -63,7 +64,7 @@ static struct sway_node *node_at_coords(
63 struct sway_seat *seat, double lx, double ly, 64 struct sway_seat *seat, double lx, double ly,
64 struct wlr_surface **surface, double *sx, double *sy) { 65 struct wlr_surface **surface, double *sx, double *sy) {
65 // check for unmanaged views first 66 // check for unmanaged views first
66#ifdef HAVE_XWAYLAND 67#if HAVE_XWAYLAND
67 struct wl_list *unmanaged = &root->xwayland_unmanaged; 68 struct wl_list *unmanaged = &root->xwayland_unmanaged;
68 struct sway_xwayland_unmanaged *unmanaged_surface; 69 struct sway_xwayland_unmanaged *unmanaged_surface;
69 wl_list_for_each_reverse(unmanaged_surface, unmanaged, link) { 70 wl_list_for_each_reverse(unmanaged_surface, unmanaged, link) {
@@ -348,7 +349,7 @@ static void handle_move_tiling_motion(struct sway_seat *seat,
348 } 349 }
349 350
350 // Find the closest edge 351 // Find the closest edge
351 size_t thickness = fmin(con->view->width, con->view->height) * 0.3; 352 size_t thickness = fmin(con->content_width, con->content_height) * 0.3;
352 size_t closest_dist = INT_MAX; 353 size_t closest_dist = INT_MAX;
353 size_t dist; 354 size_t dist;
354 seat->op_target_edge = WLR_EDGE_NONE; 355 seat->op_target_edge = WLR_EDGE_NONE;
@@ -374,10 +375,10 @@ static void handle_move_tiling_motion(struct sway_seat *seat,
374 } 375 }
375 376
376 seat->op_target_node = node; 377 seat->op_target_node = node;
377 seat->op_drop_box.x = con->view->x; 378 seat->op_drop_box.x = con->content_x;
378 seat->op_drop_box.y = con->view->y; 379 seat->op_drop_box.y = con->content_y;
379 seat->op_drop_box.width = con->view->width; 380 seat->op_drop_box.width = con->content_width;
380 seat->op_drop_box.height = con->view->height; 381 seat->op_drop_box.height = con->content_height;
381 resize_box(&seat->op_drop_box, seat->op_target_edge, thickness); 382 resize_box(&seat->op_drop_box, seat->op_target_edge, thickness);
382 desktop_damage_box(&seat->op_drop_box); 383 desktop_damage_box(&seat->op_drop_box);
383} 384}
@@ -498,13 +499,10 @@ static void handle_resize_floating_motion(struct sway_seat *seat,
498 con->width += relative_grow_width; 499 con->width += relative_grow_width;
499 con->height += relative_grow_height; 500 con->height += relative_grow_height;
500 501
501 if (con->view) { 502 con->content_x += relative_grow_x;
502 struct sway_view *view = con->view; 503 con->content_y += relative_grow_y;
503 view->x += relative_grow_x; 504 con->content_width += relative_grow_width;
504 view->y += relative_grow_y; 505 con->content_height += relative_grow_height;
505 view->width += relative_grow_width;
506 view->height += relative_grow_height;
507 }
508 506
509 arrange_container(con); 507 arrange_container(con);
510} 508}
@@ -637,7 +635,8 @@ void cursor_send_pointer_motion(struct sway_cursor *cursor,
637 cursor->previous.y = cursor->cursor->y; 635 cursor->previous.y = cursor->cursor->y;
638 cursor->previous.node = node; 636 cursor->previous.node = node;
639 637
640 if (node && config->focus_follows_mouse) { 638 if (node && (config->focus_follows_mouse == FOLLOWS_YES ||
639 config->focus_follows_mouse == FOLLOWS_ALWAYS)) {
641 struct sway_node *focus = seat_get_focus(seat); 640 struct sway_node *focus = seat_get_focus(seat);
642 if (focus && node->type == N_WORKSPACE) { 641 if (focus && node->type == N_WORKSPACE) {
643 // Only follow the mouse if it would move to a new output 642 // Only follow the mouse if it would move to a new output
@@ -652,9 +651,10 @@ void cursor_send_pointer_motion(struct sway_cursor *cursor,
652 // - cursor is over a new view, i.e. entered a new window; and 651 // - cursor is over a new view, i.e. entered a new window; and
653 // - the new view is visible, i.e. not hidden in a stack or tab; and 652 // - the new view is visible, i.e. not hidden in a stack or tab; and
654 // - the seat does not have a keyboard grab 653 // - the seat does not have a keyboard grab
655 if (!wlr_seat_keyboard_has_grab(cursor->seat->wlr_seat) && 654 if ((!wlr_seat_keyboard_has_grab(cursor->seat->wlr_seat) &&
656 node != prev_node && 655 node != prev_node &&
657 view_is_visible(node->sway_container->view)) { 656 view_is_visible(node->sway_container->view)) ||
657 config->focus_follows_mouse == FOLLOWS_ALWAYS) {
658 seat_set_focus(seat, node); 658 seat_set_focus(seat, node);
659 } else { 659 } else {
660 struct sway_node *next_focus = 660 struct sway_node *next_focus =
@@ -861,8 +861,8 @@ void dispatch_cursor_button(struct sway_cursor *cursor,
861 } 861 }
862 862
863 // Handle tiling resize via border 863 // Handle tiling resize via border
864 if (resize_edge && button == BTN_LEFT && state == WLR_BUTTON_PRESSED && 864 if (cont && resize_edge && button == BTN_LEFT &&
865 !is_floating) { 865 state == WLR_BUTTON_PRESSED && !is_floating) {
866 seat_set_focus_container(seat, cont); 866 seat_set_focus_container(seat, cont);
867 seat_begin_resize_tiling(seat, cont, button, edge); 867 seat_begin_resize_tiling(seat, cont, button, edge);
868 return; 868 return;
@@ -871,7 +871,8 @@ void dispatch_cursor_button(struct sway_cursor *cursor,
871 // Handle tiling resize via mod 871 // Handle tiling resize via mod
872 bool mod_pressed = keyboard && 872 bool mod_pressed = keyboard &&
873 (wlr_keyboard_get_modifiers(keyboard) & config->floating_mod); 873 (wlr_keyboard_get_modifiers(keyboard) & config->floating_mod);
874 if (!is_floating_or_child && mod_pressed && state == WLR_BUTTON_PRESSED) { 874 if (cont && !is_floating_or_child && mod_pressed &&
875 state == WLR_BUTTON_PRESSED) {
875 uint32_t btn_resize = config->floating_mod_inverse ? 876 uint32_t btn_resize = config->floating_mod_inverse ?
876 BTN_LEFT : BTN_RIGHT; 877 BTN_LEFT : BTN_RIGHT;
877 if (button == btn_resize) { 878 if (button == btn_resize) {
@@ -899,7 +900,7 @@ void dispatch_cursor_button(struct sway_cursor *cursor,
899 } 900 }
900 901
901 // Handle beginning floating move 902 // Handle beginning floating move
902 if (is_floating_or_child && !is_fullscreen_or_child && 903 if (cont && is_floating_or_child && !is_fullscreen_or_child &&
903 state == WLR_BUTTON_PRESSED) { 904 state == WLR_BUTTON_PRESSED) {
904 uint32_t btn_move = config->floating_mod_inverse ? BTN_RIGHT : BTN_LEFT; 905 uint32_t btn_move = config->floating_mod_inverse ? BTN_RIGHT : BTN_LEFT;
905 if (button == btn_move && state == WLR_BUTTON_PRESSED && 906 if (button == btn_move && state == WLR_BUTTON_PRESSED &&
@@ -914,7 +915,7 @@ void dispatch_cursor_button(struct sway_cursor *cursor,
914 } 915 }
915 916
916 // Handle beginning floating resize 917 // Handle beginning floating resize
917 if (is_floating_or_child && !is_fullscreen_or_child && 918 if (cont && is_floating_or_child && !is_fullscreen_or_child &&
918 state == WLR_BUTTON_PRESSED) { 919 state == WLR_BUTTON_PRESSED) {
919 // Via border 920 // Via border
920 if (button == BTN_LEFT && resize_edge != WLR_EDGE_NONE) { 921 if (button == BTN_LEFT && resize_edge != WLR_EDGE_NONE) {
@@ -979,6 +980,8 @@ static void handle_cursor_button(struct wl_listener *listener, void *data) {
979static void dispatch_cursor_axis(struct sway_cursor *cursor, 980static void dispatch_cursor_axis(struct sway_cursor *cursor,
980 struct wlr_event_pointer_axis *event) { 981 struct wlr_event_pointer_axis *event) {
981 struct sway_seat *seat = cursor->seat; 982 struct sway_seat *seat = cursor->seat;
983 struct sway_input_device *input_device = event->device->data;
984 struct input_config *ic = input_device_get_config(input_device);
982 985
983 // Determine what's under the cursor 986 // Determine what's under the cursor
984 struct wlr_surface *surface = NULL; 987 struct wlr_surface *surface = NULL;
@@ -990,6 +993,8 @@ static void dispatch_cursor_axis(struct sway_cursor *cursor,
990 enum wlr_edges edge = cont ? find_edge(cont, cursor) : WLR_EDGE_NONE; 993 enum wlr_edges edge = cont ? find_edge(cont, cursor) : WLR_EDGE_NONE;
991 bool on_border = edge != WLR_EDGE_NONE; 994 bool on_border = edge != WLR_EDGE_NONE;
992 bool on_titlebar = cont && !on_border && !surface; 995 bool on_titlebar = cont && !on_border && !surface;
996 float scroll_factor =
997 (ic == NULL || ic->scroll_factor == FLT_MIN) ? 1.0f : ic->scroll_factor;
993 998
994 // Scrolling on a tabbed or stacked title bar 999 // Scrolling on a tabbed or stacked title bar
995 if (on_titlebar) { 1000 if (on_titlebar) {
@@ -1000,7 +1005,7 @@ static void dispatch_cursor_axis(struct sway_cursor *cursor,
1000 seat_get_active_tiling_child(seat, tabcontainer); 1005 seat_get_active_tiling_child(seat, tabcontainer);
1001 list_t *siblings = container_get_siblings(cont); 1006 list_t *siblings = container_get_siblings(cont);
1002 int desired = list_find(siblings, active->sway_container) + 1007 int desired = list_find(siblings, active->sway_container) +
1003 event->delta_discrete; 1008 round(scroll_factor * event->delta_discrete);
1004 if (desired < 0) { 1009 if (desired < 0) {
1005 desired = 0; 1010 desired = 0;
1006 } else if (desired >= siblings->length) { 1011 } else if (desired >= siblings->length) {
@@ -1024,7 +1029,8 @@ static void dispatch_cursor_axis(struct sway_cursor *cursor,
1024 } 1029 }
1025 1030
1026 wlr_seat_pointer_notify_axis(cursor->seat->wlr_seat, event->time_msec, 1031 wlr_seat_pointer_notify_axis(cursor->seat->wlr_seat, event->time_msec,
1027 event->orientation, event->delta, event->delta_discrete, event->source); 1032 event->orientation, scroll_factor * event->delta,
1033 round(scroll_factor * event->delta_discrete), event->source);
1028} 1034}
1029 1035
1030static void handle_cursor_axis(struct wl_listener *listener, void *data) { 1036static void handle_cursor_axis(struct wl_listener *listener, void *data) {
diff --git a/sway/input/seat.c b/sway/input/seat.c
index 54fdf40b..663c5140 100644
--- a/sway/input/seat.c
+++ b/sway/input/seat.c
@@ -92,7 +92,7 @@ static void seat_send_focus(struct sway_node *node, struct sway_seat *seat) {
92 node->sway_container->view : NULL; 92 node->sway_container->view : NULL;
93 93
94 if (view && seat_is_input_allowed(seat, view->surface)) { 94 if (view && seat_is_input_allowed(seat, view->surface)) {
95#ifdef HAVE_XWAYLAND 95#if HAVE_XWAYLAND
96 if (view->type == SWAY_VIEW_XWAYLAND) { 96 if (view->type == SWAY_VIEW_XWAYLAND) {
97 struct wlr_xwayland *xwayland = server.xwayland.wlr_xwayland; 97 struct wlr_xwayland *xwayland = server.xwayland.wlr_xwayland;
98 wlr_xwayland_set_seat(xwayland, seat->wlr_seat); 98 wlr_xwayland_set_seat(xwayland, seat->wlr_seat);
diff --git a/sway/ipc-json.c b/sway/ipc-json.c
index cf1b42a6..05e453ec 100644
--- a/sway/ipc-json.c
+++ b/sway/ipc-json.c
@@ -12,6 +12,7 @@
12#include "sway/input/seat.h" 12#include "sway/input/seat.h"
13#include <wlr/types/wlr_box.h> 13#include <wlr/types/wlr_box.h>
14#include <wlr/types/wlr_output.h> 14#include <wlr/types/wlr_output.h>
15#include <xkbcommon/xkbcommon.h>
15#include "wlr-layer-shell-unstable-v1-protocol.h" 16#include "wlr-layer-shell-unstable-v1-protocol.h"
16 17
17static const char *ipc_json_layout_description(enum sway_container_layout l) { 18static const char *ipc_json_layout_description(enum sway_container_layout l) {
@@ -245,10 +246,10 @@ static void ipc_json_describe_view(struct sway_container *c, json_object *object
245 json_object_object_add(object, "marks", marks); 246 json_object_object_add(object, "marks", marks);
246 247
247 struct wlr_box window_box = { 248 struct wlr_box window_box = {
248 c->view->x - c->x, 249 c->content_x - c->x,
249 (c->current.border == B_PIXEL) ? c->current.border_thickness : 0, 250 (c->current.border == B_PIXEL) ? c->current.border_thickness : 0,
250 c->view->width, 251 c->content_width,
251 c->view->height 252 c->content_height
252 }; 253 };
253 254
254 json_object_object_add(object, "window_rect", ipc_json_create_rect(&window_box)); 255 json_object_object_add(object, "window_rect", ipc_json_create_rect(&window_box));
@@ -257,7 +258,7 @@ static void ipc_json_describe_view(struct sway_container *c, json_object *object
257 258
258 if (c->current.border == B_NORMAL) { 259 if (c->current.border == B_NORMAL) {
259 deco_box.width = c->width; 260 deco_box.width = c->width;
260 deco_box.height = c->view->y - c->y; 261 deco_box.height = c->content_y - c->y;
261 } 262 }
262 263
263 json_object_object_add(object, "deco_rect", ipc_json_create_rect(&deco_box)); 264 json_object_object_add(object, "deco_rect", ipc_json_create_rect(&deco_box));
@@ -265,7 +266,7 @@ static void ipc_json_describe_view(struct sway_container *c, json_object *object
265 struct wlr_box geometry = {0, 0, c->view->natural_width, c->view->natural_height}; 266 struct wlr_box geometry = {0, 0, c->view->natural_width, c->view->natural_height};
266 json_object_object_add(object, "geometry", ipc_json_create_rect(&geometry)); 267 json_object_object_add(object, "geometry", ipc_json_create_rect(&geometry));
267 268
268#ifdef HAVE_XWAYLAND 269#if HAVE_XWAYLAND
269 if (c->view->type == SWAY_VIEW_XWAYLAND) { 270 if (c->view->type == SWAY_VIEW_XWAYLAND) {
270 json_object_object_add(object, "window", 271 json_object_object_add(object, "window",
271 json_object_new_int(view_get_x11_window_id(c->view))); 272 json_object_new_int(view_get_x11_window_id(c->view)));
@@ -503,6 +504,27 @@ json_object *ipc_json_describe_input(struct sway_input_device *device) {
503 json_object_object_add(object, "type", 504 json_object_object_add(object, "type",
504 json_object_new_string(describe_device_type(device))); 505 json_object_new_string(describe_device_type(device)));
505 506
507 if (device->wlr_device->type == WLR_INPUT_DEVICE_KEYBOARD) {
508 struct wlr_keyboard *keyboard = device->wlr_device->keyboard;
509 struct xkb_keymap *keymap = keyboard->keymap;
510 struct xkb_state *state = keyboard->xkb_state;
511 xkb_layout_index_t num_layouts = xkb_keymap_num_layouts(keymap);
512 xkb_layout_index_t layout_idx;
513 for (layout_idx = 0; layout_idx < num_layouts; layout_idx++) {
514 bool is_active =
515 xkb_state_layout_index_is_active(state,
516 layout_idx,
517 XKB_STATE_LAYOUT_EFFECTIVE);
518 if (is_active) {
519 const char *layout =
520 xkb_keymap_layout_get_name(keymap, layout_idx);
521 json_object_object_add(object, "xkb_active_layout_name",
522 json_object_new_string(layout));
523 break;
524 }
525 }
526 }
527
506 return object; 528 return object;
507} 529}
508 530
diff --git a/sway/meson.build b/sway/meson.build
index debd7a91..14822dbd 100644
--- a/sway/meson.build
+++ b/sway/meson.build
@@ -136,6 +136,7 @@ sway_sources = files(
136 'commands/input/repeat_delay.c', 136 'commands/input/repeat_delay.c',
137 'commands/input/repeat_rate.c', 137 'commands/input/repeat_rate.c',
138 'commands/input/scroll_button.c', 138 'commands/input/scroll_button.c',
139 'commands/input/scroll_factor.c',
139 'commands/input/scroll_method.c', 140 'commands/input/scroll_method.c',
140 'commands/input/tap.c', 141 'commands/input/tap.c',
141 'commands/input/tap_button_map.c', 142 'commands/input/tap_button_map.c',
diff --git a/sway/server.c b/sway/server.c
index f06173d1..cd3fcdf6 100644
--- a/sway/server.c
+++ b/sway/server.c
@@ -26,7 +26,7 @@
26#include "sway/server.h" 26#include "sway/server.h"
27#include "sway/tree/root.h" 27#include "sway/tree/root.h"
28#include "config.h" 28#include "config.h"
29#ifdef HAVE_XWAYLAND 29#if HAVE_XWAYLAND
30#include "sway/xwayland.h" 30#include "sway/xwayland.h"
31#endif 31#endif
32 32
@@ -94,7 +94,7 @@ bool server_init(struct sway_server *server) {
94 setenv("XCURSOR_THEME", cursor_theme, 1); 94 setenv("XCURSOR_THEME", cursor_theme, 1);
95 } 95 }
96 96
97#ifdef HAVE_XWAYLAND 97#if HAVE_XWAYLAND
98 server->xwayland.wlr_xwayland = 98 server->xwayland.wlr_xwayland =
99 wlr_xwayland_create(server->wl_display, server->compositor, true); 99 wlr_xwayland_create(server->wl_display, server->compositor, true);
100 wl_signal_add(&server->xwayland.wlr_xwayland->events.new_surface, 100 wl_signal_add(&server->xwayland.wlr_xwayland->events.new_surface,
@@ -164,7 +164,7 @@ bool server_init(struct sway_server *server) {
164 164
165void server_fini(struct sway_server *server) { 165void server_fini(struct sway_server *server) {
166 // TODO: free sway-specific resources 166 // TODO: free sway-specific resources
167#ifdef HAVE_XWAYLAND 167#if HAVE_XWAYLAND
168 wlr_xwayland_destroy(server->xwayland.wlr_xwayland); 168 wlr_xwayland_destroy(server->xwayland.wlr_xwayland);
169#endif 169#endif
170 wl_display_destroy_clients(server->wl_display); 170 wl_display_destroy_clients(server->wl_display);
diff --git a/sway/sway-input.5.scd b/sway/sway-input.5.scd
index 82273ef3..45994644 100644
--- a/sway/sway-input.5.scd
+++ b/sway/sway-input.5.scd
@@ -105,14 +105,18 @@ The following commands may only be used in the configuration file.
105*input* <identifier> repeat\_rate <characters per second> 105*input* <identifier> repeat\_rate <characters per second>
106 Sets the frequency of key repeats once the repeat\_delay has passed. 106 Sets the frequency of key repeats once the repeat\_delay has passed.
107 107
108*input* <identifier> scroll\_method none|two\_finger|edge|on\_button\_down
109 Changes the scroll method for the specified input device.
110
111*input* <identifier> scroll\_button <button\_identifier> 108*input* <identifier> scroll\_button <button\_identifier>
112 Sets button used for scroll\_method on\_button\_down. The button identifier 109 Sets button used for scroll\_method on\_button\_down. The button identifier
113 can be obtained from `libinput debug-events`. 110 can be obtained from `libinput debug-events`.
114 If set to 0, it disables the scroll\_button on\_button\_down. 111 If set to 0, it disables the scroll\_button on\_button\_down.
115 112
113*input* <identifier> scroll\_factor <floating point value>
114 Changes the scroll factor for the specified input device. Scroll speed will
115 be scaled by the given value, which must be non-negative.
116
117*input* <identifier> scroll\_method none|two\_finger|edge|on\_button\_down
118 Changes the scroll method for the specified input device.
119
116*input* <identifier> tap enabled|disabled 120*input* <identifier> tap enabled|disabled
117 Enables or disables tap for specified input device. 121 Enables or disables tap for specified input device.
118 122
diff --git a/sway/sway.5.scd b/sway/sway.5.scd
index 4a645837..1a11015f 100644
--- a/sway/sway.5.scd
+++ b/sway/sway.5.scd
@@ -133,9 +133,12 @@ They are expected to be used with *bindsym* or at runtime through *swaymsg*(1).
133*fullscreen* 133*fullscreen*
134 Toggles fullscreen for the focused view. 134 Toggles fullscreen for the focused view.
135 135
136*gaps* inner|outer all|current set|plus|minus <amount> 136*gaps* inner|outer|horizontal|vertical|top|right|bottom|left all|current
137set|plus|minus <amount>
137 Changes the _inner_ or _outer_ gaps for either _all_ workspaces or the 138 Changes the _inner_ or _outer_ gaps for either _all_ workspaces or the
138 _current_ workspace. 139 _current_ workspace. _outer_ gaps can be altered per side with _top_,
140 _right_, _bottom_, and _left_ or per direction with _horizontal_ and
141 _vertical_.
139 142
140*layout* default|splith|splitv|stacking|tabbed 143*layout* default|splith|splitv|stacking|tabbed
141 Sets the layout mode of the focused container. 144 Sets the layout mode of the focused container.
@@ -207,11 +210,23 @@ They are expected to be used with *bindsym* or at runtime through *swaymsg*(1).
207 percentage points. If the units are omitted, floating containers are resized 210 percentage points. If the units are omitted, floating containers are resized
208 in px and tiled containers by ppt. _amount_ will default to 10 if omitted. 211 in px and tiled containers by ppt. _amount_ will default to 10 if omitted.
209 212
210*resize set* <width> [px|ppt] <height> [px|ppt] 213*resize set* height <height> [px|ppt]
211 Sets the width and height of the currently focused container to _width_ and 214 Sets the height of the container to _height_, specified in pixels or
212 _height_, specified in pixels or percentage points. If the units are 215 percentage points. If the units are omitted, floating containers are
213 omitted, floating containers are resized in px and tiled containers by ppt. 216 resized in px and tiled containers by ppt. If _height_ is 0, the container
214 If _width_ or _height_ is 0, no resize is done on that axis. 217 will not be resized.
218
219*resize set* [width] <width> [px|ppt]
220 Sets the width of the container to _width_, specified in pixels or
221 percentage points. If the units are omitted, floating containers are
222 resized in px and tiled containers by ppt. If _width_ is 0, the container
223 will not be resized.
224
225*resize set* [width] <width> [px|ppt] [height] <height> [px|ppt]
226 Sets the width and height of the container to _width_ and _height_,
227 specified in pixels or percentage points. If the units are omitted,
228 floating containers are resized in px and tiled containers by ppt. If
229 _width_ or _height_ is 0, the container will not be resized on that axis.
215 230
216*scratchpad show* 231*scratchpad show*
217 Shows a window from the scratchpad. Repeatedly using this command will 232 Shows a window from the scratchpad. Repeatedly using this command will
@@ -404,8 +419,10 @@ The default colors are:
404 specified direction while holding the floating modifier. Resets the 419 specified direction while holding the floating modifier. Resets the
405 command, when given no arguments. 420 command, when given no arguments.
406 421
407*focus\_follows\_mouse* yes|no 422*focus\_follows\_mouse* yes|no|always
408 If set to _yes_, moving your mouse over a window will focus that window. 423 If set to _yes_, moving your mouse over a window will focus that window. If
424 set to _always_, the window under the cursor will always be focused, even
425 after switching between workspaces.
409 426
410*focus\_wrapping* yes|no|force 427*focus\_wrapping* yes|no|force
411 This option determines what to do when attempting to focus over the edge 428 This option determines what to do when attempting to focus over the edge
@@ -429,14 +446,16 @@ The default colors are:
429 _focus\_wrapping force_. This is only available for convenience. Please 446 _focus\_wrapping force_. This is only available for convenience. Please
430 use _focus\_wrapping_ instead when possible. 447 use _focus\_wrapping_ instead when possible.
431 448
432*gaps* inner|outer <amount> 449*gaps* inner|outer|horizontal|vertical|top|right|bottom|left <amount>
433 Sets default _amount_ pixels of _inner_ or _outer_ gap, where the inner 450 Sets default _amount_ pixels of _inner_ or _outer_ gap, where the inner
434 affects spacing around each view and outer affects the spacing around each 451 affects spacing around each view and outer affects the spacing around each
435 workspace. Outer gaps are in addition to inner gaps. To reduce or remove 452 workspace. Outer gaps are in addition to inner gaps. To reduce or remove
436 outer gaps, outer gaps can be set to a negative value. 453 outer gaps, outer gaps can be set to a negative value. _outer_ gaps can
454 also be specified per side with _top_, _right_, _bottom_, and _left_ or
455 per direction with _horizontal_ and _vertical_.
437 456
438 This affects new workspaces only, and is used when the workspace doesn't 457 This affects new workspaces only, and is used when the workspace doesn't
439 have its own gaps settings (see: workspace <ws> gaps inner|outer <amount>). 458 have its own gaps settings (see: workspace <ws> gaps ...).
440 459
441*hide\_edge\_borders* none|vertical|horizontal|both|smart|smart\_no\_gaps 460*hide\_edge\_borders* none|vertical|horizontal|both|smart|smart\_no\_gaps
442 Hides window borders adjacent to the screen edges. Default is _none_. 461 Hides window borders adjacent to the screen edges. Default is _none_.
@@ -549,12 +568,17 @@ The default colors are:
549*workspace* back\_and\_forth 568*workspace* back\_and\_forth
550 Switches to the previously focused workspace. 569 Switches to the previously focused workspace.
551 570
552*workspace* <name> gaps inner|outer <amount> 571*workspace* <name> gaps inner|outer|horizontal|vertical|top|right|bottom|left
572<amount>
553 Specifies that workspace _name_ should have the given gaps settings when it 573 Specifies that workspace _name_ should have the given gaps settings when it
554 is created. 574 is created.
555 575
556*workspace* <name> output <output> 576*workspace* <name> output <outputs...>
557 Specifies that workspace _name_ should be shown on the specified _output_. 577 Specifies that workspace _name_ should be shown on the specified _outputs_.
578 Multiple outputs can be listed and the first available will be used. If the
579 workspace gets placed on an output further down the list and an output that
580 is higher on the list becomes available, the workspace will be move to the
581 higher priority output.
558 582
559*workspace\_auto\_back\_and\_forth* yes|no 583*workspace\_auto\_back\_and\_forth* yes|no
560 When _yes_, repeating a workspace switch command will switch back to the 584 When _yes_, repeating a workspace switch command will switch back to the
diff --git a/sway/tree/container.c b/sway/tree/container.c
index 458ed7ff..cf6f5b54 100644
--- a/sway/tree/container.c
+++ b/sway/tree/container.c
@@ -165,13 +165,13 @@ static struct sway_container *surface_at_view(struct sway_container *con, double
165 return NULL; 165 return NULL;
166 } 166 }
167 struct sway_view *view = con->view; 167 struct sway_view *view = con->view;
168 double view_sx = lx - view->x + view->geometry.x; 168 double view_sx = lx - con->content_x + view->geometry.x;
169 double view_sy = ly - view->y + view->geometry.y; 169 double view_sy = ly - con->content_y + view->geometry.y;
170 170
171 double _sx, _sy; 171 double _sx, _sy;
172 struct wlr_surface *_surface = NULL; 172 struct wlr_surface *_surface = NULL;
173 switch (view->type) { 173 switch (view->type) {
174#ifdef HAVE_XWAYLAND 174#if HAVE_XWAYLAND
175 case SWAY_VIEW_XWAYLAND: 175 case SWAY_VIEW_XWAYLAND:
176 _surface = wlr_surface_surface_at(view->surface, 176 _surface = wlr_surface_surface_at(view->surface,
177 view_sx, view_sy, &_sx, &_sy); 177 view_sx, view_sy, &_sx, &_sy);
@@ -641,16 +641,18 @@ void container_init_floating(struct sway_container *con) {
641 con->y = ws->y + (ws->height - con->height) / 2; 641 con->y = ws->y + (ws->height - con->height) / 2;
642 } else { 642 } else {
643 struct sway_view *view = con->view; 643 struct sway_view *view = con->view;
644 view->width = fmax(min_width, fmin(view->natural_width, max_width)); 644 con->content_width =
645 view->height = fmax(min_height, fmin(view->natural_height, max_height)); 645 fmax(min_width, fmin(view->natural_width, max_width));
646 view->x = ws->x + (ws->width - view->width) / 2; 646 con->content_height =
647 view->y = ws->y + (ws->height - view->height) / 2; 647 fmax(min_height, fmin(view->natural_height, max_height));
648 con->content_x = ws->x + (ws->width - con->content_width) / 2;
649 con->content_y = ws->y + (ws->height - con->content_height) / 2;
648 650
649 // If the view's border is B_NONE then these properties are ignored. 651 // If the view's border is B_NONE then these properties are ignored.
650 con->border_top = con->border_bottom = true; 652 con->border_top = con->border_bottom = true;
651 con->border_left = con->border_right = true; 653 con->border_left = con->border_right = true;
652 654
653 container_set_geometry_from_floating_view(con); 655 container_set_geometry_from_content(con);
654 } 656 }
655} 657}
656 658
@@ -707,14 +709,13 @@ void container_set_floating(struct sway_container *container, bool enable) {
707 ipc_event_window(container, "floating"); 709 ipc_event_window(container, "floating");
708} 710}
709 711
710void container_set_geometry_from_floating_view(struct sway_container *con) { 712void container_set_geometry_from_content(struct sway_container *con) {
711 if (!sway_assert(con->view, "Expected a view")) { 713 if (!sway_assert(con->view, "Expected a view")) {
712 return; 714 return;
713 } 715 }
714 if (!sway_assert(container_is_floating(con), "Expected a floating view")) { 716 if (!sway_assert(container_is_floating(con), "Expected a floating view")) {
715 return; 717 return;
716 } 718 }
717 struct sway_view *view = con->view;
718 size_t border_width = 0; 719 size_t border_width = 0;
719 size_t top = 0; 720 size_t top = 0;
720 721
@@ -724,10 +725,10 @@ void container_set_geometry_from_floating_view(struct sway_container *con) {
724 container_titlebar_height() : border_width; 725 container_titlebar_height() : border_width;
725 } 726 }
726 727
727 con->x = view->x - border_width; 728 con->x = con->content_x - border_width;
728 con->y = view->y - top; 729 con->y = con->content_y - top;
729 con->width = view->width + border_width * 2; 730 con->width = con->content_width + border_width * 2;
730 con->height = top + view->height + border_width; 731 con->height = top + con->content_height + border_width;
731 node_set_dirty(&con->node); 732 node_set_dirty(&con->node);
732} 733}
733 734
@@ -756,15 +757,16 @@ void container_floating_translate(struct sway_container *con,
756 double x_amount, double y_amount) { 757 double x_amount, double y_amount) {
757 con->x += x_amount; 758 con->x += x_amount;
758 con->y += y_amount; 759 con->y += y_amount;
759 if (con->view) { 760 con->content_x += x_amount;
760 con->view->x += x_amount; 761 con->content_y += y_amount;
761 con->view->y += y_amount; 762
762 } else { 763 if (con->children) {
763 for (int i = 0; i < con->children->length; ++i) { 764 for (int i = 0; i < con->children->length; ++i) {
764 struct sway_container *child = con->children->items[i]; 765 struct sway_container *child = con->children->items[i];
765 container_floating_translate(child, x_amount, y_amount); 766 container_floating_translate(child, x_amount, y_amount);
766 } 767 }
767 } 768 }
769
768 node_set_dirty(&con->node); 770 node_set_dirty(&con->node);
769} 771}
770 772
@@ -964,10 +966,10 @@ static void surface_send_leave_iterator(struct wlr_surface *surface,
964 966
965void container_discover_outputs(struct sway_container *con) { 967void container_discover_outputs(struct sway_container *con) {
966 struct wlr_box con_box = { 968 struct wlr_box con_box = {
967 .x = con->current.con_x, 969 .x = con->current.x,
968 .y = con->current.con_y, 970 .y = con->current.y,
969 .width = con->current.con_width, 971 .width = con->current.width,
970 .height = con->current.con_height, 972 .height = con->current.height,
971 }; 973 };
972 struct sway_output *old_output = container_get_effective_output(con); 974 struct sway_output *old_output = container_get_effective_output(con);
973 975
@@ -1009,19 +1011,25 @@ void container_discover_outputs(struct sway_container *con) {
1009} 1011}
1010 1012
1011void container_remove_gaps(struct sway_container *c) { 1013void container_remove_gaps(struct sway_container *c) {
1012 if (c->current_gaps == 0) { 1014 if (c->current_gaps.top == 0 && c->current_gaps.right == 0 &&
1015 c->current_gaps.bottom == 0 && c->current_gaps.left == 0) {
1013 return; 1016 return;
1014 } 1017 }
1015 1018
1016 c->width += c->current_gaps * 2; 1019 c->width += c->current_gaps.left + c->current_gaps.right;
1017 c->height += c->current_gaps * 2; 1020 c->height += c->current_gaps.top + c->current_gaps.bottom;
1018 c->x -= c->current_gaps; 1021 c->x -= c->current_gaps.left;
1019 c->y -= c->current_gaps; 1022 c->y -= c->current_gaps.top;
1020 c->current_gaps = 0; 1023
1024 c->current_gaps.top = 0;
1025 c->current_gaps.right = 0;
1026 c->current_gaps.bottom = 0;
1027 c->current_gaps.left = 0;
1021} 1028}
1022 1029
1023void container_add_gaps(struct sway_container *c) { 1030void container_add_gaps(struct sway_container *c) {
1024 if (c->current_gaps > 0) { 1031 if (c->current_gaps.top > 0 || c->current_gaps.right > 0 ||
1032 c->current_gaps.bottom > 0 || c->current_gaps.left > 0) {
1025 return; 1033 return;
1026 } 1034 }
1027 // Linear containers don't have gaps because it'd create double gaps 1035 // Linear containers don't have gaps because it'd create double gaps
@@ -1054,11 +1062,15 @@ void container_add_gaps(struct sway_container *c) {
1054 1062
1055 struct sway_workspace *ws = c->workspace; 1063 struct sway_workspace *ws = c->workspace;
1056 1064
1057 c->current_gaps = ws->gaps_inner; 1065 c->current_gaps.top = c->y == ws->y ? ws->gaps_inner : 0;
1058 c->x += c->current_gaps; 1066 c->current_gaps.right = ws->gaps_inner;
1059 c->y += c->current_gaps; 1067 c->current_gaps.bottom = ws->gaps_inner;
1060 c->width -= 2 * c->current_gaps; 1068 c->current_gaps.left = c->x == ws->x ? ws->gaps_inner : 0;
1061 c->height -= 2 * c->current_gaps; 1069
1070 c->x += c->current_gaps.left;
1071 c->y += c->current_gaps.top;
1072 c->width -= c->current_gaps.left + c->current_gaps.right;
1073 c->height -= c->current_gaps.top + c->current_gaps.bottom;
1062} 1074}
1063 1075
1064enum sway_container_layout container_parent_layout(struct sway_container *con) { 1076enum sway_container_layout container_parent_layout(struct sway_container *con) {
diff --git a/sway/tree/output.c b/sway/tree/output.c
index 2704920d..3c4614a8 100644
--- a/sway/tree/output.c
+++ b/sway/tree/output.c
@@ -31,6 +31,13 @@ static void restore_workspaces(struct sway_output *output) {
31 j--; 31 j--;
32 } 32 }
33 } 33 }
34
35 if (other->workspaces->length == 0) {
36 char *next = workspace_next_name(other->wlr_output->name);
37 struct sway_workspace *ws = workspace_create(other, next);
38 free(next);
39 ipc_event_workspace(NULL, ws, "init");
40 }
34 } 41 }
35 42
36 // Saved workspaces 43 // Saved workspaces
diff --git a/sway/tree/root.c b/sway/tree/root.c
index 9bda7c28..544d666a 100644
--- a/sway/tree/root.c
+++ b/sway/tree/root.c
@@ -31,7 +31,7 @@ struct sway_root *root_create(void) {
31 node_init(&root->node, N_ROOT, root); 31 node_init(&root->node, N_ROOT, root);
32 root->output_layout = wlr_output_layout_create(); 32 root->output_layout = wlr_output_layout_create();
33 wl_list_init(&root->all_outputs); 33 wl_list_init(&root->all_outputs);
34#ifdef HAVE_XWAYLAND 34#if HAVE_XWAYLAND
35 wl_list_init(&root->xwayland_unmanaged); 35 wl_list_init(&root->xwayland_unmanaged);
36#endif 36#endif
37 wl_list_init(&root->drag_icons); 37 wl_list_init(&root->drag_icons);
diff --git a/sway/tree/view.c b/sway/tree/view.c
index 1aa59e68..d7110619 100644
--- a/sway/tree/view.c
+++ b/sway/tree/view.c
@@ -8,7 +8,7 @@
8#include <wlr/types/wlr_server_decoration.h> 8#include <wlr/types/wlr_server_decoration.h>
9#include <wlr/types/wlr_xdg_decoration_v1.h> 9#include <wlr/types/wlr_xdg_decoration_v1.h>
10#include "config.h" 10#include "config.h"
11#ifdef HAVE_XWAYLAND 11#if HAVE_XWAYLAND
12#include <wlr/xwayland.h> 12#include <wlr/xwayland.h>
13#endif 13#endif
14#include "list.h" 14#include "list.h"
@@ -101,7 +101,7 @@ const char *view_get_instance(struct sway_view *view) {
101 } 101 }
102 return NULL; 102 return NULL;
103} 103}
104#ifdef HAVE_XWAYLAND 104#if HAVE_XWAYLAND
105uint32_t view_get_x11_window_id(struct sway_view *view) { 105uint32_t view_get_x11_window_id(struct sway_view *view) {
106 if (view->impl->get_int_prop) { 106 if (view->impl->get_int_prop) {
107 return view->impl->get_int_prop(view, VIEW_PROP_X11_WINDOW_ID); 107 return view->impl->get_int_prop(view, VIEW_PROP_X11_WINDOW_ID);
@@ -136,7 +136,7 @@ const char *view_get_shell(struct sway_view *view) {
136 return "xdg_shell_v6"; 136 return "xdg_shell_v6";
137 case SWAY_VIEW_XDG_SHELL: 137 case SWAY_VIEW_XDG_SHELL:
138 return "xdg_shell"; 138 return "xdg_shell";
139#ifdef HAVE_XWAYLAND 139#if HAVE_XWAYLAND
140 case SWAY_VIEW_XWAYLAND: 140 case SWAY_VIEW_XWAYLAND:
141 return "xwayland"; 141 return "xwayland";
142#endif 142#endif
@@ -185,31 +185,33 @@ bool view_is_only_visible(struct sway_view *view) {
185static bool gaps_to_edge(struct sway_view *view) { 185static bool gaps_to_edge(struct sway_view *view) {
186 struct sway_container *con = view->container; 186 struct sway_container *con = view->container;
187 while (con) { 187 while (con) {
188 if (con->current_gaps > 0) { 188 if (con->current_gaps.top > 0 || con->current_gaps.right > 0 ||
189 con->current_gaps.bottom > 0 || con->current_gaps.left > 0) {
189 return true; 190 return true;
190 } 191 }
191 con = con->parent; 192 con = con->parent;
192 } 193 }
193 return view->container->workspace->current_gaps > 0; 194 struct side_gaps gaps = view->container->workspace->current_gaps;
195 return gaps.top > 0 || gaps.right > 0 || gaps.bottom > 0 || gaps.left > 0;
194} 196}
195 197
196void view_autoconfigure(struct sway_view *view) { 198void view_autoconfigure(struct sway_view *view) {
197 if (!view->container->workspace) { 199 struct sway_container *con = view->container;
200 if (!con->workspace) {
198 // Hidden in the scratchpad 201 // Hidden in the scratchpad
199 return; 202 return;
200 } 203 }
201 struct sway_output *output = view->container->workspace->output; 204 struct sway_output *output = con->workspace->output;
202 205
203 if (view->container->is_fullscreen) { 206 if (con->is_fullscreen) {
204 view->x = output->lx; 207 con->content_x = output->lx;
205 view->y = output->ly; 208 con->content_y = output->ly;
206 view->width = output->width; 209 con->content_width = output->width;
207 view->height = output->height; 210 con->content_height = output->height;
208 return; 211 return;
209 } 212 }
210 213
211 struct sway_workspace *ws = view->container->workspace; 214 struct sway_workspace *ws = view->container->workspace;
212 struct sway_container *con = view->container;
213 215
214 bool smart = config->hide_edge_borders == E_SMART || 216 bool smart = config->hide_edge_borders == E_SMART ||
215 config->hide_edge_borders == E_SMART_NO_GAPS; 217 config->hide_edge_borders == E_SMART_NO_GAPS;
@@ -222,15 +224,15 @@ void view_autoconfigure(struct sway_view *view) {
222 if (config->hide_edge_borders == E_BOTH 224 if (config->hide_edge_borders == E_BOTH
223 || config->hide_edge_borders == E_VERTICAL 225 || config->hide_edge_borders == E_VERTICAL
224 || (smart && !other_views && no_gaps)) { 226 || (smart && !other_views && no_gaps)) {
225 con->border_left = con->x - con->current_gaps != ws->x; 227 con->border_left = con->x - con->current_gaps.left != ws->x;
226 int right_x = con->x + con->width + con->current_gaps; 228 int right_x = con->x + con->width + con->current_gaps.right;
227 con->border_right = right_x != ws->x + ws->width; 229 con->border_right = right_x != ws->x + ws->width;
228 } 230 }
229 if (config->hide_edge_borders == E_BOTH 231 if (config->hide_edge_borders == E_BOTH
230 || config->hide_edge_borders == E_HORIZONTAL 232 || config->hide_edge_borders == E_HORIZONTAL
231 || (smart && !other_views && no_gaps)) { 233 || (smart && !other_views && no_gaps)) {
232 con->border_top = con->y - con->current_gaps != ws->y; 234 con->border_top = con->y - con->current_gaps.top != ws->y;
233 int bottom_y = con->y + con->height + con->current_gaps; 235 int bottom_y = con->y + con->height + con->current_gaps.bottom;
234 con->border_bottom = bottom_y != ws->y + ws->height; 236 con->border_bottom = bottom_y != ws->y + ws->height;
235 } 237 }
236 238
@@ -287,10 +289,10 @@ void view_autoconfigure(struct sway_view *view) {
287 break; 289 break;
288 } 290 }
289 291
290 view->x = x; 292 con->content_x = x;
291 view->y = y; 293 con->content_y = y;
292 view->width = width; 294 con->content_width = width;
293 view->height = height; 295 con->content_height = height;
294} 296}
295 297
296void view_set_activated(struct sway_view *view, bool activated) { 298void view_set_activated(struct sway_view *view, bool activated) {
@@ -482,7 +484,7 @@ static struct sway_workspace *select_workspace(struct sway_view *view) {
482 484
483 // Check if there's a PID mapping 485 // Check if there's a PID mapping
484 pid_t pid; 486 pid_t pid;
485#ifdef HAVE_XWAYLAND 487#if HAVE_XWAYLAND
486 if (view->type == SWAY_VIEW_XWAYLAND) { 488 if (view->type == SWAY_VIEW_XWAYLAND) {
487 struct wlr_xwayland_surface *surf = 489 struct wlr_xwayland_surface *surf =
488 wlr_xwayland_surface_from_wlr_surface(view->surface); 490 wlr_xwayland_surface_from_wlr_surface(view->surface);
@@ -665,11 +667,11 @@ void view_update_size(struct sway_view *view, int width, int height) {
665 "Expected a floating container")) { 667 "Expected a floating container")) {
666 return; 668 return;
667 } 669 }
668 view->width = width; 670 view->container->content_width = width;
669 view->height = height; 671 view->container->content_height = height;
670 view->container->current.view_width = width; 672 view->container->current.content_width = width;
671 view->container->current.view_height = height; 673 view->container->current.content_height = height;
672 container_set_geometry_from_floating_view(view->container); 674 container_set_geometry_from_content(view->container);
673} 675}
674 676
675static void subsurface_get_root_coords(struct sway_view_child *child, 677static void subsurface_get_root_coords(struct sway_view_child *child,
@@ -705,7 +707,8 @@ static void view_child_damage(struct sway_view_child *child, bool whole) {
705 int sx, sy; 707 int sx, sy;
706 child->impl->get_root_coords(child, &sx, &sy); 708 child->impl->get_root_coords(child, &sx, &sy);
707 desktop_damage_surface(child->surface, 709 desktop_damage_surface(child->surface,
708 child->view->x + sx, child->view->y + sy, whole); 710 child->view->container->content_x + sx,
711 child->view->container->content_y + sy, whole);
709} 712}
710 713
711static void view_child_handle_surface_commit(struct wl_listener *listener, 714static void view_child_handle_surface_commit(struct wl_listener *listener,
@@ -799,7 +802,7 @@ struct sway_view *view_from_wlr_surface(struct wlr_surface *wlr_surface) {
799 wlr_xdg_surface_v6_from_wlr_surface(wlr_surface); 802 wlr_xdg_surface_v6_from_wlr_surface(wlr_surface);
800 return view_from_wlr_xdg_surface_v6(xdg_surface_v6); 803 return view_from_wlr_xdg_surface_v6(xdg_surface_v6);
801 } 804 }
802#ifdef HAVE_XWAYLAND 805#if HAVE_XWAYLAND
803 if (wlr_surface_is_xwayland_surface(wlr_surface)) { 806 if (wlr_surface_is_xwayland_surface(wlr_surface)) {
804 struct wlr_xwayland_surface *xsurface = 807 struct wlr_xwayland_surface *xsurface =
805 wlr_xwayland_surface_from_wlr_surface(wlr_surface); 808 wlr_xwayland_surface_from_wlr_surface(wlr_surface);
diff --git a/sway/tree/workspace.c b/sway/tree/workspace.c
index 05cda5c0..4be63311 100644
--- a/sway/tree/workspace.c
+++ b/sway/tree/workspace.c
@@ -33,14 +33,15 @@ struct workspace_config *workspace_find_config(const char *ws_name) {
33struct sway_output *workspace_get_initial_output(const char *name) { 33struct sway_output *workspace_get_initial_output(const char *name) {
34 // Check workspace configs for a workspace<->output pair 34 // Check workspace configs for a workspace<->output pair
35 struct workspace_config *wsc = workspace_find_config(name); 35 struct workspace_config *wsc = workspace_find_config(name);
36 if (wsc && wsc->output) { 36 if (wsc) {
37 struct sway_output *output = output_by_name(wsc->output); 37 for (int i = 0; i < wsc->outputs->length; i++) {
38 if (!output) { 38 struct sway_output *output = output_by_name(wsc->outputs->items[i]);
39 output = output_by_identifier(wsc->output); 39 if (!output) {
40 } 40 output = output_by_identifier(wsc->outputs->items[i]);
41 41 }
42 if (output) { 42 if (output) {
43 return output; 43 return output;
44 }
44 } 45 }
45 } 46 }
46 // Otherwise put it on the focused output 47 // Otherwise put it on the focused output
@@ -49,6 +50,21 @@ struct sway_output *workspace_get_initial_output(const char *name) {
49 return focus->output; 50 return focus->output;
50} 51}
51 52
53static void prevent_invalid_outer_gaps(struct sway_workspace *ws) {
54 if (ws->gaps_outer.top < -ws->gaps_inner) {
55 ws->gaps_outer.top = -ws->gaps_inner;
56 }
57 if (ws->gaps_outer.right < -ws->gaps_inner) {
58 ws->gaps_outer.right = -ws->gaps_inner;
59 }
60 if (ws->gaps_outer.bottom < -ws->gaps_inner) {
61 ws->gaps_outer.bottom = -ws->gaps_inner;
62 }
63 if (ws->gaps_outer.left < -ws->gaps_inner) {
64 ws->gaps_outer.left = -ws->gaps_inner;
65 }
66}
67
52struct sway_workspace *workspace_create(struct sway_output *output, 68struct sway_workspace *workspace_create(struct sway_output *output,
53 const char *name) { 69 const char *name) {
54 if (output == NULL) { 70 if (output == NULL) {
@@ -70,22 +86,41 @@ struct sway_workspace *workspace_create(struct sway_output *output,
70 ws->floating = create_list(); 86 ws->floating = create_list();
71 ws->tiling = create_list(); 87 ws->tiling = create_list();
72 ws->output_priority = create_list(); 88 ws->output_priority = create_list();
73 workspace_output_add_priority(ws, output);
74 89
75 ws->gaps_outer = config->gaps_outer; 90 ws->gaps_outer = config->gaps_outer;
76 ws->gaps_inner = config->gaps_inner; 91 ws->gaps_inner = config->gaps_inner;
77 if (name) { 92 if (name) {
78 struct workspace_config *wsc = workspace_find_config(name); 93 struct workspace_config *wsc = workspace_find_config(name);
79 if (wsc) { 94 if (wsc) {
80 if (wsc->gaps_outer != INT_MIN) { 95 if (wsc->gaps_outer.top != INT_MIN) {
81 ws->gaps_outer = wsc->gaps_outer; 96 ws->gaps_outer.top = wsc->gaps_outer.top;
97 }
98 if (wsc->gaps_outer.right != INT_MIN) {
99 ws->gaps_outer.right = wsc->gaps_outer.right;
100 }
101 if (wsc->gaps_outer.bottom != INT_MIN) {
102 ws->gaps_outer.bottom = wsc->gaps_outer.bottom;
103 }
104 if (wsc->gaps_outer.left != INT_MIN) {
105 ws->gaps_outer.left = wsc->gaps_outer.left;
82 } 106 }
83 if (wsc->gaps_inner != INT_MIN) { 107 if (wsc->gaps_inner != INT_MIN) {
84 ws->gaps_inner = wsc->gaps_inner; 108 ws->gaps_inner = wsc->gaps_inner;
85 } 109 }
110 // Since default outer gaps can be smaller than the negation of
111 // workspace specific inner gaps, check outer gaps again
112 prevent_invalid_outer_gaps(ws);
113
114 // Add output priorities
115 for (int i = 0; i < wsc->outputs->length; ++i) {
116 list_add(ws->output_priority, strdup(wsc->outputs->items[i]));
117 }
86 } 118 }
87 } 119 }
88 120
121 // If not already added, add the output to the lowest priority
122 workspace_output_add_priority(ws, output);
123
89 output_add_workspace(output, ws); 124 output_add_workspace(output, ws);
90 output_sort_workspaces(output); 125 output_sort_workspaces(output);
91 126
@@ -107,8 +142,7 @@ void workspace_destroy(struct sway_workspace *workspace) {
107 142
108 free(workspace->name); 143 free(workspace->name);
109 free(workspace->representation); 144 free(workspace->representation);
110 list_foreach(workspace->output_priority, free); 145 free_flat_list(workspace->output_priority);
111 list_free(workspace->output_priority);
112 list_free(workspace->floating); 146 list_free(workspace->floating);
113 list_free(workspace->tiling); 147 list_free(workspace->tiling);
114 list_free(workspace->current.floating); 148 list_free(workspace->current.floating);
@@ -150,8 +184,19 @@ static bool workspace_valid_on_output(const char *output_name,
150 char identifier[128]; 184 char identifier[128];
151 struct sway_output *output = output_by_name(output_name); 185 struct sway_output *output = output_by_name(output_name);
152 output_get_identifier(identifier, sizeof(identifier), output); 186 output_get_identifier(identifier, sizeof(identifier), output);
153 187
154 return !wsc || !wsc->output || strcmp(wsc->output, output_name) == 0 || strcasecmp(identifier, output_name) == 0; 188 if (!wsc) {
189 return true;
190 }
191
192 for (int i = 0; i < wsc->outputs->length; i++) {
193 if (strcmp(wsc->outputs->items[i], output_name) == 0 ||
194 strcmp(wsc->outputs->items[i], identifier) == 0) {
195 return true;
196 }
197 }
198
199 return false;
155} 200}
156 201
157static void workspace_name_from_binding(const struct sway_binding * binding, 202static void workspace_name_from_binding(const struct sway_binding * binding,
@@ -254,10 +299,19 @@ char *workspace_next_name(const char *output_name) {
254 for (int i = 0; i < config->workspace_configs->length; ++i) { 299 for (int i = 0; i < config->workspace_configs->length; ++i) {
255 // Unlike with bindings, this does not guarantee order 300 // Unlike with bindings, this does not guarantee order
256 const struct workspace_config *wsc = config->workspace_configs->items[i]; 301 const struct workspace_config *wsc = config->workspace_configs->items[i];
257 if (wsc->output && strcmp(wsc->output, output_name) == 0 302 if (workspace_by_name(wsc->workspace)) {
258 && workspace_by_name(wsc->workspace) == NULL) { 303 continue;
259 free(target); 304 }
260 target = strdup(wsc->workspace); 305 bool found = false;
306 for (int j = 0; j < wsc->outputs->length; ++j) {
307 if (strcmp(wsc->outputs->items[j], output_name) == 0) {
308 found = true;
309 free(target);
310 target = strdup(wsc->workspace);
311 break;
312 }
313 }
314 if (found) {
261 break; 315 break;
262 } 316 }
263 } 317 }
@@ -615,19 +669,25 @@ void workspace_insert_tiling(struct sway_workspace *workspace,
615} 669}
616 670
617void workspace_remove_gaps(struct sway_workspace *ws) { 671void workspace_remove_gaps(struct sway_workspace *ws) {
618 if (ws->current_gaps == 0) { 672 if (ws->current_gaps.top == 0 && ws->current_gaps.right == 0 &&
673 ws->current_gaps.bottom == 0 && ws->current_gaps.left == 0) {
619 return; 674 return;
620 } 675 }
621 676
622 ws->width += ws->current_gaps * 2; 677 ws->width += ws->current_gaps.left + ws->current_gaps.right;
623 ws->height += ws->current_gaps * 2; 678 ws->height += ws->current_gaps.top + ws->current_gaps.bottom;
624 ws->x -= ws->current_gaps; 679 ws->x -= ws->current_gaps.left;
625 ws->y -= ws->current_gaps; 680 ws->y -= ws->current_gaps.top;
626 ws->current_gaps = 0; 681
682 ws->current_gaps.top = 0;
683 ws->current_gaps.right = 0;
684 ws->current_gaps.bottom = 0;
685 ws->current_gaps.left = 0;
627} 686}
628 687
629void workspace_add_gaps(struct sway_workspace *ws) { 688void workspace_add_gaps(struct sway_workspace *ws) {
630 if (ws->current_gaps > 0) { 689 if (ws->current_gaps.top > 0 || ws->current_gaps.right > 0 ||
690 ws->current_gaps.bottom > 0 || ws->current_gaps.left > 0) {
631 return; 691 return;
632 } 692 }
633 if (config->smart_gaps) { 693 if (config->smart_gaps) {
@@ -643,18 +703,20 @@ void workspace_add_gaps(struct sway_workspace *ws) {
643 } 703 }
644 704
645 ws->current_gaps = ws->gaps_outer; 705 ws->current_gaps = ws->gaps_outer;
646
647 if (ws->layout == L_TABBED || ws->layout == L_STACKED) { 706 if (ws->layout == L_TABBED || ws->layout == L_STACKED) {
648 // We have to add inner gaps for this, because children of tabbed and 707 // We have to add inner gaps for this, because children of tabbed and
649 // stacked containers don't apply their own gaps - they assume the 708 // stacked containers don't apply their own gaps - they assume the
650 // tabbed/stacked container is using gaps. 709 // tabbed/stacked container is using gaps.
651 ws->current_gaps += ws->gaps_inner; 710 ws->current_gaps.top += ws->gaps_inner;
711 ws->current_gaps.right += ws->gaps_inner;
712 ws->current_gaps.bottom += ws->gaps_inner;
713 ws->current_gaps.left += ws->gaps_inner;
652 } 714 }
653 715
654 ws->x += ws->current_gaps; 716 ws->x += ws->current_gaps.left;
655 ws->y += ws->current_gaps; 717 ws->y += ws->current_gaps.top;
656 ws->width -= 2 * ws->current_gaps; 718 ws->width -= ws->current_gaps.left + ws->current_gaps.right;
657 ws->height -= 2 * ws->current_gaps; 719 ws->height -= ws->current_gaps.top + ws->current_gaps.bottom;
658} 720}
659 721
660struct sway_container *workspace_split(struct sway_workspace *workspace, 722struct sway_container *workspace_split(struct sway_workspace *workspace,
diff --git a/swaybar/config.c b/swaybar/config.c
index 98d94168..0ab346b1 100644
--- a/swaybar/config.c
+++ b/swaybar/config.c
@@ -84,6 +84,7 @@ void free_config(struct swaybar_config *config) {
84 free(config->mode); 84 free(config->mode);
85 free(config->hidden_state); 85 free(config->hidden_state);
86 free(config->sep_symbol); 86 free(config->sep_symbol);
87 free(config->modifier);
87 for (int i = 0; i < config->bindings->length; i++) { 88 for (int i = 0; i < config->bindings->length; i++) {
88 struct swaybar_binding *binding = config->bindings->items[i]; 89 struct swaybar_binding *binding = config->bindings->items[i];
89 free_binding(binding); 90 free_binding(binding);
diff --git a/swaybar/status_line.c b/swaybar/status_line.c
index 65d6c052..744d2785 100644
--- a/swaybar/status_line.c
+++ b/swaybar/status_line.c
@@ -183,6 +183,9 @@ void status_line_free(struct status_line *status) {
183 } 183 }
184 json_tokener_free(status->tokener); 184 json_tokener_free(status->tokener);
185 } 185 }
186 free(status->read);
187 free(status->write);
188 free((char*) status->text);
186 free(status->buffer); 189 free(status->buffer);
187 free(status); 190 free(status);
188} 191}
diff --git a/swayidle/main.c b/swayidle/main.c
index 7d0f23f4..2b185949 100644
--- a/swayidle/main.c
+++ b/swayidle/main.c
@@ -18,10 +18,10 @@
18#include "config.h" 18#include "config.h"
19#include "idle-client-protocol.h" 19#include "idle-client-protocol.h"
20#include "list.h" 20#include "list.h"
21#ifdef SWAY_IDLE_HAS_SYSTEMD 21#if HAVE_SYSTEMD
22#include <systemd/sd-bus.h> 22#include <systemd/sd-bus.h>
23#include <systemd/sd-login.h> 23#include <systemd/sd-login.h>
24#elif defined(SWAY_IDLE_HAS_ELOGIND) 24#elif HAVE_ELOGIND
25#include <elogind/sd-bus.h> 25#include <elogind/sd-bus.h>
26#include <elogind/sd-login.h> 26#include <elogind/sd-login.h>
27#endif 27#endif
@@ -66,7 +66,7 @@ static void cmd_exec(char *param) {
66 } 66 }
67} 67}
68 68
69#if defined(SWAY_IDLE_HAS_SYSTEMD) || defined(SWAY_IDLE_HAS_ELOGIND) 69#if HAVE_SYSTEMD || HAVE_ELOGIND
70static int lock_fd = -1; 70static int lock_fd = -1;
71static int ongoing_fd = -1; 71static int ongoing_fd = -1;
72static struct sd_bus *bus = NULL; 72static struct sd_bus *bus = NULL;
@@ -414,7 +414,7 @@ int main(int argc, char *argv[]) {
414 } 414 }
415 415
416 bool should_run = state.timeout_cmds->length > 0; 416 bool should_run = state.timeout_cmds->length > 0;
417#if defined(SWAY_IDLE_HAS_SYSTEMD) || defined(SWAY_IDLE_HAS_ELOGIND) 417#if HAVE_SYSTEMD || HAVE_ELOGIND
418 if (state.lock_cmd) { 418 if (state.lock_cmd) {
419 should_run = true; 419 should_run = true;
420 setup_sleep_listener(); 420 setup_sleep_listener();
diff --git a/swayidle/meson.build b/swayidle/meson.build
index 6c3ac119..79d2c5c4 100644
--- a/swayidle/meson.build
+++ b/swayidle/meson.build
@@ -1,18 +1,26 @@
1threads = dependency('threads') 1threads = dependency('threads')
2 2
3swayidle_deps = [
4 client_protos,
5 pixman,
6 wayland_client,
7 wayland_server,
8 wlroots,
9]
10
11if systemd.found()
12 swayidle_deps += systemd
13endif
14if elogind.found()
15 swayidle_deps += elogind
16endif
17
3executable( 18executable(
4 'swayidle', [ 19 'swayidle', [
5 'main.c', 20 'main.c',
6 ], 21 ],
7 include_directories: [sway_inc], 22 include_directories: [sway_inc],
8 dependencies: [ 23 dependencies: swayidle_deps,
9 client_protos,
10 pixman,
11 wayland_client,
12 wayland_server,
13 wlroots,
14 swayidle_deps,
15 ],
16 link_with: [lib_sway_common, lib_sway_client], 24 link_with: [lib_sway_common, lib_sway_client],
17 install_rpath : rpathdir, 25 install_rpath : rpathdir,
18 install: true 26 install: true
diff --git a/swaymsg/main.c b/swaymsg/main.c
index 663518f6..243b5fdc 100644
--- a/swaymsg/main.c
+++ b/swaymsg/main.c
@@ -111,7 +111,7 @@ static const char *pretty_type_name(const char *name) {
111} 111}
112 112
113static void pretty_print_input(json_object *i) { 113static void pretty_print_input(json_object *i) {
114 json_object *id, *name, *type, *product, *vendor; 114 json_object *id, *name, *type, *product, *vendor, *kbdlayout;
115 json_object_object_get_ex(i, "identifier", &id); 115 json_object_object_get_ex(i, "identifier", &id);
116 json_object_object_get_ex(i, "name", &name); 116 json_object_object_get_ex(i, "name", &name);
117 json_object_object_get_ex(i, "type", &type); 117 json_object_object_get_ex(i, "type", &type);
@@ -123,7 +123,7 @@ static void pretty_print_input(json_object *i) {
123 " Type: %s\n" 123 " Type: %s\n"
124 " Identifier: %s\n" 124 " Identifier: %s\n"
125 " Product ID: %d\n" 125 " Product ID: %d\n"
126 " Vendor ID: %d\n\n"; 126 " Vendor ID: %d\n";
127 127
128 128
129 printf(fmt, json_object_get_string(name), 129 printf(fmt, json_object_get_string(name),
@@ -131,6 +131,13 @@ static void pretty_print_input(json_object *i) {
131 json_object_get_string(id), 131 json_object_get_string(id),
132 json_object_get_int(product), 132 json_object_get_int(product),
133 json_object_get_int(vendor)); 133 json_object_get_int(vendor));
134
135 if (json_object_object_get_ex(i, "xkb_active_layout_name", &kbdlayout)) {
136 printf(" Active Keyboard Layout: %s\n",
137 json_object_get_string(kbdlayout));
138 }
139
140 printf("\n");
134} 141}
135 142
136static void pretty_print_seat(json_object *i) { 143static void pretty_print_seat(json_object *i) {
diff --git a/swaynag/swaynag.c b/swaynag/swaynag.c
index fa6bbe05..06185f20 100644
--- a/swaynag/swaynag.c
+++ b/swaynag/swaynag.c
@@ -412,6 +412,7 @@ void swaynag_destroy(struct swaynag *swaynag) {
412 free(button); 412 free(button);
413 } 413 }
414 list_free(swaynag->buttons); 414 list_free(swaynag->buttons);
415 free(swaynag->details.button_details);
415 free(swaynag->details.message); 416 free(swaynag->details.message);
416 free(swaynag->details.button_up.text); 417 free(swaynag->details.button_up.text);
417 free(swaynag->details.button_down.text); 418 free(swaynag->details.button_down.text);