diff options
author | emersion <contact@emersion.fr> | 2018-08-02 23:49:25 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-08-02 23:49:25 +0100 |
commit | 3a54e2291c017397ceff60511c29fe70d229bc8b (patch) | |
tree | d340b7776f945462f5ecffc830ada4d5fbe82f51 | |
parent | Enable wlr-gamma-control-unstable-v1 (diff) | |
parent | Merge pull request #2411 from emersion/fullscreen-pointer-input (diff) | |
download | sway-3a54e2291c017397ceff60511c29fe70d229bc8b.tar.gz sway-3a54e2291c017397ceff60511c29fe70d229bc8b.tar.zst sway-3a54e2291c017397ceff60511c29fe70d229bc8b.zip |
Merge branch 'master' into wlr-gamma-control
113 files changed, 5187 insertions, 1026 deletions
diff --git a/README.de.md b/README.de.md index 206a1040..a872e888 100644 --- a/README.de.md +++ b/README.de.md | |||
@@ -64,8 +64,6 @@ Abhängigkeiten: | |||
64 | * cairo | 64 | * cairo |
65 | * gdk-pixbuf2 * | 65 | * gdk-pixbuf2 * |
66 | * pam ** | 66 | * pam ** |
67 | * imagemagick (erforderlich für Bildaufnahme mit swaygrab) | ||
68 | * ffmpeg (erforderlich für Videoaufnahme swaygrab) | ||
69 | * [scdoc](https://git.sr.ht/~sircmpwn/scdoc) (erforderlich für man pages) | 67 | * [scdoc](https://git.sr.ht/~sircmpwn/scdoc) (erforderlich für man pages) |
70 | 68 | ||
71 | _\*Nur erforderlich für swaybar, swaybg, und swaylock_ | 69 | _\*Nur erforderlich für swaybar, swaybg, und swaylock_ |
diff --git a/README.el.md b/README.el.md index 5c70beff..6887fe8e 100644 --- a/README.el.md +++ b/README.el.md | |||
@@ -57,8 +57,6 @@ To username μου στο Freenode είναι kon14 και θα με βρείτ | |||
57 | * cairo | 57 | * cairo |
58 | * gdk-pixbuf2 * | 58 | * gdk-pixbuf2 * |
59 | * pam ** | 59 | * pam ** |
60 | * imagemagick (αναγκαίο για καταγραφή εικόνας μέσω του swaygrab) | ||
61 | * ffmpeg (αναγκαίο για καταγραφή video μέσω του swaygrab) | ||
62 | * [scdoc](https://git.sr.ht/~sircmpwn/scdoc) (required for man pages) | 60 | * [scdoc](https://git.sr.ht/~sircmpwn/scdoc) (required for man pages) |
63 | 61 | ||
64 | _\*Απαιτείται μόνο για swaybar, swaybg, and swaylock_ | 62 | _\*Απαιτείται μόνο για swaybar, swaybg, and swaylock_ |
diff --git a/README.fr.md b/README.fr.md index 0d2573f9..6ea4d14c 100644 --- a/README.fr.md +++ b/README.fr.md | |||
@@ -59,8 +59,6 @@ Installez les dépendances : | |||
59 | * cairo | 59 | * cairo |
60 | * gdk-pixbuf2 * | 60 | * gdk-pixbuf2 * |
61 | * pam ** | 61 | * pam ** |
62 | * imagemagick (requis pour la capture d'image avec swaygrab) | ||
63 | * ffmpeg (requis pour la capture vidéo avec swaygrab) | ||
64 | * [scdoc](https://git.sr.ht/~sircmpwn/scdoc) (requis pour les pages man) | 62 | * [scdoc](https://git.sr.ht/~sircmpwn/scdoc) (requis pour les pages man) |
65 | 63 | ||
66 | _\*Uniquement requis pour swaybar, swaybg, and swaylock_ | 64 | _\*Uniquement requis pour swaybar, swaybg, and swaylock_ |
diff --git a/README.it.md b/README.it.md index 0d81ea54..3b1b1ebc 100644 --- a/README.it.md +++ b/README.it.md | |||
@@ -60,8 +60,6 @@ Installa queste dipendenze: | |||
60 | * cairo | 60 | * cairo |
61 | * gdk-pixbuf2 * | 61 | * gdk-pixbuf2 * |
62 | * pam ** | 62 | * pam ** |
63 | * imagemagick (richiesto per catturare immagini con swaygrab) | ||
64 | * ffmpeg (rrichiesto per catturare video con swaygrab) | ||
65 | * [scdoc](https://git.sr.ht/~sircmpwn/scdoc) (rrichiesto per man pages) | 63 | * [scdoc](https://git.sr.ht/~sircmpwn/scdoc) (rrichiesto per man pages) |
66 | 64 | ||
67 | _\*Richiesto solo per swaybar, swaybg, e swaylock_ | 65 | _\*Richiesto solo per swaybar, swaybg, e swaylock_ |
diff --git a/README.ja.md b/README.ja.md index 476d7472..7b437966 100644 --- a/README.ja.md +++ b/README.ja.md | |||
@@ -50,8 +50,6 @@ Swayは沢山のディストリビューションで提供されています。" | |||
50 | * cairo | 50 | * cairo |
51 | * gdk-pixbuf2 * | 51 | * gdk-pixbuf2 * |
52 | * pam ** | 52 | * pam ** |
53 | * imagemagick (swaygrabでスクリーンショットを撮るのに必要です) | ||
54 | * ffmpeg (swaygrabで画面を録画するのに必要です) | ||
55 | * [scdoc](https://git.sr.ht/~sircmpwn/scdoc) (manで必要です) | 53 | * [scdoc](https://git.sr.ht/~sircmpwn/scdoc) (manで必要です) |
56 | 54 | ||
57 | _\*swaybar,swaybg,swaylockでのみ必要です_ | 55 | _\*swaybar,swaybg,swaylockでのみ必要です_ |
@@ -58,8 +58,6 @@ Install dependencies: | |||
58 | * gdk-pixbuf2 * | 58 | * gdk-pixbuf2 * |
59 | * pam ** | 59 | * pam ** |
60 | * dbus >= 1.10 *** | 60 | * dbus >= 1.10 *** |
61 | * imagemagick (required for image capture with swaygrab) | ||
62 | * ffmpeg (required for video capture with swaygrab) | ||
63 | * [scdoc](https://git.sr.ht/~sircmpwn/scdoc) (required for man pages) | 61 | * [scdoc](https://git.sr.ht/~sircmpwn/scdoc) (required for man pages) |
64 | 62 | ||
65 | _\*Only required for swaybar, swaybg, and swaylock_ | 63 | _\*Only required for swaybar, swaybg, and swaylock_ |
diff --git a/README.pt.md b/README.pt.md index d1ef245f..9089c8c6 100644 --- a/README.pt.md +++ b/README.pt.md | |||
@@ -66,8 +66,6 @@ Antes de iniciar a compilação, instale as dependências: | |||
66 | * cairo | 66 | * cairo |
67 | * gdk-pixbuf2 * | 67 | * gdk-pixbuf2 * |
68 | * pam ** | 68 | * pam ** |
69 | * imagemagick (capturar imagem com o swaygrab) | ||
70 | * ffmpeg (capturar vídeo com o swaygrab) | ||
71 | * [scdoc](https://git.sr.ht/~sircmpwn/scdoc) (man pages) | 69 | * [scdoc](https://git.sr.ht/~sircmpwn/scdoc) (man pages) |
72 | 70 | ||
73 | _\*Dependência apenas de swaybar, swaybg, e swaylock_ | 71 | _\*Dependência apenas de swaybar, swaybg, e swaylock_ |
diff --git a/README.ru.md b/README.ru.md index 3b3de19a..68675db3 100644 --- a/README.ru.md +++ b/README.ru.md | |||
@@ -62,8 +62,6 @@ Sway доступен во многих дистрибутивах и наход | |||
62 | * gdk-pixbuf2 * | 62 | * gdk-pixbuf2 * |
63 | * pam ** | 63 | * pam ** |
64 | * dbus >= 1.10 *** | 64 | * dbus >= 1.10 *** |
65 | * imagemagick (требуется для захвата изображений через swaygrab) | ||
66 | * ffmpeg (требуется для захвата видео через swaygrab) | ||
67 | * [scdoc](https://git.sr.ht/~sircmpwn/scdoc) (required for man pages) | 65 | * [scdoc](https://git.sr.ht/~sircmpwn/scdoc) (required for man pages) |
68 | 66 | ||
69 | _\*Требуется только для swaybar, swaybg и swaylock_ | 67 | _\*Требуется только для swaybar, swaybg и swaylock_ |
diff --git a/README.uk.md b/README.uk.md index 55698487..c31a3ea9 100644 --- a/README.uk.md +++ b/README.uk.md | |||
@@ -66,8 +66,6 @@ Sway доступний у багатьох дистрибутивах Linux (а | |||
66 | * cairo | 66 | * cairo |
67 | * gdk-pixbuf2 * | 67 | * gdk-pixbuf2 * |
68 | * pam ** | 68 | * pam ** |
69 | * imagemagick (для захоплення зображень за допомогою swaygrab) | ||
70 | * ffmpeg (для захоплення відео за допомогою swaygrab) | ||
71 | * [scdoc](https://git.sr.ht/~sircmpwn/scdoc) (required for man pages) | 69 | * [scdoc](https://git.sr.ht/~sircmpwn/scdoc) (required for man pages) |
72 | 70 | ||
73 | _\*Лише для swaybar, swaybg та swaylock_ | 71 | _\*Лише для swaybar, swaybg та swaylock_ |
diff --git a/common/util.c b/common/util.c index e8a88772..467aa4b5 100644 --- a/common/util.c +++ b/common/util.c | |||
@@ -123,6 +123,22 @@ uint32_t parse_color(const char *color) { | |||
123 | return res; | 123 | return res; |
124 | } | 124 | } |
125 | 125 | ||
126 | bool parse_boolean(const char *boolean, bool current) { | ||
127 | if (strcasecmp(boolean, "1") == 0 | ||
128 | || strcasecmp(boolean, "yes") == 0 | ||
129 | || strcasecmp(boolean, "on") == 0 | ||
130 | || strcasecmp(boolean, "true") == 0 | ||
131 | || strcasecmp(boolean, "enable") == 0 | ||
132 | || strcasecmp(boolean, "enabled") == 0 | ||
133 | || strcasecmp(boolean, "active") == 0) { | ||
134 | return true; | ||
135 | } else if (strcasecmp(boolean, "toggle") == 0) { | ||
136 | return !current; | ||
137 | } | ||
138 | // All other values are false to match i3 | ||
139 | return false; | ||
140 | } | ||
141 | |||
126 | char* resolve_path(const char* path) { | 142 | char* resolve_path(const char* path) { |
127 | struct stat sb; | 143 | struct stat sb; |
128 | ssize_t r; | 144 | ssize_t r; |
diff --git a/completions/bash/sway b/completions/bash/sway new file mode 100644 index 00000000..edd752cd --- /dev/null +++ b/completions/bash/sway | |||
@@ -0,0 +1,46 @@ | |||
1 | # sway(1) completion | ||
2 | |||
3 | _sway() | ||
4 | { | ||
5 | local cur prev | ||
6 | _get_comp_words_by_ref cur prev | ||
7 | |||
8 | short=( | ||
9 | -h | ||
10 | -c | ||
11 | -C | ||
12 | -d | ||
13 | -v | ||
14 | -V | ||
15 | ) | ||
16 | |||
17 | long=( | ||
18 | --help | ||
19 | --config | ||
20 | --validate | ||
21 | --debug | ||
22 | --version | ||
23 | --verbose | ||
24 | --get-socketpath | ||
25 | ) | ||
26 | |||
27 | case $prev in | ||
28 | -c|--config) | ||
29 | _filedir | ||
30 | return | ||
31 | ;; | ||
32 | esac | ||
33 | |||
34 | if [[ $cur == --* ]]; then | ||
35 | COMPREPLY=($(compgen -W "${long[*]}" -- "$cur")) | ||
36 | elif [[ $cur == -* ]]; then | ||
37 | COMPREPLY=($(compgen -W "${short[*]}" -- "$cur")) | ||
38 | COMPREPLY+=($(compgen -W "${long[*]}" -- "$cur")) | ||
39 | else | ||
40 | COMPREPLY=($(compgen -W "${short[*]}" -- "$cur")) | ||
41 | COMPREPLY+=($(compgen -W "${long[*]}" -- "$cur")) | ||
42 | COMPREPLY+=($(compgen -c -- "$cur")) | ||
43 | fi | ||
44 | |||
45 | } && | ||
46 | complete -F _sway sway | ||
diff --git a/completions/bash/swayidle b/completions/bash/swayidle new file mode 100644 index 00000000..a0cdc8b2 --- /dev/null +++ b/completions/bash/swayidle | |||
@@ -0,0 +1,48 @@ | |||
1 | # swaymsg(1) completion | ||
2 | |||
3 | _swayidle() | ||
4 | { | ||
5 | local cur prev | ||
6 | _get_comp_words_by_ref -n : cur prev | ||
7 | local prev2=${COMP_WORDS[COMP_CWORD-2]} | ||
8 | local prev3=${COMP_WORDS[COMP_CWORD-3]} | ||
9 | |||
10 | events=( | ||
11 | 'timeout' | ||
12 | 'before-sleep' | ||
13 | ) | ||
14 | |||
15 | short=( | ||
16 | -h | ||
17 | -d | ||
18 | ) | ||
19 | |||
20 | if [ "$prev" = timeout ]; then | ||
21 | # timeout <timeout> | ||
22 | return | ||
23 | elif [ "$prev2" = timeout ]; then | ||
24 | # timeout <timeout> <timeout command> | ||
25 | COMPREPLY=($(compgen -c -- "$cur")) | ||
26 | return | ||
27 | elif [ "$prev3" = timeout ]; then | ||
28 | # timeout <timeout> <timeout command> [resume <resume command>] | ||
29 | COMPREPLY=(resume) | ||
30 | # optional argument; no return here as user may skip 'resume' | ||
31 | fi | ||
32 | |||
33 | case "$prev" in | ||
34 | resume) | ||
35 | COMPREPLY=($(compgen -c -- "$cur")) | ||
36 | return | ||
37 | ;; | ||
38 | before-sleep) | ||
39 | COMPREPLY=($(compgen -c -- "$cur")) | ||
40 | return | ||
41 | ;; | ||
42 | esac | ||
43 | |||
44 | COMPREPLY+=($(compgen -W "${events[*]}" -- "$cur")) | ||
45 | COMPREPLY+=($(compgen -W "${short[*]}" -- "$cur")) | ||
46 | |||
47 | } && | ||
48 | complete -F _swayidle swayidle | ||
diff --git a/completions/bash/swaylock b/completions/bash/swaylock new file mode 100644 index 00000000..33925480 --- /dev/null +++ b/completions/bash/swaylock | |||
@@ -0,0 +1,66 @@ | |||
1 | # swaylock(1) completion | ||
2 | |||
3 | _swaylock() | ||
4 | { | ||
5 | local cur prev | ||
6 | _get_comp_words_by_ref -n : cur prev | ||
7 | |||
8 | short=( | ||
9 | -h | ||
10 | -c | ||
11 | -s | ||
12 | -t | ||
13 | -v | ||
14 | -i | ||
15 | -u | ||
16 | -f | ||
17 | ) | ||
18 | |||
19 | long=( | ||
20 | --help | ||
21 | --color | ||
22 | --scaling | ||
23 | --tiling | ||
24 | --version | ||
25 | --image | ||
26 | --no-unlock-indicator | ||
27 | --daemonize | ||
28 | ) | ||
29 | |||
30 | scaling=( | ||
31 | 'stretch' | ||
32 | 'fill' | ||
33 | 'fit' | ||
34 | 'center' | ||
35 | 'tile' | ||
36 | ) | ||
37 | |||
38 | case $prev in | ||
39 | -c|--color) | ||
40 | return | ||
41 | ;; | ||
42 | --scaling) | ||
43 | COMPREPLY=($(compgen -W "${scaling[*]}" -- "$cur")) | ||
44 | return | ||
45 | ;; | ||
46 | -i|--image) | ||
47 | if grep -q : <<< "$cur"; then | ||
48 | output="${cur%%:*}:" | ||
49 | cur="${cur#*:}" | ||
50 | else | ||
51 | output= | ||
52 | fi | ||
53 | COMPREPLY=($(compgen -f -- "$cur")) | ||
54 | return | ||
55 | ;; | ||
56 | esac | ||
57 | |||
58 | if [[ $cur == --* ]]; then | ||
59 | COMPREPLY=($(compgen -W "${long[*]}" -- "$cur")) | ||
60 | else | ||
61 | COMPREPLY=($(compgen -W "${short[*]}" -- "$cur")) | ||
62 | COMPREPLY+=($(compgen -W "${long[*]}" -- "$cur")) | ||
63 | fi | ||
64 | |||
65 | } && | ||
66 | complete -F _swaylock swaylock | ||
diff --git a/completions/bash/swaymsg b/completions/bash/swaymsg new file mode 100644 index 00000000..20092bdc --- /dev/null +++ b/completions/bash/swaymsg | |||
@@ -0,0 +1,59 @@ | |||
1 | # swaymsg(1) completion | ||
2 | |||
3 | _swaymsg() | ||
4 | { | ||
5 | local cur prev | ||
6 | _get_comp_words_by_ref cur prev | ||
7 | |||
8 | types=( | ||
9 | 'get_workspaces' | ||
10 | 'get_seats' | ||
11 | 'get_inputs' | ||
12 | 'get_outputs' | ||
13 | 'get_tree' | ||
14 | 'get_marks' | ||
15 | 'get_bar_config' | ||
16 | 'get_version' | ||
17 | 'get_binding_modes' | ||
18 | 'get_config' | ||
19 | 'send_tick' | ||
20 | ) | ||
21 | |||
22 | short=( | ||
23 | -h | ||
24 | -q | ||
25 | -r | ||
26 | -s | ||
27 | -t | ||
28 | -v | ||
29 | ) | ||
30 | |||
31 | long=( | ||
32 | --help | ||
33 | --quiet | ||
34 | --raw | ||
35 | --socket | ||
36 | --type | ||
37 | --verbose | ||
38 | ) | ||
39 | |||
40 | case $prev in | ||
41 | -s|--socket) | ||
42 | _filedir | ||
43 | return | ||
44 | ;; | ||
45 | -t|--type) | ||
46 | COMPREPLY=($(compgen -W "${types[*]}" -- "$cur")) | ||
47 | return | ||
48 | ;; | ||
49 | esac | ||
50 | |||
51 | if [[ $cur == --* ]]; then | ||
52 | COMPREPLY=($(compgen -W "${long[*]}" -- "$cur")) | ||
53 | else | ||
54 | COMPREPLY=($(compgen -W "${short[*]}" -- "$cur")) | ||
55 | COMPREPLY+=($(compgen -W "${long[*]}" -- "$cur")) | ||
56 | fi | ||
57 | |||
58 | } && | ||
59 | complete -F _swaymsg swaymsg | ||
diff --git a/completions/zsh/_sway b/completions/zsh/_sway index bab90fbf..05112002 100644 --- a/completions/zsh/_sway +++ b/completions/zsh/_sway | |||
@@ -18,5 +18,5 @@ _arguments -s \ | |||
18 | '(-c --config)'{-c,--config}'[Specify a config file]:files:_files' \ | 18 | '(-c --config)'{-c,--config}'[Specify a config file]:files:_files' \ |
19 | '(-C --validate)'{-C,--validate}'[Check validity of the config file, then exit]' \ | 19 | '(-C --validate)'{-C,--validate}'[Check validity of the config file, then exit]' \ |
20 | '(-d --debug)'{-d,--debug}'[Enables full logging, including debug information]' \ | 20 | '(-d --debug)'{-d,--debug}'[Enables full logging, including debug information]' \ |
21 | '(-v --verbose)'{-v,--verbose}'[Enables more verbose logging]' \ | 21 | '(-V --verbose)'{-V,--verbose}'[Enables more verbose logging]' \ |
22 | '(--get-socketpath)'--get-socketpath'[Gets the IPC socket path and prints it, then exits]' | 22 | '(--get-socketpath)'--get-socketpath'[Gets the IPC socket path and prints it, then exits]' |
diff --git a/completions/zsh/_swaygrab b/completions/zsh/_swaygrab deleted file mode 100644 index 0f9846f4..00000000 --- a/completions/zsh/_swaygrab +++ /dev/null | |||
@@ -1,23 +0,0 @@ | |||
1 | #compdef swaygrab | ||
2 | #----------------- | ||
3 | # Description | ||
4 | # ----------- | ||
5 | # | ||
6 | # Completion script for swaygrab in sway wm (http://swaywm.org) | ||
7 | # | ||
8 | # ----------------------------------------------------- | ||
9 | # Author | ||
10 | # ------ | ||
11 | # | ||
12 | # * Seth Barberee <seth.barberee@gmail.com> | ||
13 | # | ||
14 | # ------------------------------------------ | ||
15 | |||
16 | _arguments -s \ | ||
17 | '(-h --help)'{-h,--help}'[Shows help message]' \ | ||
18 | '(-c --capture)'{-c,--capture}'[Captures multiple frames as video and passes them to ffmpeg]' \ | ||
19 | '(-o --output)'{-o,--output}'[Use the specified output. If not specified then current focused output will be used]' \ | ||
20 | '(-v --version)'{-v,--version}'[Print the version (of swaymsg) and quit]' \ | ||
21 | '(-s --socket)'{-s,--socket}'[Use the specified socket path.]:files:_files' \ | ||
22 | '(-r --rate)'{-r,--rate}'[Specify a framerate (in fps). Used in combination with -c. Default is 30 and must be an integer]' \ | ||
23 | '(--raw)--raw[Instead of ImageMagick or ffmpeg, dump raw rgba data to stdout]' | ||
diff --git a/completions/zsh/_swaymsg b/completions/zsh/_swaymsg index 2e39deb6..a7a1c8e0 100644 --- a/completions/zsh/_swaymsg +++ b/completions/zsh/_swaymsg | |||
@@ -22,6 +22,9 @@ types=( | |||
22 | 'get_marks' | 22 | 'get_marks' |
23 | 'get_bar_config' | 23 | 'get_bar_config' |
24 | 'get_version' | 24 | 'get_version' |
25 | 'get_binding_modes' | ||
26 | 'get_config' | ||
27 | 'send_tick' | ||
25 | ) | 28 | ) |
26 | 29 | ||
27 | _arguments -s \ | 30 | _arguments -s \ |
@@ -16,7 +16,8 @@ set $right l | |||
16 | # Your preferred terminal emulator | 16 | # Your preferred terminal emulator |
17 | set $term urxvt | 17 | set $term urxvt |
18 | # Your preferred application launcher | 18 | # Your preferred application launcher |
19 | set $menu dmenu_run | 19 | # Note: it's recommended that you pass the final command to sway |
20 | set $menu dmenu_path | dmenu | xargs swaymsg exec | ||
20 | 21 | ||
21 | ### Output configuration | 22 | ### Output configuration |
22 | # | 23 | # |
diff --git a/include/ipc.h b/include/ipc.h index 0010718b..a3f60e19 100644 --- a/include/ipc.h +++ b/include/ipc.h | |||
@@ -15,6 +15,7 @@ enum ipc_command_type { | |||
15 | IPC_GET_VERSION = 7, | 15 | IPC_GET_VERSION = 7, |
16 | IPC_GET_BINDING_MODES = 8, | 16 | IPC_GET_BINDING_MODES = 8, |
17 | IPC_GET_CONFIG = 9, | 17 | IPC_GET_CONFIG = 9, |
18 | IPC_SEND_TICK = 10, | ||
18 | 19 | ||
19 | // sway-specific command types | 20 | // sway-specific command types |
20 | IPC_GET_INPUTS = 100, | 21 | IPC_GET_INPUTS = 100, |
@@ -27,8 +28,8 @@ enum ipc_command_type { | |||
27 | IPC_EVENT_WINDOW = ((1<<31) | 3), | 28 | IPC_EVENT_WINDOW = ((1<<31) | 3), |
28 | IPC_EVENT_BARCONFIG_UPDATE = ((1<<31) | 4), | 29 | IPC_EVENT_BARCONFIG_UPDATE = ((1<<31) | 4), |
29 | IPC_EVENT_BINDING = ((1<<31) | 5), | 30 | IPC_EVENT_BINDING = ((1<<31) | 5), |
30 | IPC_EVENT_MODIFIER = ((1<<31) | 6), | 31 | IPC_EVENT_SHUTDOWN = ((1<<31) | 6), |
31 | IPC_EVENT_INPUT = ((1<<31) | 7), | 32 | IPC_EVENT_TICK = ((1<<31) | 7), |
32 | }; | 33 | }; |
33 | 34 | ||
34 | #endif | 35 | #endif |
diff --git a/include/sway/commands.h b/include/sway/commands.h index e71a7228..41858ccc 100644 --- a/include/sway/commands.h +++ b/include/sway/commands.h | |||
@@ -106,7 +106,7 @@ sway_cmd cmd_exit; | |||
106 | sway_cmd cmd_floating; | 106 | sway_cmd cmd_floating; |
107 | sway_cmd cmd_floating_maximum_size; | 107 | sway_cmd cmd_floating_maximum_size; |
108 | sway_cmd cmd_floating_minimum_size; | 108 | sway_cmd cmd_floating_minimum_size; |
109 | sway_cmd cmd_floating_mod; | 109 | sway_cmd cmd_floating_modifier; |
110 | sway_cmd cmd_floating_scroll; | 110 | sway_cmd cmd_floating_scroll; |
111 | sway_cmd cmd_focus; | 111 | sway_cmd cmd_focus; |
112 | sway_cmd cmd_focus_follows_mouse; | 112 | sway_cmd cmd_focus_follows_mouse; |
@@ -213,8 +213,10 @@ sway_cmd input_cmd_scroll_button; | |||
213 | sway_cmd input_cmd_scroll_method; | 213 | sway_cmd input_cmd_scroll_method; |
214 | sway_cmd input_cmd_tap; | 214 | sway_cmd input_cmd_tap; |
215 | sway_cmd input_cmd_tap_button_map; | 215 | sway_cmd input_cmd_tap_button_map; |
216 | sway_cmd input_cmd_xkb_capslock; | ||
216 | sway_cmd input_cmd_xkb_layout; | 217 | sway_cmd input_cmd_xkb_layout; |
217 | sway_cmd input_cmd_xkb_model; | 218 | sway_cmd input_cmd_xkb_model; |
219 | sway_cmd input_cmd_xkb_numlock; | ||
218 | sway_cmd input_cmd_xkb_options; | 220 | sway_cmd input_cmd_xkb_options; |
219 | sway_cmd input_cmd_xkb_rules; | 221 | sway_cmd input_cmd_xkb_rules; |
220 | sway_cmd input_cmd_xkb_variant; | 222 | sway_cmd input_cmd_xkb_variant; |
diff --git a/include/sway/config.h b/include/sway/config.h index b8da29c5..909b6827 100644 --- a/include/sway/config.h +++ b/include/sway/config.h | |||
@@ -1,6 +1,5 @@ | |||
1 | #ifndef _SWAY_CONFIG_H | 1 | #ifndef _SWAY_CONFIG_H |
2 | #define _SWAY_CONFIG_H | 2 | #define _SWAY_CONFIG_H |
3 | #define PID_WORKSPACE_TIMEOUT 60 | ||
4 | #include <libinput.h> | 3 | #include <libinput.h> |
5 | #include <stdint.h> | 4 | #include <stdint.h> |
6 | #include <string.h> | 5 | #include <string.h> |
@@ -22,14 +21,28 @@ struct sway_variable { | |||
22 | char *value; | 21 | char *value; |
23 | }; | 22 | }; |
24 | 23 | ||
24 | |||
25 | enum binding_input_type { | ||
26 | BINDING_KEYCODE, | ||
27 | BINDING_KEYSYM, | ||
28 | BINDING_MOUSE, | ||
29 | }; | ||
30 | |||
31 | enum binding_flags { | ||
32 | BINDING_RELEASE=1, | ||
33 | BINDING_LOCKED=2, // keyboard only | ||
34 | BINDING_BORDER=4, // mouse only; trigger on container border | ||
35 | BINDING_CONTENTS=8, // mouse only; trigger on container contents | ||
36 | BINDING_TITLEBAR=16 // mouse only; trigger on container titlebar | ||
37 | }; | ||
38 | |||
25 | /** | 39 | /** |
26 | * A key binding and an associated command. | 40 | * A key binding and an associated command. |
27 | */ | 41 | */ |
28 | struct sway_binding { | 42 | struct sway_binding { |
43 | enum binding_input_type type; | ||
29 | int order; | 44 | int order; |
30 | bool release; | 45 | uint32_t flags; |
31 | bool locked; | ||
32 | bool bindcode; | ||
33 | list_t *keys; // sorted in ascending order | 46 | list_t *keys; // sorted in ascending order |
34 | uint32_t modifiers; | 47 | uint32_t modifiers; |
35 | char *command; | 48 | char *command; |
@@ -50,6 +63,7 @@ struct sway_mode { | |||
50 | char *name; | 63 | char *name; |
51 | list_t *keysym_bindings; | 64 | list_t *keysym_bindings; |
52 | list_t *keycode_bindings; | 65 | list_t *keycode_bindings; |
66 | list_t *mouse_bindings; | ||
53 | bool pango; | 67 | bool pango; |
54 | }; | 68 | }; |
55 | 69 | ||
@@ -87,6 +101,9 @@ struct input_config { | |||
87 | char *xkb_rules; | 101 | char *xkb_rules; |
88 | char *xkb_variant; | 102 | char *xkb_variant; |
89 | 103 | ||
104 | int xkb_numlock; | ||
105 | int xkb_capslock; | ||
106 | |||
90 | struct input_config_mapped_from_region *mapped_from_region; | 107 | struct input_config_mapped_from_region *mapped_from_region; |
91 | char *mapped_to_output; | 108 | char *mapped_to_output; |
92 | 109 | ||
@@ -146,12 +163,6 @@ struct workspace_output { | |||
146 | char *workspace; | 163 | char *workspace; |
147 | }; | 164 | }; |
148 | 165 | ||
149 | struct pid_workspace { | ||
150 | pid_t *pid; | ||
151 | char *workspace; | ||
152 | time_t *time_added; | ||
153 | }; | ||
154 | |||
155 | struct bar_config { | 166 | struct bar_config { |
156 | /** | 167 | /** |
157 | * One of "dock", "hide", "invisible" | 168 | * One of "dock", "hide", "invisible" |
@@ -302,7 +313,6 @@ struct sway_config { | |||
302 | list_t *bars; | 313 | list_t *bars; |
303 | list_t *cmd_queue; | 314 | list_t *cmd_queue; |
304 | list_t *workspace_outputs; | 315 | list_t *workspace_outputs; |
305 | list_t *pid_workspaces; | ||
306 | list_t *output_configs; | 316 | list_t *output_configs; |
307 | list_t *input_configs; | 317 | list_t *input_configs; |
308 | list_t *seat_configs; | 318 | list_t *seat_configs; |
@@ -313,6 +323,7 @@ struct sway_config { | |||
313 | struct bar_config *current_bar; | 323 | struct bar_config *current_bar; |
314 | char *swaybg_command; | 324 | char *swaybg_command; |
315 | uint32_t floating_mod; | 325 | uint32_t floating_mod; |
326 | bool floating_mod_inverse; | ||
316 | uint32_t dragging_key; | 327 | uint32_t dragging_key; |
317 | uint32_t resizing_key; | 328 | uint32_t resizing_key; |
318 | char *floating_scroll_up_cmd; | 329 | char *floating_scroll_up_cmd; |
@@ -388,9 +399,6 @@ struct sway_config { | |||
388 | } handler_context; | 399 | } handler_context; |
389 | }; | 400 | }; |
390 | 401 | ||
391 | void pid_workspace_add(struct pid_workspace *pw); | ||
392 | void free_pid_workspace(struct pid_workspace *pw); | ||
393 | |||
394 | /** | 402 | /** |
395 | * Loads the main config from the given path. is_active should be true when | 403 | * Loads the main config from the given path. is_active should be true when |
396 | * reloading the config. | 404 | * reloading the config. |
@@ -480,7 +488,7 @@ int sway_binding_cmp_keys(const void *a, const void *b); | |||
480 | 488 | ||
481 | void free_sway_binding(struct sway_binding *sb); | 489 | void free_sway_binding(struct sway_binding *sb); |
482 | 490 | ||
483 | struct sway_binding *sway_binding_dup(struct sway_binding *sb); | 491 | void seat_execute_command(struct sway_seat *seat, struct sway_binding *binding); |
484 | 492 | ||
485 | void load_swaybars(); | 493 | void load_swaybars(); |
486 | 494 | ||
diff --git a/include/sway/criteria.h b/include/sway/criteria.h index 6a8337c5..b4ff7d49 100644 --- a/include/sway/criteria.h +++ b/include/sway/criteria.h | |||
@@ -2,6 +2,7 @@ | |||
2 | #define _SWAY_CRITERIA_H | 2 | #define _SWAY_CRITERIA_H |
3 | 3 | ||
4 | #include <pcre.h> | 4 | #include <pcre.h> |
5 | #include "config.h" | ||
5 | #include "list.h" | 6 | #include "list.h" |
6 | #include "tree/view.h" | 7 | #include "tree/view.h" |
7 | 8 | ||
@@ -25,7 +26,9 @@ struct criteria { | |||
25 | pcre *instance; | 26 | pcre *instance; |
26 | pcre *con_mark; | 27 | pcre *con_mark; |
27 | uint32_t con_id; // internal ID | 28 | uint32_t con_id; // internal ID |
29 | #ifdef HAVE_XWAYLAND | ||
28 | uint32_t id; // X11 window ID | 30 | uint32_t id; // X11 window ID |
31 | #endif | ||
29 | pcre *window_role; | 32 | pcre *window_role; |
30 | uint32_t window_type; | 33 | uint32_t window_type; |
31 | bool floating; | 34 | bool floating; |
diff --git a/include/sway/desktop/transaction.h b/include/sway/desktop/transaction.h index cee4afed..56361d94 100644 --- a/include/sway/desktop/transaction.h +++ b/include/sway/desktop/transaction.h | |||
@@ -42,17 +42,4 @@ void transaction_notify_view_ready(struct sway_view *view, uint32_t serial); | |||
42 | void transaction_notify_view_ready_by_size(struct sway_view *view, | 42 | void transaction_notify_view_ready_by_size(struct sway_view *view, |
43 | int width, int height); | 43 | int width, int height); |
44 | 44 | ||
45 | /** | ||
46 | * Get the saved texture that should be rendered for a view. | ||
47 | * | ||
48 | * The addresses pointed at by the width and height pointers will be populated | ||
49 | * with the surface's dimensions, which may be different to the texture's | ||
50 | * dimensions if output scaling is used. | ||
51 | * | ||
52 | * This function should only be called if it is known that the view has | ||
53 | * instructions. | ||
54 | */ | ||
55 | struct wlr_texture *transaction_get_saved_texture(struct sway_view *view, | ||
56 | int *width, int *height); | ||
57 | |||
58 | #endif | 45 | #endif |
diff --git a/include/sway/input/cursor.h b/include/sway/input/cursor.h index 5dd109ca..7ec45120 100644 --- a/include/sway/input/cursor.h +++ b/include/sway/input/cursor.h | |||
@@ -3,6 +3,8 @@ | |||
3 | #include <stdint.h> | 3 | #include <stdint.h> |
4 | #include "sway/input/seat.h" | 4 | #include "sway/input/seat.h" |
5 | 5 | ||
6 | #define SWAY_CURSOR_PRESSED_BUTTONS_CAP 32 | ||
7 | |||
6 | struct sway_cursor { | 8 | struct sway_cursor { |
7 | struct sway_seat *seat; | 9 | struct sway_seat *seat; |
8 | struct wlr_cursor *cursor; | 10 | struct wlr_cursor *cursor; |
@@ -11,6 +13,7 @@ struct sway_cursor { | |||
11 | } previous; | 13 | } previous; |
12 | struct wlr_xcursor_manager *xcursor_manager; | 14 | struct wlr_xcursor_manager *xcursor_manager; |
13 | 15 | ||
16 | const char *image; | ||
14 | struct wl_client *image_client; | 17 | struct wl_client *image_client; |
15 | 18 | ||
16 | struct wl_listener motion; | 19 | struct wl_listener motion; |
@@ -28,6 +31,10 @@ struct sway_cursor { | |||
28 | uint32_t tool_buttons; | 31 | uint32_t tool_buttons; |
29 | 32 | ||
30 | struct wl_listener request_set_cursor; | 33 | struct wl_listener request_set_cursor; |
34 | |||
35 | // Mouse binding state | ||
36 | uint32_t pressed_buttons[SWAY_CURSOR_PRESSED_BUTTONS_CAP]; | ||
37 | size_t pressed_button_count; | ||
31 | }; | 38 | }; |
32 | 39 | ||
33 | void sway_cursor_destroy(struct sway_cursor *cursor); | 40 | void sway_cursor_destroy(struct sway_cursor *cursor); |
@@ -37,4 +44,7 @@ void cursor_send_pointer_motion(struct sway_cursor *cursor, uint32_t time_msec, | |||
37 | void dispatch_cursor_button(struct sway_cursor *cursor, uint32_t time_msec, | 44 | void dispatch_cursor_button(struct sway_cursor *cursor, uint32_t time_msec, |
38 | uint32_t button, enum wlr_button_state state); | 45 | uint32_t button, enum wlr_button_state state); |
39 | 46 | ||
47 | void cursor_set_image(struct sway_cursor *cursor, const char *image, | ||
48 | struct wl_client *client); | ||
49 | |||
40 | #endif | 50 | #endif |
diff --git a/include/sway/input/input-manager.h b/include/sway/input/input-manager.h index 89a3ac71..aa2f6f19 100644 --- a/include/sway/input/input-manager.h +++ b/include/sway/input/input-manager.h | |||
@@ -2,6 +2,7 @@ | |||
2 | #define _SWAY_INPUT_INPUT_MANAGER_H | 2 | #define _SWAY_INPUT_INPUT_MANAGER_H |
3 | #include <libinput.h> | 3 | #include <libinput.h> |
4 | #include <wlr/types/wlr_input_inhibitor.h> | 4 | #include <wlr/types/wlr_input_inhibitor.h> |
5 | #include <wlr/types/wlr_virtual_keyboard_v1.h> | ||
5 | #include "sway/server.h" | 6 | #include "sway/server.h" |
6 | #include "sway/config.h" | 7 | #include "sway/config.h" |
7 | #include "list.h" | 8 | #include "list.h" |
@@ -25,10 +26,12 @@ struct sway_input_manager { | |||
25 | struct wl_list seats; | 26 | struct wl_list seats; |
26 | 27 | ||
27 | struct wlr_input_inhibit_manager *inhibit; | 28 | struct wlr_input_inhibit_manager *inhibit; |
29 | struct wlr_virtual_keyboard_manager_v1 *virtual_keyboard; | ||
28 | 30 | ||
29 | struct wl_listener new_input; | 31 | struct wl_listener new_input; |
30 | struct wl_listener inhibit_activate; | 32 | struct wl_listener inhibit_activate; |
31 | struct wl_listener inhibit_deactivate; | 33 | struct wl_listener inhibit_deactivate; |
34 | struct wl_listener virtual_keyboard_new; | ||
32 | }; | 35 | }; |
33 | 36 | ||
34 | struct sway_input_manager *input_manager_create(struct sway_server *server); | 37 | struct sway_input_manager *input_manager_create(struct sway_server *server); |
diff --git a/include/sway/input/keyboard.h b/include/sway/input/keyboard.h index 6713398e..6d28454c 100644 --- a/include/sway/input/keyboard.h +++ b/include/sway/input/keyboard.h | |||
@@ -38,6 +38,9 @@ struct sway_keyboard { | |||
38 | struct sway_shortcut_state state_keysyms_raw; | 38 | struct sway_shortcut_state state_keysyms_raw; |
39 | struct sway_shortcut_state state_keycodes; | 39 | struct sway_shortcut_state state_keycodes; |
40 | struct sway_binding *held_binding; | 40 | struct sway_binding *held_binding; |
41 | |||
42 | struct wl_event_source *key_repeat_source; | ||
43 | struct sway_binding *repeat_binding; | ||
41 | }; | 44 | }; |
42 | 45 | ||
43 | struct sway_keyboard *sway_keyboard_create(struct sway_seat *seat, | 46 | struct sway_keyboard *sway_keyboard_create(struct sway_seat *seat, |
diff --git a/include/sway/input/seat.h b/include/sway/input/seat.h index eac1626b..92387601 100644 --- a/include/sway/input/seat.h +++ b/include/sway/input/seat.h | |||
@@ -3,6 +3,7 @@ | |||
3 | 3 | ||
4 | #include <wlr/types/wlr_layer_shell.h> | 4 | #include <wlr/types/wlr_layer_shell.h> |
5 | #include <wlr/types/wlr_seat.h> | 5 | #include <wlr/types/wlr_seat.h> |
6 | #include <wlr/util/edges.h> | ||
6 | #include "sway/input/input-manager.h" | 7 | #include "sway/input/input-manager.h" |
7 | 8 | ||
8 | struct sway_seat_device { | 9 | struct sway_seat_device { |
@@ -52,6 +53,24 @@ struct sway_seat { | |||
52 | int32_t touch_id; | 53 | int32_t touch_id; |
53 | double touch_x, touch_y; | 54 | double touch_x, touch_y; |
54 | 55 | ||
56 | // Operations (drag and resize) | ||
57 | enum { | ||
58 | OP_NONE, | ||
59 | OP_MOVE, | ||
60 | OP_RESIZE, | ||
61 | } operation; | ||
62 | |||
63 | struct sway_container *op_container; | ||
64 | enum wlr_edges op_resize_edge; | ||
65 | uint32_t op_button; | ||
66 | bool op_resize_preserve_ratio; | ||
67 | double op_ref_lx, op_ref_ly; // cursor's x/y at start of op | ||
68 | double op_ref_width, op_ref_height; // container's size at start of op | ||
69 | double op_ref_con_lx, op_ref_con_ly; // container's x/y at start of op | ||
70 | |||
71 | uint32_t last_button; | ||
72 | uint32_t last_button_serial; | ||
73 | |||
55 | struct wl_listener focus_destroy; | 74 | struct wl_listener focus_destroy; |
56 | struct wl_listener new_container; | 75 | struct wl_listener new_container; |
57 | struct wl_listener new_drag_icon; | 76 | struct wl_listener new_drag_icon; |
@@ -80,7 +99,7 @@ void seat_configure_xcursor(struct sway_seat *seat); | |||
80 | void seat_set_focus(struct sway_seat *seat, struct sway_container *container); | 99 | void seat_set_focus(struct sway_seat *seat, struct sway_container *container); |
81 | 100 | ||
82 | void seat_set_focus_warp(struct sway_seat *seat, | 101 | void seat_set_focus_warp(struct sway_seat *seat, |
83 | struct sway_container *container, bool warp); | 102 | struct sway_container *container, bool warp, bool notify); |
84 | 103 | ||
85 | void seat_set_focus_surface(struct sway_seat *seat, | 104 | void seat_set_focus_surface(struct sway_seat *seat, |
86 | struct wlr_surface *surface, bool unfocus); | 105 | struct wlr_surface *surface, bool unfocus); |
@@ -105,6 +124,9 @@ struct sway_container *seat_get_focus(struct sway_seat *seat); | |||
105 | struct sway_container *seat_get_focus_inactive(struct sway_seat *seat, | 124 | struct sway_container *seat_get_focus_inactive(struct sway_seat *seat, |
106 | struct sway_container *container); | 125 | struct sway_container *container); |
107 | 126 | ||
127 | struct sway_container *seat_get_focus_inactive_tiling(struct sway_seat *seat, | ||
128 | struct sway_container *container); | ||
129 | |||
108 | /** | 130 | /** |
109 | * Descend into the focus stack to find the focus-inactive view. Useful for | 131 | * Descend into the focus stack to find the focus-inactive view. Useful for |
110 | * container placement when they change position in the tree. | 132 | * container placement when they change position in the tree. |
@@ -134,4 +156,15 @@ bool seat_is_input_allowed(struct sway_seat *seat, struct wlr_surface *surface); | |||
134 | 156 | ||
135 | void drag_icon_update_position(struct sway_drag_icon *icon); | 157 | void drag_icon_update_position(struct sway_drag_icon *icon); |
136 | 158 | ||
159 | void seat_begin_move(struct sway_seat *seat, struct sway_container *con, | ||
160 | uint32_t button); | ||
161 | |||
162 | void seat_begin_resize(struct sway_seat *seat, struct sway_container *con, | ||
163 | uint32_t button, enum wlr_edges edge); | ||
164 | |||
165 | void seat_end_mouse_operation(struct sway_seat *seat); | ||
166 | |||
167 | void seat_pointer_notify_button(struct sway_seat *seat, uint32_t time_msec, | ||
168 | uint32_t button, enum wlr_button_state state); | ||
169 | |||
137 | #endif | 170 | #endif |
diff --git a/include/sway/ipc-server.h b/include/sway/ipc-server.h index 6469f097..4b6d0e25 100644 --- a/include/sway/ipc-server.h +++ b/include/sway/ipc-server.h | |||
@@ -16,5 +16,7 @@ void ipc_event_workspace(struct sway_container *old, | |||
16 | void ipc_event_window(struct sway_container *window, const char *change); | 16 | void ipc_event_window(struct sway_container *window, const char *change); |
17 | void ipc_event_barconfig_update(struct bar_config *bar); | 17 | void ipc_event_barconfig_update(struct bar_config *bar); |
18 | void ipc_event_mode(const char *mode, bool pango); | 18 | void ipc_event_mode(const char *mode, bool pango); |
19 | void ipc_event_shutdown(const char *reason); | ||
20 | void ipc_event_binding(struct sway_binding *binding); | ||
19 | 21 | ||
20 | #endif | 22 | #endif |
diff --git a/include/sway/output.h b/include/sway/output.h index b6cda83c..80dcd37b 100644 --- a/include/sway/output.h +++ b/include/sway/output.h | |||
@@ -5,6 +5,7 @@ | |||
5 | #include <wayland-server.h> | 5 | #include <wayland-server.h> |
6 | #include <wlr/types/wlr_box.h> | 6 | #include <wlr/types/wlr_box.h> |
7 | #include <wlr/types/wlr_output.h> | 7 | #include <wlr/types/wlr_output.h> |
8 | #include "config.h" | ||
8 | #include "sway/tree/view.h" | 9 | #include "sway/tree/view.h" |
9 | 10 | ||
10 | struct sway_server; | 11 | struct sway_server; |
@@ -38,15 +39,9 @@ struct sway_output { | |||
38 | } events; | 39 | } events; |
39 | }; | 40 | }; |
40 | 41 | ||
41 | /** | 42 | typedef void (*sway_surface_iterator_func_t)(struct sway_output *output, |
42 | * Contains a surface's root geometry information. For instance, when rendering | 43 | struct wlr_surface *surface, struct wlr_box *box, float rotation, |
43 | * a popup, this will contain the parent view's position and size. | 44 | void *user_data); |
44 | */ | ||
45 | struct root_geometry { | ||
46 | double x, y; | ||
47 | int width, height; | ||
48 | float rotation; | ||
49 | }; | ||
50 | 45 | ||
51 | void output_damage_whole(struct sway_output *output); | 46 | void output_damage_whole(struct sway_output *output); |
52 | 47 | ||
@@ -65,36 +60,37 @@ struct sway_container *output_by_name(const char *name); | |||
65 | 60 | ||
66 | void output_enable(struct sway_output *output); | 61 | void output_enable(struct sway_output *output); |
67 | 62 | ||
68 | bool output_has_opaque_lockscreen(struct sway_output *output, | 63 | bool output_has_opaque_overlay_layer_surface(struct sway_output *output); |
69 | struct sway_seat *seat); | ||
70 | 64 | ||
71 | struct sway_container *output_get_active_workspace(struct sway_output *output); | 65 | struct sway_container *output_get_active_workspace(struct sway_output *output); |
72 | 66 | ||
73 | void output_render(struct sway_output *output, struct timespec *when, | 67 | void output_render(struct sway_output *output, struct timespec *when, |
74 | pixman_region32_t *damage); | 68 | pixman_region32_t *damage); |
75 | 69 | ||
76 | bool output_get_surface_box(struct root_geometry *geo, | 70 | void output_surface_for_each_surface(struct sway_output *output, |
77 | struct sway_output *output, struct wlr_surface *surface, int sx, int sy, | 71 | struct wlr_surface *surface, double ox, double oy, |
78 | struct wlr_box *surface_box); | 72 | sway_surface_iterator_func_t iterator, void *user_data); |
79 | 73 | ||
80 | void output_surface_for_each_surface(struct wlr_surface *surface, | 74 | void output_view_for_each_surface(struct sway_output *output, |
81 | double ox, double oy, struct root_geometry *geo, | 75 | struct sway_view *view, sway_surface_iterator_func_t iterator, |
82 | wlr_surface_iterator_func_t iterator, void *user_data); | 76 | void *user_data); |
83 | 77 | ||
84 | void output_view_for_each_surface(struct sway_view *view, | 78 | void output_view_for_each_popup(struct sway_output *output, |
85 | struct sway_output *output, struct root_geometry *geo, | 79 | struct sway_view *view, sway_surface_iterator_func_t iterator, |
86 | wlr_surface_iterator_func_t iterator, void *user_data); | 80 | void *user_data); |
87 | 81 | ||
88 | void output_layer_for_each_surface(struct wl_list *layer_surfaces, | 82 | void output_layer_for_each_surface(struct sway_output *output, |
89 | struct root_geometry *geo, wlr_surface_iterator_func_t iterator, | 83 | struct wl_list *layer_surfaces, sway_surface_iterator_func_t iterator, |
90 | void *user_data); | 84 | void *user_data); |
91 | 85 | ||
92 | void output_unmanaged_for_each_surface(struct wl_list *unmanaged, | 86 | #ifdef HAVE_XWAYLAND |
93 | struct sway_output *output, struct root_geometry *geo, | 87 | void output_unmanaged_for_each_surface(struct sway_output *output, |
94 | wlr_surface_iterator_func_t iterator, void *user_data); | 88 | struct wl_list *unmanaged, sway_surface_iterator_func_t iterator, |
89 | void *user_data); | ||
90 | #endif | ||
95 | 91 | ||
96 | void output_drag_icons_for_each_surface(struct wl_list *drag_icons, | 92 | void output_drag_icons_for_each_surface(struct sway_output *output, |
97 | struct sway_output *output, struct root_geometry *geo, | 93 | struct wl_list *drag_icons, sway_surface_iterator_func_t iterator, |
98 | wlr_surface_iterator_func_t iterator, void *user_data); | 94 | void *user_data); |
99 | 95 | ||
100 | #endif | 96 | #endif |
diff --git a/include/sway/scratchpad.h b/include/sway/scratchpad.h new file mode 100644 index 00000000..5af5256f --- /dev/null +++ b/include/sway/scratchpad.h | |||
@@ -0,0 +1,26 @@ | |||
1 | #ifndef _SWAY_SCRATCHPAD_H | ||
2 | #define _SWAY_SCRATCHPAD_H | ||
3 | |||
4 | #include "tree/container.h" | ||
5 | |||
6 | /** | ||
7 | * Move a container to the scratchpad. | ||
8 | */ | ||
9 | void scratchpad_add_container(struct sway_container *con); | ||
10 | |||
11 | /** | ||
12 | * Remove a container from the scratchpad. | ||
13 | */ | ||
14 | void scratchpad_remove_container(struct sway_container *con); | ||
15 | |||
16 | /** | ||
17 | * Show or hide the next container on the scratchpad. | ||
18 | */ | ||
19 | void scratchpad_toggle_auto(void); | ||
20 | |||
21 | /** | ||
22 | * Show or hide a specific container on the scratchpad. | ||
23 | */ | ||
24 | void scratchpad_toggle_container(struct sway_container *con); | ||
25 | |||
26 | #endif | ||
diff --git a/include/sway/server.h b/include/sway/server.h index 70bde6d4..a3782f91 100644 --- a/include/sway/server.h +++ b/include/sway/server.h | |||
@@ -12,7 +12,10 @@ | |||
12 | #include <wlr/render/wlr_renderer.h> | 12 | #include <wlr/render/wlr_renderer.h> |
13 | // TODO WLR: make Xwayland optional | 13 | // TODO WLR: make Xwayland optional |
14 | #include "list.h" | 14 | #include "list.h" |
15 | #include "config.h" | ||
16 | #ifdef HAVE_XWAYLAND | ||
15 | #include "sway/xwayland.h" | 17 | #include "sway/xwayland.h" |
18 | #endif | ||
16 | 19 | ||
17 | struct sway_server { | 20 | struct sway_server { |
18 | struct wl_display *wl_display; | 21 | struct wl_display *wl_display; |
@@ -39,11 +42,11 @@ struct sway_server { | |||
39 | 42 | ||
40 | struct wlr_xdg_shell *xdg_shell; | 43 | struct wlr_xdg_shell *xdg_shell; |
41 | struct wl_listener xdg_shell_surface; | 44 | struct wl_listener xdg_shell_surface; |
42 | 45 | #ifdef HAVE_XWAYLAND | |
43 | struct sway_xwayland xwayland; | 46 | struct sway_xwayland xwayland; |
44 | struct wl_listener xwayland_surface; | 47 | struct wl_listener xwayland_surface; |
45 | struct wl_listener xwayland_ready; | 48 | struct wl_listener xwayland_ready; |
46 | 49 | #endif | |
47 | bool debug_txn_timings; | 50 | bool debug_txn_timings; |
48 | 51 | ||
49 | list_t *transactions; | 52 | list_t *transactions; |
@@ -65,6 +68,7 @@ void handle_idle_inhibitor_v1(struct wl_listener *listener, void *data); | |||
65 | void handle_layer_shell_surface(struct wl_listener *listener, void *data); | 68 | void handle_layer_shell_surface(struct wl_listener *listener, void *data); |
66 | void handle_xdg_shell_v6_surface(struct wl_listener *listener, void *data); | 69 | void handle_xdg_shell_v6_surface(struct wl_listener *listener, void *data); |
67 | void handle_xdg_shell_surface(struct wl_listener *listener, void *data); | 70 | void handle_xdg_shell_surface(struct wl_listener *listener, void *data); |
71 | #ifdef HAVE_XWAYLAND | ||
68 | void handle_xwayland_surface(struct wl_listener *listener, void *data); | 72 | void handle_xwayland_surface(struct wl_listener *listener, void *data); |
69 | 73 | #endif | |
70 | #endif | 74 | #endif |
diff --git a/include/sway/tree/container.h b/include/sway/tree/container.h index ca7a3288..c3942e9e 100644 --- a/include/sway/tree/container.h +++ b/include/sway/tree/container.h | |||
@@ -60,6 +60,8 @@ struct sway_container_state { | |||
60 | double swayc_x, swayc_y; | 60 | double swayc_x, swayc_y; |
61 | double swayc_width, swayc_height; | 61 | double swayc_width, swayc_height; |
62 | 62 | ||
63 | bool is_fullscreen; | ||
64 | |||
63 | bool has_gaps; | 65 | bool has_gaps; |
64 | double current_gaps; | 66 | double current_gaps; |
65 | double gaps_inner; | 67 | double gaps_inner; |
@@ -74,7 +76,6 @@ struct sway_container_state { | |||
74 | // View properties | 76 | // View properties |
75 | double view_x, view_y; | 77 | double view_x, view_y; |
76 | double view_width, view_height; | 78 | double view_width, view_height; |
77 | bool is_fullscreen; | ||
78 | 79 | ||
79 | enum sway_container_border border; | 80 | enum sway_container_border border; |
80 | int border_thickness; | 81 | int border_thickness; |
@@ -84,7 +85,7 @@ struct sway_container_state { | |||
84 | bool border_right; | 85 | bool border_right; |
85 | 86 | ||
86 | // Workspace properties | 87 | // Workspace properties |
87 | struct sway_view *ws_fullscreen; | 88 | struct sway_container *ws_fullscreen; |
88 | struct sway_container *ws_floating; | 89 | struct sway_container *ws_floating; |
89 | }; | 90 | }; |
90 | 91 | ||
@@ -124,6 +125,8 @@ struct sway_container { | |||
124 | double saved_x, saved_y; | 125 | double saved_x, saved_y; |
125 | double saved_width, saved_height; | 126 | double saved_width, saved_height; |
126 | 127 | ||
128 | bool is_fullscreen; | ||
129 | |||
127 | // The gaps currently applied to the container. | 130 | // The gaps currently applied to the container. |
128 | double current_gaps; | 131 | double current_gaps; |
129 | 132 | ||
@@ -135,6 +138,11 @@ struct sway_container { | |||
135 | 138 | ||
136 | struct sway_container *parent; | 139 | struct sway_container *parent; |
137 | 140 | ||
141 | // Indicates that the container is a scratchpad container. | ||
142 | // Both hidden and visible scratchpad containers have scratchpad=true. | ||
143 | // Hidden scratchpad containers have a NULL parent. | ||
144 | bool scratchpad; | ||
145 | |||
138 | float alpha; | 146 | float alpha; |
139 | 147 | ||
140 | struct wlr_texture *title_focused; | 148 | struct wlr_texture *title_focused; |
@@ -222,16 +230,13 @@ struct sway_container *container_parent(struct sway_container *container, | |||
222 | * surface-local coordinates of the given layout coordinates if the container | 230 | * surface-local coordinates of the given layout coordinates if the container |
223 | * is a view and the view contains a surface at those coordinates. | 231 | * is a view and the view contains a surface at those coordinates. |
224 | */ | 232 | */ |
225 | struct sway_container *container_at(struct sway_container *container, | 233 | struct sway_container *container_at(struct sway_container *workspace, |
226 | double ox, double oy, struct wlr_surface **surface, | 234 | double lx, double ly, struct wlr_surface **surface, |
227 | double *sx, double *sy); | 235 | double *sx, double *sy); |
228 | 236 | ||
229 | /** | 237 | struct sway_container *container_at_view(struct sway_container *view, |
230 | * Same as container_at, but only checks floating views and expects coordinates | 238 | double lx, double ly, struct wlr_surface **surface, |
231 | * to be layout coordinates, as that's what floating views use. | 239 | double *sx, double *sy); |
232 | */ | ||
233 | struct sway_container *floating_container_at(double lx, double ly, | ||
234 | struct wlr_surface **surface, double *sx, double *sy); | ||
235 | 240 | ||
236 | /** | 241 | /** |
237 | * Apply the function for each descendant of the container breadth first. | 242 | * Apply the function for each descendant of the container breadth first. |
@@ -262,6 +267,8 @@ int container_count_descendants_of_type(struct sway_container *con, | |||
262 | 267 | ||
263 | void container_create_notify(struct sway_container *container); | 268 | void container_create_notify(struct sway_container *container); |
264 | 269 | ||
270 | void container_update_textures_recursive(struct sway_container *con); | ||
271 | |||
265 | void container_damage_whole(struct sway_container *container); | 272 | void container_damage_whole(struct sway_container *container); |
266 | 273 | ||
267 | bool container_reap_empty(struct sway_container *con); | 274 | bool container_reap_empty(struct sway_container *con); |
@@ -289,6 +296,11 @@ void container_notify_subtree_changed(struct sway_container *container); | |||
289 | */ | 296 | */ |
290 | size_t container_titlebar_height(void); | 297 | size_t container_titlebar_height(void); |
291 | 298 | ||
299 | /** | ||
300 | * Resize and center the container in its workspace. | ||
301 | */ | ||
302 | void container_init_floating(struct sway_container *container); | ||
303 | |||
292 | void container_set_floating(struct sway_container *container, bool enable); | 304 | void container_set_floating(struct sway_container *container, bool enable); |
293 | 305 | ||
294 | void container_set_geometry_from_floating_view(struct sway_container *con); | 306 | void container_set_geometry_from_floating_view(struct sway_container *con); |
@@ -305,6 +317,12 @@ bool container_is_floating(struct sway_container *container); | |||
305 | void container_get_box(struct sway_container *container, struct wlr_box *box); | 317 | void container_get_box(struct sway_container *container, struct wlr_box *box); |
306 | 318 | ||
307 | /** | 319 | /** |
320 | * Move a floating container by the specified amount. | ||
321 | */ | ||
322 | void container_floating_translate(struct sway_container *con, | ||
323 | double x_amount, double y_amount); | ||
324 | |||
325 | /** | ||
308 | * Move a floating container to a new layout-local position. | 326 | * Move a floating container to a new layout-local position. |
309 | */ | 327 | */ |
310 | void container_floating_move_to(struct sway_container *con, | 328 | void container_floating_move_to(struct sway_container *con, |
@@ -318,4 +336,32 @@ void container_set_dirty(struct sway_container *container); | |||
318 | 336 | ||
319 | bool container_has_urgent_child(struct sway_container *container); | 337 | bool container_has_urgent_child(struct sway_container *container); |
320 | 338 | ||
339 | /** | ||
340 | * If the container is involved in a drag or resize operation via a mouse, this | ||
341 | * ends the operation. | ||
342 | */ | ||
343 | void container_end_mouse_operation(struct sway_container *container); | ||
344 | |||
345 | void container_set_fullscreen(struct sway_container *container, bool enable); | ||
346 | |||
347 | /** | ||
348 | * Return true if the container is floating, or a child of a floating split | ||
349 | * container. | ||
350 | */ | ||
351 | bool container_is_floating_or_child(struct sway_container *container); | ||
352 | |||
353 | /** | ||
354 | * Return true if the container is fullscreen, or a child of a fullscreen split | ||
355 | * container. | ||
356 | */ | ||
357 | bool container_is_fullscreen_or_child(struct sway_container *container); | ||
358 | |||
359 | /** | ||
360 | * Wrap the children of parent in a new container. The new container will be the | ||
361 | * only child of parent. | ||
362 | * | ||
363 | * The new container is returned. | ||
364 | */ | ||
365 | struct sway_container *container_wrap_children(struct sway_container *parent); | ||
366 | |||
321 | #endif | 367 | #endif |
diff --git a/include/sway/tree/layout.h b/include/sway/tree/layout.h index ba265623..a4c31bf6 100644 --- a/include/sway/tree/layout.h +++ b/include/sway/tree/layout.h | |||
@@ -3,6 +3,7 @@ | |||
3 | #include <wlr/types/wlr_output_layout.h> | 3 | #include <wlr/types/wlr_output_layout.h> |
4 | #include <wlr/render/wlr_texture.h> | 4 | #include <wlr/render/wlr_texture.h> |
5 | #include "sway/tree/container.h" | 5 | #include "sway/tree/container.h" |
6 | #include "config.h" | ||
6 | 7 | ||
7 | enum movement_direction { | 8 | enum movement_direction { |
8 | MOVE_LEFT, | 9 | MOVE_LEFT, |
@@ -14,10 +15,11 @@ enum movement_direction { | |||
14 | }; | 15 | }; |
15 | 16 | ||
16 | enum resize_edge { | 17 | enum resize_edge { |
17 | RESIZE_EDGE_LEFT, | 18 | RESIZE_EDGE_NONE = 0, |
18 | RESIZE_EDGE_RIGHT, | 19 | RESIZE_EDGE_LEFT = 1, |
19 | RESIZE_EDGE_TOP, | 20 | RESIZE_EDGE_RIGHT = 2, |
20 | RESIZE_EDGE_BOTTOM, | 21 | RESIZE_EDGE_TOP = 4, |
22 | RESIZE_EDGE_BOTTOM = 8, | ||
21 | }; | 23 | }; |
22 | 24 | ||
23 | struct sway_container; | 25 | struct sway_container; |
@@ -26,14 +28,17 @@ struct sway_root { | |||
26 | struct wlr_output_layout *output_layout; | 28 | struct wlr_output_layout *output_layout; |
27 | 29 | ||
28 | struct wl_listener output_layout_change; | 30 | struct wl_listener output_layout_change; |
29 | 31 | #ifdef HAVE_XWAYLAND | |
30 | struct wl_list xwayland_unmanaged; // sway_xwayland_unmanaged::link | 32 | struct wl_list xwayland_unmanaged; // sway_xwayland_unmanaged::link |
33 | #endif | ||
31 | struct wl_list drag_icons; // sway_drag_icon::link | 34 | struct wl_list drag_icons; // sway_drag_icon::link |
32 | 35 | ||
33 | struct wlr_texture *debug_tree; | 36 | struct wlr_texture *debug_tree; |
34 | 37 | ||
35 | struct wl_list outputs; // sway_output::link | 38 | struct wl_list outputs; // sway_output::link |
36 | 39 | ||
40 | list_t *scratchpad; // struct sway_container | ||
41 | |||
37 | struct { | 42 | struct { |
38 | struct wl_signal new_container; | 43 | struct wl_signal new_container; |
39 | } events; | 44 | } events; |
diff --git a/include/sway/tree/view.h b/include/sway/tree/view.h index 068d92c6..37fd02bc 100644 --- a/include/sway/tree/view.h +++ b/include/sway/tree/view.h | |||
@@ -3,7 +3,10 @@ | |||
3 | #include <wayland-server.h> | 3 | #include <wayland-server.h> |
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" | ||
7 | #ifdef HAVE_XWAYLAND | ||
6 | #include <wlr/xwayland.h> | 8 | #include <wlr/xwayland.h> |
9 | #endif | ||
7 | #include "sway/input/input-manager.h" | 10 | #include "sway/input/input-manager.h" |
8 | #include "sway/input/seat.h" | 11 | #include "sway/input/seat.h" |
9 | 12 | ||
@@ -12,7 +15,9 @@ struct sway_container; | |||
12 | enum sway_view_type { | 15 | enum sway_view_type { |
13 | SWAY_VIEW_XDG_SHELL_V6, | 16 | SWAY_VIEW_XDG_SHELL_V6, |
14 | SWAY_VIEW_XDG_SHELL, | 17 | SWAY_VIEW_XDG_SHELL, |
18 | #ifdef HAVE_XWAYLAND | ||
15 | SWAY_VIEW_XWAYLAND, | 19 | SWAY_VIEW_XWAYLAND, |
20 | #endif | ||
16 | }; | 21 | }; |
17 | 22 | ||
18 | enum sway_view_prop { | 23 | enum sway_view_prop { |
@@ -22,10 +27,14 @@ enum sway_view_prop { | |||
22 | VIEW_PROP_INSTANCE, | 27 | VIEW_PROP_INSTANCE, |
23 | VIEW_PROP_WINDOW_TYPE, | 28 | VIEW_PROP_WINDOW_TYPE, |
24 | VIEW_PROP_WINDOW_ROLE, | 29 | VIEW_PROP_WINDOW_ROLE, |
30 | #ifdef HAVE_XWAYLAND | ||
25 | VIEW_PROP_X11_WINDOW_ID, | 31 | VIEW_PROP_X11_WINDOW_ID, |
32 | #endif | ||
26 | }; | 33 | }; |
27 | 34 | ||
28 | struct sway_view_impl { | 35 | struct sway_view_impl { |
36 | void (*get_constraints)(struct sway_view *view, double *min_width, | ||
37 | double *max_width, double *min_height, double *max_height); | ||
29 | const char *(*get_string_prop)(struct sway_view *view, | 38 | const char *(*get_string_prop)(struct sway_view *view, |
30 | enum sway_view_prop prop); | 39 | enum sway_view_prop prop); |
31 | uint32_t (*get_int_prop)(struct sway_view *view, enum sway_view_prop prop); | 40 | uint32_t (*get_int_prop)(struct sway_view *view, enum sway_view_prop prop); |
@@ -38,7 +47,10 @@ struct sway_view_impl { | |||
38 | bool (*has_client_side_decorations)(struct sway_view *view); | 47 | bool (*has_client_side_decorations)(struct sway_view *view); |
39 | void (*for_each_surface)(struct sway_view *view, | 48 | void (*for_each_surface)(struct sway_view *view, |
40 | wlr_surface_iterator_func_t iterator, void *user_data); | 49 | wlr_surface_iterator_func_t iterator, void *user_data); |
50 | void (*for_each_popup)(struct sway_view *view, | ||
51 | wlr_surface_iterator_func_t iterator, void *user_data); | ||
41 | void (*close)(struct sway_view *view); | 52 | void (*close)(struct sway_view *view); |
53 | void (*close_popups)(struct sway_view *view); | ||
42 | void (*destroy)(struct sway_view *view); | 54 | void (*destroy)(struct sway_view *view); |
43 | }; | 55 | }; |
44 | 56 | ||
@@ -60,8 +72,6 @@ struct sway_view { | |||
60 | // Used when changing a view from tiled to floating. | 72 | // Used when changing a view from tiled to floating. |
61 | int natural_width, natural_height; | 73 | int natural_width, natural_height; |
62 | 74 | ||
63 | bool is_fullscreen; | ||
64 | |||
65 | char *title_format; | 75 | char *title_format; |
66 | enum sway_container_border border; | 76 | enum sway_container_border border; |
67 | int border_thickness; | 77 | int border_thickness; |
@@ -75,6 +85,9 @@ struct sway_view { | |||
75 | bool allow_request_urgent; | 85 | bool allow_request_urgent; |
76 | struct wl_event_source *urgent_timer; | 86 | struct wl_event_source *urgent_timer; |
77 | 87 | ||
88 | struct wlr_buffer *saved_buffer; | ||
89 | int saved_buffer_width, saved_buffer_height; | ||
90 | |||
78 | bool destroying; | 91 | bool destroying; |
79 | 92 | ||
80 | list_t *executed_criteria; // struct criteria * | 93 | list_t *executed_criteria; // struct criteria * |
@@ -88,7 +101,9 @@ struct sway_view { | |||
88 | union { | 101 | union { |
89 | struct wlr_xdg_surface_v6 *wlr_xdg_surface_v6; | 102 | struct wlr_xdg_surface_v6 *wlr_xdg_surface_v6; |
90 | struct wlr_xdg_surface *wlr_xdg_surface; | 103 | struct wlr_xdg_surface *wlr_xdg_surface; |
104 | #ifdef HAVE_XWAYLAND | ||
91 | struct wlr_xwayland_surface *wlr_xwayland_surface; | 105 | struct wlr_xwayland_surface *wlr_xwayland_surface; |
106 | #endif | ||
92 | struct wlr_wl_shell_surface *wlr_wl_shell_surface; | 107 | struct wlr_wl_shell_surface *wlr_wl_shell_surface; |
93 | }; | 108 | }; |
94 | 109 | ||
@@ -108,6 +123,8 @@ struct sway_xdg_shell_v6_view { | |||
108 | struct wl_listener request_resize; | 123 | struct wl_listener request_resize; |
109 | struct wl_listener request_maximize; | 124 | struct wl_listener request_maximize; |
110 | struct wl_listener request_fullscreen; | 125 | struct wl_listener request_fullscreen; |
126 | struct wl_listener set_title; | ||
127 | struct wl_listener set_app_id; | ||
111 | struct wl_listener new_popup; | 128 | struct wl_listener new_popup; |
112 | struct wl_listener map; | 129 | struct wl_listener map; |
113 | struct wl_listener unmap; | 130 | struct wl_listener unmap; |
@@ -122,12 +139,14 @@ struct sway_xdg_shell_view { | |||
122 | struct wl_listener request_resize; | 139 | struct wl_listener request_resize; |
123 | struct wl_listener request_maximize; | 140 | struct wl_listener request_maximize; |
124 | struct wl_listener request_fullscreen; | 141 | struct wl_listener request_fullscreen; |
142 | struct wl_listener set_title; | ||
143 | struct wl_listener set_app_id; | ||
125 | struct wl_listener new_popup; | 144 | struct wl_listener new_popup; |
126 | struct wl_listener map; | 145 | struct wl_listener map; |
127 | struct wl_listener unmap; | 146 | struct wl_listener unmap; |
128 | struct wl_listener destroy; | 147 | struct wl_listener destroy; |
129 | }; | 148 | }; |
130 | 149 | #ifdef HAVE_XWAYLAND | |
131 | struct sway_xwayland_view { | 150 | struct sway_xwayland_view { |
132 | struct sway_view view; | 151 | struct sway_view view; |
133 | 152 | ||
@@ -159,7 +178,7 @@ struct sway_xwayland_unmanaged { | |||
159 | struct wl_listener unmap; | 178 | struct wl_listener unmap; |
160 | struct wl_listener destroy; | 179 | struct wl_listener destroy; |
161 | }; | 180 | }; |
162 | 181 | #endif | |
163 | struct sway_view_child; | 182 | struct sway_view_child; |
164 | 183 | ||
165 | struct sway_view_child_impl { | 184 | struct sway_view_child_impl { |
@@ -215,15 +234,13 @@ uint32_t view_get_window_type(struct sway_view *view); | |||
215 | 234 | ||
216 | const char *view_get_shell(struct sway_view *view); | 235 | const char *view_get_shell(struct sway_view *view); |
217 | 236 | ||
237 | void view_get_constraints(struct sway_view *view, double *min_width, | ||
238 | double *max_width, double *min_height, double *max_height); | ||
239 | |||
218 | uint32_t view_configure(struct sway_view *view, double lx, double ly, int width, | 240 | uint32_t view_configure(struct sway_view *view, double lx, double ly, int width, |
219 | int height); | 241 | int height); |
220 | 242 | ||
221 | /** | 243 | /** |
222 | * Center the view in its workspace and build the swayc decorations around it. | ||
223 | */ | ||
224 | void view_init_floating(struct sway_view *view); | ||
225 | |||
226 | /** | ||
227 | * Configure the view's position and size based on the swayc's position and | 244 | * Configure the view's position and size based on the swayc's position and |
228 | * size, taking borders into consideration. | 245 | * size, taking borders into consideration. |
229 | */ | 246 | */ |
@@ -233,17 +250,24 @@ void view_set_activated(struct sway_view *view, bool activated); | |||
233 | 250 | ||
234 | void view_set_tiled(struct sway_view *view, bool tiled); | 251 | void view_set_tiled(struct sway_view *view, bool tiled); |
235 | 252 | ||
236 | void view_set_fullscreen_raw(struct sway_view *view, bool fullscreen); | ||
237 | |||
238 | void view_set_fullscreen(struct sway_view *view, bool fullscreen); | ||
239 | |||
240 | void view_close(struct sway_view *view); | 253 | void view_close(struct sway_view *view); |
241 | 254 | ||
255 | void view_close_popups(struct sway_view *view); | ||
256 | |||
242 | void view_damage_from(struct sway_view *view); | 257 | void view_damage_from(struct sway_view *view); |
243 | 258 | ||
259 | /** | ||
260 | * Iterate all surfaces of a view (toplevels + popups). | ||
261 | */ | ||
244 | void view_for_each_surface(struct sway_view *view, | 262 | void view_for_each_surface(struct sway_view *view, |
245 | wlr_surface_iterator_func_t iterator, void *user_data); | 263 | wlr_surface_iterator_func_t iterator, void *user_data); |
246 | 264 | ||
265 | /** | ||
266 | * Iterate all popups recursively. | ||
267 | */ | ||
268 | void view_for_each_popup(struct sway_view *view, | ||
269 | wlr_surface_iterator_func_t iterator, void *user_data); | ||
270 | |||
247 | // view implementation | 271 | // view implementation |
248 | 272 | ||
249 | void view_init(struct sway_view *view, enum sway_view_type type, | 273 | void view_init(struct sway_view *view, enum sway_view_type type, |
@@ -272,9 +296,10 @@ struct sway_view *view_from_wlr_xdg_surface( | |||
272 | struct wlr_xdg_surface *xdg_surface); | 296 | struct wlr_xdg_surface *xdg_surface); |
273 | struct sway_view *view_from_wlr_xdg_surface_v6( | 297 | struct sway_view *view_from_wlr_xdg_surface_v6( |
274 | struct wlr_xdg_surface_v6 *xdg_surface_v6); | 298 | struct wlr_xdg_surface_v6 *xdg_surface_v6); |
299 | #ifdef HAVE_XWAYLAND | ||
275 | struct sway_view *view_from_wlr_xwayland_surface( | 300 | struct sway_view *view_from_wlr_xwayland_surface( |
276 | struct wlr_xwayland_surface *xsurface); | 301 | struct wlr_xwayland_surface *xsurface); |
277 | 302 | #endif | |
278 | struct sway_view *view_from_wlr_surface(struct wlr_surface *surface); | 303 | struct sway_view *view_from_wlr_surface(struct wlr_surface *surface); |
279 | 304 | ||
280 | /** | 305 | /** |
@@ -303,6 +328,8 @@ void view_clear_marks(struct sway_view *view); | |||
303 | 328 | ||
304 | bool view_has_mark(struct sway_view *view, char *mark); | 329 | bool view_has_mark(struct sway_view *view, char *mark); |
305 | 330 | ||
331 | void view_add_mark(struct sway_view *view, char *mark); | ||
332 | |||
306 | void view_update_marks_textures(struct sway_view *view); | 333 | void view_update_marks_textures(struct sway_view *view); |
307 | 334 | ||
308 | /** | 335 | /** |
@@ -315,4 +342,8 @@ void view_set_urgent(struct sway_view *view, bool enable); | |||
315 | 342 | ||
316 | bool view_is_urgent(struct sway_view *view); | 343 | bool view_is_urgent(struct sway_view *view); |
317 | 344 | ||
345 | void view_remove_saved_buffer(struct sway_view *view); | ||
346 | |||
347 | void view_save_buffer(struct sway_view *view); | ||
348 | |||
318 | #endif | 349 | #endif |
diff --git a/include/sway/tree/workspace.h b/include/sway/tree/workspace.h index bc95317a..5ae0ae3a 100644 --- a/include/sway/tree/workspace.h +++ b/include/sway/tree/workspace.h | |||
@@ -7,7 +7,7 @@ struct sway_view; | |||
7 | 7 | ||
8 | struct sway_workspace { | 8 | struct sway_workspace { |
9 | struct sway_container *swayc; | 9 | struct sway_container *swayc; |
10 | struct sway_view *fullscreen; | 10 | struct sway_container *fullscreen; |
11 | struct sway_container *floating; | 11 | struct sway_container *floating; |
12 | list_t *output_priority; | 12 | list_t *output_priority; |
13 | bool urgent; | 13 | bool urgent; |
@@ -44,6 +44,10 @@ void workspace_output_add_priority(struct sway_container *workspace, | |||
44 | struct sway_container *workspace_output_get_highest_available( | 44 | struct sway_container *workspace_output_get_highest_available( |
45 | struct sway_container *ws, struct sway_container *exclude); | 45 | struct sway_container *ws, struct sway_container *exclude); |
46 | 46 | ||
47 | struct sway_container *workspace_for_pid(pid_t pid); | ||
48 | |||
49 | void workspace_record_pid(pid_t pid); | ||
50 | |||
47 | void workspace_detect_urgent(struct sway_container *workspace); | 51 | void workspace_detect_urgent(struct sway_container *workspace); |
48 | 52 | ||
49 | #endif | 53 | #endif |
diff --git a/include/swaygrab/json.h b/include/swaygrab/json.h deleted file mode 100644 index c1093ef1..00000000 --- a/include/swaygrab/json.h +++ /dev/null | |||
@@ -1,10 +0,0 @@ | |||
1 | #include <json-c/json.h> | ||
2 | #include "wlc/wlc.h" | ||
3 | |||
4 | void init_json_tree(int socketfd); | ||
5 | void free_json_tree(); | ||
6 | char *get_focused_output(); | ||
7 | char *create_payload(const char *output, struct wlc_geometry *g); | ||
8 | struct wlc_geometry *get_container_geometry(json_object *container); | ||
9 | json_object *get_focused_container(); | ||
10 | json_object *get_output_container(const char *output); | ||
diff --git a/include/swaynag/config.h b/include/swaynag/config.h new file mode 100644 index 00000000..0d8889de --- /dev/null +++ b/include/swaynag/config.h | |||
@@ -0,0 +1,13 @@ | |||
1 | #ifndef _SWAYNAG_CONFIG_H | ||
2 | #define _SWAYNAG_CONFIG_H | ||
3 | #include "swaynag/swaynag.h" | ||
4 | #include "list.h" | ||
5 | |||
6 | int swaynag_parse_options(int argc, char **argv, struct swaynag *swaynag, | ||
7 | list_t *types, struct swaynag_type *type, char **config, bool *debug); | ||
8 | |||
9 | char *swaynag_get_config_path(void); | ||
10 | |||
11 | int swaynag_load_config(char *path, struct swaynag *swaynag, list_t *types); | ||
12 | |||
13 | #endif | ||
diff --git a/include/swaynag/render.h b/include/swaynag/render.h new file mode 100644 index 00000000..d09e5929 --- /dev/null +++ b/include/swaynag/render.h | |||
@@ -0,0 +1,7 @@ | |||
1 | #ifndef _SWAYNAG_RENDER_H | ||
2 | #define _SWAYNAG_RENDER_H | ||
3 | #include "swaynag/swaynag.h" | ||
4 | |||
5 | void render_frame(struct swaynag *swaynag); | ||
6 | |||
7 | #endif | ||
diff --git a/include/swaynag/swaynag.h b/include/swaynag/swaynag.h new file mode 100644 index 00000000..1bf8b640 --- /dev/null +++ b/include/swaynag/swaynag.h | |||
@@ -0,0 +1,100 @@ | |||
1 | #ifndef _SWAYNAG_SWAYNAG_H | ||
2 | #define _SWAYNAG_SWAYNAG_H | ||
3 | #include <stdint.h> | ||
4 | #include <strings.h> | ||
5 | #include "list.h" | ||
6 | #include "pool-buffer.h" | ||
7 | #include "swaynag/types.h" | ||
8 | #include "xdg-output-unstable-v1-client-protocol.h" | ||
9 | |||
10 | #define SWAYNAG_MAX_HEIGHT 500 | ||
11 | |||
12 | struct swaynag; | ||
13 | |||
14 | enum swaynag_action_type { | ||
15 | SWAYNAG_ACTION_DISMISS, | ||
16 | SWAYNAG_ACTION_EXPAND, | ||
17 | SWAYNAG_ACTION_COMMAND, | ||
18 | }; | ||
19 | |||
20 | struct swaynag_pointer { | ||
21 | struct wl_pointer *pointer; | ||
22 | uint32_t serial; | ||
23 | struct wl_cursor_theme *cursor_theme; | ||
24 | struct wl_cursor_image *cursor_image; | ||
25 | struct wl_surface *cursor_surface; | ||
26 | int x; | ||
27 | int y; | ||
28 | }; | ||
29 | |||
30 | struct swaynag_output { | ||
31 | char *name; | ||
32 | struct wl_output *wl_output; | ||
33 | uint32_t wl_name; | ||
34 | uint32_t scale; | ||
35 | struct swaynag *swaynag; | ||
36 | struct wl_list link; | ||
37 | }; | ||
38 | |||
39 | struct swaynag_button { | ||
40 | char *text; | ||
41 | enum swaynag_action_type type; | ||
42 | char *action; | ||
43 | int x; | ||
44 | int y; | ||
45 | int width; | ||
46 | int height; | ||
47 | }; | ||
48 | |||
49 | struct swaynag_details { | ||
50 | bool visible; | ||
51 | char *message; | ||
52 | |||
53 | int x; | ||
54 | int y; | ||
55 | int width; | ||
56 | int height; | ||
57 | |||
58 | int offset; | ||
59 | int visible_lines; | ||
60 | int total_lines; | ||
61 | struct swaynag_button button_details; | ||
62 | struct swaynag_button button_up; | ||
63 | struct swaynag_button button_down; | ||
64 | }; | ||
65 | |||
66 | struct swaynag { | ||
67 | bool run_display; | ||
68 | int querying_outputs; | ||
69 | |||
70 | struct wl_display *display; | ||
71 | struct wl_compositor *compositor; | ||
72 | struct wl_seat *seat; | ||
73 | struct wl_shm *shm; | ||
74 | struct swaynag_pointer pointer; | ||
75 | struct zxdg_output_manager_v1 *xdg_output_manager; | ||
76 | struct wl_list outputs; // swaynag_output::link | ||
77 | struct swaynag_output *output; | ||
78 | struct zwlr_layer_shell_v1 *layer_shell; | ||
79 | struct zwlr_layer_surface_v1 *layer_surface; | ||
80 | struct wl_surface *surface; | ||
81 | |||
82 | uint32_t width; | ||
83 | uint32_t height; | ||
84 | int32_t scale; | ||
85 | struct pool_buffer buffers[2]; | ||
86 | struct pool_buffer *current_buffer; | ||
87 | |||
88 | struct swaynag_type *type; | ||
89 | char *message; | ||
90 | list_t *buttons; | ||
91 | struct swaynag_details details; | ||
92 | }; | ||
93 | |||
94 | void swaynag_setup(struct swaynag *swaynag); | ||
95 | |||
96 | void swaynag_run(struct swaynag *swaynag); | ||
97 | |||
98 | void swaynag_destroy(struct swaynag *swaynag); | ||
99 | |||
100 | #endif | ||
diff --git a/include/swaynag/types.h b/include/swaynag/types.h new file mode 100644 index 00000000..2183ce22 --- /dev/null +++ b/include/swaynag/types.h | |||
@@ -0,0 +1,39 @@ | |||
1 | #ifndef _SWAYNAG_TYPES_H | ||
2 | #define _SWAYNAG_TYPES_H | ||
3 | |||
4 | struct swaynag_type { | ||
5 | char *name; | ||
6 | |||
7 | char *font; | ||
8 | char *output; | ||
9 | uint32_t anchors; | ||
10 | |||
11 | uint32_t button_background; | ||
12 | uint32_t background; | ||
13 | uint32_t text; | ||
14 | uint32_t border; | ||
15 | uint32_t border_bottom; | ||
16 | |||
17 | uint32_t bar_border_thickness; | ||
18 | uint32_t message_padding; | ||
19 | uint32_t details_border_thickness; | ||
20 | uint32_t button_border_thickness; | ||
21 | uint32_t button_gap; | ||
22 | uint32_t button_gap_close; | ||
23 | uint32_t button_margin_right; | ||
24 | uint32_t button_padding; | ||
25 | }; | ||
26 | |||
27 | void swaynag_types_add_default(list_t *types); | ||
28 | |||
29 | struct swaynag_type *swaynag_type_get(list_t *types, char *name); | ||
30 | |||
31 | struct swaynag_type *swaynag_type_clone(struct swaynag_type *type); | ||
32 | |||
33 | void swaynag_type_merge(struct swaynag_type *dest, struct swaynag_type *src); | ||
34 | |||
35 | void swaynag_type_free(struct swaynag_type *type); | ||
36 | |||
37 | void swaynag_types_free(list_t *types); | ||
38 | |||
39 | #endif | ||
diff --git a/include/util.h b/include/util.h index f68deae8..9277fa6e 100644 --- a/include/util.h +++ b/include/util.h | |||
@@ -2,6 +2,7 @@ | |||
2 | #define _SWAY_UTIL_H | 2 | #define _SWAY_UTIL_H |
3 | 3 | ||
4 | #include <stdint.h> | 4 | #include <stdint.h> |
5 | #include <stdbool.h> | ||
5 | #include <unistd.h> | 6 | #include <unistd.h> |
6 | #include <sys/types.h> | 7 | #include <sys/types.h> |
7 | #include <xkbcommon/xkbcommon.h> | 8 | #include <xkbcommon/xkbcommon.h> |
@@ -51,6 +52,14 @@ pid_t get_parent_pid(pid_t pid); | |||
51 | uint32_t parse_color(const char *color); | 52 | uint32_t parse_color(const char *color); |
52 | 53 | ||
53 | /** | 54 | /** |
55 | * Given a string that represents a boolean, return the boolean value. This | ||
56 | * function also takes in the current boolean value to support toggling. If | ||
57 | * toggling is not desired, pass in true for current so that toggling values | ||
58 | * get parsed as not true. | ||
59 | */ | ||
60 | bool parse_boolean(const char *boolean, bool current); | ||
61 | |||
62 | /** | ||
54 | * Given a path string, recurseively resolves any symlinks to their targets | 63 | * Given a path string, recurseively resolves any symlinks to their targets |
55 | * (which may be a file, directory) and returns the result. | 64 | * (which may be a file, directory) and returns the result. |
56 | * argument is returned. Caller must free the returned buffer. | 65 | * argument is returned. Caller must free the returned buffer. |
diff --git a/meson.build b/meson.build index 1d40581a..2a020323 100644 --- a/meson.build +++ b/meson.build | |||
@@ -12,6 +12,7 @@ project( | |||
12 | add_project_arguments('-Wno-unused-parameter', language: 'c') | 12 | add_project_arguments('-Wno-unused-parameter', language: 'c') |
13 | add_project_arguments('-Wno-unused-function', language: 'c') | 13 | add_project_arguments('-Wno-unused-function', language: 'c') |
14 | add_project_arguments('-Wno-unused-result', language: 'c') | 14 | add_project_arguments('-Wno-unused-result', language: 'c') |
15 | add_project_arguments('-DWLR_USE_UNSTABLE', language: 'c') | ||
15 | 16 | ||
16 | cc = meson.get_compiler('c') | 17 | cc = meson.get_compiler('c') |
17 | 18 | ||
@@ -43,11 +44,17 @@ systemd = dependency('libsystemd', required: false) | |||
43 | elogind = dependency('libelogind', required: false) | 44 | elogind = dependency('libelogind', required: false) |
44 | math = cc.find_library('m') | 45 | math = cc.find_library('m') |
45 | rt = cc.find_library('rt') | 46 | rt = cc.find_library('rt') |
46 | xcb = dependency('xcb') | ||
47 | git = find_program('git', required: false) | 47 | git = find_program('git', required: false) |
48 | 48 | ||
49 | conf_data = configuration_data() | 49 | conf_data = configuration_data() |
50 | 50 | ||
51 | if get_option('enable-xwayland') | ||
52 | conf_data.set('HAVE_XWAYLAND', true) | ||
53 | xcb = dependency('xcb') | ||
54 | else | ||
55 | conf_data.set('HAVE_XWAYLAND', false) | ||
56 | endif | ||
57 | |||
51 | if gdk_pixbuf.found() | 58 | if gdk_pixbuf.found() |
52 | conf_data.set('HAVE_GDK_PIXBUF', true) | 59 | conf_data.set('HAVE_GDK_PIXBUF', true) |
53 | endif | 60 | endif |
@@ -75,6 +82,8 @@ if scdoc.found() | |||
75 | 'swaylock/swaylock.1.scd', | 82 | 'swaylock/swaylock.1.scd', |
76 | 'swaymsg/swaymsg.1.scd', | 83 | 'swaymsg/swaymsg.1.scd', |
77 | 'swayidle/swayidle.1.scd', | 84 | 'swayidle/swayidle.1.scd', |
85 | 'swaynag/swaynag.1.scd', | ||
86 | 'swaynag/swaynag.5.scd', | ||
78 | ] | 87 | ] |
79 | foreach filename : man_files | 88 | foreach filename : man_files |
80 | topic = filename.split('.')[-3].split('/')[-1] | 89 | topic = filename.split('.')[-3].split('/')[-1] |
@@ -123,6 +132,7 @@ subdir('swaybg') | |||
123 | subdir('swaybar') | 132 | subdir('swaybar') |
124 | subdir('swaylock') | 133 | subdir('swaylock') |
125 | subdir('swayidle') | 134 | subdir('swayidle') |
135 | subdir('swaynag') | ||
126 | 136 | ||
127 | config = configuration_data() | 137 | config = configuration_data() |
128 | config.set('sysconfdir', join_paths(prefix, sysconfdir)) | 138 | config.set('sysconfdir', join_paths(prefix, sysconfdir)) |
@@ -176,7 +186,6 @@ endif | |||
176 | if (get_option('zsh_completions')) | 186 | if (get_option('zsh_completions')) |
177 | zsh_files = files( | 187 | zsh_files = files( |
178 | 'completions/zsh/_sway', | 188 | 'completions/zsh/_sway', |
179 | 'completions/zsh/_swaygrab', | ||
180 | 'completions/zsh/_swaylock', | 189 | 'completions/zsh/_swaylock', |
181 | 'completions/zsh/_swaymsg', | 190 | 'completions/zsh/_swaymsg', |
182 | ) | 191 | ) |
@@ -184,3 +193,15 @@ if (get_option('zsh_completions')) | |||
184 | 193 | ||
185 | install_data(zsh_files, install_dir: zsh_install_dir) | 194 | install_data(zsh_files, install_dir: zsh_install_dir) |
186 | endif | 195 | endif |
196 | |||
197 | if (get_option('bash_completions')) | ||
198 | bash_files = files( | ||
199 | 'completions/bash/sway', | ||
200 | 'completions/bash/swayidle', | ||
201 | 'completions/bash/swaylock', | ||
202 | 'completions/bash/swaymsg', | ||
203 | ) | ||
204 | bash_install_dir = datadir + '/bash-completion/completions' | ||
205 | |||
206 | install_data(bash_files, install_dir: bash_install_dir) | ||
207 | endif | ||
diff --git a/meson_options.txt b/meson_options.txt index 541ccf13..7a23c206 100644 --- a/meson_options.txt +++ b/meson_options.txt | |||
@@ -1,3 +1,5 @@ | |||
1 | option('sway_version', type : 'string', description: 'The version string reported in `sway --version`.') | 1 | option('sway_version', type : 'string', description: 'The version string reported in `sway --version`.') |
2 | option('default_wallpaper', type: 'boolean', value: true, description: 'Install the default wallpaper.') | 2 | option('default_wallpaper', type: 'boolean', value: true, description: 'Install the default wallpaper.') |
3 | option('zsh_completions', type: 'boolean', value: true, description: 'Install zsh shell completions.') | 3 | option('zsh_completions', type: 'boolean', value: true, description: 'Install zsh shell completions.') |
4 | option('bash_completions', type: 'boolean', value: true, description: 'Install bash shell completions.') | ||
5 | option('enable-xwayland', type: 'boolean', value: true, description: 'Enable support for X11 applications') | ||
diff --git a/security.d/00-defaults.in b/security.d/00-defaults.in index e4626477..be7b9d06 100644 --- a/security.d/00-defaults.in +++ b/security.d/00-defaults.in | |||
@@ -12,7 +12,6 @@ | |||
12 | permit * fullscreen keyboard mouse | 12 | permit * fullscreen keyboard mouse |
13 | permit @prefix@/bin/swaylock lock | 13 | permit @prefix@/bin/swaylock lock |
14 | permit @prefix@/bin/swaybg background | 14 | permit @prefix@/bin/swaybg background |
15 | permit @prefix@/bin/swaygrab screenshot | ||
16 | permit @prefix@/bin/swaybar panel | 15 | permit @prefix@/bin/swaybar panel |
17 | 16 | ||
18 | # Configures enabled IPC features for specific programs | 17 | # Configures enabled IPC features for specific programs |
@@ -36,11 +35,6 @@ ipc @prefix@/bin/swaybar { | |||
36 | } | 35 | } |
37 | } | 36 | } |
38 | 37 | ||
39 | ipc @prefix@/bin/swaygrab { | ||
40 | outputs enabled | ||
41 | tree enabled | ||
42 | } | ||
43 | |||
44 | ipc @prefix@/bin/swaylock { | 38 | ipc @prefix@/bin/swaylock { |
45 | outputs enabled | 39 | outputs enabled |
46 | } | 40 | } |
diff --git a/sway/commands.c b/sway/commands.c index f1f03574..fdae1961 100644 --- a/sway/commands.c +++ b/sway/commands.c | |||
@@ -103,6 +103,7 @@ static struct cmd_handler handlers[] = { | |||
103 | { "exec_always", cmd_exec_always }, | 103 | { "exec_always", cmd_exec_always }, |
104 | { "floating_maximum_size", cmd_floating_maximum_size }, | 104 | { "floating_maximum_size", cmd_floating_maximum_size }, |
105 | { "floating_minimum_size", cmd_floating_minimum_size }, | 105 | { "floating_minimum_size", cmd_floating_minimum_size }, |
106 | { "floating_modifier", cmd_floating_modifier }, | ||
106 | { "focus", cmd_focus }, | 107 | { "focus", cmd_focus }, |
107 | { "focus_follows_mouse", cmd_focus_follows_mouse }, | 108 | { "focus_follows_mouse", cmd_focus_follows_mouse }, |
108 | { "focus_wrapping", cmd_focus_wrapping }, | 109 | { "focus_wrapping", cmd_focus_wrapping }, |
@@ -148,6 +149,7 @@ static struct cmd_handler command_handlers[] = { | |||
148 | { "reload", cmd_reload }, | 149 | { "reload", cmd_reload }, |
149 | { "rename", cmd_rename }, | 150 | { "rename", cmd_rename }, |
150 | { "resize", cmd_resize }, | 151 | { "resize", cmd_resize }, |
152 | { "scratchpad", cmd_scratchpad }, | ||
151 | { "split", cmd_split }, | 153 | { "split", cmd_split }, |
152 | { "splith", cmd_splith }, | 154 | { "splith", cmd_splith }, |
153 | { "splitt", cmd_splitt }, | 155 | { "splitt", cmd_splitt }, |
@@ -325,7 +327,7 @@ struct cmd_results *execute_command(char *_exec, struct sway_seat *seat) { | |||
325 | } while(head); | 327 | } while(head); |
326 | cleanup: | 328 | cleanup: |
327 | free(exec); | 329 | free(exec); |
328 | free(views); | 330 | list_free(views); |
329 | if (!results) { | 331 | if (!results) { |
330 | results = cmd_results_new(CMD_SUCCESS, NULL, NULL); | 332 | results = cmd_results_new(CMD_SUCCESS, NULL, NULL); |
331 | } | 333 | } |
diff --git a/sway/commands/bind.c b/sway/commands/bind.c index 83e9e432..8270b958 100644 --- a/sway/commands/bind.c +++ b/sway/commands/bind.c | |||
@@ -1,3 +1,4 @@ | |||
1 | #define _XOPEN_SOURCE 500 | ||
1 | #ifdef __linux__ | 2 | #ifdef __linux__ |
2 | #include <linux/input-event-codes.h> | 3 | #include <linux/input-event-codes.h> |
3 | #elif __FreeBSD__ | 4 | #elif __FreeBSD__ |
@@ -5,9 +6,11 @@ | |||
5 | #endif | 6 | #endif |
6 | #include <xkbcommon/xkbcommon.h> | 7 | #include <xkbcommon/xkbcommon.h> |
7 | #include <xkbcommon/xkbcommon-names.h> | 8 | #include <xkbcommon/xkbcommon-names.h> |
9 | #include <string.h> | ||
8 | #include <strings.h> | 10 | #include <strings.h> |
9 | #include "sway/commands.h" | 11 | #include "sway/commands.h" |
10 | #include "sway/config.h" | 12 | #include "sway/config.h" |
13 | #include "sway/ipc-server.h" | ||
11 | #include "list.h" | 14 | #include "list.h" |
12 | #include "log.h" | 15 | #include "log.h" |
13 | #include "stringop.h" | 16 | #include "stringop.h" |
@@ -27,6 +30,33 @@ void free_sway_binding(struct sway_binding *binding) { | |||
27 | free(binding); | 30 | free(binding); |
28 | } | 31 | } |
29 | 32 | ||
33 | static struct sway_binding *sway_binding_dup(struct sway_binding *sb) { | ||
34 | struct sway_binding *new_sb = calloc(1, sizeof(struct sway_binding)); | ||
35 | if (!new_sb) { | ||
36 | return NULL; | ||
37 | } | ||
38 | |||
39 | new_sb->type = sb->type; | ||
40 | new_sb->order = sb->order; | ||
41 | new_sb->flags = sb->flags; | ||
42 | new_sb->modifiers = sb->modifiers; | ||
43 | new_sb->command = strdup(sb->command); | ||
44 | |||
45 | new_sb->keys = create_list(); | ||
46 | int i; | ||
47 | for (i = 0; i < sb->keys->length; ++i) { | ||
48 | xkb_keysym_t *key = malloc(sizeof(xkb_keysym_t)); | ||
49 | if (!key) { | ||
50 | free_sway_binding(new_sb); | ||
51 | return NULL; | ||
52 | } | ||
53 | *key = *(xkb_keysym_t *)sb->keys->items[i]; | ||
54 | list_add(new_sb->keys, key); | ||
55 | } | ||
56 | |||
57 | return new_sb; | ||
58 | } | ||
59 | |||
30 | /** | 60 | /** |
31 | * Returns true if the bindings have the same key and modifier combinations. | 61 | * Returns true if the bindings have the same key and modifier combinations. |
32 | * Note that keyboard layout is not considered, so the bindings might actually | 62 | * Note that keyboard layout is not considered, so the bindings might actually |
@@ -34,11 +64,14 @@ void free_sway_binding(struct sway_binding *binding) { | |||
34 | */ | 64 | */ |
35 | static bool binding_key_compare(struct sway_binding *binding_a, | 65 | static bool binding_key_compare(struct sway_binding *binding_a, |
36 | struct sway_binding *binding_b) { | 66 | struct sway_binding *binding_b) { |
37 | if (binding_a->release != binding_b->release) { | 67 | if (binding_a->type != binding_b->type) { |
38 | return false; | 68 | return false; |
39 | } | 69 | } |
40 | 70 | ||
41 | if (binding_a->bindcode != binding_b->bindcode) { | 71 | uint32_t conflict_generating_flags = BINDING_RELEASE | BINDING_BORDER |
72 | | BINDING_CONTENTS | BINDING_TITLEBAR; | ||
73 | if ((binding_a->flags & conflict_generating_flags) != | ||
74 | (binding_b->flags & conflict_generating_flags)) { | ||
42 | return false; | 75 | return false; |
43 | } | 76 | } |
44 | 77 | ||
@@ -69,6 +102,66 @@ static int key_qsort_cmp(const void *keyp_a, const void *keyp_b) { | |||
69 | return (key_a < key_b) ? -1 : ((key_a > key_b) ? 1 : 0); | 102 | return (key_a < key_b) ? -1 : ((key_a > key_b) ? 1 : 0); |
70 | } | 103 | } |
71 | 104 | ||
105 | |||
106 | /** | ||
107 | * From a keycode, bindcode, or bindsym name and the most likely binding type, | ||
108 | * identify the appropriate numeric value corresponding to the key. Return NULL | ||
109 | * and set *key_val if successful, otherwise return a specific error. Change | ||
110 | * the value of *type if the initial type guess was incorrect and if this | ||
111 | * was the first identified key. | ||
112 | */ | ||
113 | static struct cmd_results *identify_key(const char* name, bool first_key, | ||
114 | uint32_t* key_val, enum binding_input_type* type) { | ||
115 | if (*type == BINDING_KEYCODE) { | ||
116 | // check for keycode | ||
117 | xkb_keycode_t keycode = strtol(name, NULL, 10); | ||
118 | if (!xkb_keycode_is_legal_ext(keycode)) { | ||
119 | return cmd_results_new(CMD_INVALID, "bindcode", | ||
120 | "Invalid keycode '%s'", name); | ||
121 | } | ||
122 | *key_val = keycode; | ||
123 | } else { | ||
124 | // check for keysym | ||
125 | xkb_keysym_t keysym = xkb_keysym_from_name(name, | ||
126 | XKB_KEYSYM_CASE_INSENSITIVE); | ||
127 | |||
128 | // Check for mouse binding | ||
129 | uint32_t button = 0; | ||
130 | if (strncasecmp(name, "button", strlen("button")) == 0 && | ||
131 | strlen(name) == strlen("button0")) { | ||
132 | button = name[strlen("button")] - '1' + BTN_LEFT; | ||
133 | } | ||
134 | |||
135 | if (*type == BINDING_KEYSYM) { | ||
136 | if (button) { | ||
137 | if (first_key) { | ||
138 | *type = BINDING_MOUSE; | ||
139 | *key_val = button; | ||
140 | } else { | ||
141 | return cmd_results_new(CMD_INVALID, "bindsym", | ||
142 | "Mixed button '%s' into key sequence", name); | ||
143 | } | ||
144 | } else if (keysym) { | ||
145 | *key_val = keysym; | ||
146 | } else { | ||
147 | return cmd_results_new(CMD_INVALID, "bindsym", | ||
148 | "Unknown key '%s'", name); | ||
149 | } | ||
150 | } else { | ||
151 | if (button) { | ||
152 | *key_val = button; | ||
153 | } else if (keysym) { | ||
154 | return cmd_results_new(CMD_INVALID, "bindsym", | ||
155 | "Mixed keysym '%s' into button sequence", name); | ||
156 | } else { | ||
157 | return cmd_results_new(CMD_INVALID, "bindsym", | ||
158 | "Unknown button '%s'", name); | ||
159 | } | ||
160 | } | ||
161 | } | ||
162 | return NULL; | ||
163 | } | ||
164 | |||
72 | static struct cmd_results *cmd_bindsym_or_bindcode(int argc, char **argv, | 165 | static struct cmd_results *cmd_bindsym_or_bindcode(int argc, char **argv, |
73 | bool bindcode) { | 166 | bool bindcode) { |
74 | const char *bindtype = bindcode ? "bindcode" : "bindsym"; | 167 | const char *bindtype = bindcode ? "bindcode" : "bindsym"; |
@@ -85,22 +178,34 @@ static struct cmd_results *cmd_bindsym_or_bindcode(int argc, char **argv, | |||
85 | } | 178 | } |
86 | binding->keys = create_list(); | 179 | binding->keys = create_list(); |
87 | binding->modifiers = 0; | 180 | binding->modifiers = 0; |
88 | binding->release = false; | 181 | binding->flags = 0; |
89 | binding->locked = false; | 182 | binding->type = bindcode ? BINDING_KEYCODE : BINDING_KEYSYM; |
90 | binding->bindcode = bindcode; | 183 | |
184 | bool exclude_titlebar = false; | ||
91 | 185 | ||
92 | // Handle --release and --locked | 186 | // Handle --release and --locked |
93 | while (argc > 0) { | 187 | while (argc > 0) { |
94 | if (strcmp("--release", argv[0]) == 0) { | 188 | if (strcmp("--release", argv[0]) == 0) { |
95 | binding->release = true; | 189 | binding->flags |= BINDING_RELEASE; |
96 | } else if (strcmp("--locked", argv[0]) == 0) { | 190 | } else if (strcmp("--locked", argv[0]) == 0) { |
97 | binding->locked = true; | 191 | binding->flags |= BINDING_LOCKED; |
192 | } else if (strcmp("--whole-window", argv[0]) == 0) { | ||
193 | binding->flags |= BINDING_BORDER | BINDING_CONTENTS | BINDING_TITLEBAR; | ||
194 | } else if (strcmp("--border", argv[0]) == 0) { | ||
195 | binding->flags |= BINDING_BORDER; | ||
196 | } else if (strcmp("--exclude-titlebar", argv[0]) == 0) { | ||
197 | exclude_titlebar = true; | ||
98 | } else { | 198 | } else { |
99 | break; | 199 | break; |
100 | } | 200 | } |
101 | argv++; | 201 | argv++; |
102 | argc--; | 202 | argc--; |
103 | } | 203 | } |
204 | if (binding->flags & (BINDING_BORDER | BINDING_CONTENTS | BINDING_TITLEBAR) | ||
205 | || exclude_titlebar) { | ||
206 | binding->type = BINDING_MOUSE; | ||
207 | } | ||
208 | |||
104 | if (argc < 2) { | 209 | if (argc < 2) { |
105 | free_sway_binding(binding); | 210 | free_sway_binding(binding); |
106 | return cmd_results_new(CMD_FAILURE, bindtype, | 211 | return cmd_results_new(CMD_FAILURE, bindtype, |
@@ -119,64 +224,47 @@ static struct cmd_results *cmd_bindsym_or_bindcode(int argc, char **argv, | |||
119 | continue; | 224 | continue; |
120 | } | 225 | } |
121 | 226 | ||
122 | xkb_keycode_t keycode; | 227 | // Identify the key and possibly change binding->type |
123 | xkb_keysym_t keysym; | 228 | uint32_t key_val = 0; |
124 | if (bindcode) { | 229 | error = identify_key(split->items[i], binding->keys->length == 0, |
125 | // parse keycode | 230 | &key_val, &binding->type); |
126 | keycode = (int)strtol(split->items[i], NULL, 10); | 231 | if (error) { |
127 | if (!xkb_keycode_is_legal_ext(keycode)) { | 232 | free_sway_binding(binding); |
128 | error = | 233 | list_free(split); |
129 | cmd_results_new(CMD_INVALID, "bindcode", | 234 | return error; |
130 | "Invalid keycode '%s'", (char *)split->items[i]); | ||
131 | free_sway_binding(binding); | ||
132 | list_free(split); | ||
133 | return error; | ||
134 | } | ||
135 | } else { | ||
136 | // Check for xkb key | ||
137 | keysym = xkb_keysym_from_name(split->items[i], | ||
138 | XKB_KEYSYM_CASE_INSENSITIVE); | ||
139 | |||
140 | // Check for mouse binding | ||
141 | if (strncasecmp(split->items[i], "button", strlen("button")) == 0 && | ||
142 | strlen(split->items[i]) == strlen("button0")) { | ||
143 | keysym = ((char *)split->items[i])[strlen("button")] - '1' + BTN_LEFT; | ||
144 | } | ||
145 | if (!keysym) { | ||
146 | struct cmd_results *ret = cmd_results_new(CMD_INVALID, "bindsym", | ||
147 | "Unknown key '%s'", (char *)split->items[i]); | ||
148 | free_sway_binding(binding); | ||
149 | free_flat_list(split); | ||
150 | return ret; | ||
151 | } | ||
152 | } | 235 | } |
236 | |||
153 | uint32_t *key = calloc(1, sizeof(uint32_t)); | 237 | uint32_t *key = calloc(1, sizeof(uint32_t)); |
154 | if (!key) { | 238 | if (!key) { |
155 | free_sway_binding(binding); | 239 | free_sway_binding(binding); |
156 | free_flat_list(split); | 240 | free_flat_list(split); |
157 | return cmd_results_new(CMD_FAILURE, bindtype, | 241 | return cmd_results_new(CMD_FAILURE, bindtype, |
158 | "Unable to allocate binding"); | 242 | "Unable to allocate binding key"); |
159 | } | ||
160 | |||
161 | if (bindcode) { | ||
162 | *key = (uint32_t)keycode; | ||
163 | } else { | ||
164 | *key = (uint32_t)keysym; | ||
165 | } | 243 | } |
166 | 244 | *key = key_val; | |
167 | list_add(binding->keys, key); | 245 | list_add(binding->keys, key); |
168 | } | 246 | } |
169 | free_flat_list(split); | 247 | free_flat_list(split); |
170 | binding->order = binding_order++; | 248 | binding->order = binding_order++; |
171 | 249 | ||
250 | // refine region of interest for mouse binding once we are certain | ||
251 | // that this is one | ||
252 | if (exclude_titlebar) { | ||
253 | binding->flags &= ~BINDING_TITLEBAR; | ||
254 | } else if (binding->type == BINDING_MOUSE) { | ||
255 | binding->flags |= BINDING_TITLEBAR; | ||
256 | } | ||
257 | |||
172 | // sort ascending | 258 | // sort ascending |
173 | list_qsort(binding->keys, key_qsort_cmp); | 259 | list_qsort(binding->keys, key_qsort_cmp); |
174 | 260 | ||
175 | list_t *mode_bindings; | 261 | list_t *mode_bindings; |
176 | if (bindcode) { | 262 | if (binding->type == BINDING_KEYCODE) { |
177 | mode_bindings = config->current_mode->keycode_bindings; | 263 | mode_bindings = config->current_mode->keycode_bindings; |
178 | } else { | 264 | } else if (binding->type == BINDING_KEYSYM) { |
179 | mode_bindings = config->current_mode->keysym_bindings; | 265 | mode_bindings = config->current_mode->keysym_bindings; |
266 | } else { | ||
267 | mode_bindings = config->current_mode->mouse_bindings; | ||
180 | } | 268 | } |
181 | 269 | ||
182 | // overwrite the binding if it already exists | 270 | // overwrite the binding if it already exists |
@@ -209,3 +297,39 @@ struct cmd_results *cmd_bindsym(int argc, char **argv) { | |||
209 | struct cmd_results *cmd_bindcode(int argc, char **argv) { | 297 | struct cmd_results *cmd_bindcode(int argc, char **argv) { |
210 | return cmd_bindsym_or_bindcode(argc, argv, true); | 298 | return cmd_bindsym_or_bindcode(argc, argv, true); |
211 | } | 299 | } |
300 | |||
301 | |||
302 | /** | ||
303 | * Execute the command associated to a binding | ||
304 | */ | ||
305 | void seat_execute_command(struct sway_seat *seat, struct sway_binding *binding) { | ||
306 | wlr_log(WLR_DEBUG, "running command for binding: %s", | ||
307 | binding->command); | ||
308 | |||
309 | struct sway_binding *binding_copy = binding; | ||
310 | bool reload = false; | ||
311 | // if this is a reload command we need to make a duplicate of the | ||
312 | // binding since it will be gone after the reload has completed. | ||
313 | if (strcasecmp(binding->command, "reload") == 0) { | ||
314 | reload = true; | ||
315 | binding_copy = sway_binding_dup(binding); | ||
316 | if (!binding_copy) { | ||
317 | wlr_log(WLR_ERROR, "Failed to duplicate binding during reload"); | ||
318 | return; | ||
319 | } | ||
320 | } | ||
321 | |||
322 | config->handler_context.seat = seat; | ||
323 | struct cmd_results *results = execute_command(binding->command, NULL); | ||
324 | if (results->status == CMD_SUCCESS) { | ||
325 | ipc_event_binding(binding_copy); | ||
326 | } else { | ||
327 | wlr_log(WLR_DEBUG, "could not run command for binding: %s (%s)", | ||
328 | binding->command, results->error); | ||
329 | } | ||
330 | |||
331 | if (reload) { // free the binding if we made a copy | ||
332 | free_sway_binding(binding_copy); | ||
333 | } | ||
334 | free_cmd_results(results); | ||
335 | } | ||
diff --git a/sway/commands/exec_always.c b/sway/commands/exec_always.c index c7727857..c730cb8b 100644 --- a/sway/commands/exec_always.c +++ b/sway/commands/exec_always.c | |||
@@ -4,6 +4,7 @@ | |||
4 | #include <string.h> | 4 | #include <string.h> |
5 | #include <sys/wait.h> | 5 | #include <sys/wait.h> |
6 | #include <unistd.h> | 6 | #include <unistd.h> |
7 | #include <signal.h> | ||
7 | #include "sway/commands.h" | 8 | #include "sway/commands.h" |
8 | #include "sway/config.h" | 9 | #include "sway/config.h" |
9 | #include "sway/tree/container.h" | 10 | #include "sway/tree/container.h" |
@@ -47,6 +48,9 @@ struct cmd_results *cmd_exec_always(int argc, char **argv) { | |||
47 | if ((pid = fork()) == 0) { | 48 | if ((pid = fork()) == 0) { |
48 | // Fork child process again | 49 | // Fork child process again |
49 | setsid(); | 50 | setsid(); |
51 | sigset_t set; | ||
52 | sigemptyset(&set); | ||
53 | sigprocmask(SIG_SETMASK, &set, NULL); | ||
50 | close(fd[0]); | 54 | close(fd[0]); |
51 | if ((child = fork()) == 0) { | 55 | if ((child = fork()) == 0) { |
52 | close(fd[1]); | 56 | close(fd[1]); |
@@ -74,7 +78,7 @@ struct cmd_results *cmd_exec_always(int argc, char **argv) { | |||
74 | waitpid(pid, NULL, 0); | 78 | waitpid(pid, NULL, 0); |
75 | if (child > 0) { | 79 | if (child > 0) { |
76 | wlr_log(WLR_DEBUG, "Child process created with pid %d", child); | 80 | wlr_log(WLR_DEBUG, "Child process created with pid %d", child); |
77 | // TODO: add PID to active workspace | 81 | workspace_record_pid(child); |
78 | } else { | 82 | } else { |
79 | return cmd_results_new(CMD_FAILURE, "exec_always", | 83 | return cmd_results_new(CMD_FAILURE, "exec_always", |
80 | "Second fork() failed"); | 84 | "Second fork() failed"); |
diff --git a/sway/commands/floating.c b/sway/commands/floating.c index 6ab56c3b..31de5ec3 100644 --- a/sway/commands/floating.c +++ b/sway/commands/floating.c | |||
@@ -17,9 +17,24 @@ struct cmd_results *cmd_floating(int argc, char **argv) { | |||
17 | } | 17 | } |
18 | struct sway_container *container = | 18 | struct sway_container *container = |
19 | config->handler_context.current_container; | 19 | config->handler_context.current_container; |
20 | if (container->type != C_VIEW) { | 20 | if (container->type == C_WORKSPACE && container->children->length == 0) { |
21 | // TODO: This doesn't strictly speaking have to be true | 21 | return cmd_results_new(CMD_INVALID, "floating", |
22 | return cmd_results_new(CMD_INVALID, "float", "Only views can float"); | 22 | "Can't float an empty workspace"); |
23 | } | ||
24 | if (container->type == C_WORKSPACE) { | ||
25 | // Wrap the workspace's children in a container so we can float it | ||
26 | struct sway_container *workspace = container; | ||
27 | container = container_wrap_children(container); | ||
28 | workspace->layout = L_HORIZ; | ||
29 | seat_set_focus(config->handler_context.seat, container); | ||
30 | } | ||
31 | |||
32 | // If the container is in a floating split container, | ||
33 | // operate on the split container instead of the child. | ||
34 | if (container_is_floating_or_child(container)) { | ||
35 | while (container->parent->layout != L_FLOATING) { | ||
36 | container = container->parent; | ||
37 | } | ||
23 | } | 38 | } |
24 | 39 | ||
25 | bool wants_floating; | 40 | bool wants_floating; |
diff --git a/sway/commands/floating_modifier.c b/sway/commands/floating_modifier.c new file mode 100644 index 00000000..f5d2b3fe --- /dev/null +++ b/sway/commands/floating_modifier.c | |||
@@ -0,0 +1,30 @@ | |||
1 | #include "strings.h" | ||
2 | #include "sway/commands.h" | ||
3 | #include "sway/config.h" | ||
4 | #include "util.h" | ||
5 | |||
6 | struct cmd_results *cmd_floating_modifier(int argc, char **argv) { | ||
7 | struct cmd_results *error = NULL; | ||
8 | if ((error = checkarg(argc, "floating_modifier", EXPECTED_AT_LEAST, 1))) { | ||
9 | return error; | ||
10 | } | ||
11 | |||
12 | uint32_t mod = get_modifier_mask_by_name(argv[0]); | ||
13 | if (!mod) { | ||
14 | return cmd_results_new(CMD_INVALID, "floating_modifier", | ||
15 | "Invalid modifier"); | ||
16 | } | ||
17 | |||
18 | if (argc == 1 || strcasecmp(argv[1], "normal") == 0) { | ||
19 | config->floating_mod_inverse = false; | ||
20 | } else if (strcasecmp(argv[1], "inverse") == 0) { | ||
21 | config->floating_mod_inverse = true; | ||
22 | } else { | ||
23 | return cmd_results_new(CMD_INVALID, "floating_modifier", | ||
24 | "Usage: floating_modifier <mod> [inverse|normal]"); | ||
25 | } | ||
26 | |||
27 | config->floating_mod = mod; | ||
28 | |||
29 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | ||
30 | } | ||
diff --git a/sway/commands/focus.c b/sway/commands/focus.c index 9cd8bfae..76d3f1dc 100644 --- a/sway/commands/focus.c +++ b/sway/commands/focus.c | |||
@@ -35,14 +35,25 @@ static struct cmd_results *focus_mode(struct sway_container *con, | |||
35 | struct sway_seat *seat, bool floating) { | 35 | struct sway_seat *seat, bool floating) { |
36 | struct sway_container *ws = con->type == C_WORKSPACE ? | 36 | struct sway_container *ws = con->type == C_WORKSPACE ? |
37 | con : container_parent(con, C_WORKSPACE); | 37 | con : container_parent(con, C_WORKSPACE); |
38 | struct sway_container *new_focus = ws; | 38 | |
39 | if (floating) { | 39 | // If the container is in a floating split container, |
40 | new_focus = ws->sway_workspace->floating; | 40 | // operate on the split container instead of the child. |
41 | if (new_focus->children->length == 0) { | 41 | if (container_is_floating_or_child(con)) { |
42 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | 42 | while (con->parent->layout != L_FLOATING) { |
43 | con = con->parent; | ||
43 | } | 44 | } |
44 | } | 45 | } |
45 | seat_set_focus(seat, seat_get_active_child(seat, new_focus)); | 46 | |
47 | struct sway_container *new_focus = NULL; | ||
48 | if (floating) { | ||
49 | new_focus = seat_get_focus_inactive(seat, ws->sway_workspace->floating); | ||
50 | } else { | ||
51 | new_focus = seat_get_focus_inactive_tiling(seat, ws); | ||
52 | } | ||
53 | if (!new_focus) { | ||
54 | new_focus = ws; | ||
55 | } | ||
56 | seat_set_focus(seat, new_focus); | ||
46 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | 57 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); |
47 | } | 58 | } |
48 | 59 | ||
@@ -97,7 +108,7 @@ struct cmd_results *cmd_focus(int argc, char **argv) { | |||
97 | } else if (strcmp(argv[0], "tiling") == 0) { | 108 | } else if (strcmp(argv[0], "tiling") == 0) { |
98 | return focus_mode(con, seat, false); | 109 | return focus_mode(con, seat, false); |
99 | } else if (strcmp(argv[0], "mode_toggle") == 0) { | 110 | } else if (strcmp(argv[0], "mode_toggle") == 0) { |
100 | return focus_mode(con, seat, !container_is_floating(con)); | 111 | return focus_mode(con, seat, !container_is_floating_or_child(con)); |
101 | } | 112 | } |
102 | 113 | ||
103 | if (strcmp(argv[0], "output") == 0) { | 114 | if (strcmp(argv[0], "output") == 0) { |
diff --git a/sway/commands/focus_follows_mouse.c b/sway/commands/focus_follows_mouse.c index 661e7852..0b0e334c 100644 --- a/sway/commands/focus_follows_mouse.c +++ b/sway/commands/focus_follows_mouse.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 | ||
5 | struct cmd_results *cmd_focus_follows_mouse(int argc, char **argv) { | 6 | struct cmd_results *cmd_focus_follows_mouse(int argc, char **argv) { |
6 | struct cmd_results *error = NULL; | 7 | struct cmd_results *error = NULL; |
7 | if ((error = checkarg(argc, "focus_follows_mouse", EXPECTED_EQUAL_TO, 1))) { | 8 | if ((error = checkarg(argc, "focus_follows_mouse", EXPECTED_EQUAL_TO, 1))) { |
8 | return error; | 9 | return error; |
9 | } | 10 | } |
10 | config->focus_follows_mouse = !strcasecmp(argv[0], "yes"); | 11 | config->focus_follows_mouse = |
12 | parse_boolean(argv[0], config->focus_follows_mouse); | ||
11 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | 13 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); |
12 | } | 14 | } |
diff --git a/sway/commands/focus_wrapping.c b/sway/commands/focus_wrapping.c index 0a9e0bf2..562ee4f9 100644 --- a/sway/commands/focus_wrapping.c +++ b/sway/commands/focus_wrapping.c | |||
@@ -1,6 +1,7 @@ | |||
1 | #include <strings.h> | 1 | #include <strings.h> |
2 | #include "sway/commands.h" | 2 | #include "sway/commands.h" |
3 | #include "sway/config.h" | 3 | #include "sway/config.h" |
4 | #include "util.h" | ||
4 | 5 | ||
5 | struct cmd_results *cmd_focus_wrapping(int argc, char **argv) { | 6 | struct cmd_results *cmd_focus_wrapping(int argc, char **argv) { |
6 | struct cmd_results *error = NULL; | 7 | struct cmd_results *error = NULL; |
@@ -8,15 +9,12 @@ struct cmd_results *cmd_focus_wrapping(int argc, char **argv) { | |||
8 | return error; | 9 | return error; |
9 | } | 10 | } |
10 | 11 | ||
11 | if (strcasecmp(argv[0], "no") == 0) { | 12 | if (strcasecmp(argv[0], "force") == 0) { |
12 | config->focus_wrapping = WRAP_NO; | ||
13 | } else if (strcasecmp(argv[0], "yes") == 0) { | ||
14 | config->focus_wrapping = WRAP_YES; | ||
15 | } else if (strcasecmp(argv[0], "force") == 0) { | ||
16 | config->focus_wrapping = WRAP_FORCE; | 13 | config->focus_wrapping = WRAP_FORCE; |
14 | } else if (parse_boolean(argv[0], config->focus_wrapping == WRAP_YES)) { | ||
15 | config->focus_wrapping = WRAP_YES; | ||
17 | } else { | 16 | } else { |
18 | return cmd_results_new(CMD_INVALID, "focus_wrapping", | 17 | config->focus_wrapping = WRAP_NO; |
19 | "Expected 'focus_wrapping yes|no|force'"); | ||
20 | } | 18 | } |
21 | 19 | ||
22 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | 20 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); |
diff --git a/sway/commands/force_focus_wrapping.c b/sway/commands/force_focus_wrapping.c index bc1d067f..0892d9e9 100644 --- a/sway/commands/force_focus_wrapping.c +++ b/sway/commands/force_focus_wrapping.c | |||
@@ -1,6 +1,7 @@ | |||
1 | #include <strings.h> | 1 | #include <strings.h> |
2 | #include "sway/commands.h" | 2 | #include "sway/commands.h" |
3 | #include "sway/config.h" | 3 | #include "sway/config.h" |
4 | #include "util.h" | ||
4 | 5 | ||
5 | struct cmd_results *cmd_force_focus_wrapping(int argc, char **argv) { | 6 | struct cmd_results *cmd_force_focus_wrapping(int argc, char **argv) { |
6 | struct cmd_results *error = | 7 | struct cmd_results *error = |
@@ -9,13 +10,10 @@ struct cmd_results *cmd_force_focus_wrapping(int argc, char **argv) { | |||
9 | return error; | 10 | return error; |
10 | } | 11 | } |
11 | 12 | ||
12 | if (strcasecmp(argv[0], "no") == 0) { | 13 | if (parse_boolean(argv[0], config->focus_wrapping == WRAP_FORCE)) { |
13 | config->focus_wrapping = WRAP_YES; | ||
14 | } else if (strcasecmp(argv[0], "yes") == 0) { | ||
15 | config->focus_wrapping = WRAP_FORCE; | 14 | config->focus_wrapping = WRAP_FORCE; |
16 | } else { | 15 | } else { |
17 | return cmd_results_new(CMD_INVALID, "force_focus_wrapping", | 16 | config->focus_wrapping = WRAP_YES; |
18 | "Expected 'force_focus_wrapping yes|no'"); | ||
19 | } | 17 | } |
20 | 18 | ||
21 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | 19 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); |
diff --git a/sway/commands/fullscreen.c b/sway/commands/fullscreen.c index 0b5beaa2..5ad06e40 100644 --- a/sway/commands/fullscreen.c +++ b/sway/commands/fullscreen.c | |||
@@ -5,6 +5,7 @@ | |||
5 | #include "sway/tree/container.h" | 5 | #include "sway/tree/container.h" |
6 | #include "sway/tree/view.h" | 6 | #include "sway/tree/view.h" |
7 | #include "sway/tree/layout.h" | 7 | #include "sway/tree/layout.h" |
8 | #include "util.h" | ||
8 | 9 | ||
9 | struct cmd_results *cmd_fullscreen(int argc, char **argv) { | 10 | struct cmd_results *cmd_fullscreen(int argc, char **argv) { |
10 | struct cmd_results *error = NULL; | 11 | struct cmd_results *error = NULL; |
@@ -13,25 +14,24 @@ struct cmd_results *cmd_fullscreen(int argc, char **argv) { | |||
13 | } | 14 | } |
14 | struct sway_container *container = | 15 | struct sway_container *container = |
15 | config->handler_context.current_container; | 16 | config->handler_context.current_container; |
16 | if (container->type != C_VIEW) { | 17 | if (container->type == C_WORKSPACE && container->children->length == 0) { |
17 | return cmd_results_new(CMD_INVALID, "fullscreen", | 18 | return cmd_results_new(CMD_INVALID, "fullscreen", |
18 | "Only views can fullscreen"); | 19 | "Can't fullscreen an empty workspace"); |
19 | } | 20 | } |
20 | struct sway_view *view = container->sway_view; | 21 | if (container->type == C_WORKSPACE) { |
21 | bool wants_fullscreen; | 22 | // Wrap the workspace's children in a container so we can fullscreen it |
23 | struct sway_container *workspace = container; | ||
24 | container = container_wrap_children(container); | ||
25 | workspace->layout = L_HORIZ; | ||
26 | seat_set_focus(config->handler_context.seat, container); | ||
27 | } | ||
28 | bool enable = !container->is_fullscreen; | ||
22 | 29 | ||
23 | if (argc == 0 || strcmp(argv[0], "toggle") == 0) { | 30 | if (argc) { |
24 | wants_fullscreen = !view->is_fullscreen; | 31 | enable = parse_boolean(argv[0], container->is_fullscreen); |
25 | } else if (strcmp(argv[0], "enable") == 0) { | ||
26 | wants_fullscreen = true; | ||
27 | } else if (strcmp(argv[0], "disable") == 0) { | ||
28 | wants_fullscreen = false; | ||
29 | } else { | ||
30 | return cmd_results_new(CMD_INVALID, "fullscreen", | ||
31 | "Expected 'fullscreen' or 'fullscreen <enable|disable|toggle>'"); | ||
32 | } | 32 | } |
33 | 33 | ||
34 | view_set_fullscreen(view, wants_fullscreen); | 34 | container_set_fullscreen(container, enable); |
35 | 35 | ||
36 | struct sway_container *workspace = container_parent(container, C_WORKSPACE); | 36 | struct sway_container *workspace = container_parent(container, C_WORKSPACE); |
37 | arrange_windows(workspace->parent); | 37 | arrange_windows(workspace->parent); |
diff --git a/sway/commands/input.c b/sway/commands/input.c index 5b203ea0..84888fbb 100644 --- a/sway/commands/input.c +++ b/sway/commands/input.c | |||
@@ -31,6 +31,12 @@ static struct cmd_handler input_handlers[] = { | |||
31 | { "xkb_variant", input_cmd_xkb_variant }, | 31 | { "xkb_variant", input_cmd_xkb_variant }, |
32 | }; | 32 | }; |
33 | 33 | ||
34 | // must be in order for the bsearch | ||
35 | static struct cmd_handler input_config_handlers[] = { | ||
36 | { "xkb_capslock", input_cmd_xkb_capslock }, | ||
37 | { "xkb_numlock", input_cmd_xkb_numlock }, | ||
38 | }; | ||
39 | |||
34 | struct cmd_results *cmd_input(int argc, char **argv) { | 40 | struct cmd_results *cmd_input(int argc, char **argv) { |
35 | struct cmd_results *error = NULL; | 41 | struct cmd_results *error = NULL; |
36 | if ((error = checkarg(argc, "input", EXPECTED_AT_LEAST, 2))) { | 42 | if ((error = checkarg(argc, "input", EXPECTED_AT_LEAST, 2))) { |
@@ -44,8 +50,21 @@ struct cmd_results *cmd_input(int argc, char **argv) { | |||
44 | return cmd_results_new(CMD_FAILURE, NULL, "Couldn't allocate config"); | 50 | return cmd_results_new(CMD_FAILURE, NULL, "Couldn't allocate config"); |
45 | } | 51 | } |
46 | 52 | ||
47 | struct cmd_results *res = config_subcommand(argv + 1, argc - 1, | 53 | struct cmd_results *res; |
54 | |||
55 | if (find_handler(argv[1], input_config_handlers, | ||
56 | sizeof(input_config_handlers))) { | ||
57 | if (config->reading) { | ||
58 | res = config_subcommand(argv + 1, argc - 1, | ||
59 | input_config_handlers, sizeof(input_config_handlers)); | ||
60 | } else { | ||
61 | res = cmd_results_new(CMD_FAILURE, "input", | ||
62 | "Can only be used in config file."); | ||
63 | } | ||
64 | } else { | ||
65 | res = config_subcommand(argv + 1, argc - 1, | ||
48 | input_handlers, sizeof(input_handlers)); | 66 | input_handlers, sizeof(input_handlers)); |
67 | } | ||
49 | 68 | ||
50 | free_input_config(config->handler_context.input_config); | 69 | free_input_config(config->handler_context.input_config); |
51 | config->handler_context.input_config = NULL; | 70 | config->handler_context.input_config = NULL; |
diff --git a/sway/commands/input/drag_lock.c b/sway/commands/input/drag_lock.c index 9e32816f..f9ddeef2 100644 --- a/sway/commands/input/drag_lock.c +++ b/sway/commands/input/drag_lock.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 | ||
7 | struct cmd_results *input_cmd_drag_lock(int argc, char **argv) { | 8 | struct cmd_results *input_cmd_drag_lock(int argc, char **argv) { |
8 | struct cmd_results *error = NULL; | 9 | struct cmd_results *error = NULL; |
@@ -18,14 +19,10 @@ struct cmd_results *input_cmd_drag_lock(int argc, char **argv) { | |||
18 | struct input_config *new_config = | 19 | struct input_config *new_config = |
19 | new_input_config(current_input_config->identifier); | 20 | new_input_config(current_input_config->identifier); |
20 | 21 | ||
21 | if (strcasecmp(argv[0], "enabled") == 0) { | 22 | if (parse_boolean(argv[0], true)) { |
22 | new_config->drag_lock = LIBINPUT_CONFIG_DRAG_LOCK_ENABLED; | 23 | new_config->drag_lock = LIBINPUT_CONFIG_DRAG_LOCK_ENABLED; |
23 | } else if (strcasecmp(argv[0], "disabled") == 0) { | ||
24 | new_config->drag_lock = LIBINPUT_CONFIG_DRAG_LOCK_DISABLED; | ||
25 | } else { | 24 | } else { |
26 | free_input_config(new_config); | 25 | new_config->drag_lock = LIBINPUT_CONFIG_DRAG_LOCK_DISABLED; |
27 | return cmd_results_new(CMD_INVALID, "drag_lock", | ||
28 | "Expected 'drag_lock <enabled|disabled>'"); | ||
29 | } | 26 | } |
30 | 27 | ||
31 | apply_input_config(new_config); | 28 | apply_input_config(new_config); |
diff --git a/sway/commands/input/dwt.c b/sway/commands/input/dwt.c index 73937507..15134268 100644 --- a/sway/commands/input/dwt.c +++ b/sway/commands/input/dwt.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 | ||
7 | struct cmd_results *input_cmd_dwt(int argc, char **argv) { | 8 | struct cmd_results *input_cmd_dwt(int argc, char **argv) { |
8 | struct cmd_results *error = NULL; | 9 | struct cmd_results *error = NULL; |
@@ -17,14 +18,10 @@ struct cmd_results *input_cmd_dwt(int argc, char **argv) { | |||
17 | struct input_config *new_config = | 18 | struct input_config *new_config = |
18 | new_input_config(current_input_config->identifier); | 19 | new_input_config(current_input_config->identifier); |
19 | 20 | ||
20 | if (strcasecmp(argv[0], "enabled") == 0) { | 21 | if (parse_boolean(argv[0], true)) { |
21 | new_config->dwt = LIBINPUT_CONFIG_DWT_ENABLED; | 22 | new_config->dwt = LIBINPUT_CONFIG_DWT_ENABLED; |
22 | } else if (strcasecmp(argv[0], "disabled") == 0) { | ||
23 | new_config->dwt = LIBINPUT_CONFIG_DWT_DISABLED; | ||
24 | } else { | 23 | } else { |
25 | free_input_config(new_config); | 24 | new_config->dwt = LIBINPUT_CONFIG_DWT_DISABLED; |
26 | return cmd_results_new(CMD_INVALID, "dwt", | ||
27 | "Expected 'dwt <enabled|disabled>'"); | ||
28 | } | 25 | } |
29 | 26 | ||
30 | apply_input_config(new_config); | 27 | apply_input_config(new_config); |
diff --git a/sway/commands/input/left_handed.c b/sway/commands/input/left_handed.c index 769ce98c..e770043a 100644 --- a/sway/commands/input/left_handed.c +++ b/sway/commands/input/left_handed.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 | ||
7 | struct cmd_results *input_cmd_left_handed(int argc, char **argv) { | 8 | struct cmd_results *input_cmd_left_handed(int argc, char **argv) { |
8 | struct cmd_results *error = NULL; | 9 | struct cmd_results *error = NULL; |
@@ -18,15 +19,7 @@ struct cmd_results *input_cmd_left_handed(int argc, char **argv) { | |||
18 | struct input_config *new_config = | 19 | struct input_config *new_config = |
19 | new_input_config(current_input_config->identifier); | 20 | new_input_config(current_input_config->identifier); |
20 | 21 | ||
21 | if (strcasecmp(argv[0], "enabled") == 0) { | 22 | new_config->left_handed = parse_boolean(argv[0], true); |
22 | new_config->left_handed = 1; | ||
23 | } else if (strcasecmp(argv[0], "disabled") == 0) { | ||
24 | new_config->left_handed = 0; | ||
25 | } else { | ||
26 | free_input_config(new_config); | ||
27 | return cmd_results_new(CMD_INVALID, "left_handed", | ||
28 | "Expected 'left_handed <enabled|disabled>'"); | ||
29 | } | ||
30 | 23 | ||
31 | apply_input_config(new_config); | 24 | apply_input_config(new_config); |
32 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | 25 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); |
diff --git a/sway/commands/input/middle_emulation.c b/sway/commands/input/middle_emulation.c index 7ca01629..414d4d2b 100644 --- a/sway/commands/input/middle_emulation.c +++ b/sway/commands/input/middle_emulation.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 | ||
7 | struct cmd_results *input_cmd_middle_emulation(int argc, char **argv) { | 8 | struct cmd_results *input_cmd_middle_emulation(int argc, char **argv) { |
8 | struct cmd_results *error = NULL; | 9 | struct cmd_results *error = NULL; |
@@ -18,15 +19,11 @@ struct cmd_results *input_cmd_middle_emulation(int argc, char **argv) { | |||
18 | struct input_config *new_config = | 19 | struct input_config *new_config = |
19 | new_input_config(current_input_config->identifier); | 20 | new_input_config(current_input_config->identifier); |
20 | 21 | ||
21 | if (strcasecmp(argv[0], "enabled") == 0) { | 22 | if (parse_boolean(argv[0], true)) { |
22 | new_config->middle_emulation = LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED; | 23 | new_config->middle_emulation = LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED; |
23 | } else if (strcasecmp(argv[0], "disabled") == 0) { | 24 | } else { |
24 | new_config->middle_emulation = | 25 | new_config->middle_emulation = |
25 | LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED; | 26 | LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED; |
26 | } else { | ||
27 | free_input_config(new_config); | ||
28 | return cmd_results_new(CMD_INVALID, "middle_emulation", | ||
29 | "Expected 'middle_emulation <enabled|disabled>'"); | ||
30 | } | 27 | } |
31 | 28 | ||
32 | apply_input_config(new_config); | 29 | apply_input_config(new_config); |
diff --git a/sway/commands/input/natural_scroll.c b/sway/commands/input/natural_scroll.c index 55236790..77c3ff00 100644 --- a/sway/commands/input/natural_scroll.c +++ b/sway/commands/input/natural_scroll.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 | ||
7 | struct cmd_results *input_cmd_natural_scroll(int argc, char **argv) { | 8 | struct cmd_results *input_cmd_natural_scroll(int argc, char **argv) { |
8 | struct cmd_results *error = NULL; | 9 | struct cmd_results *error = NULL; |
@@ -18,15 +19,7 @@ struct cmd_results *input_cmd_natural_scroll(int argc, char **argv) { | |||
18 | struct input_config *new_config = | 19 | struct input_config *new_config = |
19 | new_input_config(current_input_config->identifier); | 20 | new_input_config(current_input_config->identifier); |
20 | 21 | ||
21 | if (strcasecmp(argv[0], "enabled") == 0) { | 22 | new_config->natural_scroll = parse_boolean(argv[0], true); |
22 | new_config->natural_scroll = 1; | ||
23 | } else if (strcasecmp(argv[0], "disabled") == 0) { | ||
24 | new_config->natural_scroll = 0; | ||
25 | } else { | ||
26 | free_input_config(new_config); | ||
27 | return cmd_results_new(CMD_INVALID, "natural_scroll", | ||
28 | "Expected 'natural_scroll <enabled|disabled>'"); | ||
29 | } | ||
30 | 23 | ||
31 | apply_input_config(new_config); | 24 | apply_input_config(new_config); |
32 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | 25 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); |
diff --git a/sway/commands/input/tap.c b/sway/commands/input/tap.c index a8d1a10c..ac3b8237 100644 --- a/sway/commands/input/tap.c +++ b/sway/commands/input/tap.c | |||
@@ -4,6 +4,7 @@ | |||
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 "log.h" | 6 | #include "log.h" |
7 | #include "util.h" | ||
7 | 8 | ||
8 | struct cmd_results *input_cmd_tap(int argc, char **argv) { | 9 | struct cmd_results *input_cmd_tap(int argc, char **argv) { |
9 | struct cmd_results *error = NULL; | 10 | struct cmd_results *error = NULL; |
@@ -18,14 +19,10 @@ struct cmd_results *input_cmd_tap(int argc, char **argv) { | |||
18 | struct input_config *new_config = | 19 | struct input_config *new_config = |
19 | new_input_config(current_input_config->identifier); | 20 | new_input_config(current_input_config->identifier); |
20 | 21 | ||
21 | if (strcasecmp(argv[0], "enabled") == 0) { | 22 | if (parse_boolean(argv[0], true)) { |
22 | new_config->tap = LIBINPUT_CONFIG_TAP_ENABLED; | 23 | new_config->tap = LIBINPUT_CONFIG_TAP_ENABLED; |
23 | } else if (strcasecmp(argv[0], "disabled") == 0) { | ||
24 | new_config->tap = LIBINPUT_CONFIG_TAP_DISABLED; | ||
25 | } else { | 24 | } else { |
26 | free_input_config(new_config); | 25 | new_config->tap = LIBINPUT_CONFIG_TAP_DISABLED; |
27 | return cmd_results_new(CMD_INVALID, "tap", | ||
28 | "Expected 'tap <enabled|disabled>'"); | ||
29 | } | 26 | } |
30 | 27 | ||
31 | wlr_log(WLR_DEBUG, "apply-tap for device: %s", | 28 | wlr_log(WLR_DEBUG, "apply-tap for device: %s", |
diff --git a/sway/commands/input/xkb_capslock.c b/sway/commands/input/xkb_capslock.c new file mode 100644 index 00000000..5442c463 --- /dev/null +++ b/sway/commands/input/xkb_capslock.c | |||
@@ -0,0 +1,33 @@ | |||
1 | #include <string.h> | ||
2 | #include <strings.h> | ||
3 | #include "sway/config.h" | ||
4 | #include "sway/commands.h" | ||
5 | #include "sway/input/input-manager.h" | ||
6 | |||
7 | struct cmd_results *input_cmd_xkb_capslock(int argc, char **argv) { | ||
8 | struct cmd_results *error = NULL; | ||
9 | if ((error = checkarg(argc, "xkb_capslock", EXPECTED_AT_LEAST, 1))) { | ||
10 | return error; | ||
11 | } | ||
12 | struct input_config *current_input_config = | ||
13 | config->handler_context.input_config; | ||
14 | if (!current_input_config) { | ||
15 | return cmd_results_new(CMD_FAILURE, "xkb_capslock", | ||
16 | "No input device defined."); | ||
17 | } | ||
18 | struct input_config *new_config = | ||
19 | new_input_config(current_input_config->identifier); | ||
20 | |||
21 | if (strcasecmp(argv[0], "enabled") == 0) { | ||
22 | new_config->xkb_capslock = 1; | ||
23 | } else if (strcasecmp(argv[0], "disabled") == 0) { | ||
24 | new_config->xkb_capslock = 0; | ||
25 | } else { | ||
26 | free_input_config(new_config); | ||
27 | return cmd_results_new(CMD_INVALID, "xkb_capslock", | ||
28 | "Expected 'xkb_capslock <enabled|disabled>'"); | ||
29 | } | ||
30 | |||
31 | apply_input_config(new_config); | ||
32 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | ||
33 | } | ||
diff --git a/sway/commands/input/xkb_numlock.c b/sway/commands/input/xkb_numlock.c new file mode 100644 index 00000000..39675366 --- /dev/null +++ b/sway/commands/input/xkb_numlock.c | |||
@@ -0,0 +1,33 @@ | |||
1 | #include <string.h> | ||
2 | #include <strings.h> | ||
3 | #include "sway/config.h" | ||
4 | #include "sway/commands.h" | ||
5 | #include "sway/input/input-manager.h" | ||
6 | |||
7 | struct cmd_results *input_cmd_xkb_numlock(int argc, char **argv) { | ||
8 | struct cmd_results *error = NULL; | ||
9 | if ((error = checkarg(argc, "xkb_numlock", EXPECTED_AT_LEAST, 1))) { | ||
10 | return error; | ||
11 | } | ||
12 | struct input_config *current_input_config = | ||
13 | config->handler_context.input_config; | ||
14 | if (!current_input_config) { | ||
15 | return cmd_results_new(CMD_FAILURE, "xkb_numlock", | ||
16 | "No input device defined."); | ||
17 | } | ||
18 | struct input_config *new_config = | ||
19 | new_input_config(current_input_config->identifier); | ||
20 | |||
21 | if (strcasecmp(argv[0], "enabled") == 0) { | ||
22 | new_config->xkb_numlock = 1; | ||
23 | } else if (strcasecmp(argv[0], "disabled") == 0) { | ||
24 | new_config->xkb_numlock = 0; | ||
25 | } else { | ||
26 | free_input_config(new_config); | ||
27 | return cmd_results_new(CMD_INVALID, "xkb_numlock", | ||
28 | "Expected 'xkb_numlock <enabled|disabled>'"); | ||
29 | } | ||
30 | |||
31 | apply_input_config(new_config); | ||
32 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | ||
33 | } | ||
diff --git a/sway/commands/mark.c b/sway/commands/mark.c index 5a897e69..9ea8c301 100644 --- a/sway/commands/mark.c +++ b/sway/commands/mark.c | |||
@@ -58,7 +58,7 @@ struct cmd_results *cmd_mark(int argc, char **argv) { | |||
58 | view_find_and_unmark(mark); | 58 | view_find_and_unmark(mark); |
59 | 59 | ||
60 | if (!toggle || !had_mark) { | 60 | if (!toggle || !had_mark) { |
61 | list_add(view->marks, strdup(mark)); | 61 | view_add_mark(view, mark); |
62 | } | 62 | } |
63 | 63 | ||
64 | free(mark); | 64 | free(mark); |
diff --git a/sway/commands/mode.c b/sway/commands/mode.c index b460fcb5..637ca45e 100644 --- a/sway/commands/mode.c +++ b/sway/commands/mode.c | |||
@@ -56,6 +56,7 @@ struct cmd_results *cmd_mode(int argc, char **argv) { | |||
56 | mode->name = strdup(mode_name); | 56 | mode->name = strdup(mode_name); |
57 | mode->keysym_bindings = create_list(); | 57 | mode->keysym_bindings = create_list(); |
58 | mode->keycode_bindings = create_list(); | 58 | mode->keycode_bindings = create_list(); |
59 | mode->mouse_bindings = create_list(); | ||
59 | mode->pango = pango; | 60 | mode->pango = pango; |
60 | list_add(config->modes, mode); | 61 | list_add(config->modes, mode); |
61 | } | 62 | } |
diff --git a/sway/commands/move.c b/sway/commands/move.c index 6ec050a8..702b42d9 100644 --- a/sway/commands/move.c +++ b/sway/commands/move.c | |||
@@ -9,6 +9,7 @@ | |||
9 | #include "sway/input/cursor.h" | 9 | #include "sway/input/cursor.h" |
10 | #include "sway/input/seat.h" | 10 | #include "sway/input/seat.h" |
11 | #include "sway/output.h" | 11 | #include "sway/output.h" |
12 | #include "sway/scratchpad.h" | ||
12 | #include "sway/tree/arrange.h" | 13 | #include "sway/tree/arrange.h" |
13 | #include "sway/tree/container.h" | 14 | #include "sway/tree/container.h" |
14 | #include "sway/tree/layout.h" | 15 | #include "sway/tree/layout.h" |
@@ -58,8 +59,7 @@ static struct cmd_results *cmd_move_container(struct sway_container *current, | |||
58 | && strcasecmp(argv[2], "workspace") == 0) { | 59 | && strcasecmp(argv[2], "workspace") == 0) { |
59 | // move container to workspace x | 60 | // move container to workspace x |
60 | if (current->type == C_WORKSPACE) { | 61 | if (current->type == C_WORKSPACE) { |
61 | // TODO: Wrap children in a container and move that | 62 | current = container_wrap_children(current); |
62 | return cmd_results_new(CMD_FAILURE, "move", "Unimplemented"); | ||
63 | } else if (current->type != C_CONTAINER && current->type != C_VIEW) { | 63 | } else if (current->type != C_CONTAINER && current->type != C_VIEW) { |
64 | return cmd_results_new(CMD_FAILURE, "move", | 64 | return cmd_results_new(CMD_FAILURE, "move", |
65 | "Can only move containers and views."); | 65 | "Can only move containers and views."); |
@@ -97,7 +97,7 @@ static struct cmd_results *cmd_move_container(struct sway_container *current, | |||
97 | container_move_to(current, destination); | 97 | container_move_to(current, destination); |
98 | struct sway_container *focus = seat_get_focus_inactive( | 98 | struct sway_container *focus = seat_get_focus_inactive( |
99 | config->handler_context.seat, old_parent); | 99 | config->handler_context.seat, old_parent); |
100 | seat_set_focus(config->handler_context.seat, focus); | 100 | seat_set_focus_warp(config->handler_context.seat, focus, true, false); |
101 | container_reap_empty(old_parent); | 101 | container_reap_empty(old_parent); |
102 | container_reap_empty(destination->parent); | 102 | container_reap_empty(destination->parent); |
103 | 103 | ||
@@ -134,7 +134,7 @@ static struct cmd_results *cmd_move_container(struct sway_container *current, | |||
134 | struct sway_container *old_parent = current->parent; | 134 | struct sway_container *old_parent = current->parent; |
135 | struct sway_container *old_ws = container_parent(current, C_WORKSPACE); | 135 | struct sway_container *old_ws = container_parent(current, C_WORKSPACE); |
136 | container_move_to(current, focus); | 136 | container_move_to(current, focus); |
137 | seat_set_focus(config->handler_context.seat, old_parent); | 137 | seat_set_focus_warp(config->handler_context.seat, old_parent, true, false); |
138 | container_reap_empty(old_parent); | 138 | container_reap_empty(old_parent); |
139 | container_reap_empty(focus->parent); | 139 | container_reap_empty(focus->parent); |
140 | 140 | ||
@@ -195,7 +195,7 @@ static struct cmd_results *move_in_direction(struct sway_container *container, | |||
195 | "Cannot move workspaces in a direction"); | 195 | "Cannot move workspaces in a direction"); |
196 | } | 196 | } |
197 | if (container_is_floating(container)) { | 197 | if (container_is_floating(container)) { |
198 | if (container->type == C_VIEW && container->sway_view->is_fullscreen) { | 198 | if (container->is_fullscreen) { |
199 | return cmd_results_new(CMD_FAILURE, "move", | 199 | return cmd_results_new(CMD_FAILURE, "move", |
200 | "Cannot move fullscreen floating container"); | 200 | "Cannot move fullscreen floating container"); |
201 | } | 201 | } |
@@ -296,6 +296,34 @@ static struct cmd_results *move_to_position(struct sway_container *container, | |||
296 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | 296 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); |
297 | } | 297 | } |
298 | 298 | ||
299 | static struct cmd_results *move_to_scratchpad(struct sway_container *con) { | ||
300 | if (con->type == C_WORKSPACE && con->children->length == 0) { | ||
301 | return cmd_results_new(CMD_INVALID, "move", | ||
302 | "Can't move an empty workspace to the scratchpad"); | ||
303 | } | ||
304 | if (con->type == C_WORKSPACE) { | ||
305 | // Wrap the workspace's children in a container | ||
306 | struct sway_container *workspace = con; | ||
307 | con = container_wrap_children(con); | ||
308 | workspace->layout = L_HORIZ; | ||
309 | } | ||
310 | |||
311 | // If the container is in a floating split container, | ||
312 | // operate on the split container instead of the child. | ||
313 | if (container_is_floating_or_child(con)) { | ||
314 | while (con->parent->layout != L_FLOATING) { | ||
315 | con = con->parent; | ||
316 | } | ||
317 | } | ||
318 | |||
319 | if (con->scratchpad) { | ||
320 | return cmd_results_new(CMD_INVALID, "move", | ||
321 | "Container is already in the scratchpad"); | ||
322 | } | ||
323 | scratchpad_add_container(con); | ||
324 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | ||
325 | } | ||
326 | |||
299 | struct cmd_results *cmd_move(int argc, char **argv) { | 327 | struct cmd_results *cmd_move(int argc, char **argv) { |
300 | struct cmd_results *error = NULL; | 328 | struct cmd_results *error = NULL; |
301 | if ((error = checkarg(argc, "move", EXPECTED_AT_LEAST, 1))) { | 329 | if ((error = checkarg(argc, "move", EXPECTED_AT_LEAST, 1))) { |
@@ -317,10 +345,9 @@ struct cmd_results *cmd_move(int argc, char **argv) { | |||
317 | } else if (strcasecmp(argv[0], "workspace") == 0) { | 345 | } else if (strcasecmp(argv[0], "workspace") == 0) { |
318 | return cmd_move_workspace(current, argc, argv); | 346 | return cmd_move_workspace(current, argc, argv); |
319 | } else if (strcasecmp(argv[0], "scratchpad") == 0 | 347 | } else if (strcasecmp(argv[0], "scratchpad") == 0 |
320 | || (strcasecmp(argv[0], "to") == 0 | 348 | || (strcasecmp(argv[0], "to") == 0 && argc == 2 |
321 | && strcasecmp(argv[1], "scratchpad") == 0)) { | 349 | && strcasecmp(argv[1], "scratchpad") == 0)) { |
322 | // TODO: scratchpad | 350 | return move_to_scratchpad(current); |
323 | return cmd_results_new(CMD_FAILURE, "move", "Unimplemented"); | ||
324 | } else if (strcasecmp(argv[0], "position") == 0) { | 351 | } else if (strcasecmp(argv[0], "position") == 0) { |
325 | return move_to_position(current, argc, argv); | 352 | return move_to_position(current, argc, argv); |
326 | } else if (strcasecmp(argv[0], "absolute") == 0) { | 353 | } else if (strcasecmp(argv[0], "absolute") == 0) { |
diff --git a/sway/commands/output/dpms.c b/sway/commands/output/dpms.c index 0959ea6b..3492061e 100644 --- a/sway/commands/output/dpms.c +++ b/sway/commands/output/dpms.c | |||
@@ -1,5 +1,6 @@ | |||
1 | #include "sway/commands.h" | 1 | #include "sway/commands.h" |
2 | #include "sway/config.h" | 2 | #include "sway/config.h" |
3 | #include "util.h" | ||
3 | 4 | ||
4 | struct cmd_results *output_cmd_dpms(int argc, char **argv) { | 5 | struct cmd_results *output_cmd_dpms(int argc, char **argv) { |
5 | if (!config->handler_context.output_config) { | 6 | if (!config->handler_context.output_config) { |
@@ -9,13 +10,10 @@ struct cmd_results *output_cmd_dpms(int argc, char **argv) { | |||
9 | return cmd_results_new(CMD_INVALID, "output", "Missing dpms argument."); | 10 | return cmd_results_new(CMD_INVALID, "output", "Missing dpms argument."); |
10 | } | 11 | } |
11 | 12 | ||
12 | if (strcmp(*argv, "on") == 0) { | 13 | if (parse_boolean(argv[0], true)) { |
13 | config->handler_context.output_config->dpms_state = DPMS_ON; | 14 | config->handler_context.output_config->dpms_state = DPMS_ON; |
14 | } else if (strcmp(*argv, "off") == 0) { | ||
15 | config->handler_context.output_config->dpms_state = DPMS_OFF; | ||
16 | } else { | 15 | } else { |
17 | return cmd_results_new(CMD_INVALID, "output", | 16 | config->handler_context.output_config->dpms_state = DPMS_OFF; |
18 | "Invalid dpms state, valid states are on/off."); | ||
19 | } | 17 | } |
20 | 18 | ||
21 | config->handler_context.leftovers.argc = argc - 1; | 19 | config->handler_context.leftovers.argc = argc - 1; |
diff --git a/sway/commands/reload.c b/sway/commands/reload.c index cea6a94b..5c1b19b4 100644 --- a/sway/commands/reload.c +++ b/sway/commands/reload.c | |||
@@ -1,17 +1,46 @@ | |||
1 | #define _XOPEN_SOURCE 500 | ||
2 | #include <string.h> | ||
1 | #include "sway/commands.h" | 3 | #include "sway/commands.h" |
2 | #include "sway/config.h" | 4 | #include "sway/config.h" |
5 | #include "sway/ipc-server.h" | ||
3 | #include "sway/tree/arrange.h" | 6 | #include "sway/tree/arrange.h" |
7 | #include "list.h" | ||
4 | 8 | ||
5 | struct cmd_results *cmd_reload(int argc, char **argv) { | 9 | struct cmd_results *cmd_reload(int argc, char **argv) { |
6 | struct cmd_results *error = NULL; | 10 | struct cmd_results *error = NULL; |
7 | if ((error = checkarg(argc, "reload", EXPECTED_EQUAL_TO, 0))) { | 11 | if ((error = checkarg(argc, "reload", EXPECTED_EQUAL_TO, 0))) { |
8 | return error; | 12 | return error; |
9 | } | 13 | } |
14 | |||
15 | // store bar ids to check against new bars for barconfig_update events | ||
16 | list_t *bar_ids = create_list(); | ||
17 | for (int i = 0; i < config->bars->length; ++i) { | ||
18 | struct bar_config *bar = config->bars->items[i]; | ||
19 | list_add(bar_ids, strdup(bar->id)); | ||
20 | } | ||
21 | |||
10 | if (!load_main_config(config->current_config_path, true)) { | 22 | if (!load_main_config(config->current_config_path, true)) { |
11 | return cmd_results_new(CMD_FAILURE, "reload", "Error(s) reloading config."); | 23 | return cmd_results_new(CMD_FAILURE, "reload", "Error(s) reloading config."); |
12 | } | 24 | } |
25 | ipc_event_workspace(NULL, NULL, "reload"); | ||
13 | 26 | ||
14 | load_swaybars(); | 27 | load_swaybars(); |
28 | |||
29 | for (int i = 0; i < config->bars->length; ++i) { | ||
30 | struct bar_config *bar = config->bars->items[i]; | ||
31 | for (int j = 0; j < bar_ids->length; ++j) { | ||
32 | if (strcmp(bar->id, bar_ids->items[j]) == 0) { | ||
33 | ipc_event_barconfig_update(bar); | ||
34 | break; | ||
35 | } | ||
36 | } | ||
37 | } | ||
38 | |||
39 | for (int i = 0; i < bar_ids->length; ++i) { | ||
40 | free(bar_ids->items[i]); | ||
41 | } | ||
42 | list_free(bar_ids); | ||
43 | |||
15 | arrange_windows(&root_container); | 44 | arrange_windows(&root_container); |
16 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | 45 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); |
17 | } | 46 | } |
diff --git a/sway/commands/scratchpad.c b/sway/commands/scratchpad.c new file mode 100644 index 00000000..01a91d65 --- /dev/null +++ b/sway/commands/scratchpad.c | |||
@@ -0,0 +1,44 @@ | |||
1 | #include "log.h" | ||
2 | #include "sway/commands.h" | ||
3 | #include "sway/config.h" | ||
4 | #include "sway/scratchpad.h" | ||
5 | #include "sway/tree/container.h" | ||
6 | |||
7 | struct cmd_results *cmd_scratchpad(int argc, char **argv) { | ||
8 | struct cmd_results *error = NULL; | ||
9 | if ((error = checkarg(argc, "scratchpad", EXPECTED_EQUAL_TO, 1))) { | ||
10 | return error; | ||
11 | } | ||
12 | if (strcmp(argv[0], "show") != 0) { | ||
13 | return cmd_results_new(CMD_INVALID, "scratchpad", | ||
14 | "Expected 'scratchpad show'"); | ||
15 | } | ||
16 | if (!root_container.sway_root->scratchpad->length) { | ||
17 | return cmd_results_new(CMD_INVALID, "scratchpad", | ||
18 | "Scratchpad is empty"); | ||
19 | } | ||
20 | |||
21 | if (config->handler_context.using_criteria) { | ||
22 | struct sway_container *con = config->handler_context.current_container; | ||
23 | |||
24 | // If the container is in a floating split container, | ||
25 | // operate on the split container instead of the child. | ||
26 | if (container_is_floating_or_child(con)) { | ||
27 | while (con->parent->layout != L_FLOATING) { | ||
28 | con = con->parent; | ||
29 | } | ||
30 | } | ||
31 | |||
32 | // If using criteria, this command is executed for every container which | ||
33 | // matches the criteria. If this container isn't in the scratchpad, | ||
34 | // we'll just silently return a success. | ||
35 | if (!con->scratchpad) { | ||
36 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | ||
37 | } | ||
38 | scratchpad_toggle_container(con); | ||
39 | } else { | ||
40 | scratchpad_toggle_auto(); | ||
41 | } | ||
42 | |||
43 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | ||
44 | } | ||
diff --git a/sway/commands/show_marks.c b/sway/commands/show_marks.c index c7fdc538..434a0e27 100644 --- a/sway/commands/show_marks.c +++ b/sway/commands/show_marks.c | |||
@@ -7,6 +7,7 @@ | |||
7 | #include "list.h" | 7 | #include "list.h" |
8 | #include "log.h" | 8 | #include "log.h" |
9 | #include "stringop.h" | 9 | #include "stringop.h" |
10 | #include "util.h" | ||
10 | 11 | ||
11 | static void rebuild_marks_iterator(struct sway_container *con, void *data) { | 12 | static void rebuild_marks_iterator(struct sway_container *con, void *data) { |
12 | if (con->type == C_VIEW) { | 13 | if (con->type == C_VIEW) { |
@@ -20,14 +21,7 @@ struct cmd_results *cmd_show_marks(int argc, char **argv) { | |||
20 | return error; | 21 | return error; |
21 | } | 22 | } |
22 | 23 | ||
23 | if (strcmp(*argv, "yes") == 0) { | 24 | config->show_marks = parse_boolean(argv[0], config->show_marks); |
24 | config->show_marks = true; | ||
25 | } else if (strcmp(*argv, "no") == 0) { | ||
26 | config->show_marks = false; | ||
27 | } else { | ||
28 | return cmd_results_new(CMD_INVALID, "show_marks", | ||
29 | "Expected 'show_marks <yes|no>'"); | ||
30 | } | ||
31 | 25 | ||
32 | if (config->show_marks) { | 26 | if (config->show_marks) { |
33 | container_for_each_descendant_dfs(&root_container, | 27 | container_for_each_descendant_dfs(&root_container, |
diff --git a/sway/commands/split.c b/sway/commands/split.c index 313799da..a8eddf54 100644 --- a/sway/commands/split.c +++ b/sway/commands/split.c | |||
@@ -10,10 +10,6 @@ | |||
10 | 10 | ||
11 | static struct cmd_results *do_split(int layout) { | 11 | static struct cmd_results *do_split(int layout) { |
12 | struct sway_container *con = config->handler_context.current_container; | 12 | struct sway_container *con = config->handler_context.current_container; |
13 | if (container_is_floating(con)) { | ||
14 | return cmd_results_new(CMD_FAILURE, "split", | ||
15 | "Can't split a floating view"); | ||
16 | } | ||
17 | struct sway_container *parent = container_split(con, layout); | 13 | struct sway_container *parent = container_split(con, layout); |
18 | container_create_notify(parent); | 14 | container_create_notify(parent); |
19 | arrange_windows(parent->parent); | 15 | arrange_windows(parent->parent); |
diff --git a/sway/commands/swap.c b/sway/commands/swap.c index 2fc88308..4e3a9cce 100644 --- a/sway/commands/swap.c +++ b/sway/commands/swap.c | |||
@@ -1,5 +1,6 @@ | |||
1 | #include <strings.h> | 1 | #include <strings.h> |
2 | #include <wlr/util/log.h> | 2 | #include <wlr/util/log.h> |
3 | #include "config.h" | ||
3 | #include "sway/commands.h" | 4 | #include "sway/commands.h" |
4 | #include "sway/tree/arrange.h" | 5 | #include "sway/tree/arrange.h" |
5 | #include "sway/tree/layout.h" | 6 | #include "sway/tree/layout.h" |
@@ -14,10 +15,14 @@ static bool test_con_id(struct sway_container *container, void *con_id) { | |||
14 | } | 15 | } |
15 | 16 | ||
16 | static bool test_id(struct sway_container *container, void *id) { | 17 | static bool test_id(struct sway_container *container, void *id) { |
18 | #ifdef HAVE_XWAYLAND | ||
17 | xcb_window_t *wid = id; | 19 | xcb_window_t *wid = id; |
18 | return (container->type == C_VIEW | 20 | return (container->type == C_VIEW |
19 | && container->sway_view->type == SWAY_VIEW_XWAYLAND | 21 | && container->sway_view->type == SWAY_VIEW_XWAYLAND |
20 | && container->sway_view->wlr_xwayland_surface->window_id == *wid); | 22 | && container->sway_view->wlr_xwayland_surface->window_id == *wid); |
23 | #else | ||
24 | return false; | ||
25 | #endif | ||
21 | } | 26 | } |
22 | 27 | ||
23 | static bool test_mark(struct sway_container *container, void *mark) { | 28 | static bool test_mark(struct sway_container *container, void *mark) { |
@@ -43,8 +48,10 @@ struct cmd_results *cmd_swap(int argc, char **argv) { | |||
43 | 48 | ||
44 | char *value = join_args(argv + 3, argc - 3); | 49 | char *value = join_args(argv + 3, argc - 3); |
45 | if (strcasecmp(argv[2], "id") == 0) { | 50 | if (strcasecmp(argv[2], "id") == 0) { |
51 | #ifdef HAVE_XWAYLAND | ||
46 | xcb_window_t id = strtol(value, NULL, 0); | 52 | xcb_window_t id = strtol(value, NULL, 0); |
47 | other = container_find(&root_container, test_id, (void *)&id); | 53 | other = container_find(&root_container, test_id, (void *)&id); |
54 | #endif | ||
48 | } else if (strcasecmp(argv[2], "con_id") == 0) { | 55 | } else if (strcasecmp(argv[2], "con_id") == 0) { |
49 | size_t con_id = atoi(value); | 56 | size_t con_id = atoi(value); |
50 | other = container_find(&root_container, test_con_id, (void *)con_id); | 57 | other = container_find(&root_container, test_con_id, (void *)con_id); |
diff --git a/sway/commands/urgent.c b/sway/commands/urgent.c index d199858a..51c497c4 100644 --- a/sway/commands/urgent.c +++ b/sway/commands/urgent.c | |||
@@ -5,6 +5,7 @@ | |||
5 | #include "sway/tree/container.h" | 5 | #include "sway/tree/container.h" |
6 | #include "sway/tree/view.h" | 6 | #include "sway/tree/view.h" |
7 | #include "sway/tree/layout.h" | 7 | #include "sway/tree/layout.h" |
8 | #include "util.h" | ||
8 | 9 | ||
9 | struct cmd_results *cmd_urgent(int argc, char **argv) { | 10 | struct cmd_results *cmd_urgent(int argc, char **argv) { |
10 | struct cmd_results *error = NULL; | 11 | struct cmd_results *error = NULL; |
@@ -19,17 +20,12 @@ struct cmd_results *cmd_urgent(int argc, char **argv) { | |||
19 | } | 20 | } |
20 | struct sway_view *view = container->sway_view; | 21 | struct sway_view *view = container->sway_view; |
21 | 22 | ||
22 | if (strcmp(argv[0], "enable") == 0) { | 23 | if (strcmp(argv[0], "allow") == 0) { |
23 | view_set_urgent(view, true); | ||
24 | } else if (strcmp(argv[0], "disable") == 0) { | ||
25 | view_set_urgent(view, false); | ||
26 | } else if (strcmp(argv[0], "allow") == 0) { | ||
27 | view->allow_request_urgent = true; | 24 | view->allow_request_urgent = true; |
28 | } else if (strcmp(argv[0], "deny") == 0) { | 25 | } else if (strcmp(argv[0], "deny") == 0) { |
29 | view->allow_request_urgent = false; | 26 | view->allow_request_urgent = false; |
30 | } else { | 27 | } else { |
31 | return cmd_results_new(CMD_INVALID, "urgent", | 28 | view_set_urgent(view, parse_boolean(argv[0], view_is_urgent(view))); |
32 | "Expected 'urgent <enable|disable|allow|deny>'"); | ||
33 | } | 29 | } |
34 | 30 | ||
35 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); | 31 | return cmd_results_new(CMD_SUCCESS, NULL, NULL); |
diff --git a/sway/config.c b/sway/config.c index ed624bfa..2afffab1 100644 --- a/sway/config.c +++ b/sway/config.c | |||
@@ -56,6 +56,12 @@ static void free_mode(struct sway_mode *mode) { | |||
56 | } | 56 | } |
57 | list_free(mode->keycode_bindings); | 57 | list_free(mode->keycode_bindings); |
58 | } | 58 | } |
59 | if (mode->mouse_bindings) { | ||
60 | for (i = 0; i < mode->mouse_bindings->length; i++) { | ||
61 | free_sway_binding(mode->mouse_bindings->items[i]); | ||
62 | } | ||
63 | list_free(mode->mouse_bindings); | ||
64 | } | ||
59 | free(mode); | 65 | free(mode); |
60 | } | 66 | } |
61 | 67 | ||
@@ -87,7 +93,6 @@ void free_config(struct sway_config *config) { | |||
87 | } | 93 | } |
88 | list_free(config->cmd_queue); | 94 | list_free(config->cmd_queue); |
89 | list_free(config->workspace_outputs); | 95 | list_free(config->workspace_outputs); |
90 | list_free(config->pid_workspaces); | ||
91 | if (config->output_configs) { | 96 | if (config->output_configs) { |
92 | for (int i = 0; i < config->output_configs->length; i++) { | 97 | for (int i = 0; i < config->output_configs->length; i++) { |
93 | free_output_config(config->output_configs->items[i]); | 98 | free_output_config(config->output_configs->items[i]); |
@@ -157,7 +162,6 @@ static void config_defaults(struct sway_config *config) { | |||
157 | if (!(config->modes = create_list())) goto cleanup; | 162 | if (!(config->modes = create_list())) goto cleanup; |
158 | if (!(config->bars = create_list())) goto cleanup; | 163 | if (!(config->bars = create_list())) goto cleanup; |
159 | if (!(config->workspace_outputs = create_list())) goto cleanup; | 164 | if (!(config->workspace_outputs = create_list())) goto cleanup; |
160 | if (!(config->pid_workspaces = create_list())) goto cleanup; | ||
161 | if (!(config->criteria = create_list())) goto cleanup; | 165 | if (!(config->criteria = create_list())) goto cleanup; |
162 | if (!(config->no_focus = create_list())) goto cleanup; | 166 | if (!(config->no_focus = create_list())) goto cleanup; |
163 | if (!(config->input_configs = create_list())) goto cleanup; | 167 | if (!(config->input_configs = create_list())) goto cleanup; |
@@ -172,9 +176,11 @@ static void config_defaults(struct sway_config *config) { | |||
172 | strcpy(config->current_mode->name, "default"); | 176 | strcpy(config->current_mode->name, "default"); |
173 | if (!(config->current_mode->keysym_bindings = create_list())) goto cleanup; | 177 | if (!(config->current_mode->keysym_bindings = create_list())) goto cleanup; |
174 | if (!(config->current_mode->keycode_bindings = create_list())) goto cleanup; | 178 | if (!(config->current_mode->keycode_bindings = create_list())) goto cleanup; |
179 | if (!(config->current_mode->mouse_bindings = create_list())) goto cleanup; | ||
175 | list_add(config->modes, config->current_mode); | 180 | list_add(config->modes, config->current_mode); |
176 | 181 | ||
177 | config->floating_mod = 0; | 182 | config->floating_mod = 0; |
183 | config->floating_mod_inverse = false; | ||
178 | config->dragging_key = BTN_LEFT; | 184 | config->dragging_key = BTN_LEFT; |
179 | config->resizing_key = BTN_RIGHT; | 185 | config->resizing_key = BTN_RIGHT; |
180 | 186 | ||
diff --git a/sway/config/bar.c b/sway/config/bar.c index 3a74331e..ae9383d6 100644 --- a/sway/config/bar.c +++ b/sway/config/bar.c | |||
@@ -10,6 +10,7 @@ | |||
10 | #include <sys/stat.h> | 10 | #include <sys/stat.h> |
11 | #include <signal.h> | 11 | #include <signal.h> |
12 | #include <strings.h> | 12 | #include <strings.h> |
13 | #include <signal.h> | ||
13 | #include "sway/config.h" | 14 | #include "sway/config.h" |
14 | #include "stringop.h" | 15 | #include "stringop.h" |
15 | #include "list.h" | 16 | #include "list.h" |
@@ -175,6 +176,9 @@ void invoke_swaybar(struct bar_config *bar) { | |||
175 | if (bar->pid == 0) { | 176 | if (bar->pid == 0) { |
176 | setpgid(0, 0); | 177 | setpgid(0, 0); |
177 | close(filedes[0]); | 178 | close(filedes[0]); |
179 | sigset_t set; | ||
180 | sigemptyset(&set); | ||
181 | sigprocmask(SIG_SETMASK, &set, NULL); | ||
178 | 182 | ||
179 | // run custom swaybar | 183 | // run custom swaybar |
180 | size_t len = snprintf(NULL, 0, "%s -b %s", | 184 | size_t len = snprintf(NULL, 0, "%s -b %s", |
diff --git a/sway/config/input.c b/sway/config/input.c index 8d687a6d..9885e85c 100644 --- a/sway/config/input.c +++ b/sway/config/input.c | |||
@@ -33,6 +33,8 @@ struct input_config *new_input_config(const char* identifier) { | |||
33 | input->left_handed = INT_MIN; | 33 | input->left_handed = INT_MIN; |
34 | input->repeat_delay = INT_MIN; | 34 | input->repeat_delay = INT_MIN; |
35 | input->repeat_rate = INT_MIN; | 35 | input->repeat_rate = INT_MIN; |
36 | input->xkb_numlock = INT_MIN; | ||
37 | input->xkb_capslock = INT_MIN; | ||
36 | 38 | ||
37 | return input; | 39 | return input; |
38 | } | 40 | } |
@@ -104,6 +106,12 @@ void merge_input_config(struct input_config *dst, struct input_config *src) { | |||
104 | free(dst->xkb_variant); | 106 | free(dst->xkb_variant); |
105 | dst->xkb_variant = strdup(src->xkb_variant); | 107 | dst->xkb_variant = strdup(src->xkb_variant); |
106 | } | 108 | } |
109 | if (src->xkb_numlock != INT_MIN) { | ||
110 | dst->xkb_numlock = src->xkb_numlock; | ||
111 | } | ||
112 | if (src->xkb_capslock != INT_MIN) { | ||
113 | dst->xkb_capslock = src->xkb_capslock; | ||
114 | } | ||
107 | if (src->mapped_from_region) { | 115 | if (src->mapped_from_region) { |
108 | free(dst->mapped_from_region); | 116 | free(dst->mapped_from_region); |
109 | dst->mapped_from_region = | 117 | dst->mapped_from_region = |
diff --git a/sway/criteria.c b/sway/criteria.c index e2b248de..39d300ea 100644 --- a/sway/criteria.c +++ b/sway/criteria.c | |||
@@ -10,6 +10,7 @@ | |||
10 | #include "stringop.h" | 10 | #include "stringop.h" |
11 | #include "list.h" | 11 | #include "list.h" |
12 | #include "log.h" | 12 | #include "log.h" |
13 | #include "config.h" | ||
13 | 14 | ||
14 | bool criteria_is_empty(struct criteria *criteria) { | 15 | bool criteria_is_empty(struct criteria *criteria) { |
15 | return !criteria->title | 16 | return !criteria->title |
@@ -19,7 +20,9 @@ bool criteria_is_empty(struct criteria *criteria) { | |||
19 | && !criteria->instance | 20 | && !criteria->instance |
20 | && !criteria->con_mark | 21 | && !criteria->con_mark |
21 | && !criteria->con_id | 22 | && !criteria->con_id |
23 | #ifdef HAVE_XWAYLAND | ||
22 | && !criteria->id | 24 | && !criteria->id |
25 | #endif | ||
23 | && !criteria->window_role | 26 | && !criteria->window_role |
24 | && !criteria->window_type | 27 | && !criteria->window_type |
25 | && !criteria->floating | 28 | && !criteria->floating |
@@ -127,12 +130,14 @@ static bool criteria_matches_view(struct criteria *criteria, | |||
127 | } | 130 | } |
128 | } | 131 | } |
129 | 132 | ||
133 | #ifdef HAVE_XWAYLAND | ||
130 | if (criteria->id) { // X11 window ID | 134 | if (criteria->id) { // X11 window ID |
131 | uint32_t x11_window_id = view_get_x11_window_id(view); | 135 | uint32_t x11_window_id = view_get_x11_window_id(view); |
132 | if (!x11_window_id || x11_window_id != criteria->id) { | 136 | if (!x11_window_id || x11_window_id != criteria->id) { |
133 | return false; | 137 | return false; |
134 | } | 138 | } |
135 | } | 139 | } |
140 | #endif | ||
136 | 141 | ||
137 | if (criteria->window_role) { | 142 | if (criteria->window_role) { |
138 | // TODO | 143 | // TODO |
@@ -225,6 +230,15 @@ list_t *criteria_get_views(struct criteria *criteria) { | |||
225 | }; | 230 | }; |
226 | container_for_each_descendant_dfs(&root_container, | 231 | container_for_each_descendant_dfs(&root_container, |
227 | criteria_get_views_iterator, &data); | 232 | criteria_get_views_iterator, &data); |
233 | |||
234 | // Scratchpad items which are hidden are not in the tree. | ||
235 | for (int i = 0; i < root_container.sway_root->scratchpad->length; ++i) { | ||
236 | struct sway_container *con = | ||
237 | root_container.sway_root->scratchpad->items[i]; | ||
238 | if (!con->parent) { | ||
239 | criteria_get_views_iterator(con, &data); | ||
240 | } | ||
241 | } | ||
228 | return matches; | 242 | return matches; |
229 | } | 243 | } |
230 | 244 | ||
@@ -256,7 +270,9 @@ enum criteria_token { | |||
256 | T_CON_ID, | 270 | T_CON_ID, |
257 | T_CON_MARK, | 271 | T_CON_MARK, |
258 | T_FLOATING, | 272 | T_FLOATING, |
273 | #ifdef HAVE_XWAYLAND | ||
259 | T_ID, | 274 | T_ID, |
275 | #endif | ||
260 | T_INSTANCE, | 276 | T_INSTANCE, |
261 | T_SHELL, | 277 | T_SHELL, |
262 | T_TILING, | 278 | T_TILING, |
@@ -278,8 +294,10 @@ static enum criteria_token token_from_name(char *name) { | |||
278 | return T_CON_ID; | 294 | return T_CON_ID; |
279 | } else if (strcmp(name, "con_mark") == 0) { | 295 | } else if (strcmp(name, "con_mark") == 0) { |
280 | return T_CON_MARK; | 296 | return T_CON_MARK; |
297 | #ifdef HAVE_XWAYLAND | ||
281 | } else if (strcmp(name, "id") == 0) { | 298 | } else if (strcmp(name, "id") == 0) { |
282 | return T_ID; | 299 | return T_ID; |
300 | #endif | ||
283 | } else if (strcmp(name, "instance") == 0) { | 301 | } else if (strcmp(name, "instance") == 0) { |
284 | return T_INSTANCE; | 302 | return T_INSTANCE; |
285 | } else if (strcmp(name, "shell") == 0) { | 303 | } else if (strcmp(name, "shell") == 0) { |
@@ -346,7 +364,9 @@ static char *get_focused_prop(enum criteria_token token) { | |||
346 | case T_CON_ID: // These do not support __focused__ | 364 | case T_CON_ID: // These do not support __focused__ |
347 | case T_CON_MARK: | 365 | case T_CON_MARK: |
348 | case T_FLOATING: | 366 | case T_FLOATING: |
367 | #ifdef HAVE_XWAYLAND | ||
349 | case T_ID: | 368 | case T_ID: |
369 | #endif | ||
350 | case T_TILING: | 370 | case T_TILING: |
351 | case T_URGENT: | 371 | case T_URGENT: |
352 | case T_WINDOW_TYPE: | 372 | case T_WINDOW_TYPE: |
@@ -417,12 +437,14 @@ static bool parse_token(struct criteria *criteria, char *name, char *value) { | |||
417 | case T_WINDOW_TYPE: | 437 | case T_WINDOW_TYPE: |
418 | // TODO: This is a string but will be stored as an enum or integer | 438 | // TODO: This is a string but will be stored as an enum or integer |
419 | break; | 439 | break; |
440 | #ifdef HAVE_XWAYLAND | ||
420 | case T_ID: | 441 | case T_ID: |
421 | criteria->id = strtoul(effective_value, &endptr, 10); | 442 | criteria->id = strtoul(effective_value, &endptr, 10); |
422 | if (*endptr != 0) { | 443 | if (*endptr != 0) { |
423 | error = strdup("The value for 'id' should be numeric"); | 444 | error = strdup("The value for 'id' should be numeric"); |
424 | } | 445 | } |
425 | break; | 446 | break; |
447 | #endif | ||
426 | case T_FLOATING: | 448 | case T_FLOATING: |
427 | criteria->floating = true; | 449 | criteria->floating = true; |
428 | break; | 450 | break; |
diff --git a/sway/desktop/layer_shell.c b/sway/desktop/layer_shell.c index a7d96717..a2935883 100644 --- a/sway/desktop/layer_shell.c +++ b/sway/desktop/layer_shell.c | |||
@@ -7,11 +7,13 @@ | |||
7 | #include <wlr/types/wlr_output_damage.h> | 7 | #include <wlr/types/wlr_output_damage.h> |
8 | #include <wlr/types/wlr_output.h> | 8 | #include <wlr/types/wlr_output.h> |
9 | #include <wlr/util/log.h> | 9 | #include <wlr/util/log.h> |
10 | #include "sway/desktop/transaction.h" | ||
10 | #include "sway/input/input-manager.h" | 11 | #include "sway/input/input-manager.h" |
11 | #include "sway/input/seat.h" | 12 | #include "sway/input/seat.h" |
12 | #include "sway/layers.h" | 13 | #include "sway/layers.h" |
13 | #include "sway/output.h" | 14 | #include "sway/output.h" |
14 | #include "sway/server.h" | 15 | #include "sway/server.h" |
16 | #include "sway/tree/arrange.h" | ||
15 | #include "sway/tree/layout.h" | 17 | #include "sway/tree/layout.h" |
16 | #include "log.h" | 18 | #include "log.h" |
17 | 19 | ||
@@ -245,6 +247,9 @@ static void handle_surface_commit(struct wl_listener *listener, void *data) { | |||
245 | output_damage_surface(output, layer->geo.x, layer->geo.y, | 247 | output_damage_surface(output, layer->geo.x, layer->geo.y, |
246 | layer_surface->surface, false); | 248 | layer_surface->surface, false); |
247 | } | 249 | } |
250 | |||
251 | arrange_windows(output->swayc); | ||
252 | transaction_commit_dirty(); | ||
248 | } | 253 | } |
249 | 254 | ||
250 | static void unmap(struct sway_layer_surface *sway_layer) { | 255 | static void unmap(struct sway_layer_surface *sway_layer) { |
@@ -282,6 +287,8 @@ static void handle_destroy(struct wl_listener *listener, void *data) { | |||
282 | struct sway_output *output = sway_layer->layer_surface->output->data; | 287 | struct sway_output *output = sway_layer->layer_surface->output->data; |
283 | if (output != NULL && output->swayc != NULL) { | 288 | if (output != NULL && output->swayc != NULL) { |
284 | arrange_layers(output); | 289 | arrange_layers(output); |
290 | arrange_windows(output->swayc); | ||
291 | transaction_commit_dirty(); | ||
285 | } | 292 | } |
286 | wl_list_remove(&sway_layer->output_destroy.link); | 293 | wl_list_remove(&sway_layer->output_destroy.link); |
287 | sway_layer->layer_surface->output = NULL; | 294 | sway_layer->layer_surface->output = NULL; |
diff --git a/sway/desktop/output.c b/sway/desktop/output.c index a9808406..66747a3f 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c | |||
@@ -14,6 +14,7 @@ | |||
14 | #include <wlr/types/wlr_surface.h> | 14 | #include <wlr/types/wlr_surface.h> |
15 | #include <wlr/util/region.h> | 15 | #include <wlr/util/region.h> |
16 | #include "log.h" | 16 | #include "log.h" |
17 | #include "config.h" | ||
17 | #include "sway/config.h" | 18 | #include "sway/config.h" |
18 | #include "sway/input/input-manager.h" | 19 | #include "sway/input/input-manager.h" |
19 | #include "sway/input/seat.h" | 20 | #include "sway/input/seat.h" |
@@ -56,9 +57,21 @@ static void rotate_child_position(double *sx, double *sy, double sw, double sh, | |||
56 | *sy = ry + ph/2 - sh/2; | 57 | *sy = ry + ph/2 - sh/2; |
57 | } | 58 | } |
58 | 59 | ||
59 | bool output_get_surface_box(struct root_geometry *geo, | 60 | struct surface_iterator_data { |
60 | struct sway_output *output, struct wlr_surface *surface, int sx, int sy, | 61 | sway_surface_iterator_func_t user_iterator; |
62 | void *user_data; | ||
63 | |||
64 | struct sway_output *output; | ||
65 | double ox, oy; | ||
66 | int width, height; | ||
67 | float rotation; | ||
68 | }; | ||
69 | |||
70 | static bool get_surface_box(struct surface_iterator_data *data, | ||
71 | struct wlr_surface *surface, int sx, int sy, | ||
61 | struct wlr_box *surface_box) { | 72 | struct wlr_box *surface_box) { |
73 | struct sway_output *output = data->output; | ||
74 | |||
62 | if (!wlr_surface_has_buffer(surface)) { | 75 | if (!wlr_surface_has_buffer(surface)) { |
63 | return false; | 76 | return false; |
64 | } | 77 | } |
@@ -67,12 +80,12 @@ bool output_get_surface_box(struct root_geometry *geo, | |||
67 | int sh = surface->current.height; | 80 | int sh = surface->current.height; |
68 | 81 | ||
69 | double _sx = sx, _sy = sy; | 82 | double _sx = sx, _sy = sy; |
70 | rotate_child_position(&_sx, &_sy, sw, sh, geo->width, geo->height, | 83 | rotate_child_position(&_sx, &_sy, sw, sh, data->width, data->height, |
71 | geo->rotation); | 84 | data->rotation); |
72 | 85 | ||
73 | struct wlr_box box = { | 86 | struct wlr_box box = { |
74 | .x = geo->x + _sx, | 87 | .x = data->ox + _sx, |
75 | .y = geo->y + _sy, | 88 | .y = data->oy + _sy, |
76 | .width = sw, | 89 | .width = sw, |
77 | .height = sh, | 90 | .height = sh, |
78 | }; | 91 | }; |
@@ -81,7 +94,7 @@ bool output_get_surface_box(struct root_geometry *geo, | |||
81 | } | 94 | } |
82 | 95 | ||
83 | struct wlr_box rotated_box; | 96 | struct wlr_box rotated_box; |
84 | wlr_box_rotated_bounds(&box, geo->rotation, &rotated_box); | 97 | wlr_box_rotated_bounds(&box, data->rotation, &rotated_box); |
85 | 98 | ||
86 | struct wlr_box output_box = { | 99 | struct wlr_box output_box = { |
87 | .width = output->swayc->current.swayc_width, | 100 | .width = output->swayc->current.swayc_width, |
@@ -92,46 +105,90 @@ bool output_get_surface_box(struct root_geometry *geo, | |||
92 | return wlr_box_intersection(&output_box, &rotated_box, &intersection); | 105 | return wlr_box_intersection(&output_box, &rotated_box, &intersection); |
93 | } | 106 | } |
94 | 107 | ||
95 | void output_surface_for_each_surface(struct wlr_surface *surface, | 108 | static void output_for_each_surface_iterator(struct wlr_surface *surface, |
96 | double ox, double oy, struct root_geometry *geo, | 109 | int sx, int sy, void *_data) { |
97 | wlr_surface_iterator_func_t iterator, void *user_data) { | 110 | struct surface_iterator_data *data = _data; |
98 | geo->x = ox; | ||
99 | geo->y = oy; | ||
100 | geo->width = surface->current.width; | ||
101 | geo->height = surface->current.height; | ||
102 | geo->rotation = 0; | ||
103 | 111 | ||
104 | wlr_surface_for_each_surface(surface, iterator, user_data); | 112 | struct wlr_box box; |
113 | bool intersects = get_surface_box(data, surface, sx, sy, &box); | ||
114 | if (!intersects) { | ||
115 | return; | ||
116 | } | ||
117 | |||
118 | data->user_iterator(data->output, surface, &box, data->rotation, | ||
119 | data->user_data); | ||
120 | } | ||
121 | |||
122 | void output_surface_for_each_surface(struct sway_output *output, | ||
123 | struct wlr_surface *surface, double ox, double oy, | ||
124 | sway_surface_iterator_func_t iterator, void *user_data) { | ||
125 | struct surface_iterator_data data = { | ||
126 | .user_iterator = iterator, | ||
127 | .user_data = user_data, | ||
128 | .output = output, | ||
129 | .ox = ox, | ||
130 | .oy = oy, | ||
131 | .width = surface->current.width, | ||
132 | .height = surface->current.height, | ||
133 | .rotation = 0, | ||
134 | }; | ||
135 | |||
136 | wlr_surface_for_each_surface(surface, | ||
137 | output_for_each_surface_iterator, &data); | ||
105 | } | 138 | } |
106 | 139 | ||
107 | void output_view_for_each_surface(struct sway_view *view, | 140 | void output_view_for_each_surface(struct sway_output *output, |
108 | struct sway_output *output, struct root_geometry *geo, | 141 | struct sway_view *view, sway_surface_iterator_func_t iterator, |
109 | wlr_surface_iterator_func_t iterator, void *user_data) { | 142 | void *user_data) { |
110 | geo->x = view->swayc->current.view_x - output->swayc->current.swayc_x; | 143 | struct surface_iterator_data data = { |
111 | geo->y = view->swayc->current.view_y - output->swayc->current.swayc_y; | 144 | .user_iterator = iterator, |
112 | geo->width = view->swayc->current.view_width; | 145 | .user_data = user_data, |
113 | geo->height = view->swayc->current.view_height; | 146 | .output = output, |
114 | geo->rotation = 0; // TODO | 147 | .ox = view->swayc->current.view_x - output->swayc->current.swayc_x, |
148 | .oy = view->swayc->current.view_y - output->swayc->current.swayc_y, | ||
149 | .width = view->swayc->current.view_width, | ||
150 | .height = view->swayc->current.view_height, | ||
151 | .rotation = 0, // TODO | ||
152 | }; | ||
115 | 153 | ||
116 | view_for_each_surface(view, iterator, user_data); | 154 | view_for_each_surface(view, |
155 | output_for_each_surface_iterator, &data); | ||
117 | } | 156 | } |
118 | 157 | ||
119 | void output_layer_for_each_surface(struct wl_list *layer_surfaces, | 158 | void output_view_for_each_popup(struct sway_output *output, |
120 | struct root_geometry *geo, wlr_surface_iterator_func_t iterator, | 159 | struct sway_view *view, sway_surface_iterator_func_t iterator, |
160 | void *user_data) { | ||
161 | struct surface_iterator_data data = { | ||
162 | .user_iterator = iterator, | ||
163 | .user_data = user_data, | ||
164 | .output = output, | ||
165 | .ox = view->swayc->current.view_x - output->swayc->current.swayc_x, | ||
166 | .oy = view->swayc->current.view_y - output->swayc->current.swayc_y, | ||
167 | .width = view->swayc->current.view_width, | ||
168 | .height = view->swayc->current.view_height, | ||
169 | .rotation = 0, // TODO | ||
170 | }; | ||
171 | |||
172 | view_for_each_popup(view, output_for_each_surface_iterator, &data); | ||
173 | } | ||
174 | |||
175 | void output_layer_for_each_surface(struct sway_output *output, | ||
176 | struct wl_list *layer_surfaces, sway_surface_iterator_func_t iterator, | ||
121 | void *user_data) { | 177 | void *user_data) { |
122 | struct sway_layer_surface *layer_surface; | 178 | struct sway_layer_surface *layer_surface; |
123 | wl_list_for_each(layer_surface, layer_surfaces, link) { | 179 | wl_list_for_each(layer_surface, layer_surfaces, link) { |
124 | struct wlr_layer_surface *wlr_layer_surface = | 180 | struct wlr_layer_surface *wlr_layer_surface = |
125 | layer_surface->layer_surface; | 181 | layer_surface->layer_surface; |
126 | output_surface_for_each_surface(wlr_layer_surface->surface, | 182 | output_surface_for_each_surface(output, wlr_layer_surface->surface, |
127 | layer_surface->geo.x, layer_surface->geo.y, geo, iterator, | 183 | layer_surface->geo.x, layer_surface->geo.y, iterator, |
128 | user_data); | 184 | user_data); |
129 | } | 185 | } |
130 | } | 186 | } |
131 | 187 | ||
132 | void output_unmanaged_for_each_surface(struct wl_list *unmanaged, | 188 | #ifdef HAVE_XWAYLAND |
133 | struct sway_output *output, struct root_geometry *geo, | 189 | void output_unmanaged_for_each_surface(struct sway_output *output, |
134 | wlr_surface_iterator_func_t iterator, void *user_data) { | 190 | struct wl_list *unmanaged, sway_surface_iterator_func_t iterator, |
191 | void *user_data) { | ||
135 | struct sway_xwayland_unmanaged *unmanaged_surface; | 192 | struct sway_xwayland_unmanaged *unmanaged_surface; |
136 | wl_list_for_each(unmanaged_surface, unmanaged, link) { | 193 | wl_list_for_each(unmanaged_surface, unmanaged, link) { |
137 | struct wlr_xwayland_surface *xsurface = | 194 | struct wlr_xwayland_surface *xsurface = |
@@ -139,22 +196,24 @@ void output_unmanaged_for_each_surface(struct wl_list *unmanaged, | |||
139 | double ox = unmanaged_surface->lx - output->swayc->current.swayc_x; | 196 | double ox = unmanaged_surface->lx - output->swayc->current.swayc_x; |
140 | double oy = unmanaged_surface->ly - output->swayc->current.swayc_y; | 197 | double oy = unmanaged_surface->ly - output->swayc->current.swayc_y; |
141 | 198 | ||
142 | output_surface_for_each_surface(xsurface->surface, ox, oy, geo, | 199 | output_surface_for_each_surface(output, xsurface->surface, ox, oy, |
143 | iterator, user_data); | 200 | iterator, user_data); |
144 | } | 201 | } |
145 | } | 202 | } |
203 | #endif | ||
146 | 204 | ||
147 | void output_drag_icons_for_each_surface(struct wl_list *drag_icons, | 205 | void output_drag_icons_for_each_surface(struct sway_output *output, |
148 | struct sway_output *output, struct root_geometry *geo, | 206 | struct wl_list *drag_icons, sway_surface_iterator_func_t iterator, |
149 | wlr_surface_iterator_func_t iterator, void *user_data) { | 207 | void *user_data) { |
150 | struct sway_drag_icon *drag_icon; | 208 | struct sway_drag_icon *drag_icon; |
151 | wl_list_for_each(drag_icon, drag_icons, link) { | 209 | wl_list_for_each(drag_icon, drag_icons, link) { |
152 | double ox = drag_icon->x - output->swayc->x; | 210 | double ox = drag_icon->x - output->swayc->x; |
153 | double oy = drag_icon->y - output->swayc->y; | 211 | double oy = drag_icon->y - output->swayc->y; |
154 | 212 | ||
155 | if (drag_icon->wlr_drag_icon->mapped) { | 213 | if (drag_icon->wlr_drag_icon->mapped) { |
156 | output_surface_for_each_surface(drag_icon->wlr_drag_icon->surface, | 214 | output_surface_for_each_surface(output, |
157 | ox, oy, geo, iterator, user_data); | 215 | drag_icon->wlr_drag_icon->surface, ox, oy, |
216 | iterator, user_data); | ||
158 | } | 217 | } |
159 | } | 218 | } |
160 | } | 219 | } |
@@ -181,21 +240,14 @@ struct sway_container *output_get_active_workspace(struct sway_output *output) { | |||
181 | return workspace; | 240 | return workspace; |
182 | } | 241 | } |
183 | 242 | ||
184 | bool output_has_opaque_lockscreen(struct sway_output *output, | 243 | bool output_has_opaque_overlay_layer_surface(struct sway_output *output) { |
185 | struct sway_seat *seat) { | ||
186 | if (!seat->exclusive_client) { | ||
187 | return false; | ||
188 | } | ||
189 | |||
190 | struct wlr_layer_surface *wlr_layer_surface; | 244 | struct wlr_layer_surface *wlr_layer_surface; |
191 | wl_list_for_each(wlr_layer_surface, &server.layer_shell->surfaces, link) { | 245 | wl_list_for_each(wlr_layer_surface, &server.layer_shell->surfaces, link) { |
192 | if (wlr_layer_surface->output != output->wlr_output) { | 246 | if (wlr_layer_surface->output != output->wlr_output || |
247 | wlr_layer_surface->layer != ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY) { | ||
193 | continue; | 248 | continue; |
194 | } | 249 | } |
195 | struct wlr_surface *wlr_surface = wlr_layer_surface->surface; | 250 | struct wlr_surface *wlr_surface = wlr_layer_surface->surface; |
196 | if (wlr_surface->resource->client != seat->exclusive_client) { | ||
197 | continue; | ||
198 | } | ||
199 | struct sway_layer_surface *sway_layer_surface = | 251 | struct sway_layer_surface *sway_layer_surface = |
200 | layer_from_wlr_layer_surface(wlr_layer_surface); | 252 | layer_from_wlr_layer_surface(wlr_layer_surface); |
201 | pixman_box32_t output_box = { | 253 | pixman_box32_t output_box = { |
@@ -217,46 +269,38 @@ bool output_has_opaque_lockscreen(struct sway_output *output, | |||
217 | return false; | 269 | return false; |
218 | } | 270 | } |
219 | 271 | ||
220 | struct send_frame_done_data { | 272 | static void send_frame_done_iterator(struct sway_output *output, |
221 | struct root_geometry root_geo; | 273 | struct wlr_surface *surface, struct wlr_box *box, float rotation, |
222 | struct sway_output *output; | 274 | void *_data) { |
223 | struct timespec *when; | 275 | struct timespec *when = _data; |
224 | struct wl_client *exclusive_client; | 276 | wlr_surface_send_frame_done(surface, when); |
225 | }; | ||
226 | |||
227 | static void send_frame_done_iterator(struct wlr_surface *surface, | ||
228 | int sx, int sy, void *_data) { | ||
229 | struct send_frame_done_data *data = _data; | ||
230 | if (data->exclusive_client && | ||
231 | data->exclusive_client != surface->resource->client) { | ||
232 | return; | ||
233 | } | ||
234 | |||
235 | bool intersects = output_get_surface_box(&data->root_geo, data->output, surface, | ||
236 | sx, sy, NULL); | ||
237 | if (intersects) { | ||
238 | wlr_surface_send_frame_done(surface, data->when); | ||
239 | } | ||
240 | } | 277 | } |
241 | 278 | ||
242 | static void send_frame_done_layer(struct send_frame_done_data *data, | 279 | static void send_frame_done_layer(struct sway_output *output, |
243 | struct wl_list *layer_surfaces) { | 280 | struct wl_list *layer_surfaces, struct timespec *when) { |
244 | output_layer_for_each_surface(layer_surfaces, &data->root_geo, | 281 | output_layer_for_each_surface(output, layer_surfaces, |
245 | send_frame_done_iterator, data); | 282 | send_frame_done_iterator, when); |
246 | } | 283 | } |
247 | 284 | ||
248 | static void send_frame_done_unmanaged(struct send_frame_done_data *data, | 285 | #ifdef HAVE_XWAYLAND |
249 | struct wl_list *unmanaged) { | 286 | static void send_frame_done_unmanaged(struct sway_output *output, |
250 | output_unmanaged_for_each_surface(unmanaged, data->output, &data->root_geo, | 287 | struct wl_list *unmanaged, struct timespec *when) { |
251 | send_frame_done_iterator, data); | 288 | output_unmanaged_for_each_surface(output, unmanaged, |
289 | send_frame_done_iterator, when); | ||
252 | } | 290 | } |
291 | #endif | ||
253 | 292 | ||
254 | static void send_frame_done_drag_icons(struct send_frame_done_data *data, | 293 | static void send_frame_done_drag_icons(struct sway_output *output, |
255 | struct wl_list *drag_icons) { | 294 | struct wl_list *drag_icons, struct timespec *when) { |
256 | output_drag_icons_for_each_surface(drag_icons, data->output, &data->root_geo, | 295 | output_drag_icons_for_each_surface(output, drag_icons, |
257 | send_frame_done_iterator, data); | 296 | send_frame_done_iterator, when); |
258 | } | 297 | } |
259 | 298 | ||
299 | struct send_frame_done_data { | ||
300 | struct sway_output *output; | ||
301 | struct timespec *when; | ||
302 | }; | ||
303 | |||
260 | static void send_frame_done_container_iterator(struct sway_container *con, | 304 | static void send_frame_done_container_iterator(struct sway_container *con, |
261 | void *_data) { | 305 | void *_data) { |
262 | struct send_frame_done_data *data = _data; | 306 | struct send_frame_done_data *data = _data; |
@@ -268,52 +312,62 @@ static void send_frame_done_container_iterator(struct sway_container *con, | |||
268 | return; | 312 | return; |
269 | } | 313 | } |
270 | 314 | ||
271 | output_view_for_each_surface(con->sway_view, data->output, &data->root_geo, | 315 | output_view_for_each_surface(data->output, con->sway_view, |
272 | send_frame_done_iterator, data); | 316 | send_frame_done_iterator, data->when); |
273 | } | ||
274 | |||
275 | static void send_frame_done_container(struct send_frame_done_data *data, | ||
276 | struct sway_container *con) { | ||
277 | container_descendants(con, C_VIEW, | ||
278 | send_frame_done_container_iterator, data); | ||
279 | } | 317 | } |
280 | 318 | ||
281 | static void send_frame_done(struct sway_output *output, struct timespec *when) { | 319 | static void send_frame_done_container(struct sway_output *output, |
282 | struct sway_seat *seat = input_manager_current_seat(input_manager); | 320 | struct sway_container *con, struct timespec *when) { |
283 | struct send_frame_done_data data = { | 321 | struct send_frame_done_data data = { |
284 | .output = output, | 322 | .output = output, |
285 | .when = when, | 323 | .when = when, |
286 | .exclusive_client = output_has_opaque_lockscreen(output, seat) ? | ||
287 | seat->exclusive_client : NULL, | ||
288 | }; | 324 | }; |
325 | container_descendants(con, C_VIEW, | ||
326 | send_frame_done_container_iterator, &data); | ||
327 | } | ||
328 | |||
329 | static void send_frame_done(struct sway_output *output, struct timespec *when) { | ||
330 | if (output_has_opaque_overlay_layer_surface(output)) { | ||
331 | goto send_frame_overlay; | ||
332 | } | ||
289 | 333 | ||
290 | struct sway_container *workspace = output_get_active_workspace(output); | 334 | struct sway_container *workspace = output_get_active_workspace(output); |
291 | if (workspace->current.ws_fullscreen) { | 335 | if (workspace->current.ws_fullscreen) { |
292 | send_frame_done_container_iterator( | 336 | if (workspace->current.ws_fullscreen->type == C_VIEW) { |
293 | workspace->current.ws_fullscreen->swayc, &data); | 337 | output_view_for_each_surface(output, |
294 | 338 | workspace->current.ws_fullscreen->sway_view, | |
295 | if (workspace->current.ws_fullscreen->type == SWAY_VIEW_XWAYLAND) { | 339 | send_frame_done_iterator, when); |
296 | send_frame_done_unmanaged(&data, | 340 | } else { |
297 | &root_container.sway_root->xwayland_unmanaged); | 341 | send_frame_done_container(output, workspace->current.ws_fullscreen, |
342 | when); | ||
298 | } | 343 | } |
344 | #ifdef HAVE_XWAYLAND | ||
345 | send_frame_done_unmanaged(output, | ||
346 | &root_container.sway_root->xwayland_unmanaged, when); | ||
347 | #endif | ||
299 | } else { | 348 | } else { |
300 | send_frame_done_layer(&data, | 349 | send_frame_done_layer(output, |
301 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND]); | 350 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND], when); |
302 | send_frame_done_layer(&data, | 351 | send_frame_done_layer(output, |
303 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM]); | 352 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM], when); |
304 | 353 | ||
305 | send_frame_done_container(&data, workspace); | 354 | send_frame_done_container(output, workspace, when); |
306 | send_frame_done_container(&data, workspace->sway_workspace->floating); | 355 | send_frame_done_container(output, workspace->sway_workspace->floating, |
307 | 356 | when); | |
308 | send_frame_done_unmanaged(&data, | 357 | |
309 | &root_container.sway_root->xwayland_unmanaged); | 358 | #ifdef HAVE_XWAYLAND |
310 | send_frame_done_layer(&data, | 359 | send_frame_done_unmanaged(output, |
311 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]); | 360 | &root_container.sway_root->xwayland_unmanaged, when); |
361 | #endif | ||
362 | send_frame_done_layer(output, | ||
363 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP], when); | ||
312 | } | 364 | } |
313 | 365 | ||
314 | send_frame_done_layer(&data, | 366 | send_frame_overlay: |
315 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY]); | 367 | send_frame_done_layer(output, |
316 | send_frame_done_drag_icons(&data, &root_container.sway_root->drag_icons); | 368 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], when); |
369 | send_frame_done_drag_icons(output, &root_container.sway_root->drag_icons, | ||
370 | when); | ||
317 | } | 371 | } |
318 | 372 | ||
319 | static void damage_handle_frame(struct wl_listener *listener, void *data) { | 373 | static void damage_handle_frame(struct wl_listener *listener, void *data) { |
@@ -348,26 +402,13 @@ void output_damage_whole(struct sway_output *output) { | |||
348 | wlr_output_damage_add_whole(output->damage); | 402 | wlr_output_damage_add_whole(output->damage); |
349 | } | 403 | } |
350 | 404 | ||
351 | struct damage_data { | 405 | static void damage_surface_iterator(struct sway_output *output, |
352 | struct root_geometry root_geo; | 406 | struct wlr_surface *surface, struct wlr_box *_box, float rotation, |
353 | struct sway_output *output; | ||
354 | bool whole; | ||
355 | }; | ||
356 | |||
357 | static void damage_surface_iterator(struct wlr_surface *surface, int sx, int sy, | ||
358 | void *_data) { | 407 | void *_data) { |
359 | struct damage_data *data = _data; | 408 | bool *data = _data; |
360 | struct sway_output *output = data->output; | 409 | bool whole = *data; |
361 | float rotation = data->root_geo.rotation; | ||
362 | bool whole = data->whole; | ||
363 | |||
364 | struct wlr_box box; | ||
365 | bool intersects = output_get_surface_box(&data->root_geo, data->output, surface, | ||
366 | sx, sy, &box); | ||
367 | if (!intersects) { | ||
368 | return; | ||
369 | } | ||
370 | 410 | ||
411 | struct wlr_box box = *_box; | ||
371 | scale_box(&box, output->wlr_output->scale); | 412 | scale_box(&box, output->wlr_output->scale); |
372 | 413 | ||
373 | int center_x = box.x + box.width/2; | 414 | int center_x = box.x + box.width/2; |
@@ -407,13 +448,8 @@ static void damage_surface_iterator(struct wlr_surface *surface, int sx, int sy, | |||
407 | 448 | ||
408 | void output_damage_surface(struct sway_output *output, double ox, double oy, | 449 | void output_damage_surface(struct sway_output *output, double ox, double oy, |
409 | struct wlr_surface *surface, bool whole) { | 450 | struct wlr_surface *surface, bool whole) { |
410 | struct damage_data data = { | 451 | output_surface_for_each_surface(output, surface, ox, oy, |
411 | .output = output, | 452 | damage_surface_iterator, &whole); |
412 | .whole = whole, | ||
413 | }; | ||
414 | |||
415 | output_surface_for_each_surface(surface, ox, oy, &data.root_geo, | ||
416 | damage_surface_iterator, &data); | ||
417 | } | 453 | } |
418 | 454 | ||
419 | static void output_damage_view(struct sway_output *output, | 455 | static void output_damage_view(struct sway_output *output, |
@@ -426,13 +462,7 @@ static void output_damage_view(struct sway_output *output, | |||
426 | return; | 462 | return; |
427 | } | 463 | } |
428 | 464 | ||
429 | struct damage_data data = { | 465 | output_view_for_each_surface(output, view, damage_surface_iterator, &whole); |
430 | .output = output, | ||
431 | .whole = whole, | ||
432 | }; | ||
433 | |||
434 | output_view_for_each_surface(view, output, &data.root_geo, | ||
435 | damage_surface_iterator, &data); | ||
436 | } | 466 | } |
437 | 467 | ||
438 | void output_damage_from_view(struct sway_output *output, | 468 | void output_damage_from_view(struct sway_output *output, |
@@ -463,11 +493,12 @@ static void output_damage_whole_container_iterator(struct sway_container *con, | |||
463 | 493 | ||
464 | void output_damage_whole_container(struct sway_output *output, | 494 | void output_damage_whole_container(struct sway_output *output, |
465 | struct sway_container *con) { | 495 | struct sway_container *con) { |
496 | // Pad the box by 1px, because the width is a double and might be a fraction | ||
466 | struct wlr_box box = { | 497 | struct wlr_box box = { |
467 | .x = con->current.swayc_x - output->wlr_output->lx, | 498 | .x = con->current.swayc_x - output->wlr_output->lx - 1, |
468 | .y = con->current.swayc_y - output->wlr_output->ly, | 499 | .y = con->current.swayc_y - output->wlr_output->ly - 1, |
469 | .width = con->current.swayc_width, | 500 | .width = con->current.swayc_width + 2, |
470 | .height = con->current.swayc_height, | 501 | .height = con->current.swayc_height + 2, |
471 | }; | 502 | }; |
472 | scale_box(&box, output->wlr_output->scale); | 503 | scale_box(&box, output->wlr_output->scale); |
473 | wlr_output_damage_add_box(output->damage, &box); | 504 | wlr_output_damage_add_box(output->damage, &box); |
@@ -509,22 +540,14 @@ static void handle_transform(struct wl_listener *listener, void *data) { | |||
509 | transaction_commit_dirty(); | 540 | transaction_commit_dirty(); |
510 | } | 541 | } |
511 | 542 | ||
512 | static void handle_scale_iterator(struct sway_container *view, void *data) { | ||
513 | view_update_marks_textures(view->sway_view); | ||
514 | } | ||
515 | |||
516 | static void handle_scale(struct wl_listener *listener, void *data) { | 543 | static void handle_scale(struct wl_listener *listener, void *data) { |
517 | struct sway_output *output = wl_container_of(listener, output, scale); | 544 | struct sway_output *output = wl_container_of(listener, output, scale); |
518 | arrange_layers(output); | 545 | arrange_layers(output); |
519 | container_descendants(output->swayc, C_VIEW, handle_scale_iterator, NULL); | 546 | container_update_textures_recursive(output->swayc); |
520 | arrange_windows(output->swayc); | 547 | arrange_windows(output->swayc); |
521 | transaction_commit_dirty(); | 548 | transaction_commit_dirty(); |
522 | } | 549 | } |
523 | 550 | ||
524 | struct sway_output *output_from_wlr_output(struct wlr_output *wlr_output) { | ||
525 | return wlr_output->data; | ||
526 | } | ||
527 | |||
528 | void handle_new_output(struct wl_listener *listener, void *data) { | 551 | void handle_new_output(struct wl_listener *listener, void *data) { |
529 | struct sway_server *server = wl_container_of(listener, server, new_output); | 552 | struct sway_server *server = wl_container_of(listener, server, new_output); |
530 | struct wlr_output *wlr_output = data; | 553 | struct wlr_output *wlr_output = data; |
diff --git a/sway/desktop/render.c b/sway/desktop/render.c index 4c85e516..cdac9c72 100644 --- a/sway/desktop/render.c +++ b/sway/desktop/render.c | |||
@@ -14,6 +14,7 @@ | |||
14 | #include <wlr/types/wlr_surface.h> | 14 | #include <wlr/types/wlr_surface.h> |
15 | #include <wlr/util/region.h> | 15 | #include <wlr/util/region.h> |
16 | #include "log.h" | 16 | #include "log.h" |
17 | #include "config.h" | ||
17 | #include "sway/config.h" | 18 | #include "sway/config.h" |
18 | #include "sway/debug.h" | 19 | #include "sway/debug.h" |
19 | #include "sway/input/input-manager.h" | 20 | #include "sway/input/input-manager.h" |
@@ -28,10 +29,7 @@ | |||
28 | #include "sway/tree/workspace.h" | 29 | #include "sway/tree/workspace.h" |
29 | 30 | ||
30 | struct render_data { | 31 | struct render_data { |
31 | struct root_geometry root_geo; | ||
32 | struct sway_output *output; | ||
33 | pixman_region32_t *damage; | 32 | pixman_region32_t *damage; |
34 | struct sway_view *view; | ||
35 | float alpha; | 33 | float alpha; |
36 | }; | 34 | }; |
37 | 35 | ||
@@ -91,11 +89,11 @@ damage_finish: | |||
91 | pixman_region32_fini(&damage); | 89 | pixman_region32_fini(&damage); |
92 | } | 90 | } |
93 | 91 | ||
94 | static void render_surface_iterator(struct wlr_surface *surface, int sx, int sy, | 92 | static void render_surface_iterator(struct sway_output *output, |
93 | struct wlr_surface *surface, struct wlr_box *_box, float rotation, | ||
95 | void *_data) { | 94 | void *_data) { |
96 | struct render_data *data = _data; | 95 | struct render_data *data = _data; |
97 | struct wlr_output *wlr_output = data->output->wlr_output; | 96 | struct wlr_output *wlr_output = output->wlr_output; |
98 | float rotation = data->root_geo.rotation; | ||
99 | pixman_region32_t *output_damage = data->damage; | 97 | pixman_region32_t *output_damage = data->damage; |
100 | float alpha = data->alpha; | 98 | float alpha = data->alpha; |
101 | 99 | ||
@@ -104,13 +102,7 @@ static void render_surface_iterator(struct wlr_surface *surface, int sx, int sy, | |||
104 | return; | 102 | return; |
105 | } | 103 | } |
106 | 104 | ||
107 | struct wlr_box box; | 105 | struct wlr_box box = *_box; |
108 | bool intersects = output_get_surface_box(&data->root_geo, data->output, | ||
109 | surface, sx, sy, &box); | ||
110 | if (!intersects) { | ||
111 | return; | ||
112 | } | ||
113 | |||
114 | scale_box(&box, wlr_output->scale); | 106 | scale_box(&box, wlr_output->scale); |
115 | 107 | ||
116 | float matrix[9]; | 108 | float matrix[9]; |
@@ -125,33 +117,32 @@ static void render_surface_iterator(struct wlr_surface *surface, int sx, int sy, | |||
125 | static void render_layer(struct sway_output *output, | 117 | static void render_layer(struct sway_output *output, |
126 | pixman_region32_t *damage, struct wl_list *layer_surfaces) { | 118 | pixman_region32_t *damage, struct wl_list *layer_surfaces) { |
127 | struct render_data data = { | 119 | struct render_data data = { |
128 | .output = output, | ||
129 | .damage = damage, | 120 | .damage = damage, |
130 | .alpha = 1.0f, | 121 | .alpha = 1.0f, |
131 | }; | 122 | }; |
132 | output_layer_for_each_surface(layer_surfaces, &data.root_geo, | 123 | output_layer_for_each_surface(output, layer_surfaces, |
133 | render_surface_iterator, &data); | 124 | render_surface_iterator, &data); |
134 | } | 125 | } |
135 | 126 | ||
127 | #ifdef HAVE_XWAYLAND | ||
136 | static void render_unmanaged(struct sway_output *output, | 128 | static void render_unmanaged(struct sway_output *output, |
137 | pixman_region32_t *damage, struct wl_list *unmanaged) { | 129 | pixman_region32_t *damage, struct wl_list *unmanaged) { |
138 | struct render_data data = { | 130 | struct render_data data = { |
139 | .output = output, | ||
140 | .damage = damage, | 131 | .damage = damage, |
141 | .alpha = 1.0f, | 132 | .alpha = 1.0f, |
142 | }; | 133 | }; |
143 | output_unmanaged_for_each_surface(unmanaged, output, &data.root_geo, | 134 | output_unmanaged_for_each_surface(output, unmanaged, |
144 | render_surface_iterator, &data); | 135 | render_surface_iterator, &data); |
145 | } | 136 | } |
137 | #endif | ||
146 | 138 | ||
147 | static void render_drag_icons(struct sway_output *output, | 139 | static void render_drag_icons(struct sway_output *output, |
148 | pixman_region32_t *damage, struct wl_list *drag_icons) { | 140 | pixman_region32_t *damage, struct wl_list *drag_icons) { |
149 | struct render_data data = { | 141 | struct render_data data = { |
150 | .output = output, | ||
151 | .damage = damage, | 142 | .damage = damage, |
152 | .alpha = 1.0f, | 143 | .alpha = 1.0f, |
153 | }; | 144 | }; |
154 | output_drag_icons_for_each_surface(drag_icons, output, &data.root_geo, | 145 | output_drag_icons_for_each_surface(output, drag_icons, |
155 | render_surface_iterator, &data); | 146 | render_surface_iterator, &data); |
156 | } | 147 | } |
157 | 148 | ||
@@ -195,33 +186,51 @@ static void premultiply_alpha(float color[4], float opacity) { | |||
195 | color[2] *= color[3]; | 186 | color[2] *= color[3]; |
196 | } | 187 | } |
197 | 188 | ||
198 | static void render_view_surfaces(struct sway_view *view, | 189 | static void render_view_toplevels(struct sway_view *view, |
199 | struct sway_output *output, pixman_region32_t *damage, float alpha) { | 190 | struct sway_output *output, pixman_region32_t *damage, float alpha) { |
200 | struct render_data data = { | 191 | struct render_data data = { |
201 | .output = output, | ||
202 | .damage = damage, | 192 | .damage = damage, |
203 | .view = view, | ||
204 | .alpha = alpha, | 193 | .alpha = alpha, |
205 | }; | 194 | }; |
206 | output_view_for_each_surface(view, output, &data.root_geo, | 195 | // Render all toplevels without descending into popups |
207 | render_surface_iterator, &data); | 196 | output_surface_for_each_surface(output, view->surface, |
197 | view->swayc->current.view_x - output->wlr_output->lx, | ||
198 | view->swayc->current.view_y - output->wlr_output->ly, | ||
199 | render_surface_iterator, &data); | ||
200 | } | ||
201 | |||
202 | static void render_popup_iterator(struct sway_output *output, | ||
203 | struct wlr_surface *surface, struct wlr_box *box, float rotation, | ||
204 | void *data) { | ||
205 | // Render this popup's surface | ||
206 | render_surface_iterator(output, surface, box, rotation, data); | ||
207 | |||
208 | // Render this popup's child toplevels | ||
209 | output_surface_for_each_surface(output, surface, box->x, box->y, | ||
210 | render_surface_iterator, data); | ||
211 | } | ||
212 | |||
213 | static void render_view_popups(struct sway_view *view, | ||
214 | struct sway_output *output, pixman_region32_t *damage, float alpha) { | ||
215 | struct render_data data = { | ||
216 | .damage = damage, | ||
217 | .alpha = alpha, | ||
218 | }; | ||
219 | output_view_for_each_popup(output, view, render_popup_iterator, &data); | ||
208 | } | 220 | } |
209 | 221 | ||
210 | static void render_saved_view(struct sway_view *view, | 222 | static void render_saved_view(struct sway_view *view, |
211 | struct sway_output *output, pixman_region32_t *damage, float alpha) { | 223 | struct sway_output *output, pixman_region32_t *damage, float alpha) { |
212 | struct wlr_output *wlr_output = output->wlr_output; | 224 | struct wlr_output *wlr_output = output->wlr_output; |
213 | 225 | ||
214 | int width, height; | 226 | if (!view->saved_buffer || !view->saved_buffer->texture) { |
215 | struct wlr_texture *texture = | ||
216 | transaction_get_saved_texture(view, &width, &height); | ||
217 | if (!texture) { | ||
218 | return; | 227 | return; |
219 | } | 228 | } |
220 | struct wlr_box box = { | 229 | struct wlr_box box = { |
221 | .x = view->swayc->current.view_x - output->swayc->current.swayc_x, | 230 | .x = view->swayc->current.view_x - output->swayc->current.swayc_x, |
222 | .y = view->swayc->current.view_y - output->swayc->current.swayc_y, | 231 | .y = view->swayc->current.view_y - output->swayc->current.swayc_y, |
223 | .width = width, | 232 | .width = view->saved_buffer_width, |
224 | .height = height, | 233 | .height = view->saved_buffer_height, |
225 | }; | 234 | }; |
226 | 235 | ||
227 | struct wlr_box output_box = { | 236 | struct wlr_box output_box = { |
@@ -241,7 +250,8 @@ static void render_saved_view(struct sway_view *view, | |||
241 | wlr_matrix_project_box(matrix, &box, WL_OUTPUT_TRANSFORM_NORMAL, 0, | 250 | wlr_matrix_project_box(matrix, &box, WL_OUTPUT_TRANSFORM_NORMAL, 0, |
242 | wlr_output->transform_matrix); | 251 | wlr_output->transform_matrix); |
243 | 252 | ||
244 | render_texture(wlr_output, damage, texture, &box, matrix, alpha); | 253 | render_texture(wlr_output, damage, view->saved_buffer->texture, |
254 | &box, matrix, alpha); | ||
245 | } | 255 | } |
246 | 256 | ||
247 | /** | 257 | /** |
@@ -250,10 +260,10 @@ static void render_saved_view(struct sway_view *view, | |||
250 | static void render_view(struct sway_output *output, pixman_region32_t *damage, | 260 | static void render_view(struct sway_output *output, pixman_region32_t *damage, |
251 | struct sway_container *con, struct border_colors *colors) { | 261 | struct sway_container *con, struct border_colors *colors) { |
252 | struct sway_view *view = con->sway_view; | 262 | struct sway_view *view = con->sway_view; |
253 | if (view->swayc->instructions->length) { | 263 | if (view->saved_buffer) { |
254 | render_saved_view(view, output, damage, view->swayc->alpha); | 264 | render_saved_view(view, output, damage, view->swayc->alpha); |
255 | } else { | 265 | } else { |
256 | render_view_surfaces(view, output, damage, view->swayc->alpha); | 266 | render_view_toplevels(view, output, damage, view->swayc->alpha); |
257 | } | 267 | } |
258 | 268 | ||
259 | if (view->using_csd) { | 269 | if (view->using_csd) { |
@@ -778,7 +788,7 @@ static void render_floating_container(struct sway_output *soutput, | |||
778 | } | 788 | } |
779 | render_view(soutput, damage, con, colors); | 789 | render_view(soutput, damage, con, colors); |
780 | } else { | 790 | } else { |
781 | render_container(soutput, damage, con, false); | 791 | render_container(soutput, damage, con, con->current.focused); |
782 | } | 792 | } |
783 | } | 793 | } |
784 | 794 | ||
@@ -835,22 +845,13 @@ void output_render(struct sway_output *output, struct timespec *when, | |||
835 | } | 845 | } |
836 | 846 | ||
837 | struct sway_container *workspace = output_get_active_workspace(output); | 847 | struct sway_container *workspace = output_get_active_workspace(output); |
838 | struct sway_view *fullscreen_view = workspace->current.ws_fullscreen; | 848 | struct sway_container *fullscreen_con = workspace->current.ws_fullscreen; |
839 | struct sway_seat *seat = input_manager_current_seat(input_manager); | 849 | |
850 | if (output_has_opaque_overlay_layer_surface(output)) { | ||
851 | goto render_overlay; | ||
852 | } | ||
840 | 853 | ||
841 | if (output_has_opaque_lockscreen(output, seat)) { | 854 | if (fullscreen_con) { |
842 | struct wlr_layer_surface *wlr_layer_surface = seat->focused_layer; | ||
843 | struct sway_layer_surface *sway_layer_surface = | ||
844 | layer_from_wlr_layer_surface(seat->focused_layer); | ||
845 | struct render_data data = { | ||
846 | .output = output, | ||
847 | .damage = damage, | ||
848 | .alpha = 1.0f, | ||
849 | }; | ||
850 | output_surface_for_each_surface(wlr_layer_surface->surface, | ||
851 | sway_layer_surface->geo.x, sway_layer_surface->geo.y, | ||
852 | &data.root_geo, render_surface_iterator, &data); | ||
853 | } else if (fullscreen_view) { | ||
854 | float clear_color[] = {0.0f, 0.0f, 0.0f, 1.0f}; | 855 | float clear_color[] = {0.0f, 0.0f, 0.0f, 1.0f}; |
855 | 856 | ||
856 | int nrects; | 857 | int nrects; |
@@ -861,16 +862,22 @@ void output_render(struct sway_output *output, struct timespec *when, | |||
861 | } | 862 | } |
862 | 863 | ||
863 | // TODO: handle views smaller than the output | 864 | // TODO: handle views smaller than the output |
864 | if (fullscreen_view->swayc->instructions->length) { | 865 | if (fullscreen_con->type == C_VIEW) { |
865 | render_saved_view(fullscreen_view, output, damage, 1.0f); | 866 | if (fullscreen_con->sway_view->saved_buffer) { |
867 | render_saved_view(fullscreen_con->sway_view, | ||
868 | output, damage, 1.0f); | ||
869 | } else { | ||
870 | render_view_toplevels(fullscreen_con->sway_view, | ||
871 | output, damage, 1.0f); | ||
872 | } | ||
866 | } else { | 873 | } else { |
867 | render_view_surfaces(fullscreen_view, output, damage, 1.0f); | 874 | render_container(output, damage, fullscreen_con, |
868 | } | 875 | fullscreen_con->current.focused); |
869 | |||
870 | if (fullscreen_view->type == SWAY_VIEW_XWAYLAND) { | ||
871 | render_unmanaged(output, damage, | ||
872 | &root_container.sway_root->xwayland_unmanaged); | ||
873 | } | 876 | } |
877 | #ifdef HAVE_XWAYLAND | ||
878 | render_unmanaged(output, damage, | ||
879 | &root_container.sway_root->xwayland_unmanaged); | ||
880 | #endif | ||
874 | } else { | 881 | } else { |
875 | float clear_color[] = {0.25f, 0.25f, 0.25f, 1.0f}; | 882 | float clear_color[] = {0.25f, 0.25f, 0.25f, 1.0f}; |
876 | 883 | ||
@@ -888,12 +895,21 @@ void output_render(struct sway_output *output, struct timespec *when, | |||
888 | 895 | ||
889 | render_container(output, damage, workspace, workspace->current.focused); | 896 | render_container(output, damage, workspace, workspace->current.focused); |
890 | render_floating(output, damage); | 897 | render_floating(output, damage); |
891 | 898 | #ifdef HAVE_XWAYLAND | |
892 | render_unmanaged(output, damage, | 899 | render_unmanaged(output, damage, |
893 | &root_container.sway_root->xwayland_unmanaged); | 900 | &root_container.sway_root->xwayland_unmanaged); |
901 | #endif | ||
894 | render_layer(output, damage, | 902 | render_layer(output, damage, |
895 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]); | 903 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]); |
896 | } | 904 | } |
905 | |||
906 | struct sway_seat *seat = input_manager_current_seat(input_manager); | ||
907 | struct sway_container *focus = seat_get_focus(seat); | ||
908 | if (focus && focus->type == C_VIEW) { | ||
909 | render_view_popups(focus->sway_view, output, damage, focus->alpha); | ||
910 | } | ||
911 | |||
912 | render_overlay: | ||
897 | render_layer(output, damage, | 913 | render_layer(output, damage, |
898 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY]); | 914 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY]); |
899 | render_drag_icons(output, damage, &root_container.sway_root->drag_icons); | 915 | render_drag_icons(output, damage, &root_container.sway_root->drag_icons); |
diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c index 19f41efc..4e6af86a 100644 --- a/sway/desktop/transaction.c +++ b/sway/desktop/transaction.c | |||
@@ -1,4 +1,5 @@ | |||
1 | #define _POSIX_C_SOURCE 200809L | 1 | #define _POSIX_C_SOURCE 200809L |
2 | #include <errno.h> | ||
2 | #include <stdbool.h> | 3 | #include <stdbool.h> |
3 | #include <stdlib.h> | 4 | #include <stdlib.h> |
4 | #include <string.h> | 5 | #include <string.h> |
@@ -40,8 +41,6 @@ struct sway_transaction_instruction { | |||
40 | struct sway_transaction *transaction; | 41 | struct sway_transaction *transaction; |
41 | struct sway_container *container; | 42 | struct sway_container *container; |
42 | struct sway_container_state state; | 43 | struct sway_container_state state; |
43 | struct wlr_buffer *saved_buffer; | ||
44 | int saved_buffer_width, saved_buffer_height; | ||
45 | uint32_t serial; | 44 | uint32_t serial; |
46 | bool ready; | 45 | bool ready; |
47 | }; | 46 | }; |
@@ -56,27 +55,6 @@ static struct sway_transaction *transaction_create() { | |||
56 | return transaction; | 55 | return transaction; |
57 | } | 56 | } |
58 | 57 | ||
59 | static void remove_saved_view_buffer( | ||
60 | struct sway_transaction_instruction *instruction) { | ||
61 | if (instruction->saved_buffer) { | ||
62 | wlr_buffer_unref(instruction->saved_buffer); | ||
63 | instruction->saved_buffer = NULL; | ||
64 | } | ||
65 | } | ||
66 | |||
67 | static void save_view_buffer(struct sway_view *view, | ||
68 | struct sway_transaction_instruction *instruction) { | ||
69 | if (!sway_assert(instruction->saved_buffer == NULL, | ||
70 | "Didn't expect instruction to have a saved buffer already")) { | ||
71 | remove_saved_view_buffer(instruction); | ||
72 | } | ||
73 | if (view->surface && wlr_surface_has_buffer(view->surface)) { | ||
74 | instruction->saved_buffer = wlr_buffer_ref(view->surface->buffer); | ||
75 | instruction->saved_buffer_width = view->surface->current.width; | ||
76 | instruction->saved_buffer_height = view->surface->current.height; | ||
77 | } | ||
78 | } | ||
79 | |||
80 | static void transaction_destroy(struct sway_transaction *transaction) { | 58 | static void transaction_destroy(struct sway_transaction *transaction) { |
81 | // Free instructions | 59 | // Free instructions |
82 | for (int i = 0; i < transaction->instructions->length; ++i) { | 60 | for (int i = 0; i < transaction->instructions->length; ++i) { |
@@ -92,7 +70,6 @@ static void transaction_destroy(struct sway_transaction *transaction) { | |||
92 | if (con->destroying && !con->instructions->length) { | 70 | if (con->destroying && !con->instructions->length) { |
93 | container_free(con); | 71 | container_free(con); |
94 | } | 72 | } |
95 | remove_saved_view_buffer(instruction); | ||
96 | free(instruction); | 73 | free(instruction); |
97 | } | 74 | } |
98 | list_free(transaction->instructions); | 75 | list_free(transaction->instructions); |
@@ -110,6 +87,7 @@ static void copy_pending_state(struct sway_container *container, | |||
110 | state->swayc_y = container->y; | 87 | state->swayc_y = container->y; |
111 | state->swayc_width = container->width; | 88 | state->swayc_width = container->width; |
112 | state->swayc_height = container->height; | 89 | state->swayc_height = container->height; |
90 | state->is_fullscreen = container->is_fullscreen; | ||
113 | state->has_gaps = container->has_gaps; | 91 | state->has_gaps = container->has_gaps; |
114 | state->current_gaps = container->current_gaps; | 92 | state->current_gaps = container->current_gaps; |
115 | state->gaps_inner = container->gaps_inner; | 93 | state->gaps_inner = container->gaps_inner; |
@@ -122,7 +100,6 @@ static void copy_pending_state(struct sway_container *container, | |||
122 | state->view_y = view->y; | 100 | state->view_y = view->y; |
123 | state->view_width = view->width; | 101 | state->view_width = view->width; |
124 | state->view_height = view->height; | 102 | state->view_height = view->height; |
125 | state->is_fullscreen = view->is_fullscreen; | ||
126 | state->border = view->border; | 103 | state->border = view->border; |
127 | state->border_thickness = view->border_thickness; | 104 | state->border_thickness = view->border_thickness; |
128 | state->border_top = view->border_top; | 105 | state->border_top = view->border_top; |
@@ -157,9 +134,6 @@ static void transaction_add_container(struct sway_transaction *transaction, | |||
157 | 134 | ||
158 | copy_pending_state(container, &instruction->state); | 135 | copy_pending_state(container, &instruction->state); |
159 | 136 | ||
160 | if (container->type == C_VIEW) { | ||
161 | save_view_buffer(container->sway_view, instruction); | ||
162 | } | ||
163 | list_add(transaction->instructions, instruction); | 137 | list_add(transaction->instructions, instruction); |
164 | } | 138 | } |
165 | 139 | ||
@@ -219,27 +193,35 @@ static void transaction_apply(struct sway_transaction *transaction) { | |||
219 | 193 | ||
220 | memcpy(&container->current, &instruction->state, | 194 | memcpy(&container->current, &instruction->state, |
221 | sizeof(struct sway_container_state)); | 195 | sizeof(struct sway_container_state)); |
196 | |||
197 | if (container->type == C_VIEW) { | ||
198 | if (container->destroying) { | ||
199 | if (container->instructions->length == 1 && | ||
200 | container->sway_view->saved_buffer) { | ||
201 | view_remove_saved_buffer(container->sway_view); | ||
202 | } | ||
203 | } else { | ||
204 | if (container->sway_view->saved_buffer) { | ||
205 | view_remove_saved_buffer(container->sway_view); | ||
206 | } | ||
207 | if (container->instructions->length > 1) { | ||
208 | view_save_buffer(container->sway_view); | ||
209 | } | ||
210 | } | ||
211 | } | ||
222 | } | 212 | } |
223 | } | 213 | } |
224 | 214 | ||
225 | /** | ||
226 | * For simplicity, we only progress the queue if it can be completely flushed. | ||
227 | */ | ||
228 | static void transaction_progress_queue() { | 215 | static void transaction_progress_queue() { |
229 | // We iterate this list in reverse because we're more likely to find a | 216 | while (server.transactions->length) { |
230 | // waiting transactions at the end of the list. | 217 | struct sway_transaction *transaction = server.transactions->items[0]; |
231 | for (int i = server.transactions->length - 1; i >= 0; --i) { | ||
232 | struct sway_transaction *transaction = server.transactions->items[i]; | ||
233 | if (transaction->num_waiting) { | 218 | if (transaction->num_waiting) { |
234 | return; | 219 | return; |
235 | } | 220 | } |
236 | } | ||
237 | for (int i = 0; i < server.transactions->length; ++i) { | ||
238 | struct sway_transaction *transaction = server.transactions->items[i]; | ||
239 | transaction_apply(transaction); | 221 | transaction_apply(transaction); |
240 | transaction_destroy(transaction); | 222 | transaction_destroy(transaction); |
223 | list_del(server.transactions, 0); | ||
241 | } | 224 | } |
242 | server.transactions->length = 0; | ||
243 | idle_inhibit_v1_check_active(server.idle_inhibit_manager_v1); | 225 | idle_inhibit_v1_check_active(server.idle_inhibit_manager_v1); |
244 | } | 226 | } |
245 | 227 | ||
@@ -302,6 +284,9 @@ static void transaction_commit(struct sway_transaction *transaction) { | |||
302 | struct timespec when; | 284 | struct timespec when; |
303 | wlr_surface_send_frame_done(con->sway_view->surface, &when); | 285 | wlr_surface_send_frame_done(con->sway_view->surface, &when); |
304 | } | 286 | } |
287 | if (con->type == C_VIEW && !con->sway_view->saved_buffer) { | ||
288 | view_save_buffer(con->sway_view); | ||
289 | } | ||
305 | list_add(con->instructions, instruction); | 290 | list_add(con->instructions, instruction); |
306 | } | 291 | } |
307 | transaction->num_configures = transaction->num_waiting; | 292 | transaction->num_configures = transaction->num_waiting; |
@@ -324,7 +309,14 @@ static void transaction_commit(struct sway_transaction *transaction) { | |||
324 | // Set up a timer which the views must respond within | 309 | // Set up a timer which the views must respond within |
325 | transaction->timer = wl_event_loop_add_timer(server.wl_event_loop, | 310 | transaction->timer = wl_event_loop_add_timer(server.wl_event_loop, |
326 | handle_timeout, transaction); | 311 | handle_timeout, transaction); |
327 | wl_event_source_timer_update(transaction->timer, txn_timeout_ms); | 312 | if (transaction->timer) { |
313 | wl_event_source_timer_update(transaction->timer, txn_timeout_ms); | ||
314 | } else { | ||
315 | wlr_log(WLR_ERROR, "Unable to create transaction timer (%s). " | ||
316 | "Some imperfect frames might be rendered.", | ||
317 | strerror(errno)); | ||
318 | handle_timeout(transaction); | ||
319 | } | ||
328 | } | 320 | } |
329 | 321 | ||
330 | // The debug tree shows the pending/live tree. Here is a good place to | 322 | // The debug tree shows the pending/live tree. Here is a good place to |
@@ -352,13 +344,11 @@ static void set_instruction_ready( | |||
352 | 344 | ||
353 | } | 345 | } |
354 | 346 | ||
355 | // If all views are ready, apply the transaction. | ||
356 | // If the transaction has timed out then its num_waiting will be 0 already. | 347 | // If the transaction has timed out then its num_waiting will be 0 already. |
357 | if (transaction->num_waiting > 0 && --transaction->num_waiting == 0) { | 348 | if (transaction->num_waiting > 0 && --transaction->num_waiting == 0) { |
358 | if (!txn_debug) { | 349 | if (!txn_debug) { |
359 | wlr_log(WLR_DEBUG, "Transaction %p is ready", transaction); | 350 | wlr_log(WLR_DEBUG, "Transaction %p is ready", transaction); |
360 | wl_event_source_timer_update(transaction->timer, 0); | 351 | wl_event_source_timer_update(transaction->timer, 0); |
361 | transaction_progress_queue(); | ||
362 | } | 352 | } |
363 | } | 353 | } |
364 | } | 354 | } |
@@ -375,6 +365,7 @@ static void set_instructions_ready(struct sway_view *view, int index) { | |||
375 | set_instruction_ready(instruction); | 365 | set_instruction_ready(instruction); |
376 | } | 366 | } |
377 | } | 367 | } |
368 | transaction_progress_queue(); | ||
378 | } | 369 | } |
379 | 370 | ||
380 | void transaction_notify_view_ready(struct sway_view *view, uint32_t serial) { | 371 | void transaction_notify_view_ready(struct sway_view *view, uint32_t serial) { |
@@ -401,18 +392,6 @@ void transaction_notify_view_ready_by_size(struct sway_view *view, | |||
401 | } | 392 | } |
402 | } | 393 | } |
403 | 394 | ||
404 | struct wlr_texture *transaction_get_saved_texture(struct sway_view *view, | ||
405 | int *width, int *height) { | ||
406 | struct sway_transaction_instruction *instruction = | ||
407 | view->swayc->instructions->items[0]; | ||
408 | if (!instruction->saved_buffer) { | ||
409 | return NULL; | ||
410 | } | ||
411 | *width = instruction->saved_buffer_width; | ||
412 | *height = instruction->saved_buffer_height; | ||
413 | return instruction->saved_buffer->texture; | ||
414 | } | ||
415 | |||
416 | void transaction_commit_dirty(void) { | 395 | void transaction_commit_dirty(void) { |
417 | if (!server.dirty_containers->length) { | 396 | if (!server.dirty_containers->length) { |
418 | return; | 397 | return; |
diff --git a/sway/desktop/xdg_shell.c b/sway/desktop/xdg_shell.c index 98c16faf..b364663d 100644 --- a/sway/desktop/xdg_shell.c +++ b/sway/desktop/xdg_shell.c | |||
@@ -1,4 +1,5 @@ | |||
1 | #define _POSIX_C_SOURCE 199309L | 1 | #define _POSIX_C_SOURCE 199309L |
2 | #include <float.h> | ||
2 | #include <stdbool.h> | 3 | #include <stdbool.h> |
3 | #include <stdlib.h> | 4 | #include <stdlib.h> |
4 | #include <wayland-server.h> | 5 | #include <wayland-server.h> |
@@ -95,6 +96,16 @@ static struct sway_xdg_shell_view *xdg_shell_view_from_view( | |||
95 | return (struct sway_xdg_shell_view *)view; | 96 | return (struct sway_xdg_shell_view *)view; |
96 | } | 97 | } |
97 | 98 | ||
99 | static void get_constraints(struct sway_view *view, double *min_width, | ||
100 | double *max_width, double *min_height, double *max_height) { | ||
101 | struct wlr_xdg_toplevel_state *state = | ||
102 | &view->wlr_xdg_surface->toplevel->current; | ||
103 | *min_width = state->min_width > 0 ? state->min_width : DBL_MIN; | ||
104 | *max_width = state->max_width > 0 ? state->max_width : DBL_MAX; | ||
105 | *min_height = state->min_height > 0 ? state->min_height : DBL_MIN; | ||
106 | *max_height = state->max_height > 0 ? state->max_height : DBL_MAX; | ||
107 | } | ||
108 | |||
98 | static const char *get_string_prop(struct sway_view *view, enum sway_view_prop prop) { | 109 | static const char *get_string_prop(struct sway_view *view, enum sway_view_prop prop) { |
99 | if (xdg_shell_view_from_view(view) == NULL) { | 110 | if (xdg_shell_view_from_view(view) == NULL) { |
100 | return NULL; | 111 | return NULL; |
@@ -168,6 +179,14 @@ static void for_each_surface(struct sway_view *view, | |||
168 | user_data); | 179 | user_data); |
169 | } | 180 | } |
170 | 181 | ||
182 | static void for_each_popup(struct sway_view *view, | ||
183 | wlr_surface_iterator_func_t iterator, void *user_data) { | ||
184 | if (xdg_shell_view_from_view(view) == NULL) { | ||
185 | return; | ||
186 | } | ||
187 | wlr_xdg_surface_for_each_popup(view->wlr_xdg_surface, iterator, user_data); | ||
188 | } | ||
189 | |||
171 | static void _close(struct sway_view *view) { | 190 | static void _close(struct sway_view *view) { |
172 | if (xdg_shell_view_from_view(view) == NULL) { | 191 | if (xdg_shell_view_from_view(view) == NULL) { |
173 | return; | 192 | return; |
@@ -178,6 +197,18 @@ static void _close(struct sway_view *view) { | |||
178 | } | 197 | } |
179 | } | 198 | } |
180 | 199 | ||
200 | static void close_popups_iterator(struct wlr_surface *surface, | ||
201 | int sx, int sy, void *data) { | ||
202 | struct wlr_xdg_surface *xdg_surface = | ||
203 | wlr_xdg_surface_from_wlr_surface(surface); | ||
204 | wlr_xdg_surface_send_close(xdg_surface); | ||
205 | } | ||
206 | |||
207 | static void close_popups(struct sway_view *view) { | ||
208 | wlr_xdg_surface_for_each_popup(view->wlr_xdg_surface, | ||
209 | close_popups_iterator, NULL); | ||
210 | } | ||
211 | |||
181 | static void destroy(struct sway_view *view) { | 212 | static void destroy(struct sway_view *view) { |
182 | struct sway_xdg_shell_view *xdg_shell_view = | 213 | struct sway_xdg_shell_view *xdg_shell_view = |
183 | xdg_shell_view_from_view(view); | 214 | xdg_shell_view_from_view(view); |
@@ -188,6 +219,7 @@ static void destroy(struct sway_view *view) { | |||
188 | } | 219 | } |
189 | 220 | ||
190 | static const struct sway_view_impl view_impl = { | 221 | static const struct sway_view_impl view_impl = { |
222 | .get_constraints = get_constraints, | ||
191 | .get_string_prop = get_string_prop, | 223 | .get_string_prop = get_string_prop, |
192 | .configure = configure, | 224 | .configure = configure, |
193 | .set_activated = set_activated, | 225 | .set_activated = set_activated, |
@@ -195,7 +227,9 @@ static const struct sway_view_impl view_impl = { | |||
195 | .set_fullscreen = set_fullscreen, | 227 | .set_fullscreen = set_fullscreen, |
196 | .wants_floating = wants_floating, | 228 | .wants_floating = wants_floating, |
197 | .for_each_surface = for_each_surface, | 229 | .for_each_surface = for_each_surface, |
230 | .for_each_popup = for_each_popup, | ||
198 | .close = _close, | 231 | .close = _close, |
232 | .close_popups = close_popups, | ||
199 | .destroy = destroy, | 233 | .destroy = destroy, |
200 | }; | 234 | }; |
201 | 235 | ||
@@ -213,10 +247,24 @@ static void handle_commit(struct wl_listener *listener, void *data) { | |||
213 | transaction_notify_view_ready(view, xdg_surface->configure_serial); | 247 | transaction_notify_view_ready(view, xdg_surface->configure_serial); |
214 | } | 248 | } |
215 | 249 | ||
216 | view_update_title(view, false); | ||
217 | view_damage_from(view); | 250 | view_damage_from(view); |
218 | } | 251 | } |
219 | 252 | ||
253 | static void handle_set_title(struct wl_listener *listener, void *data) { | ||
254 | struct sway_xdg_shell_view *xdg_shell_view = | ||
255 | wl_container_of(listener, xdg_shell_view, set_title); | ||
256 | struct sway_view *view = &xdg_shell_view->view; | ||
257 | view_update_title(view, false); | ||
258 | view_execute_criteria(view); | ||
259 | } | ||
260 | |||
261 | static void handle_set_app_id(struct wl_listener *listener, void *data) { | ||
262 | struct sway_xdg_shell_view *xdg_shell_view = | ||
263 | wl_container_of(listener, xdg_shell_view, set_app_id); | ||
264 | struct sway_view *view = &xdg_shell_view->view; | ||
265 | view_execute_criteria(view); | ||
266 | } | ||
267 | |||
220 | static void handle_new_popup(struct wl_listener *listener, void *data) { | 268 | static void handle_new_popup(struct wl_listener *listener, void *data) { |
221 | struct sway_xdg_shell_view *xdg_shell_view = | 269 | struct sway_xdg_shell_view *xdg_shell_view = |
222 | wl_container_of(listener, xdg_shell_view, new_popup); | 270 | wl_container_of(listener, xdg_shell_view, new_popup); |
@@ -241,13 +289,41 @@ static void handle_request_fullscreen(struct wl_listener *listener, void *data) | |||
241 | return; | 289 | return; |
242 | } | 290 | } |
243 | 291 | ||
244 | view_set_fullscreen(view, e->fullscreen); | 292 | container_set_fullscreen(view->swayc, e->fullscreen); |
245 | 293 | ||
246 | struct sway_container *output = container_parent(view->swayc, C_OUTPUT); | 294 | struct sway_container *output = container_parent(view->swayc, C_OUTPUT); |
247 | arrange_windows(output); | 295 | arrange_windows(output); |
248 | transaction_commit_dirty(); | 296 | transaction_commit_dirty(); |
249 | } | 297 | } |
250 | 298 | ||
299 | static void handle_request_move(struct wl_listener *listener, void *data) { | ||
300 | struct sway_xdg_shell_view *xdg_shell_view = | ||
301 | wl_container_of(listener, xdg_shell_view, request_move); | ||
302 | struct sway_view *view = &xdg_shell_view->view; | ||
303 | if (!container_is_floating(view->swayc)) { | ||
304 | return; | ||
305 | } | ||
306 | struct wlr_xdg_toplevel_move_event *e = data; | ||
307 | struct sway_seat *seat = e->seat->seat->data; | ||
308 | if (e->serial == seat->last_button_serial) { | ||
309 | seat_begin_move(seat, view->swayc, seat->last_button); | ||
310 | } | ||
311 | } | ||
312 | |||
313 | static void handle_request_resize(struct wl_listener *listener, void *data) { | ||
314 | struct sway_xdg_shell_view *xdg_shell_view = | ||
315 | wl_container_of(listener, xdg_shell_view, request_resize); | ||
316 | struct sway_view *view = &xdg_shell_view->view; | ||
317 | if (!container_is_floating(view->swayc)) { | ||
318 | return; | ||
319 | } | ||
320 | struct wlr_xdg_toplevel_resize_event *e = data; | ||
321 | struct sway_seat *seat = e->seat->seat->data; | ||
322 | if (e->serial == seat->last_button_serial) { | ||
323 | seat_begin_resize(seat, view->swayc, seat->last_button, e->edges); | ||
324 | } | ||
325 | } | ||
326 | |||
251 | static void handle_unmap(struct wl_listener *listener, void *data) { | 327 | static void handle_unmap(struct wl_listener *listener, void *data) { |
252 | struct sway_xdg_shell_view *xdg_shell_view = | 328 | struct sway_xdg_shell_view *xdg_shell_view = |
253 | wl_container_of(listener, xdg_shell_view, unmap); | 329 | wl_container_of(listener, xdg_shell_view, unmap); |
@@ -262,6 +338,10 @@ static void handle_unmap(struct wl_listener *listener, void *data) { | |||
262 | wl_list_remove(&xdg_shell_view->commit.link); | 338 | wl_list_remove(&xdg_shell_view->commit.link); |
263 | wl_list_remove(&xdg_shell_view->new_popup.link); | 339 | wl_list_remove(&xdg_shell_view->new_popup.link); |
264 | wl_list_remove(&xdg_shell_view->request_fullscreen.link); | 340 | wl_list_remove(&xdg_shell_view->request_fullscreen.link); |
341 | wl_list_remove(&xdg_shell_view->request_move.link); | ||
342 | wl_list_remove(&xdg_shell_view->request_resize.link); | ||
343 | wl_list_remove(&xdg_shell_view->set_title.link); | ||
344 | wl_list_remove(&xdg_shell_view->set_app_id.link); | ||
265 | } | 345 | } |
266 | 346 | ||
267 | static void handle_map(struct wl_listener *listener, void *data) { | 347 | static void handle_map(struct wl_listener *listener, void *data) { |
@@ -280,7 +360,7 @@ static void handle_map(struct wl_listener *listener, void *data) { | |||
280 | view_map(view, view->wlr_xdg_surface->surface); | 360 | view_map(view, view->wlr_xdg_surface->surface); |
281 | 361 | ||
282 | if (xdg_surface->toplevel->client_pending.fullscreen) { | 362 | if (xdg_surface->toplevel->client_pending.fullscreen) { |
283 | view_set_fullscreen(view, true); | 363 | container_set_fullscreen(view->swayc, true); |
284 | struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); | 364 | struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); |
285 | arrange_windows(ws); | 365 | arrange_windows(ws); |
286 | } else { | 366 | } else { |
@@ -299,6 +379,22 @@ static void handle_map(struct wl_listener *listener, void *data) { | |||
299 | xdg_shell_view->request_fullscreen.notify = handle_request_fullscreen; | 379 | xdg_shell_view->request_fullscreen.notify = handle_request_fullscreen; |
300 | wl_signal_add(&xdg_surface->toplevel->events.request_fullscreen, | 380 | wl_signal_add(&xdg_surface->toplevel->events.request_fullscreen, |
301 | &xdg_shell_view->request_fullscreen); | 381 | &xdg_shell_view->request_fullscreen); |
382 | |||
383 | xdg_shell_view->request_move.notify = handle_request_move; | ||
384 | wl_signal_add(&xdg_surface->toplevel->events.request_move, | ||
385 | &xdg_shell_view->request_move); | ||
386 | |||
387 | xdg_shell_view->request_resize.notify = handle_request_resize; | ||
388 | wl_signal_add(&xdg_surface->toplevel->events.request_resize, | ||
389 | &xdg_shell_view->request_resize); | ||
390 | |||
391 | xdg_shell_view->set_title.notify = handle_set_title; | ||
392 | wl_signal_add(&xdg_surface->toplevel->events.set_title, | ||
393 | &xdg_shell_view->set_title); | ||
394 | |||
395 | xdg_shell_view->set_app_id.notify = handle_set_app_id; | ||
396 | wl_signal_add(&xdg_surface->toplevel->events.set_app_id, | ||
397 | &xdg_shell_view->set_app_id); | ||
302 | } | 398 | } |
303 | 399 | ||
304 | static void handle_destroy(struct wl_listener *listener, void *data) { | 400 | static void handle_destroy(struct wl_listener *listener, void *data) { |
@@ -344,9 +440,6 @@ void handle_xdg_shell_surface(struct wl_listener *listener, void *data) { | |||
344 | view_init(&xdg_shell_view->view, SWAY_VIEW_XDG_SHELL, &view_impl); | 440 | view_init(&xdg_shell_view->view, SWAY_VIEW_XDG_SHELL, &view_impl); |
345 | xdg_shell_view->view.wlr_xdg_surface = xdg_surface; | 441 | xdg_shell_view->view.wlr_xdg_surface = xdg_surface; |
346 | 442 | ||
347 | // TODO: | ||
348 | // - Look up pid and open on appropriate workspace | ||
349 | |||
350 | xdg_shell_view->map.notify = handle_map; | 443 | xdg_shell_view->map.notify = handle_map; |
351 | wl_signal_add(&xdg_surface->events.map, &xdg_shell_view->map); | 444 | wl_signal_add(&xdg_surface->events.map, &xdg_shell_view->map); |
352 | 445 | ||
diff --git a/sway/desktop/xdg_shell_v6.c b/sway/desktop/xdg_shell_v6.c index 4d76f0a7..ffea03ad 100644 --- a/sway/desktop/xdg_shell_v6.c +++ b/sway/desktop/xdg_shell_v6.c | |||
@@ -1,4 +1,5 @@ | |||
1 | #define _POSIX_C_SOURCE 199309L | 1 | #define _POSIX_C_SOURCE 199309L |
2 | #include <float.h> | ||
2 | #include <stdbool.h> | 3 | #include <stdbool.h> |
3 | #include <stdlib.h> | 4 | #include <stdlib.h> |
4 | #include <wayland-server.h> | 5 | #include <wayland-server.h> |
@@ -94,6 +95,16 @@ static struct sway_xdg_shell_v6_view *xdg_shell_v6_view_from_view( | |||
94 | return (struct sway_xdg_shell_v6_view *)view; | 95 | return (struct sway_xdg_shell_v6_view *)view; |
95 | } | 96 | } |
96 | 97 | ||
98 | static void get_constraints(struct sway_view *view, double *min_width, | ||
99 | double *max_width, double *min_height, double *max_height) { | ||
100 | struct wlr_xdg_toplevel_v6_state *state = | ||
101 | &view->wlr_xdg_surface_v6->toplevel->current; | ||
102 | *min_width = state->min_width > 0 ? state->min_width : DBL_MIN; | ||
103 | *max_width = state->max_width > 0 ? state->max_width : DBL_MAX; | ||
104 | *min_height = state->min_height > 0 ? state->min_height : DBL_MIN; | ||
105 | *max_height = state->max_height > 0 ? state->max_height : DBL_MAX; | ||
106 | } | ||
107 | |||
97 | static const char *get_string_prop(struct sway_view *view, enum sway_view_prop prop) { | 108 | static const char *get_string_prop(struct sway_view *view, enum sway_view_prop prop) { |
98 | if (xdg_shell_v6_view_from_view(view) == NULL) { | 109 | if (xdg_shell_v6_view_from_view(view) == NULL) { |
99 | return NULL; | 110 | return NULL; |
@@ -164,6 +175,15 @@ static void for_each_surface(struct sway_view *view, | |||
164 | user_data); | 175 | user_data); |
165 | } | 176 | } |
166 | 177 | ||
178 | static void for_each_popup(struct sway_view *view, | ||
179 | wlr_surface_iterator_func_t iterator, void *user_data) { | ||
180 | if (xdg_shell_v6_view_from_view(view) == NULL) { | ||
181 | return; | ||
182 | } | ||
183 | wlr_xdg_surface_v6_for_each_popup(view->wlr_xdg_surface_v6, iterator, | ||
184 | user_data); | ||
185 | } | ||
186 | |||
167 | static void _close(struct sway_view *view) { | 187 | static void _close(struct sway_view *view) { |
168 | if (xdg_shell_v6_view_from_view(view) == NULL) { | 188 | if (xdg_shell_v6_view_from_view(view) == NULL) { |
169 | return; | 189 | return; |
@@ -174,6 +194,18 @@ static void _close(struct sway_view *view) { | |||
174 | } | 194 | } |
175 | } | 195 | } |
176 | 196 | ||
197 | static void close_popups_iterator(struct wlr_surface *surface, | ||
198 | int sx, int sy, void *data) { | ||
199 | struct wlr_xdg_surface_v6 *xdg_surface_v6 = | ||
200 | wlr_xdg_surface_v6_from_wlr_surface(surface); | ||
201 | wlr_xdg_surface_v6_send_close(xdg_surface_v6); | ||
202 | } | ||
203 | |||
204 | static void close_popups(struct sway_view *view) { | ||
205 | wlr_xdg_surface_v6_for_each_popup(view->wlr_xdg_surface_v6, | ||
206 | close_popups_iterator, NULL); | ||
207 | } | ||
208 | |||
177 | static void destroy(struct sway_view *view) { | 209 | static void destroy(struct sway_view *view) { |
178 | struct sway_xdg_shell_v6_view *xdg_shell_v6_view = | 210 | struct sway_xdg_shell_v6_view *xdg_shell_v6_view = |
179 | xdg_shell_v6_view_from_view(view); | 211 | xdg_shell_v6_view_from_view(view); |
@@ -184,6 +216,7 @@ static void destroy(struct sway_view *view) { | |||
184 | } | 216 | } |
185 | 217 | ||
186 | static const struct sway_view_impl view_impl = { | 218 | static const struct sway_view_impl view_impl = { |
219 | .get_constraints = get_constraints, | ||
187 | .get_string_prop = get_string_prop, | 220 | .get_string_prop = get_string_prop, |
188 | .configure = configure, | 221 | .configure = configure, |
189 | .set_activated = set_activated, | 222 | .set_activated = set_activated, |
@@ -191,7 +224,9 @@ static const struct sway_view_impl view_impl = { | |||
191 | .set_fullscreen = set_fullscreen, | 224 | .set_fullscreen = set_fullscreen, |
192 | .wants_floating = wants_floating, | 225 | .wants_floating = wants_floating, |
193 | .for_each_surface = for_each_surface, | 226 | .for_each_surface = for_each_surface, |
227 | .for_each_popup = for_each_popup, | ||
194 | .close = _close, | 228 | .close = _close, |
229 | .close_popups = close_popups, | ||
195 | .destroy = destroy, | 230 | .destroy = destroy, |
196 | }; | 231 | }; |
197 | 232 | ||
@@ -208,10 +243,24 @@ static void handle_commit(struct wl_listener *listener, void *data) { | |||
208 | transaction_notify_view_ready(view, xdg_surface_v6->configure_serial); | 243 | transaction_notify_view_ready(view, xdg_surface_v6->configure_serial); |
209 | } | 244 | } |
210 | 245 | ||
211 | view_update_title(view, false); | ||
212 | view_damage_from(view); | 246 | view_damage_from(view); |
213 | } | 247 | } |
214 | 248 | ||
249 | static void handle_set_title(struct wl_listener *listener, void *data) { | ||
250 | struct sway_xdg_shell_v6_view *xdg_shell_v6_view = | ||
251 | wl_container_of(listener, xdg_shell_v6_view, set_title); | ||
252 | struct sway_view *view = &xdg_shell_v6_view->view; | ||
253 | view_update_title(view, false); | ||
254 | view_execute_criteria(view); | ||
255 | } | ||
256 | |||
257 | static void handle_set_app_id(struct wl_listener *listener, void *data) { | ||
258 | struct sway_xdg_shell_v6_view *xdg_shell_v6_view = | ||
259 | wl_container_of(listener, xdg_shell_v6_view, set_app_id); | ||
260 | struct sway_view *view = &xdg_shell_v6_view->view; | ||
261 | view_execute_criteria(view); | ||
262 | } | ||
263 | |||
215 | static void handle_new_popup(struct wl_listener *listener, void *data) { | 264 | static void handle_new_popup(struct wl_listener *listener, void *data) { |
216 | struct sway_xdg_shell_v6_view *xdg_shell_v6_view = | 265 | struct sway_xdg_shell_v6_view *xdg_shell_v6_view = |
217 | wl_container_of(listener, xdg_shell_v6_view, new_popup); | 266 | wl_container_of(listener, xdg_shell_v6_view, new_popup); |
@@ -236,13 +285,41 @@ static void handle_request_fullscreen(struct wl_listener *listener, void *data) | |||
236 | return; | 285 | return; |
237 | } | 286 | } |
238 | 287 | ||
239 | view_set_fullscreen(view, e->fullscreen); | 288 | container_set_fullscreen(view->swayc, e->fullscreen); |
240 | 289 | ||
241 | struct sway_container *output = container_parent(view->swayc, C_OUTPUT); | 290 | struct sway_container *output = container_parent(view->swayc, C_OUTPUT); |
242 | arrange_windows(output); | 291 | arrange_windows(output); |
243 | transaction_commit_dirty(); | 292 | transaction_commit_dirty(); |
244 | } | 293 | } |
245 | 294 | ||
295 | static void handle_request_move(struct wl_listener *listener, void *data) { | ||
296 | struct sway_xdg_shell_v6_view *xdg_shell_v6_view = | ||
297 | wl_container_of(listener, xdg_shell_v6_view, request_move); | ||
298 | struct sway_view *view = &xdg_shell_v6_view->view; | ||
299 | if (!container_is_floating(view->swayc)) { | ||
300 | return; | ||
301 | } | ||
302 | struct wlr_xdg_toplevel_v6_move_event *e = data; | ||
303 | struct sway_seat *seat = e->seat->seat->data; | ||
304 | if (e->serial == seat->last_button_serial) { | ||
305 | seat_begin_move(seat, view->swayc, seat->last_button); | ||
306 | } | ||
307 | } | ||
308 | |||
309 | static void handle_request_resize(struct wl_listener *listener, void *data) { | ||
310 | struct sway_xdg_shell_v6_view *xdg_shell_v6_view = | ||
311 | wl_container_of(listener, xdg_shell_v6_view, request_resize); | ||
312 | struct sway_view *view = &xdg_shell_v6_view->view; | ||
313 | if (!container_is_floating(view->swayc)) { | ||
314 | return; | ||
315 | } | ||
316 | struct wlr_xdg_toplevel_v6_resize_event *e = data; | ||
317 | struct sway_seat *seat = e->seat->seat->data; | ||
318 | if (e->serial == seat->last_button_serial) { | ||
319 | seat_begin_resize(seat, view->swayc, seat->last_button, e->edges); | ||
320 | } | ||
321 | } | ||
322 | |||
246 | static void handle_unmap(struct wl_listener *listener, void *data) { | 323 | static void handle_unmap(struct wl_listener *listener, void *data) { |
247 | struct sway_xdg_shell_v6_view *xdg_shell_v6_view = | 324 | struct sway_xdg_shell_v6_view *xdg_shell_v6_view = |
248 | wl_container_of(listener, xdg_shell_v6_view, unmap); | 325 | wl_container_of(listener, xdg_shell_v6_view, unmap); |
@@ -257,6 +334,10 @@ static void handle_unmap(struct wl_listener *listener, void *data) { | |||
257 | wl_list_remove(&xdg_shell_v6_view->commit.link); | 334 | wl_list_remove(&xdg_shell_v6_view->commit.link); |
258 | wl_list_remove(&xdg_shell_v6_view->new_popup.link); | 335 | wl_list_remove(&xdg_shell_v6_view->new_popup.link); |
259 | wl_list_remove(&xdg_shell_v6_view->request_fullscreen.link); | 336 | wl_list_remove(&xdg_shell_v6_view->request_fullscreen.link); |
337 | wl_list_remove(&xdg_shell_v6_view->request_move.link); | ||
338 | wl_list_remove(&xdg_shell_v6_view->request_resize.link); | ||
339 | wl_list_remove(&xdg_shell_v6_view->set_title.link); | ||
340 | wl_list_remove(&xdg_shell_v6_view->set_app_id.link); | ||
260 | } | 341 | } |
261 | 342 | ||
262 | static void handle_map(struct wl_listener *listener, void *data) { | 343 | static void handle_map(struct wl_listener *listener, void *data) { |
@@ -275,7 +356,7 @@ static void handle_map(struct wl_listener *listener, void *data) { | |||
275 | view_map(view, view->wlr_xdg_surface_v6->surface); | 356 | view_map(view, view->wlr_xdg_surface_v6->surface); |
276 | 357 | ||
277 | if (xdg_surface->toplevel->client_pending.fullscreen) { | 358 | if (xdg_surface->toplevel->client_pending.fullscreen) { |
278 | view_set_fullscreen(view, true); | 359 | container_set_fullscreen(view->swayc, true); |
279 | struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); | 360 | struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); |
280 | arrange_windows(ws); | 361 | arrange_windows(ws); |
281 | } else { | 362 | } else { |
@@ -294,6 +375,22 @@ static void handle_map(struct wl_listener *listener, void *data) { | |||
294 | xdg_shell_v6_view->request_fullscreen.notify = handle_request_fullscreen; | 375 | xdg_shell_v6_view->request_fullscreen.notify = handle_request_fullscreen; |
295 | wl_signal_add(&xdg_surface->toplevel->events.request_fullscreen, | 376 | wl_signal_add(&xdg_surface->toplevel->events.request_fullscreen, |
296 | &xdg_shell_v6_view->request_fullscreen); | 377 | &xdg_shell_v6_view->request_fullscreen); |
378 | |||
379 | xdg_shell_v6_view->request_move.notify = handle_request_move; | ||
380 | wl_signal_add(&xdg_surface->toplevel->events.request_move, | ||
381 | &xdg_shell_v6_view->request_move); | ||
382 | |||
383 | xdg_shell_v6_view->request_resize.notify = handle_request_resize; | ||
384 | wl_signal_add(&xdg_surface->toplevel->events.request_resize, | ||
385 | &xdg_shell_v6_view->request_resize); | ||
386 | |||
387 | xdg_shell_v6_view->set_title.notify = handle_set_title; | ||
388 | wl_signal_add(&xdg_surface->toplevel->events.set_title, | ||
389 | &xdg_shell_v6_view->set_title); | ||
390 | |||
391 | xdg_shell_v6_view->set_app_id.notify = handle_set_app_id; | ||
392 | wl_signal_add(&xdg_surface->toplevel->events.set_app_id, | ||
393 | &xdg_shell_v6_view->set_app_id); | ||
297 | } | 394 | } |
298 | 395 | ||
299 | static void handle_destroy(struct wl_listener *listener, void *data) { | 396 | static void handle_destroy(struct wl_listener *listener, void *data) { |
@@ -335,9 +432,6 @@ void handle_xdg_shell_v6_surface(struct wl_listener *listener, void *data) { | |||
335 | view_init(&xdg_shell_v6_view->view, SWAY_VIEW_XDG_SHELL_V6, &view_impl); | 432 | view_init(&xdg_shell_v6_view->view, SWAY_VIEW_XDG_SHELL_V6, &view_impl); |
336 | xdg_shell_v6_view->view.wlr_xdg_surface_v6 = xdg_surface; | 433 | xdg_shell_v6_view->view.wlr_xdg_surface_v6 = xdg_surface; |
337 | 434 | ||
338 | // TODO: | ||
339 | // - Look up pid and open on appropriate workspace | ||
340 | |||
341 | xdg_shell_v6_view->map.notify = handle_map; | 435 | xdg_shell_v6_view->map.notify = handle_map; |
342 | wl_signal_add(&xdg_surface->events.map, &xdg_shell_v6_view->map); | 436 | wl_signal_add(&xdg_surface->events.map, &xdg_shell_v6_view->map); |
343 | 437 | ||
diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c index bce0a37b..398446f8 100644 --- a/sway/desktop/xwayland.c +++ b/sway/desktop/xwayland.c | |||
@@ -69,11 +69,13 @@ static void unmanaged_handle_map(struct wl_listener *listener, void *data) { | |||
69 | surface->ly = xsurface->y; | 69 | surface->ly = xsurface->y; |
70 | desktop_damage_surface(xsurface->surface, surface->lx, surface->ly, true); | 70 | desktop_damage_surface(xsurface->surface, surface->lx, surface->ly, true); |
71 | 71 | ||
72 | struct sway_seat *seat = input_manager_current_seat(input_manager); | 72 | if (wlr_xwayland_or_surface_wants_focus(xsurface)) { |
73 | struct wlr_xwayland *xwayland = | 73 | struct sway_seat *seat = input_manager_current_seat(input_manager); |
74 | seat->input->server->xwayland.wlr_xwayland; | 74 | struct wlr_xwayland *xwayland = |
75 | wlr_xwayland_set_seat(xwayland, seat->wlr_seat); | 75 | seat->input->server->xwayland.wlr_xwayland; |
76 | seat_set_focus_surface(seat, xsurface->surface, false); | 76 | wlr_xwayland_set_seat(xwayland, seat->wlr_seat); |
77 | seat_set_focus_surface(seat, xsurface->surface, false); | ||
78 | } | ||
77 | } | 79 | } |
78 | 80 | ||
79 | static void unmanaged_handle_unmap(struct wl_listener *listener, void *data) { | 81 | static void unmanaged_handle_unmap(struct wl_listener *listener, void *data) { |
@@ -305,6 +307,8 @@ static void handle_destroy(struct wl_listener *listener, void *data) { | |||
305 | wl_list_remove(&xwayland_view->destroy.link); | 307 | wl_list_remove(&xwayland_view->destroy.link); |
306 | wl_list_remove(&xwayland_view->request_configure.link); | 308 | wl_list_remove(&xwayland_view->request_configure.link); |
307 | wl_list_remove(&xwayland_view->request_fullscreen.link); | 309 | wl_list_remove(&xwayland_view->request_fullscreen.link); |
310 | wl_list_remove(&xwayland_view->request_move.link); | ||
311 | wl_list_remove(&xwayland_view->request_resize.link); | ||
308 | wl_list_remove(&xwayland_view->set_title.link); | 312 | wl_list_remove(&xwayland_view->set_title.link); |
309 | wl_list_remove(&xwayland_view->set_class.link); | 313 | wl_list_remove(&xwayland_view->set_class.link); |
310 | wl_list_remove(&xwayland_view->set_window_type.link); | 314 | wl_list_remove(&xwayland_view->set_window_type.link); |
@@ -355,7 +359,7 @@ static void handle_map(struct wl_listener *listener, void *data) { | |||
355 | view_map(view, xsurface->surface); | 359 | view_map(view, xsurface->surface); |
356 | 360 | ||
357 | if (xsurface->fullscreen) { | 361 | if (xsurface->fullscreen) { |
358 | view_set_fullscreen(view, true); | 362 | container_set_fullscreen(view->swayc, true); |
359 | struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); | 363 | struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); |
360 | arrange_windows(ws); | 364 | arrange_windows(ws); |
361 | } else { | 365 | } else { |
@@ -393,13 +397,44 @@ static void handle_request_fullscreen(struct wl_listener *listener, void *data) | |||
393 | if (!xsurface->mapped) { | 397 | if (!xsurface->mapped) { |
394 | return; | 398 | return; |
395 | } | 399 | } |
396 | view_set_fullscreen(view, xsurface->fullscreen); | 400 | container_set_fullscreen(view->swayc, xsurface->fullscreen); |
397 | 401 | ||
398 | struct sway_container *output = container_parent(view->swayc, C_OUTPUT); | 402 | struct sway_container *output = container_parent(view->swayc, C_OUTPUT); |
399 | arrange_windows(output); | 403 | arrange_windows(output); |
400 | transaction_commit_dirty(); | 404 | transaction_commit_dirty(); |
401 | } | 405 | } |
402 | 406 | ||
407 | static void handle_request_move(struct wl_listener *listener, void *data) { | ||
408 | struct sway_xwayland_view *xwayland_view = | ||
409 | wl_container_of(listener, xwayland_view, request_move); | ||
410 | struct sway_view *view = &xwayland_view->view; | ||
411 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; | ||
412 | if (!xsurface->mapped) { | ||
413 | return; | ||
414 | } | ||
415 | if (!container_is_floating(view->swayc)) { | ||
416 | return; | ||
417 | } | ||
418 | struct sway_seat *seat = input_manager_current_seat(input_manager); | ||
419 | seat_begin_move(seat, view->swayc, seat->last_button); | ||
420 | } | ||
421 | |||
422 | static void handle_request_resize(struct wl_listener *listener, void *data) { | ||
423 | struct sway_xwayland_view *xwayland_view = | ||
424 | wl_container_of(listener, xwayland_view, request_resize); | ||
425 | struct sway_view *view = &xwayland_view->view; | ||
426 | struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; | ||
427 | if (!xsurface->mapped) { | ||
428 | return; | ||
429 | } | ||
430 | if (!container_is_floating(view->swayc)) { | ||
431 | return; | ||
432 | } | ||
433 | struct wlr_xwayland_resize_event *e = data; | ||
434 | struct sway_seat *seat = input_manager_current_seat(input_manager); | ||
435 | seat_begin_resize(seat, view->swayc, seat->last_button, e->edges); | ||
436 | } | ||
437 | |||
403 | static void handle_set_title(struct wl_listener *listener, void *data) { | 438 | static void handle_set_title(struct wl_listener *listener, void *data) { |
404 | struct sway_xwayland_view *xwayland_view = | 439 | struct sway_xwayland_view *xwayland_view = |
405 | wl_container_of(listener, xwayland_view, set_title); | 440 | wl_container_of(listener, xwayland_view, set_title); |
@@ -481,9 +516,6 @@ void handle_xwayland_surface(struct wl_listener *listener, void *data) { | |||
481 | view_init(&xwayland_view->view, SWAY_VIEW_XWAYLAND, &view_impl); | 516 | view_init(&xwayland_view->view, SWAY_VIEW_XWAYLAND, &view_impl); |
482 | xwayland_view->view.wlr_xwayland_surface = xsurface; | 517 | xwayland_view->view.wlr_xwayland_surface = xsurface; |
483 | 518 | ||
484 | // TODO: | ||
485 | // - Look up pid and open on appropriate workspace | ||
486 | |||
487 | wl_signal_add(&xsurface->events.destroy, &xwayland_view->destroy); | 519 | wl_signal_add(&xsurface->events.destroy, &xwayland_view->destroy); |
488 | xwayland_view->destroy.notify = handle_destroy; | 520 | xwayland_view->destroy.notify = handle_destroy; |
489 | 521 | ||
@@ -495,6 +527,14 @@ void handle_xwayland_surface(struct wl_listener *listener, void *data) { | |||
495 | &xwayland_view->request_fullscreen); | 527 | &xwayland_view->request_fullscreen); |
496 | xwayland_view->request_fullscreen.notify = handle_request_fullscreen; | 528 | xwayland_view->request_fullscreen.notify = handle_request_fullscreen; |
497 | 529 | ||
530 | wl_signal_add(&xsurface->events.request_move, | ||
531 | &xwayland_view->request_move); | ||
532 | xwayland_view->request_move.notify = handle_request_move; | ||
533 | |||
534 | wl_signal_add(&xsurface->events.request_resize, | ||
535 | &xwayland_view->request_resize); | ||
536 | xwayland_view->request_resize.notify = handle_request_resize; | ||
537 | |||
498 | wl_signal_add(&xsurface->events.set_title, &xwayland_view->set_title); | 538 | wl_signal_add(&xsurface->events.set_title, &xwayland_view->set_title); |
499 | xwayland_view->set_title.notify = handle_set_title; | 539 | xwayland_view->set_title.notify = handle_set_title; |
500 | 540 | ||
diff --git a/sway/input/cursor.c b/sway/input/cursor.c index c76c20b3..c2fc4e9e 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c | |||
@@ -5,15 +5,20 @@ | |||
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 <limits.h> | ||
8 | #include <wlr/types/wlr_cursor.h> | 9 | #include <wlr/types/wlr_cursor.h> |
9 | #include <wlr/types/wlr_xcursor_manager.h> | 10 | #include <wlr/types/wlr_xcursor_manager.h> |
10 | #include <wlr/types/wlr_idle.h> | 11 | #include <wlr/types/wlr_idle.h> |
11 | #include "list.h" | 12 | #include "list.h" |
12 | #include "log.h" | 13 | #include "log.h" |
14 | #include "config.h" | ||
15 | #include "sway/desktop.h" | ||
13 | #include "sway/desktop/transaction.h" | 16 | #include "sway/desktop/transaction.h" |
14 | #include "sway/input/cursor.h" | 17 | #include "sway/input/cursor.h" |
18 | #include "sway/input/keyboard.h" | ||
15 | #include "sway/layers.h" | 19 | #include "sway/layers.h" |
16 | #include "sway/output.h" | 20 | #include "sway/output.h" |
21 | #include "sway/tree/arrange.h" | ||
17 | #include "sway/tree/view.h" | 22 | #include "sway/tree/view.h" |
18 | #include "sway/tree/workspace.h" | 23 | #include "sway/tree/workspace.h" |
19 | #include "wlr-layer-shell-unstable-v1-protocol.h" | 24 | #include "wlr-layer-shell-unstable-v1-protocol.h" |
@@ -50,6 +55,7 @@ static struct sway_container *container_at_coords( | |||
50 | struct sway_seat *seat, double lx, double ly, | 55 | struct sway_seat *seat, double lx, double ly, |
51 | struct wlr_surface **surface, double *sx, double *sy) { | 56 | struct wlr_surface **surface, double *sx, double *sy) { |
52 | // check for unmanaged views first | 57 | // check for unmanaged views first |
58 | #ifdef HAVE_XWAYLAND | ||
53 | struct wl_list *unmanaged = &root_container.sway_root->xwayland_unmanaged; | 59 | struct wl_list *unmanaged = &root_container.sway_root->xwayland_unmanaged; |
54 | struct sway_xwayland_unmanaged *unmanaged_surface; | 60 | struct sway_xwayland_unmanaged *unmanaged_surface; |
55 | wl_list_for_each_reverse(unmanaged_surface, unmanaged, link) { | 61 | wl_list_for_each_reverse(unmanaged_surface, unmanaged, link) { |
@@ -65,7 +71,7 @@ static struct sway_container *container_at_coords( | |||
65 | return NULL; | 71 | return NULL; |
66 | } | 72 | } |
67 | } | 73 | } |
68 | 74 | #endif | |
69 | // find the output the cursor is on | 75 | // find the output the cursor is on |
70 | struct wlr_output_layout *output_layout = | 76 | struct wlr_output_layout *output_layout = |
71 | root_container.sway_root->output_layout; | 77 | root_container.sway_root->output_layout; |
@@ -93,14 +99,8 @@ static struct sway_container *container_at_coords( | |||
93 | return ws; | 99 | return ws; |
94 | } | 100 | } |
95 | if (ws->sway_workspace->fullscreen) { | 101 | if (ws->sway_workspace->fullscreen) { |
96 | struct wlr_surface *wlr_surface = ws->sway_workspace->fullscreen->surface; | 102 | return container_at_view(ws->sway_workspace->fullscreen, lx, ly, |
97 | if (wlr_surface_point_accepts_input(wlr_surface, ox, oy)) { | 103 | surface, sx, sy); |
98 | *sx = ox; | ||
99 | *sy = oy; | ||
100 | *surface = wlr_surface; | ||
101 | return ws->sway_workspace->fullscreen->swayc; | ||
102 | } | ||
103 | return NULL; | ||
104 | } | 104 | } |
105 | if ((*surface = layer_surface_at(output, | 105 | if ((*surface = layer_surface_at(output, |
106 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP], | 106 | &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP], |
@@ -109,9 +109,6 @@ static struct sway_container *container_at_coords( | |||
109 | } | 109 | } |
110 | 110 | ||
111 | struct sway_container *c; | 111 | struct sway_container *c; |
112 | if ((c = floating_container_at(lx, ly, surface, sx, sy))) { | ||
113 | return c; | ||
114 | } | ||
115 | if ((c = container_at(ws, lx, ly, surface, sx, sy))) { | 112 | if ((c = container_at(ws, lx, ly, surface, sx, sy))) { |
116 | return c; | 113 | return c; |
117 | } | 114 | } |
@@ -127,7 +124,7 @@ static struct sway_container *container_at_coords( | |||
127 | return ws; | 124 | return ws; |
128 | } | 125 | } |
129 | 126 | ||
130 | c = seat_get_focus_inactive(seat, output->swayc); | 127 | c = seat_get_active_child(seat, output->swayc); |
131 | if (c) { | 128 | if (c) { |
132 | return c; | 129 | return c; |
133 | } | 130 | } |
@@ -139,6 +136,171 @@ static struct sway_container *container_at_coords( | |||
139 | return output->swayc; | 136 | return output->swayc; |
140 | } | 137 | } |
141 | 138 | ||
139 | static enum wlr_edges find_resize_edge(struct sway_container *cont, | ||
140 | struct sway_cursor *cursor) { | ||
141 | if (cont->type != C_VIEW) { | ||
142 | return WLR_EDGE_NONE; | ||
143 | } | ||
144 | struct sway_view *view = cont->sway_view; | ||
145 | if (view->border == B_NONE || !view->border_thickness || view->using_csd) { | ||
146 | return WLR_EDGE_NONE; | ||
147 | } | ||
148 | |||
149 | enum wlr_edges edge = 0; | ||
150 | if (cursor->cursor->x < cont->x + view->border_thickness) { | ||
151 | edge |= WLR_EDGE_LEFT; | ||
152 | } | ||
153 | if (cursor->cursor->y < cont->y + view->border_thickness) { | ||
154 | edge |= WLR_EDGE_TOP; | ||
155 | } | ||
156 | if (cursor->cursor->x >= cont->x + cont->width - view->border_thickness) { | ||
157 | edge |= WLR_EDGE_RIGHT; | ||
158 | } | ||
159 | if (cursor->cursor->y >= cont->y + cont->height - view->border_thickness) { | ||
160 | edge |= WLR_EDGE_BOTTOM; | ||
161 | } | ||
162 | return edge; | ||
163 | } | ||
164 | |||
165 | static void handle_move_motion(struct sway_seat *seat, | ||
166 | struct sway_cursor *cursor) { | ||
167 | struct sway_container *con = seat->op_container; | ||
168 | desktop_damage_whole_container(con); | ||
169 | container_floating_translate(con, | ||
170 | cursor->cursor->x - cursor->previous.x, | ||
171 | cursor->cursor->y - cursor->previous.y); | ||
172 | desktop_damage_whole_container(con); | ||
173 | } | ||
174 | |||
175 | static void calculate_floating_constraints(struct sway_container *con, | ||
176 | int *min_width, int *max_width, int *min_height, int *max_height) { | ||
177 | if (config->floating_minimum_width == -1) { // no minimum | ||
178 | *min_width = 0; | ||
179 | } else if (config->floating_minimum_width == 0) { // automatic | ||
180 | *min_width = 75; | ||
181 | } else { | ||
182 | *min_width = config->floating_minimum_width; | ||
183 | } | ||
184 | |||
185 | if (config->floating_minimum_height == -1) { // no minimum | ||
186 | *min_height = 0; | ||
187 | } else if (config->floating_minimum_height == 0) { // automatic | ||
188 | *min_height = 50; | ||
189 | } else { | ||
190 | *min_height = config->floating_minimum_height; | ||
191 | } | ||
192 | |||
193 | if (config->floating_maximum_width == -1) { // no maximum | ||
194 | *max_width = INT_MAX; | ||
195 | } else if (config->floating_maximum_width == 0) { // automatic | ||
196 | struct sway_container *ws = container_parent(con, C_WORKSPACE); | ||
197 | *max_width = ws->width; | ||
198 | } else { | ||
199 | *max_width = config->floating_maximum_width; | ||
200 | } | ||
201 | |||
202 | if (config->floating_maximum_height == -1) { // no maximum | ||
203 | *max_height = INT_MAX; | ||
204 | } else if (config->floating_maximum_height == 0) { // automatic | ||
205 | struct sway_container *ws = container_parent(con, C_WORKSPACE); | ||
206 | *max_height = ws->height; | ||
207 | } else { | ||
208 | *max_height = config->floating_maximum_height; | ||
209 | } | ||
210 | } | ||
211 | |||
212 | static void handle_resize_motion(struct sway_seat *seat, | ||
213 | struct sway_cursor *cursor) { | ||
214 | struct sway_container *con = seat->op_container; | ||
215 | enum wlr_edges edge = seat->op_resize_edge; | ||
216 | |||
217 | // The amount the mouse has moved since the start of the resize operation | ||
218 | // Positive is down/right | ||
219 | double mouse_move_x = cursor->cursor->x - seat->op_ref_lx; | ||
220 | double mouse_move_y = cursor->cursor->y - seat->op_ref_ly; | ||
221 | |||
222 | if (edge == WLR_EDGE_TOP || edge == WLR_EDGE_BOTTOM) { | ||
223 | mouse_move_x = 0; | ||
224 | } | ||
225 | if (edge == WLR_EDGE_LEFT || edge == WLR_EDGE_RIGHT) { | ||
226 | mouse_move_y = 0; | ||
227 | } | ||
228 | |||
229 | double grow_width = edge & WLR_EDGE_LEFT ? -mouse_move_x : mouse_move_x; | ||
230 | double grow_height = edge & WLR_EDGE_TOP ? -mouse_move_y : mouse_move_y; | ||
231 | |||
232 | if (seat->op_resize_preserve_ratio) { | ||
233 | double x_multiplier = grow_width / seat->op_ref_width; | ||
234 | double y_multiplier = grow_height / seat->op_ref_height; | ||
235 | double max_multiplier = fmax(x_multiplier, y_multiplier); | ||
236 | grow_width = seat->op_ref_width * max_multiplier; | ||
237 | grow_height = seat->op_ref_height * max_multiplier; | ||
238 | } | ||
239 | |||
240 | // Determine new width/height, and accommodate for floating min/max values | ||
241 | double width = seat->op_ref_width + grow_width; | ||
242 | double height = seat->op_ref_height + grow_height; | ||
243 | int min_width, max_width, min_height, max_height; | ||
244 | calculate_floating_constraints(con, &min_width, &max_width, | ||
245 | &min_height, &max_height); | ||
246 | width = fmax(min_width, fmin(width, max_width)); | ||
247 | height = fmax(min_height, fmin(height, max_height)); | ||
248 | |||
249 | // Apply the view's min/max size | ||
250 | if (con->type == C_VIEW) { | ||
251 | double view_min_width, view_max_width, view_min_height, view_max_height; | ||
252 | view_get_constraints(con->sway_view, &view_min_width, &view_max_width, | ||
253 | &view_min_height, &view_max_height); | ||
254 | width = fmax(view_min_width, fmin(width, view_max_width)); | ||
255 | height = fmax(view_min_height, fmin(height, view_max_height)); | ||
256 | } | ||
257 | |||
258 | // Recalculate these, in case we hit a min/max limit | ||
259 | grow_width = width - seat->op_ref_width; | ||
260 | grow_height = height - seat->op_ref_height; | ||
261 | |||
262 | // Determine grow x/y values - these are relative to the container's x/y at | ||
263 | // the start of the resize operation. | ||
264 | double grow_x = 0, grow_y = 0; | ||
265 | if (edge & WLR_EDGE_LEFT) { | ||
266 | grow_x = -grow_width; | ||
267 | } else if (edge & WLR_EDGE_RIGHT) { | ||
268 | grow_x = 0; | ||
269 | } else { | ||
270 | grow_x = -grow_width / 2; | ||
271 | } | ||
272 | if (edge & WLR_EDGE_TOP) { | ||
273 | grow_y = -grow_height; | ||
274 | } else if (edge & WLR_EDGE_BOTTOM) { | ||
275 | grow_y = 0; | ||
276 | } else { | ||
277 | grow_y = -grow_height / 2; | ||
278 | } | ||
279 | |||
280 | // Determine the amounts we need to bump everything relative to the current | ||
281 | // size. | ||
282 | int relative_grow_width = width - con->width; | ||
283 | int relative_grow_height = height - con->height; | ||
284 | int relative_grow_x = (seat->op_ref_con_lx + grow_x) - con->x; | ||
285 | int relative_grow_y = (seat->op_ref_con_ly + grow_y) - con->y; | ||
286 | |||
287 | // Actually resize stuff | ||
288 | con->x += relative_grow_x; | ||
289 | con->y += relative_grow_y; | ||
290 | con->width += relative_grow_width; | ||
291 | con->height += relative_grow_height; | ||
292 | |||
293 | if (con->type == C_VIEW) { | ||
294 | struct sway_view *view = con->sway_view; | ||
295 | view->x += relative_grow_x; | ||
296 | view->y += relative_grow_y; | ||
297 | view->width += relative_grow_width; | ||
298 | view->height += relative_grow_height; | ||
299 | } | ||
300 | |||
301 | arrange_windows(con); | ||
302 | } | ||
303 | |||
142 | void cursor_send_pointer_motion(struct sway_cursor *cursor, uint32_t time_msec, | 304 | void cursor_send_pointer_motion(struct sway_cursor *cursor, uint32_t time_msec, |
143 | bool allow_refocusing) { | 305 | bool allow_refocusing) { |
144 | if (time_msec == 0) { | 306 | if (time_msec == 0) { |
@@ -146,6 +308,18 @@ void cursor_send_pointer_motion(struct sway_cursor *cursor, uint32_t time_msec, | |||
146 | } | 308 | } |
147 | 309 | ||
148 | struct sway_seat *seat = cursor->seat; | 310 | struct sway_seat *seat = cursor->seat; |
311 | |||
312 | if (seat->operation != OP_NONE) { | ||
313 | if (seat->operation == OP_MOVE) { | ||
314 | handle_move_motion(seat, cursor); | ||
315 | } else { | ||
316 | handle_resize_motion(seat, cursor); | ||
317 | } | ||
318 | cursor->previous.x = cursor->cursor->x; | ||
319 | cursor->previous.y = cursor->cursor->y; | ||
320 | return; | ||
321 | } | ||
322 | |||
149 | struct wlr_seat *wlr_seat = seat->wlr_seat; | 323 | struct wlr_seat *wlr_seat = seat->wlr_seat; |
150 | struct wlr_surface *surface = NULL; | 324 | struct wlr_surface *surface = NULL; |
151 | double sx, sy; | 325 | double sx, sy; |
@@ -172,7 +346,7 @@ void cursor_send_pointer_motion(struct sway_cursor *cursor, uint32_t time_msec, | |||
172 | output = container_parent(c, C_OUTPUT); | 346 | output = container_parent(c, C_OUTPUT); |
173 | } | 347 | } |
174 | if (output != focus) { | 348 | if (output != focus) { |
175 | seat_set_focus_warp(seat, c, false); | 349 | seat_set_focus_warp(seat, c, false, true); |
176 | } | 350 | } |
177 | } else if (c->type == C_VIEW) { | 351 | } else if (c->type == C_VIEW) { |
178 | // Focus c if the following are true: | 352 | // Focus c if the following are true: |
@@ -182,27 +356,33 @@ void cursor_send_pointer_motion(struct sway_cursor *cursor, uint32_t time_msec, | |||
182 | if (!wlr_seat_keyboard_has_grab(cursor->seat->wlr_seat) && | 356 | if (!wlr_seat_keyboard_has_grab(cursor->seat->wlr_seat) && |
183 | c != prev_c && | 357 | c != prev_c && |
184 | view_is_visible(c->sway_view)) { | 358 | view_is_visible(c->sway_view)) { |
185 | seat_set_focus_warp(seat, c, false); | 359 | seat_set_focus_warp(seat, c, false, true); |
186 | } else { | 360 | } else { |
187 | struct sway_container *next_focus = | 361 | struct sway_container *next_focus = |
188 | seat_get_focus_inactive(seat, &root_container); | 362 | seat_get_focus_inactive(seat, &root_container); |
189 | if (next_focus && next_focus->type == C_VIEW && | 363 | if (next_focus && next_focus->type == C_VIEW && |
190 | view_is_visible(next_focus->sway_view)) { | 364 | view_is_visible(next_focus->sway_view)) { |
191 | seat_set_focus_warp(seat, next_focus, false); | 365 | seat_set_focus_warp(seat, next_focus, false, true); |
192 | } | 366 | } |
193 | } | 367 | } |
194 | } | 368 | } |
195 | } | 369 | } |
196 | 370 | ||
197 | // reset cursor if switching between clients | 371 | // Handle cursor image |
198 | struct wl_client *client = NULL; | 372 | if (surface) { |
199 | if (surface != NULL) { | 373 | // Reset cursor if switching between clients |
200 | client = wl_resource_get_client(surface->resource); | 374 | struct wl_client *client = wl_resource_get_client(surface->resource); |
201 | } | 375 | if (client != cursor->image_client) { |
202 | if (client != cursor->image_client) { | 376 | cursor_set_image(cursor, "left_ptr", client); |
203 | wlr_xcursor_manager_set_cursor_image(cursor->xcursor_manager, | 377 | } |
204 | "left_ptr", cursor->cursor); | 378 | } else if (c && container_is_floating(c)) { |
205 | cursor->image_client = client; | 379 | // Try a floating container's resize edge |
380 | enum wlr_edges edge = find_resize_edge(c, cursor); | ||
381 | const char *image = edge == WLR_EDGE_NONE ? | ||
382 | "left_ptr" : wlr_xcursor_get_resize_name(edge); | ||
383 | cursor_set_image(cursor, image, NULL); | ||
384 | } else { | ||
385 | cursor_set_image(cursor, "left_ptr", NULL); | ||
206 | } | 386 | } |
207 | 387 | ||
208 | // send pointer enter/leave | 388 | // send pointer enter/leave |
@@ -243,8 +423,142 @@ static void handle_cursor_motion_absolute( | |||
243 | transaction_commit_dirty(); | 423 | transaction_commit_dirty(); |
244 | } | 424 | } |
245 | 425 | ||
426 | static void dispatch_cursor_button_floating(struct sway_cursor *cursor, | ||
427 | uint32_t time_msec, uint32_t button, enum wlr_button_state state, | ||
428 | struct wlr_surface *surface, double sx, double sy, | ||
429 | struct sway_container *cont) { | ||
430 | struct sway_seat *seat = cursor->seat; | ||
431 | |||
432 | // Deny moving or resizing a fullscreen container | ||
433 | if (container_is_fullscreen_or_child(cont)) { | ||
434 | seat_pointer_notify_button(seat, time_msec, button, state); | ||
435 | return; | ||
436 | } | ||
437 | struct sway_container *floater = cont; | ||
438 | while (floater->parent->layout != L_FLOATING) { | ||
439 | floater = floater->parent; | ||
440 | } | ||
441 | |||
442 | struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(seat->wlr_seat); | ||
443 | bool mod_pressed = keyboard && | ||
444 | (wlr_keyboard_get_modifiers(keyboard) & config->floating_mod); | ||
445 | enum wlr_edges edge = find_resize_edge(floater, cursor); | ||
446 | bool over_title = edge == WLR_EDGE_NONE && !surface; | ||
447 | |||
448 | // Check for beginning move | ||
449 | uint32_t btn_move = config->floating_mod_inverse ? BTN_RIGHT : BTN_LEFT; | ||
450 | if (button == btn_move && state == WLR_BUTTON_PRESSED && | ||
451 | (mod_pressed || over_title)) { | ||
452 | seat_begin_move(seat, floater, button); | ||
453 | return; | ||
454 | } | ||
455 | |||
456 | // Check for beginning resize | ||
457 | bool resizing_via_border = button == BTN_LEFT && edge != WLR_EDGE_NONE; | ||
458 | uint32_t btn_resize = config->floating_mod_inverse ? BTN_LEFT : BTN_RIGHT; | ||
459 | bool resizing_via_mod = button == btn_resize && mod_pressed; | ||
460 | if ((resizing_via_border || resizing_via_mod) && | ||
461 | state == WLR_BUTTON_PRESSED) { | ||
462 | if (edge == WLR_EDGE_NONE) { | ||
463 | edge |= cursor->cursor->x > floater->x + floater->width / 2 ? | ||
464 | WLR_EDGE_RIGHT : WLR_EDGE_LEFT; | ||
465 | edge |= cursor->cursor->y > floater->y + floater->height / 2 ? | ||
466 | WLR_EDGE_BOTTOM : WLR_EDGE_TOP; | ||
467 | } | ||
468 | seat_begin_resize(seat, floater, button, edge); | ||
469 | return; | ||
470 | } | ||
471 | |||
472 | // Send event to surface | ||
473 | seat_set_focus(seat, cont); | ||
474 | seat_pointer_notify_button(seat, time_msec, button, state); | ||
475 | } | ||
476 | |||
477 | /** | ||
478 | * Remove a button (and duplicates) to the sorted list of currently pressed buttons | ||
479 | */ | ||
480 | static void state_erase_button(struct sway_cursor *cursor, uint32_t button) { | ||
481 | size_t j = 0; | ||
482 | for (size_t i = 0; i < cursor->pressed_button_count; ++i) { | ||
483 | if (i > j) { | ||
484 | cursor->pressed_buttons[j] = cursor->pressed_buttons[i]; | ||
485 | } | ||
486 | if (cursor->pressed_buttons[i] != button) { | ||
487 | ++j; | ||
488 | } | ||
489 | } | ||
490 | while (cursor->pressed_button_count > j) { | ||
491 | --cursor->pressed_button_count; | ||
492 | cursor->pressed_buttons[cursor->pressed_button_count] = 0; | ||
493 | } | ||
494 | } | ||
495 | |||
496 | /** | ||
497 | * Add a button to the sorted list of currently pressed buttons, if there | ||
498 | * is space. | ||
499 | */ | ||
500 | static void state_add_button(struct sway_cursor *cursor, uint32_t button) { | ||
501 | if (cursor->pressed_button_count >= SWAY_CURSOR_PRESSED_BUTTONS_CAP) { | ||
502 | return; | ||
503 | } | ||
504 | size_t i = 0; | ||
505 | while (i < cursor->pressed_button_count && cursor->pressed_buttons[i] < button) { | ||
506 | ++i; | ||
507 | } | ||
508 | size_t j = cursor->pressed_button_count; | ||
509 | while (j > i) { | ||
510 | cursor->pressed_buttons[j] = cursor->pressed_buttons[j - 1]; | ||
511 | --j; | ||
512 | } | ||
513 | cursor->pressed_buttons[i] = button; | ||
514 | cursor->pressed_button_count++; | ||
515 | } | ||
516 | |||
517 | /** | ||
518 | * Return the mouse binding which matches modifier, click location, release, | ||
519 | * and pressed button state, otherwise return null. | ||
520 | */ | ||
521 | static struct sway_binding* get_active_mouse_binding(const struct sway_cursor *cursor, | ||
522 | list_t *bindings, uint32_t modifiers, bool release, bool on_titlebar, | ||
523 | bool on_border, bool on_content) { | ||
524 | uint32_t click_region = (on_titlebar ? BINDING_TITLEBAR : 0) | | ||
525 | (on_border ? BINDING_BORDER : 0) | | ||
526 | (on_content ? BINDING_CONTENTS : 0); | ||
527 | |||
528 | for (int i = 0; i < bindings->length; ++i) { | ||
529 | struct sway_binding *binding = bindings->items[i]; | ||
530 | if (modifiers ^ binding->modifiers || | ||
531 | cursor->pressed_button_count != (size_t)binding->keys->length || | ||
532 | release != (binding->flags & BINDING_RELEASE) || | ||
533 | !(click_region & binding->flags)) { | ||
534 | continue; | ||
535 | } | ||
536 | |||
537 | bool match = true; | ||
538 | for (size_t j = 0; j < cursor->pressed_button_count; j++) { | ||
539 | uint32_t key = *(uint32_t *)binding->keys->items[j]; | ||
540 | if (key != cursor->pressed_buttons[j]) { | ||
541 | match = false; | ||
542 | break; | ||
543 | } | ||
544 | } | ||
545 | if (!match) { | ||
546 | continue; | ||
547 | } | ||
548 | |||
549 | return binding; | ||
550 | } | ||
551 | return NULL; | ||
552 | } | ||
553 | |||
246 | void dispatch_cursor_button(struct sway_cursor *cursor, | 554 | void dispatch_cursor_button(struct sway_cursor *cursor, |
247 | uint32_t time_msec, uint32_t button, enum wlr_button_state state) { | 555 | uint32_t time_msec, uint32_t button, enum wlr_button_state state) { |
556 | if (cursor->seat->operation != OP_NONE && | ||
557 | button == cursor->seat->op_button && state == WLR_BUTTON_RELEASED) { | ||
558 | seat_end_mouse_operation(cursor->seat); | ||
559 | seat_pointer_notify_button(cursor->seat, time_msec, button, state); | ||
560 | return; | ||
561 | } | ||
248 | if (time_msec == 0) { | 562 | if (time_msec == 0) { |
249 | time_msec = get_current_time_msec(); | 563 | time_msec = get_current_time_msec(); |
250 | } | 564 | } |
@@ -253,12 +567,41 @@ void dispatch_cursor_button(struct sway_cursor *cursor, | |||
253 | double sx, sy; | 567 | double sx, sy; |
254 | struct sway_container *cont = container_at_coords(cursor->seat, | 568 | struct sway_container *cont = container_at_coords(cursor->seat, |
255 | cursor->cursor->x, cursor->cursor->y, &surface, &sx, &sy); | 569 | cursor->cursor->x, cursor->cursor->y, &surface, &sx, &sy); |
570 | |||
571 | // Handle mouse bindings | ||
572 | bool on_border = cont && (find_resize_edge(cont, cursor) != WLR_EDGE_NONE); | ||
573 | bool on_contents = !on_border && surface; | ||
574 | bool on_titlebar = !on_border && !surface; | ||
575 | struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(cursor->seat->wlr_seat); | ||
576 | uint32_t modifiers = keyboard ? wlr_keyboard_get_modifiers(keyboard) : 0; | ||
577 | |||
578 | struct sway_binding *binding = NULL; | ||
579 | if (state == WLR_BUTTON_PRESSED) { | ||
580 | state_add_button(cursor, button); | ||
581 | binding = get_active_mouse_binding(cursor, | ||
582 | config->current_mode->mouse_bindings, modifiers, false, | ||
583 | on_titlebar, on_border, on_contents); | ||
584 | } else { | ||
585 | binding = get_active_mouse_binding(cursor, | ||
586 | config->current_mode->mouse_bindings, modifiers, true, | ||
587 | on_titlebar, on_border, on_contents); | ||
588 | state_erase_button(cursor, button); | ||
589 | } | ||
590 | if (binding) { | ||
591 | seat_execute_command(cursor->seat, binding); | ||
592 | // TODO: do we want to pass on the event? | ||
593 | } | ||
594 | |||
256 | if (surface && wlr_surface_is_layer_surface(surface)) { | 595 | if (surface && wlr_surface_is_layer_surface(surface)) { |
257 | struct wlr_layer_surface *layer = | 596 | struct wlr_layer_surface *layer = |
258 | wlr_layer_surface_from_wlr_surface(surface); | 597 | wlr_layer_surface_from_wlr_surface(surface); |
259 | if (layer->current.keyboard_interactive) { | 598 | if (layer->current.keyboard_interactive) { |
260 | seat_set_focus_layer(cursor->seat, layer); | 599 | seat_set_focus_layer(cursor->seat, layer); |
261 | } | 600 | } |
601 | seat_pointer_notify_button(cursor->seat, time_msec, button, state); | ||
602 | } else if (cont && container_is_floating_or_child(cont)) { | ||
603 | dispatch_cursor_button_floating(cursor, time_msec, button, state, | ||
604 | surface, sx, sy, cont); | ||
262 | } else if (surface && cont && cont->type != C_VIEW) { | 605 | } else if (surface && cont && cont->type != C_VIEW) { |
263 | // Avoid moving keyboard focus from a surface that accepts it to one | 606 | // Avoid moving keyboard focus from a surface that accepts it to one |
264 | // that does not unless the change would move us to a new workspace. | 607 | // that does not unless the change would move us to a new workspace. |
@@ -275,12 +618,14 @@ void dispatch_cursor_button(struct sway_cursor *cursor, | |||
275 | if (new_ws != old_ws) { | 618 | if (new_ws != old_ws) { |
276 | seat_set_focus(cursor->seat, cont); | 619 | seat_set_focus(cursor->seat, cont); |
277 | } | 620 | } |
621 | seat_pointer_notify_button(cursor->seat, time_msec, button, state); | ||
278 | } else if (cont) { | 622 | } else if (cont) { |
279 | seat_set_focus(cursor->seat, cont); | 623 | seat_set_focus(cursor->seat, cont); |
624 | seat_pointer_notify_button(cursor->seat, time_msec, button, state); | ||
625 | } else { | ||
626 | seat_pointer_notify_button(cursor->seat, time_msec, button, state); | ||
280 | } | 627 | } |
281 | 628 | ||
282 | wlr_seat_pointer_notify_button(cursor->seat->wlr_seat, | ||
283 | time_msec, button, state); | ||
284 | transaction_commit_dirty(); | 629 | transaction_commit_dirty(); |
285 | } | 630 | } |
286 | 631 | ||
@@ -467,6 +812,9 @@ static void handle_request_set_cursor(struct wl_listener *listener, | |||
467 | void *data) { | 812 | void *data) { |
468 | struct sway_cursor *cursor = | 813 | struct sway_cursor *cursor = |
469 | wl_container_of(listener, cursor, request_set_cursor); | 814 | wl_container_of(listener, cursor, request_set_cursor); |
815 | if (cursor->seat->operation != OP_NONE) { | ||
816 | return; | ||
817 | } | ||
470 | struct wlr_seat_pointer_request_set_cursor_event *event = data; | 818 | struct wlr_seat_pointer_request_set_cursor_event *event = data; |
471 | 819 | ||
472 | struct wl_client *focused_client = NULL; | 820 | struct wl_client *focused_client = NULL; |
@@ -485,9 +833,20 @@ static void handle_request_set_cursor(struct wl_listener *listener, | |||
485 | 833 | ||
486 | wlr_cursor_set_surface(cursor->cursor, event->surface, event->hotspot_x, | 834 | wlr_cursor_set_surface(cursor->cursor, event->surface, event->hotspot_x, |
487 | event->hotspot_y); | 835 | event->hotspot_y); |
836 | cursor->image = NULL; | ||
488 | cursor->image_client = focused_client; | 837 | cursor->image_client = focused_client; |
489 | } | 838 | } |
490 | 839 | ||
840 | void cursor_set_image(struct sway_cursor *cursor, const char *image, | ||
841 | struct wl_client *client) { | ||
842 | if (!cursor->image || strcmp(cursor->image, image) != 0) { | ||
843 | wlr_xcursor_manager_set_cursor_image(cursor->xcursor_manager, image, | ||
844 | cursor->cursor); | ||
845 | cursor->image = image; | ||
846 | } | ||
847 | cursor->image_client = client; | ||
848 | } | ||
849 | |||
491 | void sway_cursor_destroy(struct sway_cursor *cursor) { | 850 | void sway_cursor_destroy(struct sway_cursor *cursor) { |
492 | if (!cursor) { | 851 | if (!cursor) { |
493 | return; | 852 | return; |
diff --git a/sway/input/input-manager.c b/sway/input/input-manager.c index 0b7cb766..c820e032 100644 --- a/sway/input/input-manager.c +++ b/sway/input/input-manager.c | |||
@@ -8,6 +8,7 @@ | |||
8 | #include <math.h> | 8 | #include <math.h> |
9 | #include <wlr/backend/libinput.h> | 9 | #include <wlr/backend/libinput.h> |
10 | #include <wlr/types/wlr_input_inhibitor.h> | 10 | #include <wlr/types/wlr_input_inhibitor.h> |
11 | #include <wlr/types/wlr_virtual_keyboard_v1.h> | ||
11 | #include "sway/config.h" | 12 | #include "sway/config.h" |
12 | #include "sway/input/input-manager.h" | 13 | #include "sway/input/input-manager.h" |
13 | #include "sway/input/seat.h" | 14 | #include "sway/input/seat.h" |
@@ -303,6 +304,35 @@ static void handle_inhibit_deactivate(struct wl_listener *listener, void *data) | |||
303 | } | 304 | } |
304 | } | 305 | } |
305 | 306 | ||
307 | void handle_virtual_keyboard(struct wl_listener *listener, void *data) { | ||
308 | struct sway_input_manager *input_manager = | ||
309 | wl_container_of(listener, input_manager, virtual_keyboard_new); | ||
310 | struct wlr_virtual_keyboard_v1 *keyboard = data; | ||
311 | struct wlr_input_device *device = &keyboard->input_device; | ||
312 | |||
313 | struct sway_seat *seat = input_manager_get_default_seat(input_manager); | ||
314 | |||
315 | // TODO: The user might want this on a different seat | ||
316 | struct sway_input_device *input_device = | ||
317 | calloc(1, sizeof(struct sway_input_device)); | ||
318 | if (!sway_assert(input_device, "could not allocate input device")) { | ||
319 | return; | ||
320 | } | ||
321 | device->data = input_device; | ||
322 | |||
323 | input_device->wlr_device = device; | ||
324 | input_device->identifier = get_device_identifier(device); | ||
325 | wl_list_insert(&input_manager->devices, &input_device->link); | ||
326 | |||
327 | wlr_log(WLR_DEBUG, "adding virtual keyboard: '%s'", | ||
328 | input_device->identifier); | ||
329 | |||
330 | wl_signal_add(&device->events.destroy, &input_device->device_destroy); | ||
331 | input_device->device_destroy.notify = handle_device_destroy; | ||
332 | |||
333 | seat_add_device(seat, input_device); | ||
334 | } | ||
335 | |||
306 | struct sway_input_manager *input_manager_create( | 336 | struct sway_input_manager *input_manager_create( |
307 | struct sway_server *server) { | 337 | struct sway_server *server) { |
308 | struct sway_input_manager *input = | 338 | struct sway_input_manager *input = |
@@ -321,6 +351,12 @@ struct sway_input_manager *input_manager_create( | |||
321 | input->new_input.notify = handle_new_input; | 351 | input->new_input.notify = handle_new_input; |
322 | wl_signal_add(&server->backend->events.new_input, &input->new_input); | 352 | wl_signal_add(&server->backend->events.new_input, &input->new_input); |
323 | 353 | ||
354 | input->virtual_keyboard = wlr_virtual_keyboard_manager_v1_create( | ||
355 | server->wl_display); | ||
356 | wl_signal_add(&input->virtual_keyboard->events.new_virtual_keyboard, | ||
357 | &input->virtual_keyboard_new); | ||
358 | input->virtual_keyboard_new.notify = handle_virtual_keyboard; | ||
359 | |||
324 | input->inhibit = wlr_input_inhibit_manager_create(server->wl_display); | 360 | input->inhibit = wlr_input_inhibit_manager_create(server->wl_display); |
325 | input->inhibit_activate.notify = handle_inhibit_activate; | 361 | input->inhibit_activate.notify = handle_inhibit_activate; |
326 | wl_signal_add(&input->inhibit->events.activate, | 362 | wl_signal_add(&input->inhibit->events.activate, |
diff --git a/sway/input/keyboard.c b/sway/input/keyboard.c index ede38519..160ef10b 100644 --- a/sway/input/keyboard.c +++ b/sway/input/keyboard.c | |||
@@ -3,11 +3,12 @@ | |||
3 | #include <wlr/backend/multi.h> | 3 | #include <wlr/backend/multi.h> |
4 | #include <wlr/backend/session.h> | 4 | #include <wlr/backend/session.h> |
5 | #include <wlr/types/wlr_idle.h> | 5 | #include <wlr/types/wlr_idle.h> |
6 | #include <wlr/interfaces/wlr_keyboard.h> | ||
7 | #include "sway/commands.h" | ||
6 | #include "sway/desktop/transaction.h" | 8 | #include "sway/desktop/transaction.h" |
7 | #include "sway/input/seat.h" | ||
8 | #include "sway/input/keyboard.h" | ||
9 | #include "sway/input/input-manager.h" | 9 | #include "sway/input/input-manager.h" |
10 | #include "sway/commands.h" | 10 | #include "sway/input/keyboard.h" |
11 | #include "sway/input/seat.h" | ||
11 | #include "log.h" | 12 | #include "log.h" |
12 | 13 | ||
13 | /** | 14 | /** |
@@ -88,11 +89,13 @@ static void get_active_binding(const struct sway_shortcut_state *state, | |||
88 | uint32_t modifiers, bool release, bool locked) { | 89 | uint32_t modifiers, bool release, bool locked) { |
89 | for (int i = 0; i < bindings->length; ++i) { | 90 | for (int i = 0; i < bindings->length; ++i) { |
90 | struct sway_binding *binding = bindings->items[i]; | 91 | struct sway_binding *binding = bindings->items[i]; |
92 | bool binding_locked = binding->flags & BINDING_LOCKED; | ||
93 | bool binding_release = binding->flags & BINDING_RELEASE; | ||
91 | 94 | ||
92 | if (modifiers ^ binding->modifiers || | 95 | if (modifiers ^ binding->modifiers || |
93 | state->npressed != (size_t)binding->keys->length || | 96 | state->npressed != (size_t)binding->keys->length || |
94 | locked > binding->locked || | 97 | release != binding_release || |
95 | release != binding->release) { | 98 | locked > binding_locked) { |
96 | continue; | 99 | continue; |
97 | } | 100 | } |
98 | 101 | ||
@@ -119,23 +122,6 @@ static void get_active_binding(const struct sway_shortcut_state *state, | |||
119 | } | 122 | } |
120 | 123 | ||
121 | /** | 124 | /** |
122 | * Execute the command associated to a binding | ||
123 | */ | ||
124 | static void keyboard_execute_command(struct sway_keyboard *keyboard, | ||
125 | struct sway_binding *binding) { | ||
126 | wlr_log(WLR_DEBUG, "running command for binding: %s", | ||
127 | binding->command); | ||
128 | config->handler_context.seat = keyboard->seat_device->sway_seat; | ||
129 | struct cmd_results *results = execute_command(binding->command, NULL); | ||
130 | transaction_commit_dirty(); | ||
131 | if (results->status != CMD_SUCCESS) { | ||
132 | wlr_log(WLR_DEBUG, "could not run command for binding: %s (%s)", | ||
133 | binding->command, results->error); | ||
134 | } | ||
135 | free_cmd_results(results); | ||
136 | } | ||
137 | |||
138 | /** | ||
139 | * Execute a built-in, hardcoded compositor binding. These are triggered from a | 125 | * Execute a built-in, hardcoded compositor binding. These are triggered from a |
140 | * single keysym. | 126 | * single keysym. |
141 | * | 127 | * |
@@ -211,12 +197,13 @@ static size_t keyboard_keysyms_raw(struct sway_keyboard *keyboard, | |||
211 | static void handle_keyboard_key(struct wl_listener *listener, void *data) { | 197 | static void handle_keyboard_key(struct wl_listener *listener, void *data) { |
212 | struct sway_keyboard *keyboard = | 198 | struct sway_keyboard *keyboard = |
213 | wl_container_of(listener, keyboard, keyboard_key); | 199 | wl_container_of(listener, keyboard, keyboard_key); |
214 | struct wlr_seat *wlr_seat = keyboard->seat_device->sway_seat->wlr_seat; | 200 | struct sway_seat* seat = keyboard->seat_device->sway_seat; |
201 | struct wlr_seat *wlr_seat = seat->wlr_seat; | ||
215 | struct wlr_input_device *wlr_device = | 202 | struct wlr_input_device *wlr_device = |
216 | keyboard->seat_device->input_device->wlr_device; | 203 | keyboard->seat_device->input_device->wlr_device; |
217 | wlr_idle_notify_activity(keyboard->seat_device->sway_seat->input->server->idle, wlr_seat); | 204 | wlr_idle_notify_activity(seat->input->server->idle, wlr_seat); |
218 | struct wlr_event_keyboard_key *event = data; | 205 | struct wlr_event_keyboard_key *event = data; |
219 | bool input_inhibited = keyboard->seat_device->sway_seat->exclusive_client != NULL; | 206 | bool input_inhibited = seat->exclusive_client != NULL; |
220 | 207 | ||
221 | // Identify new keycode, raw keysym(s), and translated keysym(s) | 208 | // Identify new keycode, raw keysym(s), and translated keysym(s) |
222 | xkb_keycode_t keycode = event->keycode + 8; | 209 | xkb_keycode_t keycode = event->keycode + 8; |
@@ -266,7 +253,7 @@ static void handle_keyboard_key(struct wl_listener *listener, void *data) { | |||
266 | // Execute stored release binding once no longer active | 253 | // Execute stored release binding once no longer active |
267 | if (keyboard->held_binding && binding_released != keyboard->held_binding && | 254 | if (keyboard->held_binding && binding_released != keyboard->held_binding && |
268 | event->state == WLR_KEY_RELEASED) { | 255 | event->state == WLR_KEY_RELEASED) { |
269 | keyboard_execute_command(keyboard, keyboard->held_binding); | 256 | seat_execute_command(seat, keyboard->held_binding); |
270 | handled = true; | 257 | handled = true; |
271 | } | 258 | } |
272 | if (binding_released != keyboard->held_binding) { | 259 | if (binding_released != keyboard->held_binding) { |
@@ -277,6 +264,7 @@ static void handle_keyboard_key(struct wl_listener *listener, void *data) { | |||
277 | } | 264 | } |
278 | 265 | ||
279 | // Identify and execute active pressed binding | 266 | // Identify and execute active pressed binding |
267 | struct sway_binding *next_repeat_binding = NULL; | ||
280 | if (event->state == WLR_KEY_PRESSED) { | 268 | if (event->state == WLR_KEY_PRESSED) { |
281 | struct sway_binding *binding_pressed = NULL; | 269 | struct sway_binding *binding_pressed = NULL; |
282 | get_active_binding(&keyboard->state_keycodes, | 270 | get_active_binding(&keyboard->state_keycodes, |
@@ -290,8 +278,23 @@ static void handle_keyboard_key(struct wl_listener *listener, void *data) { | |||
290 | raw_modifiers, false, input_inhibited); | 278 | raw_modifiers, false, input_inhibited); |
291 | 279 | ||
292 | if (binding_pressed) { | 280 | if (binding_pressed) { |
293 | keyboard_execute_command(keyboard, binding_pressed); | 281 | seat_execute_command(seat, binding_pressed); |
294 | handled = true; | 282 | handled = true; |
283 | next_repeat_binding = binding_pressed; | ||
284 | } | ||
285 | } | ||
286 | |||
287 | // Set up (or clear) keyboard repeat for a pressed binding | ||
288 | if (next_repeat_binding && wlr_device->keyboard->repeat_info.delay > 0) { | ||
289 | keyboard->repeat_binding = next_repeat_binding; | ||
290 | if (wl_event_source_timer_update(keyboard->key_repeat_source, | ||
291 | wlr_device->keyboard->repeat_info.delay) < 0) { | ||
292 | wlr_log(WLR_DEBUG, "failed to set key repeat timer"); | ||
293 | } | ||
294 | } else if (keyboard->repeat_binding) { | ||
295 | keyboard->repeat_binding = NULL; | ||
296 | if (wl_event_source_timer_update(keyboard->key_repeat_source, 0) < 0) { | ||
297 | wlr_log(WLR_DEBUG, "failed to disarm key repeat timer"); | ||
295 | } | 298 | } |
296 | } | 299 | } |
297 | 300 | ||
@@ -312,6 +315,28 @@ static void handle_keyboard_key(struct wl_listener *listener, void *data) { | |||
312 | wlr_seat_keyboard_notify_key(wlr_seat, event->time_msec, | 315 | wlr_seat_keyboard_notify_key(wlr_seat, event->time_msec, |
313 | event->keycode, event->state); | 316 | event->keycode, event->state); |
314 | } | 317 | } |
318 | |||
319 | transaction_commit_dirty(); | ||
320 | } | ||
321 | |||
322 | static int handle_keyboard_repeat(void *data) { | ||
323 | struct sway_keyboard *keyboard = (struct sway_keyboard *)data; | ||
324 | struct wlr_keyboard *wlr_device = | ||
325 | keyboard->seat_device->input_device->wlr_device->keyboard; | ||
326 | if (keyboard->repeat_binding) { | ||
327 | if (wlr_device->repeat_info.rate > 0) { | ||
328 | // We queue the next event first, as the command might cancel it | ||
329 | if (wl_event_source_timer_update(keyboard->key_repeat_source, | ||
330 | 1000 / wlr_device->repeat_info.rate) < 0) { | ||
331 | wlr_log(WLR_DEBUG, "failed to update key repeat timer"); | ||
332 | } | ||
333 | } | ||
334 | |||
335 | seat_execute_command(keyboard->seat_device->sway_seat, | ||
336 | keyboard->repeat_binding); | ||
337 | transaction_commit_dirty(); | ||
338 | } | ||
339 | return 0; | ||
315 | } | 340 | } |
316 | 341 | ||
317 | static void handle_keyboard_modifiers(struct wl_listener *listener, | 342 | static void handle_keyboard_modifiers(struct wl_listener *listener, |
@@ -339,6 +364,9 @@ struct sway_keyboard *sway_keyboard_create(struct sway_seat *seat, | |||
339 | wl_list_init(&keyboard->keyboard_key.link); | 364 | wl_list_init(&keyboard->keyboard_key.link); |
340 | wl_list_init(&keyboard->keyboard_modifiers.link); | 365 | wl_list_init(&keyboard->keyboard_modifiers.link); |
341 | 366 | ||
367 | keyboard->key_repeat_source = wl_event_loop_add_timer(server.wl_event_loop, | ||
368 | handle_keyboard_repeat, keyboard); | ||
369 | |||
342 | return keyboard; | 370 | return keyboard; |
343 | } | 371 | } |
344 | 372 | ||
@@ -397,6 +425,31 @@ void sway_keyboard_configure(struct sway_keyboard *keyboard) { | |||
397 | keyboard->keymap = keymap; | 425 | keyboard->keymap = keymap; |
398 | wlr_keyboard_set_keymap(wlr_device->keyboard, keyboard->keymap); | 426 | wlr_keyboard_set_keymap(wlr_device->keyboard, keyboard->keymap); |
399 | 427 | ||
428 | xkb_mod_mask_t locked_mods = 0; | ||
429 | if (input_config && input_config->xkb_numlock > 0) { | ||
430 | xkb_mod_index_t mod_index = xkb_map_mod_get_index(keymap, XKB_MOD_NAME_NUM); | ||
431 | if (mod_index != XKB_MOD_INVALID) { | ||
432 | locked_mods |= (uint32_t)1 << mod_index; | ||
433 | } | ||
434 | } | ||
435 | if (input_config && input_config->xkb_capslock > 0) { | ||
436 | xkb_mod_index_t mod_index = xkb_map_mod_get_index(keymap, XKB_MOD_NAME_CAPS); | ||
437 | if (mod_index != XKB_MOD_INVALID) { | ||
438 | locked_mods |= (uint32_t)1 << mod_index; | ||
439 | } | ||
440 | } | ||
441 | if (locked_mods) { | ||
442 | wlr_keyboard_notify_modifiers(wlr_device->keyboard, 0, 0, locked_mods, 0); | ||
443 | uint32_t leds = 0; | ||
444 | for (uint32_t i = 0; i < WLR_LED_COUNT; ++i) { | ||
445 | if (xkb_state_led_index_is_active(wlr_device->keyboard->xkb_state, | ||
446 | wlr_device->keyboard->led_indexes[i])) { | ||
447 | leds |= (1 << i); | ||
448 | } | ||
449 | } | ||
450 | wlr_keyboard_led_update(wlr_device->keyboard, leds); | ||
451 | } | ||
452 | |||
400 | if (input_config && input_config->repeat_delay != INT_MIN | 453 | if (input_config && input_config->repeat_delay != INT_MIN |
401 | && input_config->repeat_rate != INT_MIN) { | 454 | && input_config->repeat_rate != INT_MIN) { |
402 | wlr_keyboard_set_repeat_info(wlr_device->keyboard, | 455 | wlr_keyboard_set_repeat_info(wlr_device->keyboard, |
@@ -427,5 +480,6 @@ void sway_keyboard_destroy(struct sway_keyboard *keyboard) { | |||
427 | } | 480 | } |
428 | wl_list_remove(&keyboard->keyboard_key.link); | 481 | wl_list_remove(&keyboard->keyboard_key.link); |
429 | wl_list_remove(&keyboard->keyboard_modifiers.link); | 482 | wl_list_remove(&keyboard->keyboard_modifiers.link); |
483 | wl_event_source_remove(keyboard->key_repeat_source); | ||
430 | free(keyboard); | 484 | free(keyboard); |
431 | } | 485 | } |
diff --git a/sway/input/seat.c b/sway/input/seat.c index e77d88a8..dd4d5c3b 100644 --- a/sway/input/seat.c +++ b/sway/input/seat.c | |||
@@ -1,12 +1,19 @@ | |||
1 | #define _XOPEN_SOURCE 700 | 1 | #define _XOPEN_SOURCE 700 |
2 | #define _POSIX_C_SOURCE 199309L | 2 | #define _POSIX_C_SOURCE 199309L |
3 | #include <assert.h> | 3 | #include <assert.h> |
4 | #include <errno.h> | ||
5 | #ifdef __linux__ | ||
6 | #include <linux/input-event-codes.h> | ||
7 | #elif __FreeBSD__ | ||
8 | #include <dev/evdev/input-event-codes.h> | ||
9 | #endif | ||
4 | #include <strings.h> | 10 | #include <strings.h> |
5 | #include <time.h> | 11 | #include <time.h> |
6 | #include <wlr/types/wlr_cursor.h> | 12 | #include <wlr/types/wlr_cursor.h> |
7 | #include <wlr/types/wlr_output_layout.h> | 13 | #include <wlr/types/wlr_output_layout.h> |
8 | #include <wlr/types/wlr_xcursor_manager.h> | 14 | #include <wlr/types/wlr_xcursor_manager.h> |
9 | #include "log.h" | 15 | #include "log.h" |
16 | #include "config.h" | ||
10 | #include "sway/debug.h" | 17 | #include "sway/debug.h" |
11 | #include "sway/desktop.h" | 18 | #include "sway/desktop.h" |
12 | #include "sway/input/cursor.h" | 19 | #include "sway/input/cursor.h" |
@@ -98,11 +105,13 @@ static void seat_send_focus(struct sway_container *con, | |||
98 | 105 | ||
99 | if (con->type == C_VIEW | 106 | if (con->type == C_VIEW |
100 | && seat_is_input_allowed(seat, con->sway_view->surface)) { | 107 | && seat_is_input_allowed(seat, con->sway_view->surface)) { |
108 | #ifdef HAVE_XWAYLAND | ||
101 | if (con->sway_view->type == SWAY_VIEW_XWAYLAND) { | 109 | if (con->sway_view->type == SWAY_VIEW_XWAYLAND) { |
102 | struct wlr_xwayland *xwayland = | 110 | struct wlr_xwayland *xwayland = |
103 | seat->input->server->xwayland.wlr_xwayland; | 111 | seat->input->server->xwayland.wlr_xwayland; |
104 | wlr_xwayland_set_seat(xwayland, seat->wlr_seat); | 112 | wlr_xwayland_set_seat(xwayland, seat->wlr_seat); |
105 | } | 113 | } |
114 | #endif | ||
106 | struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(seat->wlr_seat); | 115 | struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(seat->wlr_seat); |
107 | if (keyboard) { | 116 | if (keyboard) { |
108 | wlr_seat_keyboard_notify_enter(seat->wlr_seat, | 117 | wlr_seat_keyboard_notify_enter(seat->wlr_seat, |
@@ -116,12 +125,14 @@ static void seat_send_focus(struct sway_container *con, | |||
116 | } | 125 | } |
117 | 126 | ||
118 | static struct sway_container *seat_get_focus_by_type(struct sway_seat *seat, | 127 | static struct sway_container *seat_get_focus_by_type(struct sway_seat *seat, |
119 | struct sway_container *container, enum sway_container_type type) { | 128 | struct sway_container *container, enum sway_container_type type, |
129 | bool only_tiling) { | ||
120 | if (container->type == C_VIEW) { | 130 | if (container->type == C_VIEW) { |
121 | return container; | 131 | return container; |
122 | } | 132 | } |
123 | 133 | ||
124 | struct sway_container *floating = container->type == C_WORKSPACE ? | 134 | struct sway_container *floating = |
135 | container->type == C_WORKSPACE && !only_tiling ? | ||
125 | container->sway_workspace->floating : NULL; | 136 | container->sway_workspace->floating : NULL; |
126 | if (container->children->length == 0 && | 137 | if (container->children->length == 0 && |
127 | (!floating || floating->children->length == 0)) { | 138 | (!floating || floating->children->length == 0)) { |
@@ -135,6 +146,10 @@ static struct sway_container *seat_get_focus_by_type(struct sway_seat *seat, | |||
135 | } | 146 | } |
136 | 147 | ||
137 | if (container_has_child(container, current->container)) { | 148 | if (container_has_child(container, current->container)) { |
149 | if (only_tiling && | ||
150 | container_is_floating_or_child(current->container)) { | ||
151 | continue; | ||
152 | } | ||
138 | return current->container; | 153 | return current->container; |
139 | } | 154 | } |
140 | if (floating && container_has_child(floating, current->container)) { | 155 | if (floating && container_has_child(floating, current->container)) { |
@@ -161,7 +176,7 @@ void seat_focus_inactive_children_for_each(struct sway_seat *seat, | |||
161 | 176 | ||
162 | struct sway_container *seat_get_focus_inactive_view(struct sway_seat *seat, | 177 | struct sway_container *seat_get_focus_inactive_view(struct sway_seat *seat, |
163 | struct sway_container *container) { | 178 | struct sway_container *container) { |
164 | return seat_get_focus_by_type(seat, container, C_VIEW); | 179 | return seat_get_focus_by_type(seat, container, C_VIEW, false); |
165 | } | 180 | } |
166 | 181 | ||
167 | static void handle_seat_container_destroy(struct wl_listener *listener, | 182 | static void handle_seat_container_destroy(struct wl_listener *listener, |
@@ -183,7 +198,7 @@ static void handle_seat_container_destroy(struct wl_listener *listener, | |||
183 | if (set_focus) { | 198 | if (set_focus) { |
184 | struct sway_container *next_focus = NULL; | 199 | struct sway_container *next_focus = NULL; |
185 | while (next_focus == NULL) { | 200 | while (next_focus == NULL) { |
186 | next_focus = seat_get_focus_by_type(seat, parent, C_VIEW); | 201 | next_focus = seat_get_focus_by_type(seat, parent, C_VIEW, false); |
187 | 202 | ||
188 | if (next_focus == NULL && parent->type == C_WORKSPACE) { | 203 | if (next_focus == NULL && parent->type == C_WORKSPACE) { |
189 | next_focus = parent; | 204 | next_focus = parent; |
@@ -348,6 +363,7 @@ struct sway_seat *seat_create(struct sway_input_manager *input, | |||
348 | free(seat); | 363 | free(seat); |
349 | return NULL; | 364 | return NULL; |
350 | } | 365 | } |
366 | seat->wlr_seat->data = seat; | ||
351 | 367 | ||
352 | seat->cursor = sway_cursor_create(seat); | 368 | seat->cursor = sway_cursor_create(seat); |
353 | if (!seat->cursor) { | 369 | if (!seat->cursor) { |
@@ -377,7 +393,6 @@ struct sway_seat *seat_create(struct sway_input_manager *input, | |||
377 | WL_SEAT_CAPABILITY_POINTER | | 393 | WL_SEAT_CAPABILITY_POINTER | |
378 | WL_SEAT_CAPABILITY_TOUCH); | 394 | WL_SEAT_CAPABILITY_TOUCH); |
379 | 395 | ||
380 | seat_configure_xcursor(seat); | ||
381 | 396 | ||
382 | wl_list_insert(&input->seats, &seat->link); | 397 | wl_list_insert(&input->seats, &seat->link); |
383 | 398 | ||
@@ -422,6 +437,7 @@ static void seat_apply_input_config(struct sway_seat *seat, | |||
422 | 437 | ||
423 | static void seat_configure_pointer(struct sway_seat *seat, | 438 | static void seat_configure_pointer(struct sway_seat *seat, |
424 | struct sway_seat_device *sway_device) { | 439 | struct sway_seat_device *sway_device) { |
440 | seat_configure_xcursor(seat); | ||
425 | wlr_cursor_attach_input_device(seat->cursor->cursor, | 441 | wlr_cursor_attach_input_device(seat->cursor->cursor, |
426 | sway_device->input_device->wlr_device); | 442 | sway_device->input_device->wlr_device); |
427 | seat_apply_input_config(seat, sway_device); | 443 | seat_apply_input_config(seat, sway_device); |
@@ -601,7 +617,7 @@ static int handle_urgent_timeout(void *data) { | |||
601 | } | 617 | } |
602 | 618 | ||
603 | void seat_set_focus_warp(struct sway_seat *seat, | 619 | void seat_set_focus_warp(struct sway_seat *seat, |
604 | struct sway_container *container, bool warp) { | 620 | struct sway_container *container, bool warp, bool notify) { |
605 | if (seat->focused_layer) { | 621 | if (seat->focused_layer) { |
606 | return; | 622 | return; |
607 | } | 623 | } |
@@ -622,7 +638,7 @@ void seat_set_focus_warp(struct sway_seat *seat, | |||
622 | 638 | ||
623 | if (last_workspace && last_workspace == new_workspace | 639 | if (last_workspace && last_workspace == new_workspace |
624 | && last_workspace->sway_workspace->fullscreen | 640 | && last_workspace->sway_workspace->fullscreen |
625 | && !container->sway_view->is_fullscreen) { | 641 | && container && !container_is_fullscreen_or_child(container)) { |
626 | return; | 642 | return; |
627 | } | 643 | } |
628 | 644 | ||
@@ -639,7 +655,7 @@ void seat_set_focus_warp(struct sway_seat *seat, | |||
639 | struct sway_container *new_output_last_ws = NULL; | 655 | struct sway_container *new_output_last_ws = NULL; |
640 | if (last_output && new_output && last_output != new_output) { | 656 | if (last_output && new_output && last_output != new_output) { |
641 | new_output_last_ws = | 657 | new_output_last_ws = |
642 | seat_get_focus_by_type(seat, new_output, C_WORKSPACE); | 658 | seat_get_focus_by_type(seat, new_output, C_WORKSPACE, false); |
643 | } | 659 | } |
644 | 660 | ||
645 | if (container && container->parent) { | 661 | if (container && container->parent) { |
@@ -686,8 +702,14 @@ void seat_set_focus_warp(struct sway_seat *seat, | |||
686 | config->urgent_timeout > 0) { | 702 | config->urgent_timeout > 0) { |
687 | view->urgent_timer = wl_event_loop_add_timer(server.wl_event_loop, | 703 | view->urgent_timer = wl_event_loop_add_timer(server.wl_event_loop, |
688 | handle_urgent_timeout, view); | 704 | handle_urgent_timeout, view); |
689 | wl_event_source_timer_update(view->urgent_timer, | 705 | if (view->urgent_timer) { |
690 | config->urgent_timeout); | 706 | wl_event_source_timer_update(view->urgent_timer, |
707 | config->urgent_timeout); | ||
708 | } else { | ||
709 | wlr_log(WLR_ERROR, "Unable to create urgency timer (%s)", | ||
710 | strerror(errno)); | ||
711 | handle_urgent_timeout(view); | ||
712 | } | ||
691 | } else { | 713 | } else { |
692 | view_set_urgent(view, false); | 714 | view_set_urgent(view, false); |
693 | } | 715 | } |
@@ -715,9 +737,18 @@ void seat_set_focus_warp(struct sway_seat *seat, | |||
715 | } | 737 | } |
716 | } | 738 | } |
717 | 739 | ||
740 | // Close any popups on the old focus | ||
741 | if (last_focus && last_focus != container) { | ||
742 | if (last_focus->type == C_VIEW) { | ||
743 | view_close_popups(last_focus->sway_view); | ||
744 | } | ||
745 | } | ||
746 | |||
718 | if (last_focus) { | 747 | if (last_focus) { |
719 | if (last_workspace) { | 748 | if (last_workspace) { |
720 | ipc_event_workspace(last_workspace, container, "focus"); | 749 | if (notify && last_workspace != new_workspace) { |
750 | ipc_event_workspace(last_workspace, new_workspace, "focus"); | ||
751 | } | ||
721 | if (!workspace_is_visible(last_workspace) | 752 | if (!workspace_is_visible(last_workspace) |
722 | && workspace_is_empty(last_workspace)) { | 753 | && workspace_is_empty(last_workspace)) { |
723 | if (last_workspace == last_focus) { | 754 | if (last_workspace == last_focus) { |
@@ -744,8 +775,12 @@ void seat_set_focus_warp(struct sway_seat *seat, | |||
744 | } | 775 | } |
745 | } | 776 | } |
746 | 777 | ||
747 | if (last_focus != NULL) { | 778 | if (container) { |
748 | cursor_send_pointer_motion(seat->cursor, 0, true); | 779 | if (container->type == C_VIEW) { |
780 | ipc_event_window(container, "focus"); | ||
781 | } else if (container->type == C_WORKSPACE) { | ||
782 | ipc_event_workspace(NULL, container, "focus"); | ||
783 | } | ||
749 | } | 784 | } |
750 | 785 | ||
751 | seat->has_focus = (container != NULL); | 786 | seat->has_focus = (container != NULL); |
@@ -755,7 +790,7 @@ void seat_set_focus_warp(struct sway_seat *seat, | |||
755 | 790 | ||
756 | void seat_set_focus(struct sway_seat *seat, | 791 | void seat_set_focus(struct sway_seat *seat, |
757 | struct sway_container *container) { | 792 | struct sway_container *container) { |
758 | seat_set_focus_warp(seat, container, true); | 793 | seat_set_focus_warp(seat, container, true, true); |
759 | } | 794 | } |
760 | 795 | ||
761 | void seat_set_focus_surface(struct sway_seat *seat, | 796 | void seat_set_focus_surface(struct sway_seat *seat, |
@@ -848,7 +883,12 @@ void seat_set_exclusive_client(struct sway_seat *seat, | |||
848 | 883 | ||
849 | struct sway_container *seat_get_focus_inactive(struct sway_seat *seat, | 884 | struct sway_container *seat_get_focus_inactive(struct sway_seat *seat, |
850 | struct sway_container *container) { | 885 | struct sway_container *container) { |
851 | return seat_get_focus_by_type(seat, container, C_TYPES); | 886 | return seat_get_focus_by_type(seat, container, C_TYPES, false); |
887 | } | ||
888 | |||
889 | struct sway_container *seat_get_focus_inactive_tiling(struct sway_seat *seat, | ||
890 | struct sway_container *container) { | ||
891 | return seat_get_focus_by_type(seat, container, C_TYPES, true); | ||
852 | } | 892 | } |
853 | 893 | ||
854 | struct sway_container *seat_get_active_child(struct sway_seat *seat, | 894 | struct sway_container *seat_get_active_child(struct sway_seat *seat, |
@@ -894,3 +934,68 @@ struct seat_config *seat_get_config(struct sway_seat *seat) { | |||
894 | 934 | ||
895 | return NULL; | 935 | return NULL; |
896 | } | 936 | } |
937 | |||
938 | void seat_begin_move(struct sway_seat *seat, struct sway_container *con, | ||
939 | uint32_t button) { | ||
940 | if (!seat->cursor) { | ||
941 | wlr_log(WLR_DEBUG, "Ignoring move request due to no cursor device"); | ||
942 | return; | ||
943 | } | ||
944 | seat->operation = OP_MOVE; | ||
945 | seat->op_container = con; | ||
946 | seat->op_button = button; | ||
947 | cursor_set_image(seat->cursor, "grab", NULL); | ||
948 | } | ||
949 | |||
950 | void seat_begin_resize(struct sway_seat *seat, struct sway_container *con, | ||
951 | uint32_t button, enum wlr_edges edge) { | ||
952 | if (!seat->cursor) { | ||
953 | wlr_log(WLR_DEBUG, "Ignoring resize request due to no cursor device"); | ||
954 | return; | ||
955 | } | ||
956 | struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(seat->wlr_seat); | ||
957 | seat->operation = OP_RESIZE; | ||
958 | seat->op_container = con; | ||
959 | seat->op_resize_preserve_ratio = keyboard && | ||
960 | (wlr_keyboard_get_modifiers(keyboard) & WLR_MODIFIER_SHIFT); | ||
961 | seat->op_resize_edge = edge == WLR_EDGE_NONE ? | ||
962 | RESIZE_EDGE_BOTTOM | RESIZE_EDGE_RIGHT : edge; | ||
963 | seat->op_button = button; | ||
964 | seat->op_ref_lx = seat->cursor->cursor->x; | ||
965 | seat->op_ref_ly = seat->cursor->cursor->y; | ||
966 | seat->op_ref_con_lx = con->x; | ||
967 | seat->op_ref_con_ly = con->y; | ||
968 | seat->op_ref_width = con->width; | ||
969 | seat->op_ref_height = con->height; | ||
970 | |||
971 | const char *image = edge == WLR_EDGE_NONE ? | ||
972 | "se-resize" : wlr_xcursor_get_resize_name(edge); | ||
973 | cursor_set_image(seat->cursor, image, NULL); | ||
974 | } | ||
975 | |||
976 | void seat_end_mouse_operation(struct sway_seat *seat) { | ||
977 | switch (seat->operation) { | ||
978 | case OP_MOVE: | ||
979 | { | ||
980 | // We "move" the container to its own location so it discovers its | ||
981 | // output again. | ||
982 | struct sway_container *con = seat->op_container; | ||
983 | container_floating_move_to(con, con->x, con->y); | ||
984 | } | ||
985 | case OP_RESIZE: | ||
986 | // Don't need to do anything here. | ||
987 | break; | ||
988 | case OP_NONE: | ||
989 | break; | ||
990 | } | ||
991 | seat->operation = OP_NONE; | ||
992 | seat->op_container = NULL; | ||
993 | cursor_set_image(seat->cursor, "left_ptr", NULL); | ||
994 | } | ||
995 | |||
996 | void seat_pointer_notify_button(struct sway_seat *seat, uint32_t time_msec, | ||
997 | uint32_t button, enum wlr_button_state state) { | ||
998 | seat->last_button = button; | ||
999 | seat->last_button_serial = wlr_seat_pointer_notify_button(seat->wlr_seat, | ||
1000 | time_msec, button, state); | ||
1001 | } | ||
diff --git a/sway/ipc-json.c b/sway/ipc-json.c index c49ea47e..4c2bcc98 100644 --- a/sway/ipc-json.c +++ b/sway/ipc-json.c | |||
@@ -201,6 +201,15 @@ static void ipc_json_describe_view(struct sway_container *c, json_object *object | |||
201 | bool urgent = c->type == C_VIEW ? | 201 | bool urgent = c->type == C_VIEW ? |
202 | view_is_urgent(c->sway_view) : container_has_urgent_child(c); | 202 | view_is_urgent(c->sway_view) : container_has_urgent_child(c); |
203 | json_object_object_add(object, "urgent", json_object_new_boolean(urgent)); | 203 | json_object_object_add(object, "urgent", json_object_new_boolean(urgent)); |
204 | |||
205 | if (c->type == C_VIEW) { | ||
206 | json_object *marks = json_object_new_array(); | ||
207 | list_t *view_marks = c->sway_view->marks; | ||
208 | for (int i = 0; i < view_marks->length; ++i) { | ||
209 | json_object_array_add(marks, json_object_new_string(view_marks->items[i])); | ||
210 | } | ||
211 | json_object_object_add(object, "marks", marks); | ||
212 | } | ||
204 | } | 213 | } |
205 | 214 | ||
206 | static void focus_inactive_children_iterator(struct sway_container *c, void *data) { | 215 | static void focus_inactive_children_iterator(struct sway_container *c, void *data) { |
diff --git a/sway/ipc-server.c b/sway/ipc-server.c index be703915..7d2d8969 100644 --- a/sway/ipc-server.c +++ b/sway/ipc-server.c | |||
@@ -3,12 +3,18 @@ | |||
3 | // Any value will hide SOCK_CLOEXEC on FreeBSD (__BSD_VISIBLE=0) | 3 | // Any value will hide SOCK_CLOEXEC on FreeBSD (__BSD_VISIBLE=0) |
4 | #define _XOPEN_SOURCE 700 | 4 | #define _XOPEN_SOURCE 700 |
5 | #endif | 5 | #endif |
6 | #ifdef __linux__ | ||
7 | #include <linux/input-event-codes.h> | ||
8 | #elif __FreeBSD__ | ||
9 | #include <dev/evdev/input-event-codes.h> | ||
10 | #endif | ||
6 | #include <assert.h> | 11 | #include <assert.h> |
7 | #include <errno.h> | 12 | #include <errno.h> |
8 | #include <fcntl.h> | 13 | #include <fcntl.h> |
9 | #include <json-c/json.h> | 14 | #include <json-c/json.h> |
10 | #include <stdbool.h> | 15 | #include <stdbool.h> |
11 | #include <stdint.h> | 16 | #include <stdint.h> |
17 | #include <stdio.h> | ||
12 | #include <stdlib.h> | 18 | #include <stdlib.h> |
13 | #include <string.h> | 19 | #include <string.h> |
14 | #include <sys/socket.h> | 20 | #include <sys/socket.h> |
@@ -28,6 +34,7 @@ | |||
28 | #include "sway/tree/view.h" | 34 | #include "sway/tree/view.h" |
29 | #include "list.h" | 35 | #include "list.h" |
30 | #include "log.h" | 36 | #include "log.h" |
37 | #include "util.h" | ||
31 | 38 | ||
32 | static int ipc_socket = -1; | 39 | static int ipc_socket = -1; |
33 | static struct wl_event_source *ipc_event_source = NULL; | 40 | static struct wl_event_source *ipc_event_source = NULL; |
@@ -291,13 +298,11 @@ void ipc_event_workspace(struct sway_container *old, | |||
291 | wlr_log(WLR_DEBUG, "Sending workspace::%s event", change); | 298 | wlr_log(WLR_DEBUG, "Sending workspace::%s event", change); |
292 | json_object *obj = json_object_new_object(); | 299 | json_object *obj = json_object_new_object(); |
293 | json_object_object_add(obj, "change", json_object_new_string(change)); | 300 | json_object_object_add(obj, "change", json_object_new_string(change)); |
294 | if (strcmp("focus", change) == 0) { | 301 | if (old) { |
295 | if (old) { | 302 | json_object_object_add(obj, "old", |
296 | json_object_object_add(obj, "old", | 303 | ipc_json_describe_container_recursive(old)); |
297 | ipc_json_describe_container_recursive(old)); | 304 | } else { |
298 | } else { | 305 | json_object_object_add(obj, "old", NULL); |
299 | json_object_object_add(obj, "old", NULL); | ||
300 | } | ||
301 | } | 306 | } |
302 | 307 | ||
303 | if (new) { | 308 | if (new) { |
@@ -353,6 +358,104 @@ void ipc_event_mode(const char *mode, bool pango) { | |||
353 | json_object_put(obj); | 358 | json_object_put(obj); |
354 | } | 359 | } |
355 | 360 | ||
361 | void ipc_event_shutdown(const char *reason) { | ||
362 | if (!ipc_has_event_listeners(IPC_EVENT_SHUTDOWN)) { | ||
363 | return; | ||
364 | } | ||
365 | wlr_log(WLR_DEBUG, "Sending shutdown::%s event", reason); | ||
366 | |||
367 | json_object *json = json_object_new_object(); | ||
368 | json_object_object_add(json, "change", json_object_new_string(reason)); | ||
369 | |||
370 | const char *json_string = json_object_to_json_string(json); | ||
371 | ipc_send_event(json_string, IPC_EVENT_SHUTDOWN); | ||
372 | json_object_put(json); | ||
373 | } | ||
374 | |||
375 | void ipc_event_binding(struct sway_binding *binding) { | ||
376 | if (!ipc_has_event_listeners(IPC_EVENT_BINDING)) { | ||
377 | return; | ||
378 | } | ||
379 | wlr_log(WLR_DEBUG, "Sending binding event"); | ||
380 | |||
381 | json_object *json_binding = json_object_new_object(); | ||
382 | json_object_object_add(json_binding, "command", json_object_new_string(binding->command)); | ||
383 | |||
384 | const char *names[10]; | ||
385 | int len = get_modifier_names(names, binding->modifiers); | ||
386 | json_object *modifiers = json_object_new_array(); | ||
387 | for (int i = 0; i < len; ++i) { | ||
388 | json_object_array_add(modifiers, json_object_new_string(names[i])); | ||
389 | } | ||
390 | json_object_object_add(json_binding, "event_state_mask", modifiers); | ||
391 | |||
392 | json_object *input_codes = json_object_new_array(); | ||
393 | int input_code = 0; | ||
394 | json_object *symbols = json_object_new_array(); | ||
395 | json_object *symbol = NULL; | ||
396 | |||
397 | if (binding->type == BINDING_KEYCODE) { // bindcode: populate input_codes | ||
398 | uint32_t keycode; | ||
399 | for (int i = 0; i < binding->keys->length; ++i) { | ||
400 | keycode = *(uint32_t *)binding->keys->items[i]; | ||
401 | json_object_array_add(input_codes, json_object_new_int(keycode)); | ||
402 | if (i == 0) { | ||
403 | input_code = keycode; | ||
404 | } | ||
405 | } | ||
406 | } else { // bindsym/mouse: populate symbols | ||
407 | uint32_t keysym; | ||
408 | char buffer[64]; | ||
409 | for (int i = 0; i < binding->keys->length; ++i) { | ||
410 | keysym = *(uint32_t *)binding->keys->items[i]; | ||
411 | if (keysym >= BTN_LEFT && keysym <= BTN_LEFT + 8) { | ||
412 | snprintf(buffer, 64, "button%u", keysym - BTN_LEFT + 1); | ||
413 | } else if (xkb_keysym_get_name(keysym, buffer, 64) < 0) { | ||
414 | continue; | ||
415 | } | ||
416 | |||
417 | json_object *str = json_object_new_string(buffer); | ||
418 | if (i == 0) { | ||
419 | // str is owned by both symbol and symbols. Make sure | ||
420 | // to bump the ref count. | ||
421 | json_object_array_add(symbols, json_object_get(str)); | ||
422 | symbol = str; | ||
423 | } else { | ||
424 | json_object_array_add(symbols, str); | ||
425 | } | ||
426 | } | ||
427 | } | ||
428 | |||
429 | json_object_object_add(json_binding, "input_codes", input_codes); | ||
430 | json_object_object_add(json_binding, "input_code", json_object_new_int(input_code)); | ||
431 | json_object_object_add(json_binding, "symbols", symbols); | ||
432 | json_object_object_add(json_binding, "symbol", symbol); | ||
433 | json_object_object_add(json_binding, "input_type", binding->type == BINDING_MOUSE ? | ||
434 | json_object_new_string("mouse") : json_object_new_string("keyboard")); | ||
435 | |||
436 | json_object *json = json_object_new_object(); | ||
437 | json_object_object_add(json, "change", json_object_new_string("run")); | ||
438 | json_object_object_add(json, "binding", json_binding); | ||
439 | const char *json_string = json_object_to_json_string(json); | ||
440 | ipc_send_event(json_string, IPC_EVENT_BINDING); | ||
441 | json_object_put(json); | ||
442 | } | ||
443 | |||
444 | static void ipc_event_tick(const char *payload) { | ||
445 | if (!ipc_has_event_listeners(IPC_EVENT_TICK)) { | ||
446 | return; | ||
447 | } | ||
448 | wlr_log(WLR_DEBUG, "Sending tick event"); | ||
449 | |||
450 | json_object *json = json_object_new_object(); | ||
451 | json_object_object_add(json, "first", json_object_new_boolean(false)); | ||
452 | json_object_object_add(json, "payload", json_object_new_string(payload)); | ||
453 | |||
454 | const char *json_string = json_object_to_json_string(json); | ||
455 | ipc_send_event(json_string, IPC_EVENT_TICK); | ||
456 | json_object_put(json); | ||
457 | } | ||
458 | |||
356 | int ipc_client_handle_writable(int client_fd, uint32_t mask, void *data) { | 459 | int ipc_client_handle_writable(int client_fd, uint32_t mask, void *data) { |
357 | struct ipc_client *client = data; | 460 | struct ipc_client *client = data; |
358 | 461 | ||
@@ -494,6 +597,13 @@ void ipc_client_handle_command(struct ipc_client *client) { | |||
494 | goto exit_cleanup; | 597 | goto exit_cleanup; |
495 | } | 598 | } |
496 | 599 | ||
600 | case IPC_SEND_TICK: | ||
601 | { | ||
602 | ipc_event_tick(buf); | ||
603 | ipc_send_reply(client, "{\"success\": true}", 17); | ||
604 | goto exit_cleanup; | ||
605 | } | ||
606 | |||
497 | case IPC_GET_OUTPUTS: | 607 | case IPC_GET_OUTPUTS: |
498 | { | 608 | { |
499 | json_object *outputs = json_object_new_array(); | 609 | json_object *outputs = json_object_new_array(); |
@@ -540,6 +650,7 @@ void ipc_client_handle_command(struct ipc_client *client) { | |||
540 | goto exit_cleanup; | 650 | goto exit_cleanup; |
541 | } | 651 | } |
542 | 652 | ||
653 | bool is_tick = false; | ||
543 | // parse requested event types | 654 | // parse requested event types |
544 | for (size_t i = 0; i < json_object_array_length(request); i++) { | 655 | for (size_t i = 0; i < json_object_array_length(request); i++) { |
545 | const char *event_type = json_object_get_string(json_object_array_get_idx(request, i)); | 656 | const char *event_type = json_object_get_string(json_object_array_get_idx(request, i)); |
@@ -549,12 +660,15 @@ void ipc_client_handle_command(struct ipc_client *client) { | |||
549 | client->subscribed_events |= event_mask(IPC_EVENT_BARCONFIG_UPDATE); | 660 | client->subscribed_events |= event_mask(IPC_EVENT_BARCONFIG_UPDATE); |
550 | } else if (strcmp(event_type, "mode") == 0) { | 661 | } else if (strcmp(event_type, "mode") == 0) { |
551 | client->subscribed_events |= event_mask(IPC_EVENT_MODE); | 662 | client->subscribed_events |= event_mask(IPC_EVENT_MODE); |
663 | } else if (strcmp(event_type, "shutdown") == 0) { | ||
664 | client->subscribed_events |= event_mask(IPC_EVENT_SHUTDOWN); | ||
552 | } else if (strcmp(event_type, "window") == 0) { | 665 | } else if (strcmp(event_type, "window") == 0) { |
553 | client->subscribed_events |= event_mask(IPC_EVENT_WINDOW); | 666 | client->subscribed_events |= event_mask(IPC_EVENT_WINDOW); |
554 | } else if (strcmp(event_type, "modifier") == 0) { | ||
555 | client->subscribed_events |= event_mask(IPC_EVENT_MODIFIER); | ||
556 | } else if (strcmp(event_type, "binding") == 0) { | 667 | } else if (strcmp(event_type, "binding") == 0) { |
557 | client->subscribed_events |= event_mask(IPC_EVENT_BINDING); | 668 | client->subscribed_events |= event_mask(IPC_EVENT_BINDING); |
669 | } else if (strcmp(event_type, "tick") == 0) { | ||
670 | client->subscribed_events |= event_mask(IPC_EVENT_TICK); | ||
671 | is_tick = true; | ||
558 | } else { | 672 | } else { |
559 | client_valid = | 673 | client_valid = |
560 | ipc_send_reply(client, "{\"success\": false}", 18); | 674 | ipc_send_reply(client, "{\"success\": false}", 18); |
@@ -566,6 +680,10 @@ void ipc_client_handle_command(struct ipc_client *client) { | |||
566 | 680 | ||
567 | json_object_put(request); | 681 | json_object_put(request); |
568 | client_valid = ipc_send_reply(client, "{\"success\": true}", 17); | 682 | client_valid = ipc_send_reply(client, "{\"success\": true}", 17); |
683 | if (is_tick) { | ||
684 | client->current_command = IPC_EVENT_TICK; | ||
685 | ipc_send_reply(client, "{\"first\": true, \"payload\": \"\"}", 30); | ||
686 | } | ||
569 | goto exit_cleanup; | 687 | goto exit_cleanup; |
570 | } | 688 | } |
571 | 689 | ||
diff --git a/sway/main.c b/sway/main.c index a20f1dac..477ffa5a 100644 --- a/sway/main.c +++ b/sway/main.c | |||
@@ -36,6 +36,7 @@ struct sway_server server; | |||
36 | void sway_terminate(int exit_code) { | 36 | void sway_terminate(int exit_code) { |
37 | terminate_request = true; | 37 | terminate_request = true; |
38 | exit_value = exit_code; | 38 | exit_value = exit_code; |
39 | ipc_event_shutdown("exit"); | ||
39 | wl_display_terminate(server.wl_display); | 40 | wl_display_terminate(server.wl_display); |
40 | } | 41 | } |
41 | 42 | ||
diff --git a/sway/meson.build b/sway/meson.build index 09bc40b8..a9503c3b 100644 --- a/sway/meson.build +++ b/sway/meson.build | |||
@@ -7,6 +7,7 @@ sway_sources = files( | |||
7 | 'debug-tree.c', | 7 | 'debug-tree.c', |
8 | 'ipc-json.c', | 8 | 'ipc-json.c', |
9 | 'ipc-server.c', | 9 | 'ipc-server.c', |
10 | 'scratchpad.c', | ||
10 | 'security.c', | 11 | 'security.c', |
11 | 12 | ||
12 | 'desktop/desktop.c', | 13 | 'desktop/desktop.c', |
@@ -17,7 +18,6 @@ sway_sources = files( | |||
17 | 'desktop/transaction.c', | 18 | 'desktop/transaction.c', |
18 | 'desktop/xdg_shell_v6.c', | 19 | 'desktop/xdg_shell_v6.c', |
19 | 'desktop/xdg_shell.c', | 20 | 'desktop/xdg_shell.c', |
20 | 'desktop/xwayland.c', | ||
21 | 21 | ||
22 | 'input/input-manager.c', | 22 | 'input/input-manager.c', |
23 | 'input/seat.c', | 23 | 'input/seat.c', |
@@ -42,6 +42,7 @@ sway_sources = files( | |||
42 | 'commands/exec_always.c', | 42 | 'commands/exec_always.c', |
43 | 'commands/floating.c', | 43 | 'commands/floating.c', |
44 | 'commands/floating_minmax_size.c', | 44 | 'commands/floating_minmax_size.c', |
45 | 'commands/floating_modifier.c', | ||
45 | 'commands/focus.c', | 46 | 'commands/focus.c', |
46 | 'commands/focus_follows_mouse.c', | 47 | 'commands/focus_follows_mouse.c', |
47 | 'commands/focus_wrapping.c', | 48 | 'commands/focus_wrapping.c', |
@@ -66,6 +67,7 @@ sway_sources = files( | |||
66 | 'commands/reload.c', | 67 | 'commands/reload.c', |
67 | 'commands/rename.c', | 68 | 'commands/rename.c', |
68 | 'commands/resize.c', | 69 | 'commands/resize.c', |
70 | 'commands/scratchpad.c', | ||
69 | 'commands/seat.c', | 71 | 'commands/seat.c', |
70 | 'commands/seat/attach.c', | 72 | 'commands/seat/attach.c', |
71 | 'commands/seat/cursor.c', | 73 | 'commands/seat/cursor.c', |
@@ -126,8 +128,10 @@ sway_sources = files( | |||
126 | 'commands/input/scroll_method.c', | 128 | 'commands/input/scroll_method.c', |
127 | 'commands/input/tap.c', | 129 | 'commands/input/tap.c', |
128 | 'commands/input/tap_button_map.c', | 130 | 'commands/input/tap_button_map.c', |
131 | 'commands/input/xkb_capslock.c', | ||
129 | 'commands/input/xkb_layout.c', | 132 | 'commands/input/xkb_layout.c', |
130 | 'commands/input/xkb_model.c', | 133 | 'commands/input/xkb_model.c', |
134 | 'commands/input/xkb_numlock.c', | ||
131 | 'commands/input/xkb_options.c', | 135 | 'commands/input/xkb_options.c', |
132 | 'commands/input/xkb_rules.c', | 136 | 'commands/input/xkb_rules.c', |
133 | 'commands/input/xkb_variant.c', | 137 | 'commands/input/xkb_variant.c', |
@@ -162,10 +166,14 @@ sway_deps = [ | |||
162 | server_protos, | 166 | server_protos, |
163 | wayland_server, | 167 | wayland_server, |
164 | wlroots, | 168 | wlroots, |
165 | xcb, | ||
166 | xkbcommon, | 169 | xkbcommon, |
167 | ] | 170 | ] |
168 | 171 | ||
172 | if get_option('enable-xwayland') | ||
173 | sway_sources += 'desktop/xwayland.c' | ||
174 | sway_deps += xcb | ||
175 | endif | ||
176 | |||
169 | executable( | 177 | executable( |
170 | 'sway', | 178 | 'sway', |
171 | sway_sources, | 179 | sway_sources, |
diff --git a/sway/scratchpad.c b/sway/scratchpad.c new file mode 100644 index 00000000..b7d6fd99 --- /dev/null +++ b/sway/scratchpad.c | |||
@@ -0,0 +1,181 @@ | |||
1 | #define _XOPEN_SOURCE 700 | ||
2 | #include <stdlib.h> | ||
3 | #include <stdio.h> | ||
4 | #include <stdbool.h> | ||
5 | #include "sway/scratchpad.h" | ||
6 | #include "sway/input/seat.h" | ||
7 | #include "sway/tree/arrange.h" | ||
8 | #include "sway/tree/container.h" | ||
9 | #include "sway/tree/view.h" | ||
10 | #include "sway/tree/workspace.h" | ||
11 | #include "list.h" | ||
12 | #include "log.h" | ||
13 | |||
14 | void scratchpad_add_container(struct sway_container *con) { | ||
15 | if (!sway_assert(!con->scratchpad, "Container is already in scratchpad")) { | ||
16 | return; | ||
17 | } | ||
18 | con->scratchpad = true; | ||
19 | list_add(root_container.sway_root->scratchpad, con); | ||
20 | |||
21 | struct sway_container *parent = con->parent; | ||
22 | container_set_floating(con, true); | ||
23 | container_remove_child(con); | ||
24 | arrange_windows(parent); | ||
25 | |||
26 | struct sway_seat *seat = input_manager_current_seat(input_manager); | ||
27 | seat_set_focus(seat, seat_get_focus_inactive(seat, parent)); | ||
28 | } | ||
29 | |||
30 | void scratchpad_remove_container(struct sway_container *con) { | ||
31 | if (!sway_assert(con->scratchpad, "Container is not in scratchpad")) { | ||
32 | return; | ||
33 | } | ||
34 | con->scratchpad = false; | ||
35 | for (int i = 0; i < root_container.sway_root->scratchpad->length; ++i) { | ||
36 | if (root_container.sway_root->scratchpad->items[i] == con) { | ||
37 | list_del(root_container.sway_root->scratchpad, i); | ||
38 | break; | ||
39 | } | ||
40 | } | ||
41 | } | ||
42 | |||
43 | /** | ||
44 | * Show a single scratchpad container. | ||
45 | * The container might be visible on another workspace already. | ||
46 | */ | ||
47 | static void scratchpad_show(struct sway_container *con) { | ||
48 | struct sway_seat *seat = input_manager_current_seat(input_manager); | ||
49 | struct sway_container *ws = seat_get_focus(seat); | ||
50 | if (ws->type != C_WORKSPACE) { | ||
51 | ws = container_parent(ws, C_WORKSPACE); | ||
52 | } | ||
53 | |||
54 | // If the current con or any of its parents are in fullscreen mode, we | ||
55 | // first need to disable it before showing the scratchpad con. | ||
56 | if (ws->sway_workspace->fullscreen) { | ||
57 | container_set_fullscreen(ws->sway_workspace->fullscreen, false); | ||
58 | } | ||
59 | |||
60 | // Show the container | ||
61 | if (con->parent) { | ||
62 | container_remove_child(con); | ||
63 | } | ||
64 | container_add_child(ws->sway_workspace->floating, con); | ||
65 | |||
66 | // Make sure the container's center point overlaps this workspace | ||
67 | double center_lx = con->x + con->width / 2; | ||
68 | double center_ly = con->y + con->height / 2; | ||
69 | |||
70 | struct wlr_box workspace_box; | ||
71 | container_get_box(ws, &workspace_box); | ||
72 | if (!wlr_box_contains_point(&workspace_box, center_lx, center_ly)) { | ||
73 | // Maybe resize it | ||
74 | if (con->width > ws->width || con->height > ws->height) { | ||
75 | container_init_floating(con); | ||
76 | } | ||
77 | |||
78 | // Center it | ||
79 | double new_lx = ws->x + (ws->width - con->width) / 2; | ||
80 | double new_ly = ws->y + (ws->height - con->height) / 2; | ||
81 | container_floating_move_to(con, new_lx, new_ly); | ||
82 | } | ||
83 | |||
84 | arrange_windows(ws); | ||
85 | seat_set_focus(seat, seat_get_focus_inactive(seat, con)); | ||
86 | |||
87 | container_set_dirty(con->parent); | ||
88 | } | ||
89 | |||
90 | /** | ||
91 | * Hide a single scratchpad container. | ||
92 | * The container might not be the focused container (eg. when using criteria). | ||
93 | */ | ||
94 | static void scratchpad_hide(struct sway_container *con) { | ||
95 | struct sway_seat *seat = input_manager_current_seat(input_manager); | ||
96 | struct sway_container *focus = seat_get_focus(seat); | ||
97 | struct sway_container *ws = container_parent(con, C_WORKSPACE); | ||
98 | |||
99 | container_remove_child(con); | ||
100 | arrange_windows(ws); | ||
101 | if (con == focus) { | ||
102 | seat_set_focus(seat, seat_get_focus_inactive(seat, ws)); | ||
103 | } | ||
104 | list_move_to_end(root_container.sway_root->scratchpad, con); | ||
105 | } | ||
106 | |||
107 | void scratchpad_toggle_auto(void) { | ||
108 | struct sway_seat *seat = input_manager_current_seat(input_manager); | ||
109 | struct sway_container *focus = seat_get_focus(seat); | ||
110 | struct sway_container *ws = focus->type == C_WORKSPACE ? | ||
111 | focus : container_parent(focus, C_WORKSPACE); | ||
112 | |||
113 | // If the focus is in a floating split container, | ||
114 | // operate on the split container instead of the child. | ||
115 | if (container_is_floating_or_child(focus)) { | ||
116 | while (focus->parent->layout != L_FLOATING) { | ||
117 | focus = focus->parent; | ||
118 | } | ||
119 | } | ||
120 | |||
121 | |||
122 | // Check if the currently focused window is a scratchpad window and should | ||
123 | // be hidden again. | ||
124 | if (focus->scratchpad) { | ||
125 | wlr_log(WLR_DEBUG, "Focus is a scratchpad window - hiding %s", | ||
126 | focus->name); | ||
127 | scratchpad_hide(focus); | ||
128 | return; | ||
129 | } | ||
130 | |||
131 | // Check if there is an unfocused scratchpad window on the current workspace | ||
132 | // and focus it. | ||
133 | for (int i = 0; i < ws->sway_workspace->floating->children->length; ++i) { | ||
134 | struct sway_container *floater = | ||
135 | ws->sway_workspace->floating->children->items[i]; | ||
136 | if (floater->scratchpad && focus != floater) { | ||
137 | wlr_log(WLR_DEBUG, | ||
138 | "Focusing other scratchpad window (%s) in this workspace", | ||
139 | floater->name); | ||
140 | scratchpad_show(floater); | ||
141 | return; | ||
142 | } | ||
143 | } | ||
144 | |||
145 | // Check if there is a visible scratchpad window on another workspace. | ||
146 | // In this case we move it to the current workspace. | ||
147 | for (int i = 0; i < root_container.sway_root->scratchpad->length; ++i) { | ||
148 | struct sway_container *con = | ||
149 | root_container.sway_root->scratchpad->items[i]; | ||
150 | if (con->parent) { | ||
151 | wlr_log(WLR_DEBUG, | ||
152 | "Moving a visible scratchpad window (%s) to this workspace", | ||
153 | con->name); | ||
154 | scratchpad_show(con); | ||
155 | return; | ||
156 | } | ||
157 | } | ||
158 | |||
159 | // Take the container at the bottom of the scratchpad list | ||
160 | if (!sway_assert(root_container.sway_root->scratchpad->length, | ||
161 | "Scratchpad is empty")) { | ||
162 | return; | ||
163 | } | ||
164 | struct sway_container *con = root_container.sway_root->scratchpad->items[0]; | ||
165 | wlr_log(WLR_DEBUG, "Showing %s from list", con->name); | ||
166 | scratchpad_show(con); | ||
167 | } | ||
168 | |||
169 | void scratchpad_toggle_container(struct sway_container *con) { | ||
170 | if (!sway_assert(con->scratchpad, "Container isn't in the scratchpad")) { | ||
171 | return; | ||
172 | } | ||
173 | |||
174 | // Check if it matches a currently visible scratchpad window and hide it. | ||
175 | if (con->parent) { | ||
176 | scratchpad_hide(con); | ||
177 | return; | ||
178 | } | ||
179 | |||
180 | scratchpad_show(con); | ||
181 | } | ||
diff --git a/sway/server.c b/sway/server.c index 91ae7c97..e8755360 100644 --- a/sway/server.c +++ b/sway/server.c | |||
@@ -26,7 +26,10 @@ | |||
26 | #include "sway/input/input-manager.h" | 26 | #include "sway/input/input-manager.h" |
27 | #include "sway/server.h" | 27 | #include "sway/server.h" |
28 | #include "sway/tree/layout.h" | 28 | #include "sway/tree/layout.h" |
29 | #include "config.h" | ||
30 | #ifdef HAVE_XWAYLAND | ||
29 | #include "sway/xwayland.h" | 31 | #include "sway/xwayland.h" |
32 | #endif | ||
30 | 33 | ||
31 | bool server_privileged_prepare(struct sway_server *server) { | 34 | bool server_privileged_prepare(struct sway_server *server) { |
32 | wlr_log(WLR_DEBUG, "Preparing Wayland server initialization"); | 35 | wlr_log(WLR_DEBUG, "Preparing Wayland server initialization"); |
@@ -83,6 +86,7 @@ bool server_init(struct sway_server *server) { | |||
83 | server->xdg_shell_surface.notify = handle_xdg_shell_surface; | 86 | server->xdg_shell_surface.notify = handle_xdg_shell_surface; |
84 | 87 | ||
85 | // TODO make xwayland optional | 88 | // TODO make xwayland optional |
89 | #ifdef HAVE_XWAYLAND | ||
86 | server->xwayland.wlr_xwayland = | 90 | server->xwayland.wlr_xwayland = |
87 | wlr_xwayland_create(server->wl_display, server->compositor, true); | 91 | wlr_xwayland_create(server->wl_display, server->compositor, true); |
88 | wl_signal_add(&server->xwayland.wlr_xwayland->events.new_surface, | 92 | wl_signal_add(&server->xwayland.wlr_xwayland->events.new_surface, |
@@ -103,6 +107,7 @@ bool server_init(struct sway_server *server) { | |||
103 | image->width * 4, image->width, image->height, image->hotspot_x, | 107 | image->width * 4, image->width, image->height, image->hotspot_x, |
104 | image->hotspot_y); | 108 | image->hotspot_y); |
105 | } | 109 | } |
110 | #endif | ||
106 | 111 | ||
107 | // TODO: Integration with sway borders | 112 | // TODO: Integration with sway borders |
108 | struct wlr_server_decoration_manager *deco_manager = | 113 | struct wlr_server_decoration_manager *deco_manager = |
diff --git a/sway/sway-input.5.scd b/sway/sway-input.5.scd index b6391431..707c36af 100644 --- a/sway/sway-input.5.scd +++ b/sway/sway-input.5.scd | |||
@@ -33,6 +33,14 @@ For more information on these xkb configuration options, see | |||
33 | *input* <identifier> xkb\_variant <variant> | 33 | *input* <identifier> xkb\_variant <variant> |
34 | Sets the variant of the keyboard like _dvorak_ or _colemak_. | 34 | Sets the variant of the keyboard like _dvorak_ or _colemak_. |
35 | 35 | ||
36 | The following commands may only be used in the configuration file. | ||
37 | |||
38 | *input* <identifier> xkb\_capslock enabled|disabled | ||
39 | Initially enables or disables CapsLock, the default is disabled. | ||
40 | |||
41 | *input* <identifier> xkb\_numlock enabled|disabled | ||
42 | Initially enables or disables NumLock, the default is disabled. | ||
43 | |||
36 | ## MAPPING CONFIGURATION | 44 | ## MAPPING CONFIGURATION |
37 | 45 | ||
38 | *input* <identifier> map\_to\_output <identifier> | 46 | *input* <identifier> map\_to\_output <identifier> |
@@ -100,7 +108,7 @@ For more information on these xkb configuration options, see | |||
100 | *input* <identifier> tap enabled|disabled | 108 | *input* <identifier> tap enabled|disabled |
101 | Enables or disables tap for specified input device. | 109 | Enables or disables tap for specified input device. |
102 | 110 | ||
103 | *input* <identifier> tap_button_map lrm|lmr | 111 | *input* <identifier> tap\_button\_map lrm|lmr |
104 | Specifies which button mapping to use for tapping. _lrm_ treats 1 finger as | 112 | Specifies which button mapping to use for tapping. _lrm_ treats 1 finger as |
105 | left click, 2 fingers as right click, and 3 fingers as middle click. _lmr_ | 113 | left click, 2 fingers as right click, and 3 fingers as middle click. _lmr_ |
106 | treats 1 finger as left click, 2 fingers as middle click, and 3 fingers as | 114 | treats 1 finger as left click, 2 fingers as middle click, and 3 fingers as |
diff --git a/sway/sway.1.scd b/sway/sway.1.scd index 5b770cce..0c2ee974 100644 --- a/sway/sway.1.scd +++ b/sway/sway.1.scd | |||
@@ -92,4 +92,4 @@ source contributors. For more information about sway development, see | |||
92 | 92 | ||
93 | # SEE ALSO | 93 | # SEE ALSO |
94 | 94 | ||
95 | *sway*(5) *swaymsg*(1) *swaygrab*(1) *sway-input*(5) *sway-bar*(5) | 95 | *sway*(5) *swaymsg*(1) *sway-input*(5) *sway-bar*(5) |
diff --git a/sway/tree/arrange.c b/sway/tree/arrange.c index 533cf71c..5452b13c 100644 --- a/sway/tree/arrange.c +++ b/sway/tree/arrange.c | |||
@@ -220,8 +220,22 @@ static void arrange_workspace(struct sway_container *workspace) { | |||
220 | container_set_dirty(workspace); | 220 | container_set_dirty(workspace); |
221 | wlr_log(WLR_DEBUG, "Arranging workspace '%s' at %f, %f", workspace->name, | 221 | wlr_log(WLR_DEBUG, "Arranging workspace '%s' at %f, %f", workspace->name, |
222 | workspace->x, workspace->y); | 222 | workspace->x, workspace->y); |
223 | arrange_floating(workspace->sway_workspace->floating); | 223 | if (workspace->sway_workspace->fullscreen) { |
224 | arrange_children_of(workspace); | 224 | struct sway_container *fs = workspace->sway_workspace->fullscreen; |
225 | fs->x = workspace->parent->x; | ||
226 | fs->y = workspace->parent->y; | ||
227 | fs->width = workspace->parent->width; | ||
228 | fs->height = workspace->parent->height; | ||
229 | if (fs->type == C_VIEW) { | ||
230 | view_autoconfigure(fs->sway_view); | ||
231 | } else { | ||
232 | arrange_children_of(fs); | ||
233 | } | ||
234 | container_set_dirty(fs); | ||
235 | } else { | ||
236 | arrange_floating(workspace->sway_workspace->floating); | ||
237 | arrange_children_of(workspace); | ||
238 | } | ||
225 | } | 239 | } |
226 | 240 | ||
227 | static void arrange_output(struct sway_container *output) { | 241 | static void arrange_output(struct sway_container *output) { |
diff --git a/sway/tree/container.c b/sway/tree/container.c index 4dbfbb29..46c54e2d 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c | |||
@@ -17,6 +17,7 @@ | |||
17 | #include "sway/input/seat.h" | 17 | #include "sway/input/seat.h" |
18 | #include "sway/ipc-server.h" | 18 | #include "sway/ipc-server.h" |
19 | #include "sway/output.h" | 19 | #include "sway/output.h" |
20 | #include "sway/scratchpad.h" | ||
20 | #include "sway/server.h" | 21 | #include "sway/server.h" |
21 | #include "sway/tree/arrange.h" | 22 | #include "sway/tree/arrange.h" |
22 | #include "sway/tree/layout.h" | 23 | #include "sway/tree/layout.h" |
@@ -61,13 +62,17 @@ void container_create_notify(struct sway_container *container) { | |||
61 | // TODO send ipc event type based on the container type | 62 | // TODO send ipc event type based on the container type |
62 | wl_signal_emit(&root_container.sway_root->events.new_container, container); | 63 | wl_signal_emit(&root_container.sway_root->events.new_container, container); |
63 | 64 | ||
64 | if (container->type == C_VIEW || container->type == C_CONTAINER) { | 65 | if (container->type == C_VIEW) { |
65 | ipc_event_window(container, "new"); | 66 | ipc_event_window(container, "new"); |
67 | } else if (container->type == C_WORKSPACE) { | ||
68 | ipc_event_workspace(NULL, container, "init"); | ||
66 | } | 69 | } |
67 | } | 70 | } |
68 | 71 | ||
69 | static void container_update_textures_recursive(struct sway_container *con) { | 72 | void container_update_textures_recursive(struct sway_container *con) { |
70 | container_update_title_textures(con); | 73 | if (con->type == C_CONTAINER || con->type == C_VIEW) { |
74 | container_update_title_textures(con); | ||
75 | } | ||
71 | 76 | ||
72 | if (con->type == C_VIEW) { | 77 | if (con->type == C_VIEW) { |
73 | view_update_marks_textures(con->sway_view); | 78 | view_update_marks_textures(con->sway_view); |
@@ -76,6 +81,10 @@ static void container_update_textures_recursive(struct sway_container *con) { | |||
76 | struct sway_container *child = con->children->items[i]; | 81 | struct sway_container *child = con->children->items[i]; |
77 | container_update_textures_recursive(child); | 82 | container_update_textures_recursive(child); |
78 | } | 83 | } |
84 | |||
85 | if (con->type == C_WORKSPACE) { | ||
86 | container_update_textures_recursive(con->sway_workspace->floating); | ||
87 | } | ||
79 | } | 88 | } |
80 | } | 89 | } |
81 | 90 | ||
@@ -139,8 +148,6 @@ struct sway_container *container_create(enum sway_container_type type) { | |||
139 | static void container_workspace_free(struct sway_workspace *ws) { | 148 | static void container_workspace_free(struct sway_workspace *ws) { |
140 | list_foreach(ws->output_priority, free); | 149 | list_foreach(ws->output_priority, free); |
141 | list_free(ws->output_priority); | 150 | list_free(ws->output_priority); |
142 | ws->floating->destroying = true; | ||
143 | container_free(ws->floating); | ||
144 | free(ws); | 151 | free(ws); |
145 | } | 152 | } |
146 | 153 | ||
@@ -193,6 +200,9 @@ void container_free(struct sway_container *cont) { | |||
193 | free(cont); | 200 | free(cont); |
194 | } | 201 | } |
195 | 202 | ||
203 | static struct sway_container *container_destroy_noreaping( | ||
204 | struct sway_container *con); | ||
205 | |||
196 | static struct sway_container *container_workspace_destroy( | 206 | static struct sway_container *container_workspace_destroy( |
197 | struct sway_container *workspace) { | 207 | struct sway_container *workspace) { |
198 | if (!sway_assert(workspace, "cannot destroy null workspace")) { | 208 | if (!sway_assert(workspace, "cannot destroy null workspace")) { |
@@ -237,6 +247,8 @@ static struct sway_container *container_workspace_destroy( | |||
237 | } | 247 | } |
238 | } | 248 | } |
239 | 249 | ||
250 | container_destroy_noreaping(workspace->sway_workspace->floating); | ||
251 | |||
240 | return output; | 252 | return output; |
241 | } | 253 | } |
242 | 254 | ||
@@ -271,7 +283,7 @@ static struct sway_container *container_output_destroy( | |||
271 | container_remove_child(workspace); | 283 | container_remove_child(workspace); |
272 | if (!workspace_is_empty(workspace)) { | 284 | if (!workspace_is_empty(workspace)) { |
273 | container_add_child(new_output, workspace); | 285 | container_add_child(new_output, workspace); |
274 | ipc_event_workspace(workspace, NULL, "move"); | 286 | ipc_event_workspace(NULL, workspace, "move"); |
275 | } else { | 287 | } else { |
276 | container_destroy(workspace); | 288 | container_destroy(workspace); |
277 | } | 289 | } |
@@ -309,7 +321,13 @@ static struct sway_container *container_destroy_noreaping( | |||
309 | } | 321 | } |
310 | 322 | ||
311 | wl_signal_emit(&con->events.destroy, con); | 323 | wl_signal_emit(&con->events.destroy, con); |
312 | ipc_event_window(con, "close"); | 324 | |
325 | // emit IPC event | ||
326 | if (con->type == C_VIEW) { | ||
327 | ipc_event_window(con, "close"); | ||
328 | } else if (con->type == C_WORKSPACE) { | ||
329 | ipc_event_workspace(NULL, con, "empty"); | ||
330 | } | ||
313 | 331 | ||
314 | // The below functions move their children to somewhere else. | 332 | // The below functions move their children to somewhere else. |
315 | if (con->type == C_OUTPUT) { | 333 | if (con->type == C_OUTPUT) { |
@@ -323,9 +341,15 @@ static struct sway_container *container_destroy_noreaping( | |||
323 | } | 341 | } |
324 | } | 342 | } |
325 | 343 | ||
344 | container_end_mouse_operation(con); | ||
345 | |||
326 | con->destroying = true; | 346 | con->destroying = true; |
327 | container_set_dirty(con); | 347 | container_set_dirty(con); |
328 | 348 | ||
349 | if (con->scratchpad) { | ||
350 | scratchpad_remove_container(con); | ||
351 | } | ||
352 | |||
329 | if (!con->parent) { | 353 | if (!con->parent) { |
330 | return NULL; | 354 | return NULL; |
331 | } | 355 | } |
@@ -398,6 +422,10 @@ struct sway_container *container_flatten(struct sway_container *container) { | |||
398 | * This function just wraps container_destroy_noreaping(), then does reaping. | 422 | * This function just wraps container_destroy_noreaping(), then does reaping. |
399 | */ | 423 | */ |
400 | struct sway_container *container_destroy(struct sway_container *con) { | 424 | struct sway_container *container_destroy(struct sway_container *con) { |
425 | if (con->is_fullscreen) { | ||
426 | struct sway_container *ws = container_parent(con, C_WORKSPACE); | ||
427 | ws->sway_workspace->fullscreen = NULL; | ||
428 | } | ||
401 | struct sway_container *parent = container_destroy_noreaping(con); | 429 | struct sway_container *parent = container_destroy_noreaping(con); |
402 | 430 | ||
403 | if (!parent) { | 431 | if (!parent) { |
@@ -507,7 +535,7 @@ struct sway_container *container_parent(struct sway_container *container, | |||
507 | return container; | 535 | return container; |
508 | } | 536 | } |
509 | 537 | ||
510 | static struct sway_container *container_at_view(struct sway_container *swayc, | 538 | struct sway_container *container_at_view(struct sway_container *swayc, |
511 | double lx, double ly, | 539 | double lx, double ly, |
512 | struct wlr_surface **surface, double *sx, double *sy) { | 540 | struct wlr_surface **surface, double *sx, double *sy) { |
513 | if (!sway_assert(swayc->type == C_VIEW, "Expected a view")) { | 541 | if (!sway_assert(swayc->type == C_VIEW, "Expected a view")) { |
@@ -520,10 +548,12 @@ static struct sway_container *container_at_view(struct sway_container *swayc, | |||
520 | double _sx, _sy; | 548 | double _sx, _sy; |
521 | struct wlr_surface *_surface = NULL; | 549 | struct wlr_surface *_surface = NULL; |
522 | switch (sview->type) { | 550 | switch (sview->type) { |
551 | #ifdef HAVE_XWAYLAND | ||
523 | case SWAY_VIEW_XWAYLAND: | 552 | case SWAY_VIEW_XWAYLAND: |
524 | _surface = wlr_surface_surface_at(sview->surface, | 553 | _surface = wlr_surface_surface_at(sview->surface, |
525 | view_sx, view_sy, &_sx, &_sy); | 554 | view_sx, view_sy, &_sx, &_sy); |
526 | break; | 555 | break; |
556 | #endif | ||
527 | case SWAY_VIEW_XDG_SHELL_V6: | 557 | case SWAY_VIEW_XDG_SHELL_V6: |
528 | _surface = wlr_xdg_surface_v6_surface_at( | 558 | _surface = wlr_xdg_surface_v6_surface_at( |
529 | sview->wlr_xdg_surface_v6, | 559 | sview->wlr_xdg_surface_v6, |
@@ -539,10 +569,15 @@ static struct sway_container *container_at_view(struct sway_container *swayc, | |||
539 | *sx = _sx; | 569 | *sx = _sx; |
540 | *sy = _sy; | 570 | *sy = _sy; |
541 | *surface = _surface; | 571 | *surface = _surface; |
572 | return swayc; | ||
542 | } | 573 | } |
543 | return swayc; | 574 | return NULL; |
544 | } | 575 | } |
545 | 576 | ||
577 | static struct sway_container *tiling_container_at( | ||
578 | struct sway_container *con, double lx, double ly, | ||
579 | struct wlr_surface **surface, double *sx, double *sy); | ||
580 | |||
546 | /** | 581 | /** |
547 | * container_at for a container with layout L_TABBED. | 582 | * container_at for a container with layout L_TABBED. |
548 | */ | 583 | */ |
@@ -569,7 +604,7 @@ static struct sway_container *container_at_tabbed(struct sway_container *parent, | |||
569 | // Surfaces | 604 | // Surfaces |
570 | struct sway_container *current = seat_get_active_child(seat, parent); | 605 | struct sway_container *current = seat_get_active_child(seat, parent); |
571 | 606 | ||
572 | return container_at(current, lx, ly, surface, sx, sy); | 607 | return tiling_container_at(current, lx, ly, surface, sx, sy); |
573 | } | 608 | } |
574 | 609 | ||
575 | /** | 610 | /** |
@@ -594,7 +629,7 @@ static struct sway_container *container_at_stacked( | |||
594 | // Surfaces | 629 | // Surfaces |
595 | struct sway_container *current = seat_get_active_child(seat, parent); | 630 | struct sway_container *current = seat_get_active_child(seat, parent); |
596 | 631 | ||
597 | return container_at(current, lx, ly, surface, sx, sy); | 632 | return tiling_container_at(current, lx, ly, surface, sx, sy); |
598 | } | 633 | } |
599 | 634 | ||
600 | /** | 635 | /** |
@@ -612,45 +647,13 @@ static struct sway_container *container_at_linear(struct sway_container *parent, | |||
612 | .height = child->height, | 647 | .height = child->height, |
613 | }; | 648 | }; |
614 | if (wlr_box_contains_point(&box, lx, ly)) { | 649 | if (wlr_box_contains_point(&box, lx, ly)) { |
615 | return container_at(child, lx, ly, surface, sx, sy); | 650 | return tiling_container_at(child, lx, ly, surface, sx, sy); |
616 | } | 651 | } |
617 | } | 652 | } |
618 | return NULL; | 653 | return NULL; |
619 | } | 654 | } |
620 | 655 | ||
621 | struct sway_container *container_at(struct sway_container *parent, | 656 | static struct sway_container *floating_container_at(double lx, double ly, |
622 | double lx, double ly, | ||
623 | struct wlr_surface **surface, double *sx, double *sy) { | ||
624 | if (!sway_assert(parent->type >= C_WORKSPACE, | ||
625 | "Expected workspace or deeper")) { | ||
626 | return NULL; | ||
627 | } | ||
628 | if (parent->type == C_VIEW) { | ||
629 | return container_at_view(parent, lx, ly, surface, sx, sy); | ||
630 | } | ||
631 | if (!parent->children->length) { | ||
632 | return NULL; | ||
633 | } | ||
634 | |||
635 | switch (parent->layout) { | ||
636 | case L_HORIZ: | ||
637 | case L_VERT: | ||
638 | return container_at_linear(parent, lx, ly, surface, sx, sy); | ||
639 | case L_TABBED: | ||
640 | return container_at_tabbed(parent, lx, ly, surface, sx, sy); | ||
641 | case L_STACKED: | ||
642 | return container_at_stacked(parent, lx, ly, surface, sx, sy); | ||
643 | case L_FLOATING: | ||
644 | sway_assert(false, "Didn't expect to see floating here"); | ||
645 | return NULL; | ||
646 | case L_NONE: | ||
647 | return NULL; | ||
648 | } | ||
649 | |||
650 | return NULL; | ||
651 | } | ||
652 | |||
653 | struct sway_container *floating_container_at(double lx, double ly, | ||
654 | struct wlr_surface **surface, double *sx, double *sy) { | 657 | struct wlr_surface **surface, double *sx, double *sy) { |
655 | for (int i = 0; i < root_container.children->length; ++i) { | 658 | for (int i = 0; i < root_container.children->length; ++i) { |
656 | struct sway_container *output = root_container.children->items[i]; | 659 | struct sway_container *output = root_container.children->items[i]; |
@@ -672,7 +675,8 @@ struct sway_container *floating_container_at(double lx, double ly, | |||
672 | .height = floater->height, | 675 | .height = floater->height, |
673 | }; | 676 | }; |
674 | if (wlr_box_contains_point(&box, lx, ly)) { | 677 | if (wlr_box_contains_point(&box, lx, ly)) { |
675 | return container_at(floater, lx, ly, surface, sx, sy); | 678 | return tiling_container_at(floater, lx, ly, |
679 | surface, sx, sy); | ||
676 | } | 680 | } |
677 | } | 681 | } |
678 | } | 682 | } |
@@ -680,6 +684,90 @@ struct sway_container *floating_container_at(double lx, double ly, | |||
680 | return NULL; | 684 | return NULL; |
681 | } | 685 | } |
682 | 686 | ||
687 | static struct sway_container *tiling_container_at( | ||
688 | struct sway_container *con, double lx, double ly, | ||
689 | struct wlr_surface **surface, double *sx, double *sy) { | ||
690 | if (con->type == C_VIEW) { | ||
691 | return container_at_view(con, lx, ly, surface, sx, sy); | ||
692 | } | ||
693 | if (!con->children->length) { | ||
694 | return NULL; | ||
695 | } | ||
696 | |||
697 | switch (con->layout) { | ||
698 | case L_HORIZ: | ||
699 | case L_VERT: | ||
700 | return container_at_linear(con, lx, ly, surface, sx, sy); | ||
701 | case L_TABBED: | ||
702 | return container_at_tabbed(con, lx, ly, surface, sx, sy); | ||
703 | case L_STACKED: | ||
704 | return container_at_stacked(con, lx, ly, surface, sx, sy); | ||
705 | case L_FLOATING: | ||
706 | sway_assert(false, "Didn't expect to see floating here"); | ||
707 | return NULL; | ||
708 | case L_NONE: | ||
709 | return NULL; | ||
710 | } | ||
711 | return NULL; | ||
712 | } | ||
713 | |||
714 | static bool surface_is_popup(struct wlr_surface *surface) { | ||
715 | if (wlr_surface_is_xdg_surface(surface)) { | ||
716 | struct wlr_xdg_surface *xdg_surface = | ||
717 | wlr_xdg_surface_from_wlr_surface(surface); | ||
718 | while (xdg_surface) { | ||
719 | if (xdg_surface->role == WLR_XDG_SURFACE_ROLE_POPUP) { | ||
720 | return true; | ||
721 | } | ||
722 | xdg_surface = xdg_surface->toplevel->parent; | ||
723 | } | ||
724 | return false; | ||
725 | } | ||
726 | |||
727 | if (wlr_surface_is_xdg_surface_v6(surface)) { | ||
728 | struct wlr_xdg_surface_v6 *xdg_surface_v6 = | ||
729 | wlr_xdg_surface_v6_from_wlr_surface(surface); | ||
730 | while (xdg_surface_v6) { | ||
731 | if (xdg_surface_v6->role == WLR_XDG_SURFACE_V6_ROLE_POPUP) { | ||
732 | return true; | ||
733 | } | ||
734 | xdg_surface_v6 = xdg_surface_v6->toplevel->parent; | ||
735 | } | ||
736 | return false; | ||
737 | } | ||
738 | |||
739 | return false; | ||
740 | } | ||
741 | |||
742 | struct sway_container *container_at(struct sway_container *workspace, | ||
743 | double lx, double ly, | ||
744 | struct wlr_surface **surface, double *sx, double *sy) { | ||
745 | if (!sway_assert(workspace->type == C_WORKSPACE, "Expected a workspace")) { | ||
746 | return NULL; | ||
747 | } | ||
748 | struct sway_container *c; | ||
749 | // Focused view's popups | ||
750 | struct sway_seat *seat = input_manager_current_seat(input_manager); | ||
751 | struct sway_container *focus = | ||
752 | seat_get_focus_inactive(seat, &root_container); | ||
753 | if (focus && focus->type == C_VIEW) { | ||
754 | container_at_view(focus, lx, ly, surface, sx, sy); | ||
755 | if (*surface && surface_is_popup(*surface)) { | ||
756 | return focus; | ||
757 | } | ||
758 | *surface = NULL; | ||
759 | } | ||
760 | // Floating | ||
761 | if ((c = floating_container_at(lx, ly, surface, sx, sy))) { | ||
762 | return c; | ||
763 | } | ||
764 | // Tiling | ||
765 | if ((c = tiling_container_at(workspace, lx, ly, surface, sx, sy))) { | ||
766 | return c; | ||
767 | } | ||
768 | return NULL; | ||
769 | } | ||
770 | |||
683 | void container_for_each_descendant_dfs(struct sway_container *container, | 771 | void container_for_each_descendant_dfs(struct sway_container *container, |
684 | void (*f)(struct sway_container *container, void *data), | 772 | void (*f)(struct sway_container *container, void *data), |
685 | void *data) { | 773 | void *data) { |
@@ -934,36 +1022,104 @@ size_t container_titlebar_height() { | |||
934 | return config->font_height + TITLEBAR_V_PADDING * 2; | 1022 | return config->font_height + TITLEBAR_V_PADDING * 2; |
935 | } | 1023 | } |
936 | 1024 | ||
1025 | void container_init_floating(struct sway_container *con) { | ||
1026 | if (!sway_assert(con->type == C_VIEW || con->type == C_CONTAINER, | ||
1027 | "Expected a view or container")) { | ||
1028 | return; | ||
1029 | } | ||
1030 | struct sway_container *ws = container_parent(con, C_WORKSPACE); | ||
1031 | int min_width, min_height; | ||
1032 | int max_width, max_height; | ||
1033 | |||
1034 | if (config->floating_minimum_width == -1) { // no minimum | ||
1035 | min_width = 0; | ||
1036 | } else if (config->floating_minimum_width == 0) { // automatic | ||
1037 | min_width = 75; | ||
1038 | } else { | ||
1039 | min_width = config->floating_minimum_width; | ||
1040 | } | ||
1041 | |||
1042 | if (config->floating_minimum_height == -1) { // no minimum | ||
1043 | min_height = 0; | ||
1044 | } else if (config->floating_minimum_height == 0) { // automatic | ||
1045 | min_height = 50; | ||
1046 | } else { | ||
1047 | min_height = config->floating_minimum_height; | ||
1048 | } | ||
1049 | |||
1050 | if (config->floating_maximum_width == -1) { // no maximum | ||
1051 | max_width = INT_MAX; | ||
1052 | } else if (config->floating_maximum_width == 0) { // automatic | ||
1053 | max_width = ws->width * 0.6666; | ||
1054 | } else { | ||
1055 | max_width = config->floating_maximum_width; | ||
1056 | } | ||
1057 | |||
1058 | if (config->floating_maximum_height == -1) { // no maximum | ||
1059 | max_height = INT_MAX; | ||
1060 | } else if (config->floating_maximum_height == 0) { // automatic | ||
1061 | max_height = ws->height * 0.6666; | ||
1062 | } else { | ||
1063 | max_height = config->floating_maximum_height; | ||
1064 | } | ||
1065 | |||
1066 | if (con->type == C_CONTAINER) { | ||
1067 | con->width = max_width; | ||
1068 | con->height = max_height; | ||
1069 | con->x = ws->x + (ws->width - con->width) / 2; | ||
1070 | con->y = ws->y + (ws->height - con->height) / 2; | ||
1071 | } else { | ||
1072 | struct sway_view *view = con->sway_view; | ||
1073 | view->width = fmax(min_width, fmin(view->natural_width, max_width)); | ||
1074 | view->height = fmax(min_height, fmin(view->natural_height, max_height)); | ||
1075 | view->x = ws->x + (ws->width - view->width) / 2; | ||
1076 | view->y = ws->y + (ws->height - view->height) / 2; | ||
1077 | |||
1078 | // If the view's border is B_NONE then these properties are ignored. | ||
1079 | view->border_top = view->border_bottom = true; | ||
1080 | view->border_left = view->border_right = true; | ||
1081 | |||
1082 | container_set_geometry_from_floating_view(view->swayc); | ||
1083 | } | ||
1084 | } | ||
1085 | |||
937 | void container_set_floating(struct sway_container *container, bool enable) { | 1086 | void container_set_floating(struct sway_container *container, bool enable) { |
938 | if (container_is_floating(container) == enable) { | 1087 | if (container_is_floating(container) == enable) { |
939 | return; | 1088 | return; |
940 | } | 1089 | } |
941 | 1090 | ||
942 | struct sway_container *workspace = container_parent(container, C_WORKSPACE); | ||
943 | struct sway_seat *seat = input_manager_current_seat(input_manager); | 1091 | struct sway_seat *seat = input_manager_current_seat(input_manager); |
1092 | struct sway_container *workspace = container_parent(container, C_WORKSPACE); | ||
944 | 1093 | ||
945 | if (enable) { | 1094 | if (enable) { |
946 | container_remove_child(container); | 1095 | container_remove_child(container); |
947 | container_add_child(workspace->sway_workspace->floating, container); | 1096 | container_add_child(workspace->sway_workspace->floating, container); |
1097 | container_init_floating(container); | ||
948 | if (container->type == C_VIEW) { | 1098 | if (container->type == C_VIEW) { |
949 | view_init_floating(container->sway_view); | ||
950 | view_set_tiled(container->sway_view, false); | 1099 | view_set_tiled(container->sway_view, false); |
951 | } | 1100 | } |
952 | seat_set_focus(seat, seat_get_focus_inactive(seat, container)); | ||
953 | container_reap_empty_recursive(workspace); | ||
954 | } else { | 1101 | } else { |
955 | // Returning to tiled | 1102 | // Returning to tiled |
1103 | if (container->scratchpad) { | ||
1104 | scratchpad_remove_container(container); | ||
1105 | } | ||
956 | container_remove_child(container); | 1106 | container_remove_child(container); |
957 | container_add_child(workspace, container); | 1107 | struct sway_container *reference = |
1108 | seat_get_focus_inactive_tiling(seat, workspace); | ||
1109 | if (reference->type == C_VIEW) { | ||
1110 | reference = reference->parent; | ||
1111 | } | ||
1112 | container_add_child(reference, container); | ||
958 | container->width = container->parent->width; | 1113 | container->width = container->parent->width; |
959 | container->height = container->parent->height; | 1114 | container->height = container->parent->height; |
960 | if (container->type == C_VIEW) { | 1115 | if (container->type == C_VIEW) { |
961 | view_set_tiled(container->sway_view, true); | 1116 | view_set_tiled(container->sway_view, true); |
962 | } | 1117 | } |
963 | container->is_sticky = false; | 1118 | container->is_sticky = false; |
964 | container_reap_empty_recursive(workspace->sway_workspace->floating); | ||
965 | } | 1119 | } |
966 | 1120 | ||
1121 | container_end_mouse_operation(container); | ||
1122 | |||
967 | ipc_event_window(container, "floating"); | 1123 | ipc_event_window(container, "floating"); |
968 | } | 1124 | } |
969 | 1125 | ||
@@ -1009,7 +1165,7 @@ void container_get_box(struct sway_container *container, struct wlr_box *box) { | |||
1009 | /** | 1165 | /** |
1010 | * Translate the container's position as well as all children. | 1166 | * Translate the container's position as well as all children. |
1011 | */ | 1167 | */ |
1012 | static void container_floating_translate(struct sway_container *con, | 1168 | void container_floating_translate(struct sway_container *con, |
1013 | double x_amount, double y_amount) { | 1169 | double x_amount, double y_amount) { |
1014 | con->x += x_amount; | 1170 | con->x += x_amount; |
1015 | con->y += y_amount; | 1171 | con->y += y_amount; |
@@ -1105,3 +1261,110 @@ static bool find_urgent_iterator(struct sway_container *con, | |||
1105 | bool container_has_urgent_child(struct sway_container *container) { | 1261 | bool container_has_urgent_child(struct sway_container *container) { |
1106 | return container_find(container, find_urgent_iterator, NULL); | 1262 | return container_find(container, find_urgent_iterator, NULL); |
1107 | } | 1263 | } |
1264 | |||
1265 | void container_end_mouse_operation(struct sway_container *container) { | ||
1266 | struct sway_seat *seat; | ||
1267 | wl_list_for_each(seat, &input_manager->seats, link) { | ||
1268 | if (seat->op_container == container) { | ||
1269 | seat_end_mouse_operation(seat); | ||
1270 | } | ||
1271 | } | ||
1272 | } | ||
1273 | |||
1274 | static void set_fullscreen_iterator(struct sway_container *con, void *data) { | ||
1275 | if (con->type != C_VIEW) { | ||
1276 | return; | ||
1277 | } | ||
1278 | if (con->sway_view->impl->set_fullscreen) { | ||
1279 | bool *enable = data; | ||
1280 | con->sway_view->impl->set_fullscreen(con->sway_view, *enable); | ||
1281 | } | ||
1282 | } | ||
1283 | |||
1284 | void container_set_fullscreen(struct sway_container *container, bool enable) { | ||
1285 | if (container->is_fullscreen == enable) { | ||
1286 | return; | ||
1287 | } | ||
1288 | |||
1289 | struct sway_container *workspace = container_parent(container, C_WORKSPACE); | ||
1290 | if (enable && workspace->sway_workspace->fullscreen) { | ||
1291 | container_set_fullscreen(workspace->sway_workspace->fullscreen, false); | ||
1292 | } | ||
1293 | |||
1294 | container_for_each_descendant_dfs(container, | ||
1295 | set_fullscreen_iterator, &enable); | ||
1296 | |||
1297 | container->is_fullscreen = enable; | ||
1298 | |||
1299 | if (enable) { | ||
1300 | workspace->sway_workspace->fullscreen = container; | ||
1301 | container->saved_x = container->x; | ||
1302 | container->saved_y = container->y; | ||
1303 | container->saved_width = container->width; | ||
1304 | container->saved_height = container->height; | ||
1305 | |||
1306 | struct sway_seat *seat; | ||
1307 | struct sway_container *focus, *focus_ws; | ||
1308 | wl_list_for_each(seat, &input_manager->seats, link) { | ||
1309 | focus = seat_get_focus(seat); | ||
1310 | if (focus) { | ||
1311 | focus_ws = focus; | ||
1312 | if (focus_ws->type != C_WORKSPACE) { | ||
1313 | focus_ws = container_parent(focus_ws, C_WORKSPACE); | ||
1314 | } | ||
1315 | if (focus_ws == workspace) { | ||
1316 | seat_set_focus(seat, container); | ||
1317 | } | ||
1318 | } | ||
1319 | } | ||
1320 | } else { | ||
1321 | workspace->sway_workspace->fullscreen = NULL; | ||
1322 | if (container_is_floating(container)) { | ||
1323 | container->x = container->saved_x; | ||
1324 | container->y = container->saved_y; | ||
1325 | container->width = container->saved_width; | ||
1326 | container->height = container->saved_height; | ||
1327 | } else { | ||
1328 | container->width = container->saved_width; | ||
1329 | container->height = container->saved_height; | ||
1330 | } | ||
1331 | } | ||
1332 | |||
1333 | container_end_mouse_operation(container); | ||
1334 | |||
1335 | ipc_event_window(container, "fullscreen_mode"); | ||
1336 | } | ||
1337 | |||
1338 | bool container_is_floating_or_child(struct sway_container *container) { | ||
1339 | do { | ||
1340 | if (container->parent && container->parent->layout == L_FLOATING) { | ||
1341 | return true; | ||
1342 | } | ||
1343 | container = container->parent; | ||
1344 | } while (container && container->type != C_WORKSPACE); | ||
1345 | |||
1346 | return false; | ||
1347 | } | ||
1348 | |||
1349 | bool container_is_fullscreen_or_child(struct sway_container *container) { | ||
1350 | do { | ||
1351 | if (container->is_fullscreen) { | ||
1352 | return true; | ||
1353 | } | ||
1354 | container = container->parent; | ||
1355 | } while (container && container->type != C_WORKSPACE); | ||
1356 | |||
1357 | return false; | ||
1358 | } | ||
1359 | |||
1360 | struct sway_container *container_wrap_children(struct sway_container *parent) { | ||
1361 | struct sway_container *middle = container_create(C_CONTAINER); | ||
1362 | middle->layout = parent->layout; | ||
1363 | while (parent->children->length) { | ||
1364 | struct sway_container *child = parent->children->items[0]; | ||
1365 | container_remove_child(child); | ||
1366 | container_add_child(middle, child); | ||
1367 | } | ||
1368 | container_add_child(parent, middle); | ||
1369 | return middle; | ||
1370 | } | ||
diff --git a/sway/tree/layout.c b/sway/tree/layout.c index 1f898f8a..1f82e534 100644 --- a/sway/tree/layout.c +++ b/sway/tree/layout.c | |||
@@ -6,6 +6,7 @@ | |||
6 | #include <string.h> | 6 | #include <string.h> |
7 | #include <wlr/types/wlr_output.h> | 7 | #include <wlr/types/wlr_output.h> |
8 | #include <wlr/types/wlr_output_layout.h> | 8 | #include <wlr/types/wlr_output_layout.h> |
9 | #include "config.h" | ||
9 | #include "sway/debug.h" | 10 | #include "sway/debug.h" |
10 | #include "sway/tree/arrange.h" | 11 | #include "sway/tree/arrange.h" |
11 | #include "sway/tree/container.h" | 12 | #include "sway/tree/container.h" |
@@ -39,9 +40,12 @@ void layout_init(void) { | |||
39 | root_container.sway_root = calloc(1, sizeof(*root_container.sway_root)); | 40 | root_container.sway_root = calloc(1, sizeof(*root_container.sway_root)); |
40 | root_container.sway_root->output_layout = wlr_output_layout_create(); | 41 | root_container.sway_root->output_layout = wlr_output_layout_create(); |
41 | wl_list_init(&root_container.sway_root->outputs); | 42 | wl_list_init(&root_container.sway_root->outputs); |
43 | #ifdef HAVE_XWAYLAND | ||
42 | wl_list_init(&root_container.sway_root->xwayland_unmanaged); | 44 | wl_list_init(&root_container.sway_root->xwayland_unmanaged); |
45 | #endif | ||
43 | wl_list_init(&root_container.sway_root->drag_icons); | 46 | wl_list_init(&root_container.sway_root->drag_icons); |
44 | wl_signal_init(&root_container.sway_root->events.new_container); | 47 | wl_signal_init(&root_container.sway_root->events.new_container); |
48 | root_container.sway_root->scratchpad = create_list(); | ||
45 | 49 | ||
46 | root_container.sway_root->output_layout_change.notify = | 50 | root_container.sway_root->output_layout_change.notify = |
47 | output_layout_handle_change; | 51 | output_layout_handle_change; |
@@ -62,10 +66,9 @@ static int index_child(const struct sway_container *child) { | |||
62 | 66 | ||
63 | static void container_handle_fullscreen_reparent(struct sway_container *con, | 67 | static void container_handle_fullscreen_reparent(struct sway_container *con, |
64 | struct sway_container *old_parent) { | 68 | struct sway_container *old_parent) { |
65 | if (con->type != C_VIEW || !con->sway_view->is_fullscreen) { | 69 | if (!con->is_fullscreen) { |
66 | return; | 70 | return; |
67 | } | 71 | } |
68 | struct sway_view *view = con->sway_view; | ||
69 | struct sway_container *old_workspace = old_parent; | 72 | struct sway_container *old_workspace = old_parent; |
70 | if (old_workspace && old_workspace->type != C_WORKSPACE) { | 73 | if (old_workspace && old_workspace->type != C_WORKSPACE) { |
71 | old_workspace = container_parent(old_workspace, C_WORKSPACE); | 74 | old_workspace = container_parent(old_workspace, C_WORKSPACE); |
@@ -81,19 +84,27 @@ static void container_handle_fullscreen_reparent(struct sway_container *con, | |||
81 | 84 | ||
82 | // Mark the new workspace as fullscreen | 85 | // Mark the new workspace as fullscreen |
83 | if (new_workspace->sway_workspace->fullscreen) { | 86 | if (new_workspace->sway_workspace->fullscreen) { |
84 | view_set_fullscreen(new_workspace->sway_workspace->fullscreen, false); | 87 | container_set_fullscreen( |
88 | new_workspace->sway_workspace->fullscreen, false); | ||
85 | } | 89 | } |
86 | new_workspace->sway_workspace->fullscreen = view; | 90 | new_workspace->sway_workspace->fullscreen = con; |
87 | // Resize view to new output dimensions | 91 | |
92 | // Resize container to new output dimensions | ||
88 | struct sway_container *output = new_workspace->parent; | 93 | struct sway_container *output = new_workspace->parent; |
89 | view->x = output->x; | ||
90 | view->y = output->y; | ||
91 | view->width = output->width; | ||
92 | view->height = output->height; | ||
93 | con->x = output->x; | 94 | con->x = output->x; |
94 | con->y = output->y; | 95 | con->y = output->y; |
95 | con->width = output->width; | 96 | con->width = output->width; |
96 | con->height = output->height; | 97 | con->height = output->height; |
98 | |||
99 | if (con->type == C_VIEW) { | ||
100 | struct sway_view *view = con->sway_view; | ||
101 | view->x = output->x; | ||
102 | view->y = output->y; | ||
103 | view->width = output->width; | ||
104 | view->height = output->height; | ||
105 | } else { | ||
106 | arrange_windows(new_workspace); | ||
107 | } | ||
97 | } | 108 | } |
98 | 109 | ||
99 | void container_insert_child(struct sway_container *parent, | 110 | void container_insert_child(struct sway_container *parent, |
@@ -135,10 +146,14 @@ void container_add_child(struct sway_container *parent, | |||
135 | list_add(parent->children, child); | 146 | list_add(parent->children, child); |
136 | child->parent = parent; | 147 | child->parent = parent; |
137 | container_handle_fullscreen_reparent(child, old_parent); | 148 | container_handle_fullscreen_reparent(child, old_parent); |
149 | if (old_parent) { | ||
150 | container_set_dirty(old_parent); | ||
151 | } | ||
152 | container_set_dirty(child); | ||
138 | } | 153 | } |
139 | 154 | ||
140 | struct sway_container *container_remove_child(struct sway_container *child) { | 155 | struct sway_container *container_remove_child(struct sway_container *child) { |
141 | if (child->type == C_VIEW && child->sway_view->is_fullscreen) { | 156 | if (child->is_fullscreen) { |
142 | struct sway_container *workspace = container_parent(child, C_WORKSPACE); | 157 | struct sway_container *workspace = container_parent(child, C_WORKSPACE); |
143 | workspace->sway_workspace->fullscreen = NULL; | 158 | workspace->sway_workspace->fullscreen = NULL; |
144 | } | 159 | } |
@@ -153,6 +168,9 @@ struct sway_container *container_remove_child(struct sway_container *child) { | |||
153 | child->parent = NULL; | 168 | child->parent = NULL; |
154 | container_notify_subtree_changed(parent); | 169 | container_notify_subtree_changed(parent); |
155 | 170 | ||
171 | container_set_dirty(parent); | ||
172 | container_set_dirty(child); | ||
173 | |||
156 | return parent; | 174 | return parent; |
157 | } | 175 | } |
158 | 176 | ||
@@ -199,7 +217,9 @@ void container_move_to(struct sway_container *container, | |||
199 | container_sort_workspaces(new_parent); | 217 | container_sort_workspaces(new_parent); |
200 | seat_set_focus(seat, new_parent); | 218 | seat_set_focus(seat, new_parent); |
201 | workspace_output_raise_priority(container, old_parent, new_parent); | 219 | workspace_output_raise_priority(container, old_parent, new_parent); |
202 | ipc_event_workspace(container, NULL, "move"); | 220 | ipc_event_workspace(NULL, container, "move"); |
221 | } else if (container->type == C_VIEW) { | ||
222 | ipc_event_window(container, "move"); | ||
203 | } | 223 | } |
204 | container_notify_subtree_changed(old_parent); | 224 | container_notify_subtree_changed(old_parent); |
205 | container_notify_subtree_changed(new_parent); | 225 | container_notify_subtree_changed(new_parent); |
@@ -218,10 +238,10 @@ void container_move_to(struct sway_container *container, | |||
218 | if (focus_ws->type != C_WORKSPACE) { | 238 | if (focus_ws->type != C_WORKSPACE) { |
219 | focus_ws = container_parent(focus_ws, C_WORKSPACE); | 239 | focus_ws = container_parent(focus_ws, C_WORKSPACE); |
220 | } | 240 | } |
221 | seat_set_focus(seat, | 241 | if (focus_ws == new_workspace) { |
222 | new_workspace->sway_workspace->fullscreen->swayc); | 242 | struct sway_container *new_focus = seat_get_focus_inactive(seat, |
223 | if (focus_ws != new_workspace) { | 243 | new_workspace->sway_workspace->fullscreen); |
224 | seat_set_focus(seat, focus); | 244 | seat_set_focus(seat, new_focus); |
225 | } | 245 | } |
226 | } | 246 | } |
227 | } | 247 | } |
@@ -364,10 +384,18 @@ void container_move(struct sway_container *container, | |||
364 | struct sway_container *sibling = NULL; | 384 | struct sway_container *sibling = NULL; |
365 | struct sway_container *current = container; | 385 | struct sway_container *current = container; |
366 | struct sway_container *parent = current->parent; | 386 | struct sway_container *parent = current->parent; |
387 | struct sway_container *top = &root_container; | ||
367 | 388 | ||
368 | // If moving a fullscreen view, only consider outputs | 389 | // If moving a fullscreen view, only consider outputs |
369 | if (container->type == C_VIEW && container->sway_view->is_fullscreen) { | 390 | if (container->is_fullscreen) { |
370 | current = container_parent(container, C_OUTPUT); | 391 | current = container_parent(container, C_OUTPUT); |
392 | } else if (container_is_fullscreen_or_child(container) || | ||
393 | container_is_floating_or_child(container)) { | ||
394 | // If we've fullscreened a split container, only allow the child to move | ||
395 | // around within the fullscreen parent. | ||
396 | // Same with floating a split container. | ||
397 | struct sway_container *ws = container_parent(container, C_WORKSPACE); | ||
398 | top = ws->sway_workspace->fullscreen; | ||
371 | } | 399 | } |
372 | 400 | ||
373 | struct sway_container *new_parent = container_flatten(parent); | 401 | struct sway_container *new_parent = container_flatten(parent); |
@@ -377,7 +405,7 @@ void container_move(struct sway_container *container, | |||
377 | } | 405 | } |
378 | 406 | ||
379 | while (!sibling) { | 407 | while (!sibling) { |
380 | if (current->type == C_ROOT) { | 408 | if (current == top) { |
381 | return; | 409 | return; |
382 | } | 410 | } |
383 | 411 | ||
@@ -441,8 +469,12 @@ void container_move(struct sway_container *container, | |||
441 | if ((index == parent->children->length - 1 && offs > 0) | 469 | if ((index == parent->children->length - 1 && offs > 0) |
442 | || (index == 0 && offs < 0)) { | 470 | || (index == 0 && offs < 0)) { |
443 | if (current->parent == container->parent) { | 471 | if (current->parent == container->parent) { |
444 | if (parent->layout == L_TABBED | 472 | if (parent->parent->layout == L_FLOATING) { |
445 | || parent->layout == L_STACKED) { | 473 | return; |
474 | } | ||
475 | if (!parent->is_fullscreen && | ||
476 | (parent->layout == L_TABBED || | ||
477 | parent->layout == L_STACKED)) { | ||
446 | move_out_of_tabs_stacks(container, current, | 478 | move_out_of_tabs_stacks(container, current, |
447 | move_dir, offs); | 479 | move_dir, offs); |
448 | return; | 480 | return; |
@@ -463,10 +495,14 @@ void container_move(struct sway_container *container, | |||
463 | sibling = parent->children->items[index + offs]; | 495 | sibling = parent->children->items[index + offs]; |
464 | wlr_log(WLR_DEBUG, "Selecting sibling id:%zd", sibling->id); | 496 | wlr_log(WLR_DEBUG, "Selecting sibling id:%zd", sibling->id); |
465 | } | 497 | } |
466 | } else if (parent->layout == L_TABBED | 498 | } else if (!parent->is_fullscreen && |
467 | || parent->layout == L_STACKED) { | 499 | parent->parent->layout != L_FLOATING && |
500 | (parent->layout == L_TABBED || | ||
501 | parent->layout == L_STACKED)) { | ||
468 | move_out_of_tabs_stacks(container, current, move_dir, offs); | 502 | move_out_of_tabs_stacks(container, current, move_dir, offs); |
469 | return; | 503 | return; |
504 | } else if (parent->parent->layout == L_FLOATING) { | ||
505 | return; | ||
470 | } else { | 506 | } else { |
471 | wlr_log(WLR_DEBUG, "Moving up to find a parallel container"); | 507 | wlr_log(WLR_DEBUG, "Moving up to find a parallel container"); |
472 | current = current->parent; | 508 | current = current->parent; |
@@ -544,6 +580,10 @@ void container_move(struct sway_container *container, | |||
544 | container_notify_subtree_changed(old_parent); | 580 | container_notify_subtree_changed(old_parent); |
545 | container_notify_subtree_changed(container->parent); | 581 | container_notify_subtree_changed(container->parent); |
546 | 582 | ||
583 | if (container->type == C_VIEW) { | ||
584 | ipc_event_window(container, "move"); | ||
585 | } | ||
586 | |||
547 | if (old_parent) { | 587 | if (old_parent) { |
548 | seat_set_focus(config->handler_context.seat, old_parent); | 588 | seat_set_focus(config->handler_context.seat, old_parent); |
549 | seat_set_focus(config->handler_context.seat, container); | 589 | seat_set_focus(config->handler_context.seat, container); |
@@ -558,10 +598,11 @@ void container_move(struct sway_container *container, | |||
558 | next_ws = container_parent(next_ws, C_WORKSPACE); | 598 | next_ws = container_parent(next_ws, C_WORKSPACE); |
559 | } | 599 | } |
560 | if (last_ws && next_ws && last_ws != next_ws) { | 600 | if (last_ws && next_ws && last_ws != next_ws) { |
561 | ipc_event_workspace(last_ws, container, "focus"); | 601 | ipc_event_workspace(last_ws, next_ws, "focus"); |
562 | workspace_detect_urgent(last_ws); | 602 | workspace_detect_urgent(last_ws); |
563 | workspace_detect_urgent(next_ws); | 603 | workspace_detect_urgent(next_ws); |
564 | } | 604 | } |
605 | container_end_mouse_operation(container); | ||
565 | } | 606 | } |
566 | 607 | ||
567 | enum sway_container_layout container_get_default_layout( | 608 | enum sway_container_layout container_get_default_layout( |
@@ -691,22 +732,18 @@ struct sway_container *container_get_in_direction( | |||
691 | enum movement_direction dir) { | 732 | enum movement_direction dir) { |
692 | struct sway_container *parent = container->parent; | 733 | struct sway_container *parent = container->parent; |
693 | 734 | ||
694 | if (container_is_floating(container)) { | 735 | if (dir == MOVE_CHILD) { |
695 | return NULL; | 736 | return seat_get_focus_inactive(seat, container); |
696 | } | 737 | } |
697 | 738 | if (container->is_fullscreen) { | |
698 | if (container->type == C_VIEW && container->sway_view->is_fullscreen) { | 739 | if (dir == MOVE_PARENT) { |
699 | if (dir == MOVE_PARENT || dir == MOVE_CHILD) { | ||
700 | return NULL; | 740 | return NULL; |
701 | } | 741 | } |
702 | container = container_parent(container, C_OUTPUT); | 742 | container = container_parent(container, C_OUTPUT); |
703 | parent = container->parent; | 743 | parent = container->parent; |
704 | } else { | 744 | } else { |
705 | if (dir == MOVE_CHILD) { | ||
706 | return seat_get_focus_inactive(seat, container); | ||
707 | } | ||
708 | if (dir == MOVE_PARENT) { | 745 | if (dir == MOVE_PARENT) { |
709 | if (parent->type == C_OUTPUT) { | 746 | if (parent->type == C_OUTPUT || container_is_floating(container)) { |
710 | return NULL; | 747 | return NULL; |
711 | } else { | 748 | } else { |
712 | return parent; | 749 | return parent; |
@@ -755,7 +792,8 @@ struct sway_container *container_get_in_direction( | |||
755 | } | 792 | } |
756 | sway_assert(next_workspace, "Next container has no workspace"); | 793 | sway_assert(next_workspace, "Next container has no workspace"); |
757 | if (next_workspace->sway_workspace->fullscreen) { | 794 | if (next_workspace->sway_workspace->fullscreen) { |
758 | return next_workspace->sway_workspace->fullscreen->swayc; | 795 | return seat_get_focus_inactive(seat, |
796 | next_workspace->sway_workspace->fullscreen); | ||
759 | } | 797 | } |
760 | if (next->children && next->children->length) { | 798 | if (next->children && next->children->length) { |
761 | // TODO consider floating children as well | 799 | // TODO consider floating children as well |
@@ -963,13 +1001,13 @@ static void swap_focus(struct sway_container *con1, | |||
963 | if (focus == con1 && (con2->parent->layout == L_TABBED | 1001 | if (focus == con1 && (con2->parent->layout == L_TABBED |
964 | || con2->parent->layout == L_STACKED)) { | 1002 | || con2->parent->layout == L_STACKED)) { |
965 | if (workspace_is_visible(ws2)) { | 1003 | if (workspace_is_visible(ws2)) { |
966 | seat_set_focus_warp(seat, con2, false); | 1004 | seat_set_focus_warp(seat, con2, false, true); |
967 | } | 1005 | } |
968 | seat_set_focus(seat, ws1 != ws2 ? con2 : con1); | 1006 | seat_set_focus(seat, ws1 != ws2 ? con2 : con1); |
969 | } else if (focus == con2 && (con1->parent->layout == L_TABBED | 1007 | } else if (focus == con2 && (con1->parent->layout == L_TABBED |
970 | || con1->parent->layout == L_STACKED)) { | 1008 | || con1->parent->layout == L_STACKED)) { |
971 | if (workspace_is_visible(ws1)) { | 1009 | if (workspace_is_visible(ws1)) { |
972 | seat_set_focus_warp(seat, con1, false); | 1010 | seat_set_focus_warp(seat, con1, false, true); |
973 | } | 1011 | } |
974 | seat_set_focus(seat, ws1 != ws2 ? con1 : con2); | 1012 | seat_set_focus(seat, ws1 != ws2 ? con1 : con2); |
975 | } else if (ws1 != ws2) { | 1013 | } else if (ws1 != ws2) { |
@@ -1002,13 +1040,13 @@ void container_swap(struct sway_container *con1, struct sway_container *con2) { | |||
1002 | 1040 | ||
1003 | wlr_log(WLR_DEBUG, "Swapping containers %zu and %zu", con1->id, con2->id); | 1041 | wlr_log(WLR_DEBUG, "Swapping containers %zu and %zu", con1->id, con2->id); |
1004 | 1042 | ||
1005 | int fs1 = con1->type == C_VIEW && con1->sway_view->is_fullscreen; | 1043 | int fs1 = con1->is_fullscreen; |
1006 | int fs2 = con2->type == C_VIEW && con2->sway_view->is_fullscreen; | 1044 | int fs2 = con2->is_fullscreen; |
1007 | if (fs1) { | 1045 | if (fs1) { |
1008 | view_set_fullscreen(con1->sway_view, false); | 1046 | container_set_fullscreen(con1, false); |
1009 | } | 1047 | } |
1010 | if (fs2) { | 1048 | if (fs2) { |
1011 | view_set_fullscreen(con2->sway_view, false); | 1049 | container_set_fullscreen(con2, false); |
1012 | } | 1050 | } |
1013 | 1051 | ||
1014 | struct sway_seat *seat = input_manager_get_default_seat(input_manager); | 1052 | struct sway_seat *seat = input_manager_get_default_seat(input_manager); |
@@ -1041,10 +1079,10 @@ void container_swap(struct sway_container *con1, struct sway_container *con2) { | |||
1041 | prev_workspace_name = stored_prev_name; | 1079 | prev_workspace_name = stored_prev_name; |
1042 | } | 1080 | } |
1043 | 1081 | ||
1044 | if (fs1 && con2->type == C_VIEW) { | 1082 | if (fs1) { |
1045 | view_set_fullscreen(con2->sway_view, true); | 1083 | container_set_fullscreen(con2, true); |
1046 | } | 1084 | } |
1047 | if (fs2 && con1->type == C_VIEW) { | 1085 | if (fs2) { |
1048 | view_set_fullscreen(con1->sway_view, true); | 1086 | container_set_fullscreen(con1, true); |
1049 | } | 1087 | } |
1050 | } | 1088 | } |
diff --git a/sway/tree/output.c b/sway/tree/output.c index da535c18..31e3bf9b 100644 --- a/sway/tree/output.c +++ b/sway/tree/output.c | |||
@@ -22,7 +22,7 @@ static void restore_workspaces(struct sway_container *output) { | |||
22 | if (highest == output) { | 22 | if (highest == output) { |
23 | container_remove_child(ws); | 23 | container_remove_child(ws); |
24 | container_add_child(output, ws); | 24 | container_add_child(output, ws); |
25 | ipc_event_workspace(ws, NULL, "move"); | 25 | ipc_event_workspace(NULL, ws, "move"); |
26 | j--; | 26 | j--; |
27 | } | 27 | } |
28 | } | 28 | } |
diff --git a/sway/tree/view.c b/sway/tree/view.c index 7881e6d7..97318daa 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c | |||
@@ -2,7 +2,12 @@ | |||
2 | #include <stdlib.h> | 2 | #include <stdlib.h> |
3 | #include <wayland-server.h> | 3 | #include <wayland-server.h> |
4 | #include <wlr/render/wlr_renderer.h> | 4 | #include <wlr/render/wlr_renderer.h> |
5 | #include <wlr/types/wlr_buffer.h> | ||
5 | #include <wlr/types/wlr_output_layout.h> | 6 | #include <wlr/types/wlr_output_layout.h> |
7 | #include "config.h" | ||
8 | #ifdef HAVE_XWAYLAND | ||
9 | #include <wlr/xwayland.h> | ||
10 | #endif | ||
6 | #include "list.h" | 11 | #include "list.h" |
7 | #include "log.h" | 12 | #include "log.h" |
8 | #include "sway/criteria.h" | 13 | #include "sway/criteria.h" |
@@ -107,14 +112,14 @@ const char *view_get_instance(struct sway_view *view) { | |||
107 | } | 112 | } |
108 | return NULL; | 113 | return NULL; |
109 | } | 114 | } |
110 | 115 | #ifdef HAVE_XWAYLAND | |
111 | uint32_t view_get_x11_window_id(struct sway_view *view) { | 116 | uint32_t view_get_x11_window_id(struct sway_view *view) { |
112 | if (view->impl->get_int_prop) { | 117 | if (view->impl->get_int_prop) { |
113 | return view->impl->get_int_prop(view, VIEW_PROP_X11_WINDOW_ID); | 118 | return view->impl->get_int_prop(view, VIEW_PROP_X11_WINDOW_ID); |
114 | } | 119 | } |
115 | return 0; | 120 | return 0; |
116 | } | 121 | } |
117 | 122 | #endif | |
118 | const char *view_get_window_role(struct sway_view *view) { | 123 | const char *view_get_window_role(struct sway_view *view) { |
119 | if (view->impl->get_string_prop) { | 124 | if (view->impl->get_string_prop) { |
120 | return view->impl->get_string_prop(view, VIEW_PROP_WINDOW_ROLE); | 125 | return view->impl->get_string_prop(view, VIEW_PROP_WINDOW_ROLE); |
@@ -135,12 +140,27 @@ const char *view_get_shell(struct sway_view *view) { | |||
135 | return "xdg_shell_v6"; | 140 | return "xdg_shell_v6"; |
136 | case SWAY_VIEW_XDG_SHELL: | 141 | case SWAY_VIEW_XDG_SHELL: |
137 | return "xdg_shell"; | 142 | return "xdg_shell"; |
143 | #ifdef HAVE_XWAYLAND | ||
138 | case SWAY_VIEW_XWAYLAND: | 144 | case SWAY_VIEW_XWAYLAND: |
139 | return "xwayland"; | 145 | return "xwayland"; |
146 | #endif | ||
140 | } | 147 | } |
141 | return "unknown"; | 148 | return "unknown"; |
142 | } | 149 | } |
143 | 150 | ||
151 | void view_get_constraints(struct sway_view *view, double *min_width, | ||
152 | double *max_width, double *min_height, double *max_height) { | ||
153 | if (view->impl->get_constraints) { | ||
154 | view->impl->get_constraints(view, | ||
155 | min_width, max_width, min_height, max_height); | ||
156 | } else { | ||
157 | *min_width = DBL_MIN; | ||
158 | *max_width = DBL_MAX; | ||
159 | *min_height = DBL_MIN; | ||
160 | *max_height = DBL_MAX; | ||
161 | } | ||
162 | } | ||
163 | |||
144 | uint32_t view_configure(struct sway_view *view, double lx, double ly, int width, | 164 | uint32_t view_configure(struct sway_view *view, double lx, double ly, int width, |
145 | int height) { | 165 | int height) { |
146 | if (view->impl->configure) { | 166 | if (view->impl->configure) { |
@@ -149,55 +169,6 @@ uint32_t view_configure(struct sway_view *view, double lx, double ly, int width, | |||
149 | return 0; | 169 | return 0; |
150 | } | 170 | } |
151 | 171 | ||
152 | void view_init_floating(struct sway_view *view) { | ||
153 | struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); | ||
154 | int min_width, min_height; | ||
155 | int max_width, max_height; | ||
156 | |||
157 | if (config->floating_minimum_width == -1) { // no minimum | ||
158 | min_width = 0; | ||
159 | } else if (config->floating_minimum_width == 0) { // automatic | ||
160 | min_width = 75; | ||
161 | } else { | ||
162 | min_width = config->floating_minimum_width; | ||
163 | } | ||
164 | |||
165 | if (config->floating_minimum_height == -1) { // no minimum | ||
166 | min_height = 0; | ||
167 | } else if (config->floating_minimum_height == 0) { // automatic | ||
168 | min_height = 50; | ||
169 | } else { | ||
170 | min_height = config->floating_minimum_height; | ||
171 | } | ||
172 | |||
173 | if (config->floating_maximum_width == -1) { // no maximum | ||
174 | max_width = INT_MAX; | ||
175 | } else if (config->floating_maximum_width == 0) { // automatic | ||
176 | max_width = ws->width * 0.6666; | ||
177 | } else { | ||
178 | max_width = config->floating_maximum_width; | ||
179 | } | ||
180 | |||
181 | if (config->floating_maximum_height == -1) { // no maximum | ||
182 | max_height = INT_MAX; | ||
183 | } else if (config->floating_maximum_height == 0) { // automatic | ||
184 | max_height = ws->height * 0.6666; | ||
185 | } else { | ||
186 | max_height = config->floating_maximum_height; | ||
187 | } | ||
188 | |||
189 | view->width = fmax(min_width, fmin(view->natural_width, max_width)); | ||
190 | view->height = fmax(min_height, fmin(view->natural_height, max_height)); | ||
191 | view->x = ws->x + (ws->width - view->width) / 2; | ||
192 | view->y = ws->y + (ws->height - view->height) / 2; | ||
193 | |||
194 | // If the view's border is B_NONE then these properties are ignored. | ||
195 | view->border_top = view->border_bottom = true; | ||
196 | view->border_left = view->border_right = true; | ||
197 | |||
198 | container_set_geometry_from_floating_view(view->swayc); | ||
199 | } | ||
200 | |||
201 | void view_autoconfigure(struct sway_view *view) { | 172 | void view_autoconfigure(struct sway_view *view) { |
202 | if (!sway_assert(view->swayc, | 173 | if (!sway_assert(view->swayc, |
203 | "Called view_autoconfigure() on a view without a swayc")) { | 174 | "Called view_autoconfigure() on a view without a swayc")) { |
@@ -206,7 +177,7 @@ void view_autoconfigure(struct sway_view *view) { | |||
206 | 177 | ||
207 | struct sway_container *output = container_parent(view->swayc, C_OUTPUT); | 178 | struct sway_container *output = container_parent(view->swayc, C_OUTPUT); |
208 | 179 | ||
209 | if (view->is_fullscreen) { | 180 | if (view->swayc->is_fullscreen) { |
210 | view->x = output->x; | 181 | view->x = output->x; |
211 | view->y = output->y; | 182 | view->y = output->y; |
212 | view->width = output->width; | 183 | view->width = output->width; |
@@ -214,10 +185,6 @@ void view_autoconfigure(struct sway_view *view) { | |||
214 | return; | 185 | return; |
215 | } | 186 | } |
216 | 187 | ||
217 | if (container_is_floating(view->swayc)) { | ||
218 | return; | ||
219 | } | ||
220 | |||
221 | struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); | 188 | struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); |
222 | 189 | ||
223 | int other_views = 0; | 190 | int other_views = 0; |
@@ -330,72 +297,18 @@ void view_set_tiled(struct sway_view *view, bool tiled) { | |||
330 | } | 297 | } |
331 | } | 298 | } |
332 | 299 | ||
333 | void view_set_fullscreen(struct sway_view *view, bool fullscreen) { | ||
334 | if (view->is_fullscreen == fullscreen) { | ||
335 | return; | ||
336 | } | ||
337 | |||
338 | struct sway_container *workspace = | ||
339 | container_parent(view->swayc, C_WORKSPACE); | ||
340 | |||
341 | if (view->impl->set_fullscreen) { | ||
342 | view->impl->set_fullscreen(view, fullscreen); | ||
343 | } | ||
344 | |||
345 | view->is_fullscreen = fullscreen; | ||
346 | |||
347 | if (fullscreen) { | ||
348 | if (workspace->sway_workspace->fullscreen) { | ||
349 | view_set_fullscreen(workspace->sway_workspace->fullscreen, false); | ||
350 | } | ||
351 | workspace->sway_workspace->fullscreen = view; | ||
352 | view->saved_x = view->x; | ||
353 | view->saved_y = view->y; | ||
354 | view->saved_width = view->width; | ||
355 | view->saved_height = view->height; | ||
356 | view->swayc->saved_x = view->swayc->x; | ||
357 | view->swayc->saved_y = view->swayc->y; | ||
358 | view->swayc->saved_width = view->swayc->width; | ||
359 | view->swayc->saved_height = view->swayc->height; | ||
360 | |||
361 | struct sway_seat *seat; | ||
362 | struct sway_container *focus, *focus_ws; | ||
363 | wl_list_for_each(seat, &input_manager->seats, link) { | ||
364 | focus = seat_get_focus(seat); | ||
365 | if (focus) { | ||
366 | focus_ws = focus; | ||
367 | if (focus && focus_ws->type != C_WORKSPACE) { | ||
368 | focus_ws = container_parent(focus_ws, C_WORKSPACE); | ||
369 | } | ||
370 | seat_set_focus(seat, view->swayc); | ||
371 | if (focus_ws != workspace) { | ||
372 | seat_set_focus(seat, focus); | ||
373 | } | ||
374 | } | ||
375 | } | ||
376 | } else { | ||
377 | workspace->sway_workspace->fullscreen = NULL; | ||
378 | if (container_is_floating(view->swayc)) { | ||
379 | view->x = view->saved_x; | ||
380 | view->y = view->saved_y; | ||
381 | view->width = view->saved_width; | ||
382 | view->height = view->saved_height; | ||
383 | container_set_geometry_from_floating_view(view->swayc); | ||
384 | } else { | ||
385 | view->swayc->width = view->swayc->saved_width; | ||
386 | view->swayc->height = view->swayc->saved_height; | ||
387 | } | ||
388 | } | ||
389 | |||
390 | ipc_event_window(view->swayc, "fullscreen_mode"); | ||
391 | } | ||
392 | |||
393 | void view_close(struct sway_view *view) { | 300 | void view_close(struct sway_view *view) { |
394 | if (view->impl->close) { | 301 | if (view->impl->close) { |
395 | view->impl->close(view); | 302 | view->impl->close(view); |
396 | } | 303 | } |
397 | } | 304 | } |
398 | 305 | ||
306 | void view_close_popups(struct sway_view *view) { | ||
307 | if (view->impl->close_popups) { | ||
308 | view->impl->close_popups(view); | ||
309 | } | ||
310 | } | ||
311 | |||
399 | void view_damage_from(struct sway_view *view) { | 312 | void view_damage_from(struct sway_view *view) { |
400 | for (int i = 0; i < root_container.children->length; ++i) { | 313 | for (int i = 0; i < root_container.children->length; ++i) { |
401 | struct sway_container *cont = root_container.children->items[i]; | 314 | struct sway_container *cont = root_container.children->items[i]; |
@@ -426,6 +339,16 @@ void view_for_each_surface(struct sway_view *view, | |||
426 | } | 339 | } |
427 | } | 340 | } |
428 | 341 | ||
342 | void view_for_each_popup(struct sway_view *view, | ||
343 | wlr_surface_iterator_func_t iterator, void *user_data) { | ||
344 | if (!view->surface) { | ||
345 | return; | ||
346 | } | ||
347 | if (view->impl->for_each_popup) { | ||
348 | view->impl->for_each_popup(view, iterator, user_data); | ||
349 | } | ||
350 | } | ||
351 | |||
429 | static void view_subsurface_create(struct sway_view *view, | 352 | static void view_subsurface_create(struct sway_view *view, |
430 | struct wlr_subsurface *subsurface); | 353 | struct wlr_subsurface *subsurface); |
431 | 354 | ||
@@ -521,12 +444,82 @@ void view_execute_criteria(struct sway_view *view) { | |||
521 | seat_set_focus(seat, prior_focus); | 444 | seat_set_focus(seat, prior_focus); |
522 | } | 445 | } |
523 | 446 | ||
447 | static struct sway_container *select_workspace(struct sway_view *view) { | ||
448 | struct sway_seat *seat = input_manager_current_seat(input_manager); | ||
449 | |||
450 | // Check if there's any `assign` criteria for the view | ||
451 | list_t *criterias = criteria_for_view(view, | ||
452 | CT_ASSIGN_WORKSPACE | CT_ASSIGN_OUTPUT); | ||
453 | struct sway_container *ws = NULL; | ||
454 | for (int i = 0; i < criterias->length; ++i) { | ||
455 | struct criteria *criteria = criterias->items[i]; | ||
456 | if (criteria->type == CT_ASSIGN_WORKSPACE) { | ||
457 | ws = workspace_by_name(criteria->target); | ||
458 | if (!ws) { | ||
459 | ws = workspace_create(NULL, criteria->target); | ||
460 | } | ||
461 | break; | ||
462 | } else { | ||
463 | // CT_ASSIGN_OUTPUT | ||
464 | struct sway_container *output = output_by_name(criteria->target); | ||
465 | if (output) { | ||
466 | ws = seat_get_active_child(seat, output); | ||
467 | break; | ||
468 | } | ||
469 | } | ||
470 | } | ||
471 | list_free(criterias); | ||
472 | if (ws) { | ||
473 | return ws; | ||
474 | } | ||
475 | |||
476 | // Check if there's a PID mapping | ||
477 | pid_t pid; | ||
478 | #ifdef HAVE_XWAYLAND | ||
479 | if (view->type == SWAY_VIEW_XWAYLAND) { | ||
480 | struct wlr_xwayland_surface *surf = | ||
481 | wlr_xwayland_surface_from_wlr_surface(view->surface); | ||
482 | pid = surf->pid; | ||
483 | } else { | ||
484 | struct wl_client *client = | ||
485 | wl_resource_get_client(view->surface->resource); | ||
486 | wl_client_get_credentials(client, &pid, NULL, NULL); | ||
487 | } | ||
488 | #else | ||
489 | struct wl_client *client = | ||
490 | wl_resource_get_client(view->surface->resource); | ||
491 | wl_client_get_credentials(client, &pid, NULL, NULL); | ||
492 | #endif | ||
493 | ws = workspace_for_pid(pid); | ||
494 | if (ws) { | ||
495 | return ws; | ||
496 | } | ||
497 | |||
498 | // Use the focused workspace | ||
499 | ws = seat_get_focus_inactive(seat, &root_container); | ||
500 | if (ws->type != C_WORKSPACE) { | ||
501 | ws = container_parent(ws, C_WORKSPACE); | ||
502 | } | ||
503 | return ws; | ||
504 | } | ||
505 | |||
524 | static bool should_focus(struct sway_view *view) { | 506 | static bool should_focus(struct sway_view *view) { |
507 | struct sway_seat *seat = input_manager_current_seat(input_manager); | ||
508 | struct sway_container *prev_focus = | ||
509 | seat_get_focus_inactive(seat, &root_container); | ||
510 | struct sway_container *prev_ws = prev_focus->type == C_WORKSPACE ? | ||
511 | prev_focus : container_parent(prev_focus, C_WORKSPACE); | ||
512 | struct sway_container *map_ws = container_parent(view->swayc, C_WORKSPACE); | ||
513 | |||
514 | // Views can only take focus if they are mapped into the active workspace | ||
515 | if (prev_ws != map_ws) { | ||
516 | return false; | ||
517 | } | ||
518 | |||
525 | // If the view is the only one in the focused workspace, it'll get focus | 519 | // If the view is the only one in the focused workspace, it'll get focus |
526 | // regardless of any no_focus criteria. | 520 | // regardless of any no_focus criteria. |
527 | struct sway_container *parent = view->swayc->parent; | 521 | struct sway_container *parent = view->swayc->parent; |
528 | struct sway_seat *seat = input_manager_current_seat(input_manager); | 522 | if (parent->type == C_WORKSPACE && prev_focus == parent) { |
529 | if (parent->type == C_WORKSPACE && seat_get_focus(seat) == parent) { | ||
530 | size_t num_children = parent->children->length + | 523 | size_t num_children = parent->children->length + |
531 | parent->sway_workspace->floating->children->length; | 524 | parent->sway_workspace->floating->children->length; |
532 | if (num_children == 1) { | 525 | if (num_children == 1) { |
@@ -545,42 +538,19 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface) { | |||
545 | if (!sway_assert(view->surface == NULL, "cannot map mapped view")) { | 538 | if (!sway_assert(view->surface == NULL, "cannot map mapped view")) { |
546 | return; | 539 | return; |
547 | } | 540 | } |
541 | view->surface = wlr_surface; | ||
548 | 542 | ||
549 | struct sway_seat *seat = input_manager_current_seat(input_manager); | 543 | struct sway_seat *seat = input_manager_current_seat(input_manager); |
550 | struct sway_container *focus = | 544 | struct sway_container *ws = select_workspace(view); |
551 | seat_get_focus_inactive(seat, &root_container); | 545 | struct sway_container *target_sibling = seat_get_focus_inactive(seat, ws); |
552 | struct sway_container *cont = NULL; | ||
553 | 546 | ||
554 | // Check if there's any `assign` criteria for the view | ||
555 | list_t *criterias = criteria_for_view(view, | ||
556 | CT_ASSIGN_WORKSPACE | CT_ASSIGN_OUTPUT); | ||
557 | struct sway_container *workspace = NULL; | ||
558 | if (criterias->length) { | ||
559 | struct criteria *criteria = criterias->items[0]; | ||
560 | if (criteria->type == CT_ASSIGN_WORKSPACE) { | ||
561 | workspace = workspace_by_name(criteria->target); | ||
562 | if (!workspace) { | ||
563 | workspace = workspace_create(NULL, criteria->target); | ||
564 | } | ||
565 | focus = seat_get_focus_inactive(seat, workspace); | ||
566 | } else { | ||
567 | // CT_ASSIGN_OUTPUT | ||
568 | struct sway_container *output = output_by_name(criteria->target); | ||
569 | if (output) { | ||
570 | focus = seat_get_focus_inactive(seat, output); | ||
571 | } | ||
572 | } | ||
573 | } | ||
574 | // If we're about to launch the view into the floating container, then | 547 | // If we're about to launch the view into the floating container, then |
575 | // launch it as a tiled view in the root of the workspace instead. | 548 | // launch it as a tiled view in the root of the workspace instead. |
576 | if (container_is_floating(focus)) { | 549 | if (container_is_floating(target_sibling)) { |
577 | focus = focus->parent->parent; | 550 | target_sibling = target_sibling->parent->parent; |
578 | } | 551 | } |
579 | list_free(criterias); | ||
580 | cont = container_view_create(focus, view); | ||
581 | 552 | ||
582 | view->surface = wlr_surface; | 553 | view->swayc = container_view_create(target_sibling, view); |
583 | view->swayc = cont; | ||
584 | 554 | ||
585 | view_init_subsurfaces(view, wlr_surface); | 555 | view_init_subsurfaces(view, wlr_surface); |
586 | wl_signal_add(&wlr_surface->events.new_subsurface, | 556 | wl_signal_add(&wlr_surface->events.new_subsurface, |
@@ -601,10 +571,7 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface) { | |||
601 | } | 571 | } |
602 | 572 | ||
603 | if (should_focus(view)) { | 573 | if (should_focus(view)) { |
604 | input_manager_set_focus(input_manager, cont); | 574 | input_manager_set_focus(input_manager, view->swayc); |
605 | if (workspace) { | ||
606 | workspace_switch(workspace); | ||
607 | } | ||
608 | } | 575 | } |
609 | 576 | ||
610 | view_update_title(view, false); | 577 | view_update_title(view, false); |
@@ -628,10 +595,8 @@ void view_unmap(struct sway_view *view) { | |||
628 | struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); | 595 | struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); |
629 | 596 | ||
630 | struct sway_container *parent; | 597 | struct sway_container *parent; |
631 | if (view->is_fullscreen) { | 598 | if (container_is_fullscreen_or_child(view->swayc)) { |
632 | ws->sway_workspace->fullscreen = NULL; | ||
633 | parent = container_destroy(view->swayc); | 599 | parent = container_destroy(view->swayc); |
634 | |||
635 | arrange_windows(ws->parent); | 600 | arrange_windows(ws->parent); |
636 | } else { | 601 | } else { |
637 | parent = container_destroy(view->swayc); | 602 | parent = container_destroy(view->swayc); |
@@ -784,11 +749,13 @@ struct sway_view *view_from_wlr_surface(struct wlr_surface *wlr_surface) { | |||
784 | wlr_xdg_surface_v6_from_wlr_surface(wlr_surface); | 749 | wlr_xdg_surface_v6_from_wlr_surface(wlr_surface); |
785 | return view_from_wlr_xdg_surface_v6(xdg_surface_v6); | 750 | return view_from_wlr_xdg_surface_v6(xdg_surface_v6); |
786 | } | 751 | } |
752 | #ifdef HAVE_XWAYLAND | ||
787 | if (wlr_surface_is_xwayland_surface(wlr_surface)) { | 753 | if (wlr_surface_is_xwayland_surface(wlr_surface)) { |
788 | struct wlr_xwayland_surface *xsurface = | 754 | struct wlr_xwayland_surface *xsurface = |
789 | wlr_xwayland_surface_from_wlr_surface(wlr_surface); | 755 | wlr_xwayland_surface_from_wlr_surface(wlr_surface); |
790 | return view_from_wlr_xwayland_surface(xsurface); | 756 | return view_from_wlr_xwayland_surface(xsurface); |
791 | } | 757 | } |
758 | #endif | ||
792 | if (wlr_surface_is_subsurface(wlr_surface)) { | 759 | if (wlr_surface_is_subsurface(wlr_surface)) { |
793 | struct wlr_subsurface *subsurface = | 760 | struct wlr_subsurface *subsurface = |
794 | wlr_subsurface_from_wlr_surface(wlr_surface); | 761 | wlr_subsurface_from_wlr_surface(wlr_surface); |
@@ -915,6 +882,8 @@ void view_update_title(struct sway_view *view, bool force) { | |||
915 | 882 | ||
916 | // Update title after the global font height is updated | 883 | // Update title after the global font height is updated |
917 | container_update_title_textures(view->swayc); | 884 | container_update_title_textures(view->swayc); |
885 | |||
886 | ipc_event_window(view->swayc, "title"); | ||
918 | } | 887 | } |
919 | 888 | ||
920 | static bool find_by_mark_iterator(struct sway_container *con, | 889 | static bool find_by_mark_iterator(struct sway_container *con, |
@@ -937,6 +906,7 @@ bool view_find_and_unmark(char *mark) { | |||
937 | free(view_mark); | 906 | free(view_mark); |
938 | list_del(view->marks, i); | 907 | list_del(view->marks, i); |
939 | view_update_marks_textures(view); | 908 | view_update_marks_textures(view); |
909 | ipc_event_window(container, "mark"); | ||
940 | return true; | 910 | return true; |
941 | } | 911 | } |
942 | } | 912 | } |
@@ -944,11 +914,10 @@ bool view_find_and_unmark(char *mark) { | |||
944 | } | 914 | } |
945 | 915 | ||
946 | void view_clear_marks(struct sway_view *view) { | 916 | void view_clear_marks(struct sway_view *view) { |
947 | for (int i = 0; i < view->marks->length; ++i) { | 917 | while (view->marks->length) { |
948 | free(view->marks->items[i]); | 918 | list_del(view->marks, 0); |
919 | ipc_event_window(view->swayc, "mark"); | ||
949 | } | 920 | } |
950 | list_free(view->marks); | ||
951 | view->marks = create_list(); | ||
952 | } | 921 | } |
953 | 922 | ||
954 | bool view_has_mark(struct sway_view *view, char *mark) { | 923 | bool view_has_mark(struct sway_view *view, char *mark) { |
@@ -961,6 +930,11 @@ bool view_has_mark(struct sway_view *view, char *mark) { | |||
961 | return false; | 930 | return false; |
962 | } | 931 | } |
963 | 932 | ||
933 | void view_add_mark(struct sway_view *view, char *mark) { | ||
934 | list_add(view->marks, strdup(mark)); | ||
935 | ipc_event_window(view->swayc, "mark"); | ||
936 | } | ||
937 | |||
964 | static void update_marks_texture(struct sway_view *view, | 938 | static void update_marks_texture(struct sway_view *view, |
965 | struct wlr_texture **texture, struct border_colors *class) { | 939 | struct wlr_texture **texture, struct border_colors *class) { |
966 | struct sway_container *output = container_parent(view->swayc, C_OUTPUT); | 940 | struct sway_container *output = container_parent(view->swayc, C_OUTPUT); |
@@ -1055,6 +1029,9 @@ bool view_is_visible(struct sway_view *view) { | |||
1055 | } | 1029 | } |
1056 | struct sway_container *workspace = | 1030 | struct sway_container *workspace = |
1057 | container_parent(view->swayc, C_WORKSPACE); | 1031 | container_parent(view->swayc, C_WORKSPACE); |
1032 | if (!workspace) { | ||
1033 | return false; | ||
1034 | } | ||
1058 | // Determine if view is nested inside a floating container which is sticky. | 1035 | // Determine if view is nested inside a floating container which is sticky. |
1059 | // A simple floating view will have this ancestry: | 1036 | // A simple floating view will have this ancestry: |
1060 | // C_VIEW -> floating -> workspace | 1037 | // C_VIEW -> floating -> workspace |
@@ -1079,7 +1056,8 @@ bool view_is_visible(struct sway_view *view) { | |||
1079 | container = container->parent; | 1056 | container = container->parent; |
1080 | } | 1057 | } |
1081 | // Check view isn't hidden by another fullscreen view | 1058 | // Check view isn't hidden by another fullscreen view |
1082 | if (workspace->sway_workspace->fullscreen && !view->is_fullscreen) { | 1059 | if (workspace->sway_workspace->fullscreen && |
1060 | !container_is_fullscreen_or_child(view->swayc)) { | ||
1083 | return false; | 1061 | return false; |
1084 | } | 1062 | } |
1085 | // Check the workspace is visible | 1063 | // Check the workspace is visible |
@@ -1117,3 +1095,22 @@ void view_set_urgent(struct sway_view *view, bool enable) { | |||
1117 | bool view_is_urgent(struct sway_view *view) { | 1095 | bool view_is_urgent(struct sway_view *view) { |
1118 | return view->urgent.tv_sec || view->urgent.tv_nsec; | 1096 | return view->urgent.tv_sec || view->urgent.tv_nsec; |
1119 | } | 1097 | } |
1098 | |||
1099 | void view_remove_saved_buffer(struct sway_view *view) { | ||
1100 | if (!sway_assert(view->saved_buffer, "Expected a saved buffer")) { | ||
1101 | return; | ||
1102 | } | ||
1103 | wlr_buffer_unref(view->saved_buffer); | ||
1104 | view->saved_buffer = NULL; | ||
1105 | } | ||
1106 | |||
1107 | void view_save_buffer(struct sway_view *view) { | ||
1108 | if (!sway_assert(!view->saved_buffer, "Didn't expect saved buffer")) { | ||
1109 | view_remove_saved_buffer(view); | ||
1110 | } | ||
1111 | if (view->surface && wlr_surface_has_buffer(view->surface)) { | ||
1112 | view->saved_buffer = wlr_buffer_ref(view->surface->buffer); | ||
1113 | view->saved_buffer_width = view->surface->current.width; | ||
1114 | view->saved_buffer_height = view->surface->current.height; | ||
1115 | } | ||
1116 | } | ||
diff --git a/sway/tree/workspace.c b/sway/tree/workspace.c index 622f01ec..588e2aae 100644 --- a/sway/tree/workspace.c +++ b/sway/tree/workspace.c | |||
@@ -9,6 +9,7 @@ | |||
9 | #include "sway/input/input-manager.h" | 9 | #include "sway/input/input-manager.h" |
10 | #include "sway/input/seat.h" | 10 | #include "sway/input/seat.h" |
11 | #include "sway/ipc-server.h" | 11 | #include "sway/ipc-server.h" |
12 | #include "sway/output.h" | ||
12 | #include "sway/tree/arrange.h" | 13 | #include "sway/tree/arrange.h" |
13 | #include "sway/tree/container.h" | 14 | #include "sway/tree/container.h" |
14 | #include "sway/tree/view.h" | 15 | #include "sway/tree/view.h" |
@@ -107,96 +108,100 @@ static bool workspace_valid_on_output(const char *output_name, | |||
107 | return true; | 108 | return true; |
108 | } | 109 | } |
109 | 110 | ||
110 | char *workspace_next_name(const char *output_name) { | 111 | static void workspace_name_from_binding(const struct sway_binding * binding, |
111 | wlr_log(WLR_DEBUG, "Workspace: Generating new workspace name for output %s", | 112 | const char* output_name, int *min_order, char **earliest_name) { |
112 | output_name); | 113 | char *cmdlist = strdup(binding->command); |
113 | // Scan all workspace bindings to find the next available workspace name, | 114 | char *dup = cmdlist; |
114 | // if none are found/available then default to a number | 115 | char *name = NULL; |
115 | struct sway_mode *mode = config->current_mode; | ||
116 | 116 | ||
117 | // TODO: iterate over keycode bindings too | 117 | // workspace n |
118 | int order = INT_MAX; | 118 | char *cmd = argsep(&cmdlist, " "); |
119 | char *target = NULL; | 119 | if (cmdlist) { |
120 | for (int i = 0; i < mode->keysym_bindings->length; ++i) { | 120 | name = argsep(&cmdlist, ",;"); |
121 | struct sway_binding *binding = mode->keysym_bindings->items[i]; | 121 | } |
122 | char *cmdlist = strdup(binding->command); | ||
123 | char *dup = cmdlist; | ||
124 | char *name = NULL; | ||
125 | |||
126 | // workspace n | ||
127 | char *cmd = argsep(&cmdlist, " "); | ||
128 | if (cmdlist) { | ||
129 | name = argsep(&cmdlist, ",;"); | ||
130 | } | ||
131 | 122 | ||
132 | if (strcmp("workspace", cmd) == 0 && name) { | 123 | if (strcmp("workspace", cmd) == 0 && name) { |
133 | char *_target = strdup(name); | 124 | char *_target = strdup(name); |
134 | _target = do_var_replacement(_target); | 125 | _target = do_var_replacement(_target); |
135 | strip_quotes(_target); | 126 | strip_quotes(_target); |
136 | while (isspace(*_target)) { | 127 | wlr_log(WLR_DEBUG, "Got valid workspace command for target: '%s'", |
137 | memmove(_target, _target+1, strlen(_target+1)); | 128 | _target); |
138 | } | ||
139 | wlr_log(WLR_DEBUG, "Got valid workspace command for target: '%s'", | ||
140 | _target); | ||
141 | 129 | ||
142 | // Make sure that the command references an actual workspace | 130 | // Make sure that the command references an actual workspace |
143 | // not a command about workspaces | 131 | // not a command about workspaces |
144 | if (strcmp(_target, "next") == 0 || | 132 | if (strcmp(_target, "next") == 0 || |
145 | strcmp(_target, "prev") == 0 || | 133 | strcmp(_target, "prev") == 0 || |
146 | strcmp(_target, "next_on_output") == 0 || | 134 | strcmp(_target, "next_on_output") == 0 || |
147 | strcmp(_target, "prev_on_output") == 0 || | 135 | strcmp(_target, "prev_on_output") == 0 || |
148 | strcmp(_target, "number") == 0 || | 136 | strcmp(_target, "number") == 0 || |
149 | strcmp(_target, "back_and_forth") == 0 || | 137 | strcmp(_target, "back_and_forth") == 0 || |
150 | strcmp(_target, "current") == 0) | 138 | strcmp(_target, "current") == 0) { |
151 | { | 139 | free(_target); |
152 | free(_target); | 140 | free(dup); |
153 | free(dup); | 141 | return; |
154 | continue; | 142 | } |
155 | } | ||
156 | |||
157 | // If the command is workspace number <name>, isolate the name | ||
158 | if (strncmp(_target, "number ", strlen("number ")) == 0) { | ||
159 | size_t length = strlen(_target) - strlen("number ") + 1; | ||
160 | char *temp = malloc(length); | ||
161 | strncpy(temp, _target + strlen("number "), length - 1); | ||
162 | temp[length - 1] = '\0'; | ||
163 | free(_target); | ||
164 | _target = temp; | ||
165 | wlr_log(WLR_DEBUG, "Isolated name from workspace number: '%s'", _target); | ||
166 | |||
167 | // Make sure the workspace number doesn't already exist | ||
168 | if (workspace_by_number(_target)) { | ||
169 | free(_target); | ||
170 | free(dup); | ||
171 | continue; | ||
172 | } | ||
173 | } | ||
174 | 143 | ||
175 | // Make sure that the workspace doesn't already exist | 144 | // If the command is workspace number <name>, isolate the name |
176 | if (workspace_by_name(_target)) { | 145 | if (strncmp(_target, "number ", strlen("number ")) == 0) { |
146 | size_t length = strlen(_target) - strlen("number ") + 1; | ||
147 | char *temp = malloc(length); | ||
148 | strncpy(temp, _target + strlen("number "), length - 1); | ||
149 | temp[length - 1] = '\0'; | ||
150 | free(_target); | ||
151 | _target = temp; | ||
152 | wlr_log(WLR_DEBUG, "Isolated name from workspace number: '%s'", _target); | ||
153 | |||
154 | // Make sure the workspace number doesn't already exist | ||
155 | if (workspace_by_number(_target)) { | ||
177 | free(_target); | 156 | free(_target); |
178 | free(dup); | 157 | free(dup); |
179 | continue; | 158 | return; |
180 | } | 159 | } |
160 | } | ||
181 | 161 | ||
182 | // make sure that the workspace can appear on the given | 162 | // Make sure that the workspace doesn't already exist |
183 | // output | 163 | if (workspace_by_name(_target)) { |
184 | if (!workspace_valid_on_output(output_name, _target)) { | 164 | free(_target); |
185 | free(_target); | 165 | free(dup); |
186 | free(dup); | 166 | return; |
187 | continue; | 167 | } |
188 | } | ||
189 | 168 | ||
190 | if (binding->order < order) { | 169 | // make sure that the workspace can appear on the given |
191 | order = binding->order; | 170 | // output |
192 | free(target); | 171 | if (!workspace_valid_on_output(output_name, _target)) { |
193 | target = _target; | 172 | free(_target); |
194 | wlr_log(WLR_DEBUG, "Workspace: Found free name %s", _target); | 173 | free(dup); |
195 | } else { | 174 | return; |
196 | free(_target); | ||
197 | } | ||
198 | } | 175 | } |
199 | free(dup); | 176 | |
177 | if (binding->order < *min_order) { | ||
178 | *min_order = binding->order; | ||
179 | free(*earliest_name); | ||
180 | *earliest_name = _target; | ||
181 | wlr_log(WLR_DEBUG, "Workspace: Found free name %s", _target); | ||
182 | } else { | ||
183 | free(_target); | ||
184 | } | ||
185 | } | ||
186 | free(dup); | ||
187 | } | ||
188 | |||
189 | char *workspace_next_name(const char *output_name) { | ||
190 | wlr_log(WLR_DEBUG, "Workspace: Generating new workspace name for output %s", | ||
191 | output_name); | ||
192 | // Scan all workspace bindings to find the next available workspace name, | ||
193 | // if none are found/available then default to a number | ||
194 | struct sway_mode *mode = config->current_mode; | ||
195 | |||
196 | int order = INT_MAX; | ||
197 | char *target = NULL; | ||
198 | for (int i = 0; i < mode->keysym_bindings->length; ++i) { | ||
199 | workspace_name_from_binding(mode->keysym_bindings->items[i], | ||
200 | output_name, &order, &target); | ||
201 | } | ||
202 | for (int i = 0; i < mode->keycode_bindings->length; ++i) { | ||
203 | workspace_name_from_binding(mode->keycode_bindings->items[i], | ||
204 | output_name, &order, &target); | ||
200 | } | 205 | } |
201 | if (target != NULL) { | 206 | if (target != NULL) { |
202 | return target; | 207 | return target; |
@@ -529,3 +534,116 @@ void workspace_detect_urgent(struct sway_container *workspace) { | |||
529 | container_damage_whole(workspace); | 534 | container_damage_whole(workspace); |
530 | } | 535 | } |
531 | } | 536 | } |
537 | |||
538 | struct pid_workspace { | ||
539 | pid_t pid; | ||
540 | char *workspace; | ||
541 | struct timespec time_added; | ||
542 | |||
543 | struct sway_container *output; | ||
544 | struct wl_listener output_destroy; | ||
545 | |||
546 | struct wl_list link; | ||
547 | }; | ||
548 | |||
549 | static struct wl_list pid_workspaces; | ||
550 | |||
551 | struct sway_container *workspace_for_pid(pid_t pid) { | ||
552 | if (!pid_workspaces.prev && !pid_workspaces.next) { | ||
553 | wl_list_init(&pid_workspaces); | ||
554 | return NULL; | ||
555 | } | ||
556 | |||
557 | struct sway_container *ws = NULL; | ||
558 | struct pid_workspace *pw = NULL; | ||
559 | |||
560 | wlr_log(WLR_DEBUG, "Looking up workspace for pid %d", pid); | ||
561 | |||
562 | do { | ||
563 | struct pid_workspace *_pw = NULL; | ||
564 | wl_list_for_each(_pw, &pid_workspaces, link) { | ||
565 | if (pid == _pw->pid) { | ||
566 | pw = _pw; | ||
567 | wlr_log(WLR_DEBUG, | ||
568 | "found pid_workspace for pid %d, workspace %s", | ||
569 | pid, pw->workspace); | ||
570 | goto found; | ||
571 | } | ||
572 | } | ||
573 | pid = get_parent_pid(pid); | ||
574 | } while (pid > 1); | ||
575 | found: | ||
576 | |||
577 | if (pw && pw->workspace) { | ||
578 | ws = workspace_by_name(pw->workspace); | ||
579 | |||
580 | if (!ws) { | ||
581 | wlr_log(WLR_DEBUG, | ||
582 | "Creating workspace %s for pid %d because it disappeared", | ||
583 | pw->workspace, pid); | ||
584 | ws = workspace_create(pw->output, pw->workspace); | ||
585 | } | ||
586 | |||
587 | wl_list_remove(&pw->output_destroy.link); | ||
588 | wl_list_remove(&pw->link); | ||
589 | free(pw->workspace); | ||
590 | free(pw); | ||
591 | } | ||
592 | |||
593 | return ws; | ||
594 | } | ||
595 | |||
596 | static void pw_handle_output_destroy(struct wl_listener *listener, void *data) { | ||
597 | struct pid_workspace *pw = wl_container_of(listener, pw, output_destroy); | ||
598 | pw->output = NULL; | ||
599 | wl_list_remove(&pw->output_destroy.link); | ||
600 | wl_list_init(&pw->output_destroy.link); | ||
601 | } | ||
602 | |||
603 | void workspace_record_pid(pid_t pid) { | ||
604 | wlr_log(WLR_DEBUG, "Recording workspace for process %d", pid); | ||
605 | if (!pid_workspaces.prev && !pid_workspaces.next) { | ||
606 | wl_list_init(&pid_workspaces); | ||
607 | } | ||
608 | |||
609 | struct sway_seat *seat = input_manager_current_seat(input_manager); | ||
610 | struct sway_container *ws = | ||
611 | seat_get_focus_inactive(seat, &root_container); | ||
612 | if (ws && ws->type != C_WORKSPACE) { | ||
613 | ws = container_parent(ws, C_WORKSPACE); | ||
614 | } | ||
615 | if (!ws) { | ||
616 | wlr_log(WLR_DEBUG, "Bailing out, no workspace"); | ||
617 | return; | ||
618 | } | ||
619 | struct sway_container *output = ws->parent; | ||
620 | if (!output) { | ||
621 | wlr_log(WLR_DEBUG, "Bailing out, no output"); | ||
622 | return; | ||
623 | } | ||
624 | |||
625 | struct timespec now; | ||
626 | clock_gettime(CLOCK_MONOTONIC, &now); | ||
627 | |||
628 | // Remove expired entries | ||
629 | static const int timeout = 60; | ||
630 | struct pid_workspace *old, *_old; | ||
631 | wl_list_for_each_safe(old, _old, &pid_workspaces, link) { | ||
632 | if (now.tv_sec - old->time_added.tv_sec >= timeout) { | ||
633 | wl_list_remove(&old->output_destroy.link); | ||
634 | wl_list_remove(&old->link); | ||
635 | free(old->workspace); | ||
636 | free(old); | ||
637 | } | ||
638 | } | ||
639 | |||
640 | struct pid_workspace *pw = calloc(1, sizeof(struct pid_workspace)); | ||
641 | pw->workspace = strdup(ws->name); | ||
642 | pw->output = output; | ||
643 | pw->pid = pid; | ||
644 | memcpy(&pw->time_added, &now, sizeof(struct timespec)); | ||
645 | pw->output_destroy.notify = pw_handle_output_destroy; | ||
646 | wl_signal_add(&output->sway_output->wlr_output->events.destroy, | ||
647 | &pw->output_destroy); | ||
648 | wl_list_insert(&pid_workspaces, &pw->link); | ||
649 | } | ||
diff --git a/swayidle/swayidle.1.scd b/swayidle/swayidle.1.scd index 5cd4a7fd..7c1b138a 100644 --- a/swayidle/swayidle.1.scd +++ b/swayidle/swayidle.1.scd | |||
@@ -58,4 +58,4 @@ https://github.com/swaywm/sway. | |||
58 | 58 | ||
59 | # SEE ALSO | 59 | # SEE ALSO |
60 | 60 | ||
61 | *sway*(5) *swaymsg*(1) *swaygrab*(1) *sway-input*(5) *sway-bar*(5) | 61 | *sway*(5) *swaymsg*(1) *sway-input*(5) *sway-bar*(5) |
diff --git a/swaymsg/main.c b/swaymsg/main.c index c4141ca5..3767daf3 100644 --- a/swaymsg/main.c +++ b/swaymsg/main.c | |||
@@ -250,12 +250,16 @@ static void pretty_print(int type, json_object *resp) { | |||
250 | if (type != IPC_COMMAND && type != IPC_GET_WORKSPACES && | 250 | if (type != IPC_COMMAND && type != IPC_GET_WORKSPACES && |
251 | type != IPC_GET_INPUTS && type != IPC_GET_OUTPUTS && | 251 | type != IPC_GET_INPUTS && type != IPC_GET_OUTPUTS && |
252 | type != IPC_GET_VERSION && type != IPC_GET_SEATS && | 252 | type != IPC_GET_VERSION && type != IPC_GET_SEATS && |
253 | type != IPC_GET_CONFIG) { | 253 | type != IPC_GET_CONFIG && type != IPC_SEND_TICK) { |
254 | printf("%s\n", json_object_to_json_string_ext(resp, | 254 | printf("%s\n", json_object_to_json_string_ext(resp, |
255 | JSON_C_TO_STRING_PRETTY | JSON_C_TO_STRING_SPACED)); | 255 | JSON_C_TO_STRING_PRETTY | JSON_C_TO_STRING_SPACED)); |
256 | return; | 256 | return; |
257 | } | 257 | } |
258 | 258 | ||
259 | if (type == IPC_SEND_TICK) { | ||
260 | return; | ||
261 | } | ||
262 | |||
259 | if (type == IPC_GET_VERSION) { | 263 | if (type == IPC_GET_VERSION) { |
260 | pretty_print_version(resp); | 264 | pretty_print_version(resp); |
261 | return; | 265 | return; |
@@ -384,6 +388,8 @@ int main(int argc, char **argv) { | |||
384 | type = IPC_GET_BINDING_MODES; | 388 | type = IPC_GET_BINDING_MODES; |
385 | } else if (strcasecmp(cmdtype, "get_config") == 0) { | 389 | } else if (strcasecmp(cmdtype, "get_config") == 0) { |
386 | type = IPC_GET_CONFIG; | 390 | type = IPC_GET_CONFIG; |
391 | } else if (strcasecmp(cmdtype, "send_tick") == 0) { | ||
392 | type = IPC_SEND_TICK; | ||
387 | } else { | 393 | } else { |
388 | sway_abort("Unknown message type %s", cmdtype); | 394 | sway_abort("Unknown message type %s", cmdtype); |
389 | } | 395 | } |
diff --git a/swaymsg/swaymsg.1.scd b/swaymsg/swaymsg.1.scd index a6e279da..8cf1b222 100644 --- a/swaymsg/swaymsg.1.scd +++ b/swaymsg/swaymsg.1.scd | |||
@@ -64,3 +64,6 @@ _swaymsg_ [options...] [message] | |||
64 | 64 | ||
65 | *get\_config* | 65 | *get\_config* |
66 | Gets a JSON-encoded copy of the current configuration. | 66 | Gets a JSON-encoded copy of the current configuration. |
67 | |||
68 | *send\_tick* | ||
69 | Sends a tick event to all subscribed clients. | ||
diff --git a/swaynag/config.c b/swaynag/config.c new file mode 100644 index 00000000..d6c5739d --- /dev/null +++ b/swaynag/config.c | |||
@@ -0,0 +1,401 @@ | |||
1 | #define _XOPEN_SOURCE 700 | ||
2 | #define _POSIX_C_SOURCE 200112L | ||
3 | #include <getopt.h> | ||
4 | #include <stdlib.h> | ||
5 | #include <wordexp.h> | ||
6 | #include "log.h" | ||
7 | #include "list.h" | ||
8 | #include "readline.h" | ||
9 | #include "swaynag/swaynag.h" | ||
10 | #include "swaynag/types.h" | ||
11 | #include "util.h" | ||
12 | #include "wlr-layer-shell-unstable-v1-client-protocol.h" | ||
13 | |||
14 | static char *read_from_stdin() { | ||
15 | char *buffer = NULL; | ||
16 | while (!feof(stdin)) { | ||
17 | char *line = read_line(stdin); | ||
18 | if (!line) { | ||
19 | continue; | ||
20 | } | ||
21 | |||
22 | size_t curlen = buffer ? strlen(buffer) : 0; | ||
23 | buffer = realloc(buffer, curlen + strlen(line) + 2); | ||
24 | snprintf(buffer + curlen, strlen(line) + 2, "%s\n", line); | ||
25 | |||
26 | free(line); | ||
27 | } | ||
28 | |||
29 | while (buffer && buffer[strlen(buffer) - 1] == '\n') { | ||
30 | buffer[strlen(buffer) - 1] = '\0'; | ||
31 | } | ||
32 | |||
33 | return buffer; | ||
34 | } | ||
35 | |||
36 | int swaynag_parse_options(int argc, char **argv, struct swaynag *swaynag, | ||
37 | list_t *types, struct swaynag_type *type, char **config, bool *debug) { | ||
38 | enum type_options { | ||
39 | TO_COLOR_BACKGROUND = 256, | ||
40 | TO_COLOR_BORDER, | ||
41 | TO_COLOR_BORDER_BOTTOM, | ||
42 | TO_COLOR_BUTTON, | ||
43 | TO_COLOR_TEXT, | ||
44 | TO_THICK_BAR_BORDER, | ||
45 | TO_PADDING_MESSAGE, | ||
46 | TO_THICK_DET_BORDER, | ||
47 | TO_THICK_BTN_BORDER, | ||
48 | TO_GAP_BTN, | ||
49 | TO_GAP_BTN_DISMISS, | ||
50 | TO_MARGIN_BTN_RIGHT, | ||
51 | TO_PADDING_BTN, | ||
52 | }; | ||
53 | |||
54 | static struct option opts[] = { | ||
55 | {"button", required_argument, NULL, 'b'}, | ||
56 | {"config", required_argument, NULL, 'c'}, | ||
57 | {"debug", no_argument, NULL, 'd'}, | ||
58 | {"edge", required_argument, NULL, 'e'}, | ||
59 | {"font", required_argument, NULL, 'f'}, | ||
60 | {"help", no_argument, NULL, 'h'}, | ||
61 | {"detailed-message", no_argument, NULL, 'l'}, | ||
62 | {"detailed-button", required_argument, NULL, 'L'}, | ||
63 | {"message", required_argument, NULL, 'm'}, | ||
64 | {"output", required_argument, NULL, 'o'}, | ||
65 | {"dismiss-button", required_argument, NULL, 's'}, | ||
66 | {"type", required_argument, NULL, 't'}, | ||
67 | {"version", no_argument, NULL, 'v'}, | ||
68 | |||
69 | {"background", required_argument, NULL, TO_COLOR_BACKGROUND}, | ||
70 | {"border", required_argument, NULL, TO_COLOR_BORDER}, | ||
71 | {"border-bottom", required_argument, NULL, TO_COLOR_BORDER_BOTTOM}, | ||
72 | {"button-background", required_argument, NULL, TO_COLOR_BUTTON}, | ||
73 | {"text", required_argument, NULL, TO_COLOR_TEXT}, | ||
74 | {"border-bottom-size", required_argument, NULL, TO_THICK_BAR_BORDER}, | ||
75 | {"message-padding", required_argument, NULL, TO_PADDING_MESSAGE}, | ||
76 | {"details-border-size", required_argument, NULL, TO_THICK_DET_BORDER}, | ||
77 | {"button-border-size", required_argument, NULL, TO_THICK_BTN_BORDER}, | ||
78 | {"button-gap", required_argument, NULL, TO_GAP_BTN}, | ||
79 | {"button-dismiss-gap", required_argument, NULL, TO_GAP_BTN_DISMISS}, | ||
80 | {"button-margin-right", required_argument, NULL, TO_MARGIN_BTN_RIGHT}, | ||
81 | {"button-padding", required_argument, NULL, TO_PADDING_BTN}, | ||
82 | |||
83 | {0, 0, 0, 0} | ||
84 | }; | ||
85 | |||
86 | const char *usage = | ||
87 | "Usage: swaynag [options...]\n" | ||
88 | "\n" | ||
89 | " -b, --button <text> <action> Create a button with text that " | ||
90 | "executes action when pressed. Multiple buttons can be defined.\n" | ||
91 | " -c, --config <path> Path to config file.\n" | ||
92 | " -d, --debug Enable debugging.\n" | ||
93 | " -e, --edge top|bottom Set the edge to use.\n" | ||
94 | " -f, --font <font> Set the font to use.\n" | ||
95 | " -h, --help Show help message and quit.\n" | ||
96 | " -l, --detailed-message Read a detailed message from stdin.\n" | ||
97 | " -L, --detailed-button <text> Set the text of the detail button.\n" | ||
98 | " -m, --message <msg> Set the message text.\n" | ||
99 | " -o, --output <output> Set the output to use.\n" | ||
100 | " -s, --dismiss-button <text> Set the dismiss button text.\n" | ||
101 | " -t, --type <type> Set the message type.\n" | ||
102 | " -v, --version Show the version number and quit.\n" | ||
103 | "\n" | ||
104 | "The following appearance options can also be given:\n" | ||
105 | " --background RRGGBB[AA] Background color.\n" | ||
106 | " --border RRGGBB[AA] Border color.\n" | ||
107 | " --border-bottom RRGGBB[AA] Bottom border color.\n" | ||
108 | " --button-background RRGGBB[AA] Button background color.\n" | ||
109 | " --text RRGGBB[AA] Text color.\n" | ||
110 | " --border-bottom-size size Thickness of the bar border.\n" | ||
111 | " --message-padding padding Padding for the message.\n" | ||
112 | " --details-border-size size Thickness for the details border.\n" | ||
113 | " --button-border-size size Thickness for the button border.\n" | ||
114 | " --button-gap gap Size of the gap between buttons\n" | ||
115 | " --button-dismiss-gap gap Size of the gap for dismiss button.\n" | ||
116 | " --button-margin-right margin Margin from dismiss button to edge.\n" | ||
117 | " --button-padding padding Padding for the button text.\n"; | ||
118 | |||
119 | optind = 1; | ||
120 | while (1) { | ||
121 | int c = getopt_long(argc, argv, "b:c:de:f:hlL:m:o:s:t:v", opts, NULL); | ||
122 | if (c == -1) { | ||
123 | break; | ||
124 | } | ||
125 | switch (c) { | ||
126 | case 'b': // Button | ||
127 | if (swaynag) { | ||
128 | if (optind >= argc) { | ||
129 | fprintf(stderr, "Missing action for button %s\n", optarg); | ||
130 | return EXIT_FAILURE; | ||
131 | } | ||
132 | struct swaynag_button *button; | ||
133 | button = calloc(sizeof(struct swaynag_button), 1); | ||
134 | button->text = strdup(optarg); | ||
135 | button->type = SWAYNAG_ACTION_COMMAND; | ||
136 | button->action = strdup(argv[optind]); | ||
137 | list_add(swaynag->buttons, button); | ||
138 | } | ||
139 | optind++; | ||
140 | break; | ||
141 | case 'c': // Config | ||
142 | if (config) { | ||
143 | *config = strdup(optarg); | ||
144 | } | ||
145 | break; | ||
146 | case 'd': // Debug | ||
147 | if (debug) { | ||
148 | *debug = true; | ||
149 | } | ||
150 | break; | ||
151 | case 'e': // Edge | ||
152 | if (type) { | ||
153 | if (strcmp(optarg, "top") == 0) { | ||
154 | type->anchors = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | ||
155 | | ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | ||
156 | | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT; | ||
157 | } else if (strcmp(optarg, "bottom") == 0) { | ||
158 | type->anchors = ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM | ||
159 | | ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | ||
160 | | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT; | ||
161 | } else { | ||
162 | fprintf(stderr, "Invalid edge: %s\n", optarg); | ||
163 | return EXIT_FAILURE; | ||
164 | } | ||
165 | } | ||
166 | break; | ||
167 | case 'f': // Font | ||
168 | if (type) { | ||
169 | free(type->font); | ||
170 | type->font = strdup(optarg); | ||
171 | } | ||
172 | break; | ||
173 | case 'l': // Detailed Message | ||
174 | if (swaynag) { | ||
175 | free(swaynag->details.message); | ||
176 | swaynag->details.message = read_from_stdin(); | ||
177 | swaynag->details.button_up.text = strdup("▲"); | ||
178 | swaynag->details.button_down.text = strdup("▼"); | ||
179 | } | ||
180 | break; | ||
181 | case 'L': // Detailed Button Text | ||
182 | if (swaynag) { | ||
183 | free(swaynag->details.button_details.text); | ||
184 | swaynag->details.button_details.text = strdup(optarg); | ||
185 | } | ||
186 | break; | ||
187 | case 'm': // Message | ||
188 | if (swaynag) { | ||
189 | free(swaynag->message); | ||
190 | swaynag->message = strdup(optarg); | ||
191 | } | ||
192 | break; | ||
193 | case 'o': // Output | ||
194 | if (type) { | ||
195 | free(type->output); | ||
196 | type->output = strdup(optarg); | ||
197 | } | ||
198 | break; | ||
199 | case 's': // Dismiss Button Text | ||
200 | if (swaynag) { | ||
201 | struct swaynag_button *button_close; | ||
202 | button_close = swaynag->buttons->items[0]; | ||
203 | free(button_close->text); | ||
204 | button_close->text = strdup(optarg); | ||
205 | } | ||
206 | break; | ||
207 | case 't': // Type | ||
208 | if (swaynag) { | ||
209 | swaynag->type = swaynag_type_get(types, optarg); | ||
210 | if (!swaynag->type) { | ||
211 | fprintf(stderr, "Unknown type %s\n", optarg); | ||
212 | return EXIT_FAILURE; | ||
213 | } | ||
214 | } | ||
215 | break; | ||
216 | case 'v': // Version | ||
217 | fprintf(stdout, "swaynag version " SWAY_VERSION "\n"); | ||
218 | return -1; | ||
219 | case TO_COLOR_BACKGROUND: // Background color | ||
220 | if (type) { | ||
221 | type->background = parse_color(optarg); | ||
222 | } | ||
223 | break; | ||
224 | case TO_COLOR_BORDER: // Border color | ||
225 | if (type) { | ||
226 | type->border = parse_color(optarg); | ||
227 | } | ||
228 | break; | ||
229 | case TO_COLOR_BORDER_BOTTOM: // Bottom border color | ||
230 | if (type) { | ||
231 | type->border_bottom = parse_color(optarg); | ||
232 | } | ||
233 | break; | ||
234 | case TO_COLOR_BUTTON: // Button background color | ||
235 | if (type) { | ||
236 | type->button_background = parse_color(optarg); | ||
237 | } | ||
238 | break; | ||
239 | case TO_COLOR_TEXT: // Text color | ||
240 | if (type) { | ||
241 | type->text = parse_color(optarg); | ||
242 | } | ||
243 | break; | ||
244 | case TO_THICK_BAR_BORDER: // Bottom border thickness | ||
245 | if (type) { | ||
246 | type->bar_border_thickness = strtol(optarg, NULL, 0); | ||
247 | } | ||
248 | break; | ||
249 | case TO_PADDING_MESSAGE: // Message padding | ||
250 | if (type) { | ||
251 | type->message_padding = strtol(optarg, NULL, 0); | ||
252 | } | ||
253 | break; | ||
254 | case TO_THICK_DET_BORDER: // Details border thickness | ||
255 | if (type) { | ||
256 | type->details_border_thickness = strtol(optarg, NULL, 0); | ||
257 | } | ||
258 | break; | ||
259 | case TO_THICK_BTN_BORDER: // Button border thickness | ||
260 | if (type) { | ||
261 | type->button_border_thickness = strtol(optarg, NULL, 0); | ||
262 | } | ||
263 | break; | ||
264 | case TO_GAP_BTN: // Gap between buttons | ||
265 | if (type) { | ||
266 | type->button_gap = strtol(optarg, NULL, 0); | ||
267 | } | ||
268 | break; | ||
269 | case TO_GAP_BTN_DISMISS: // Gap between dismiss button | ||
270 | if (type) { | ||
271 | type->button_gap_close = strtol(optarg, NULL, 0); | ||
272 | } | ||
273 | break; | ||
274 | case TO_MARGIN_BTN_RIGHT: // Margin on the right side of button area | ||
275 | if (type) { | ||
276 | type->button_margin_right = strtol(optarg, NULL, 0); | ||
277 | } | ||
278 | break; | ||
279 | case TO_PADDING_BTN: // Padding for the button text | ||
280 | if (type) { | ||
281 | type->button_padding = strtol(optarg, NULL, 0); | ||
282 | } | ||
283 | break; | ||
284 | default: // Help or unknown flag | ||
285 | fprintf(c == 'h' ? stdout : stderr, "%s", usage); | ||
286 | return -1; | ||
287 | } | ||
288 | } | ||
289 | |||
290 | return 0; | ||
291 | } | ||
292 | |||
293 | static bool file_exists(const char *path) { | ||
294 | return path && access(path, R_OK) != -1; | ||
295 | } | ||
296 | |||
297 | char *swaynag_get_config_path(void) { | ||
298 | static const char *config_paths[] = { | ||
299 | "$HOME/.swaynag/config", | ||
300 | "$XDG_CONFIG_HOME/swaynag/config", | ||
301 | SYSCONFDIR "/swaynag/config", | ||
302 | }; | ||
303 | |||
304 | if (!getenv("XDG_CONFIG_HOME")) { | ||
305 | char *home = getenv("HOME"); | ||
306 | char *config_home = malloc(strlen(home) + strlen("/.config") + 1); | ||
307 | if (!config_home) { | ||
308 | wlr_log(WLR_ERROR, "Unable to allocate $HOME/.config"); | ||
309 | } else { | ||
310 | strcpy(config_home, home); | ||
311 | strcat(config_home, "/.config"); | ||
312 | setenv("XDG_CONFIG_HOME", config_home, 1); | ||
313 | wlr_log(WLR_DEBUG, "Set XDG_CONFIG_HOME to %s", config_home); | ||
314 | free(config_home); | ||
315 | } | ||
316 | } | ||
317 | |||
318 | wordexp_t p; | ||
319 | char *path; | ||
320 | for (size_t i = 0; i < sizeof(config_paths) / sizeof(char *); ++i) { | ||
321 | if (wordexp(config_paths[i], &p, 0) == 0) { | ||
322 | path = strdup(p.we_wordv[0]); | ||
323 | wordfree(&p); | ||
324 | if (file_exists(path)) { | ||
325 | return path; | ||
326 | } | ||
327 | free(path); | ||
328 | } | ||
329 | } | ||
330 | |||
331 | return NULL; | ||
332 | } | ||
333 | |||
334 | int swaynag_load_config(char *path, struct swaynag *swaynag, list_t *types) { | ||
335 | FILE *config = fopen(path, "r"); | ||
336 | if (!config) { | ||
337 | fprintf(stderr, "Failed to read config. Running without it.\n"); | ||
338 | return 0; | ||
339 | } | ||
340 | |||
341 | struct swaynag_type *type; | ||
342 | type = calloc(1, sizeof(struct swaynag_type)); | ||
343 | type->name = strdup("<config>"); | ||
344 | list_add(types, type); | ||
345 | |||
346 | char *line; | ||
347 | int line_number = 0; | ||
348 | while (!feof(config)) { | ||
349 | line = read_line(config); | ||
350 | if (!line) { | ||
351 | continue; | ||
352 | } | ||
353 | |||
354 | line_number++; | ||
355 | if (line[0] == '#') { | ||
356 | free(line); | ||
357 | continue; | ||
358 | } | ||
359 | if (strlen(line) == 0) { | ||
360 | free(line); | ||
361 | continue; | ||
362 | } | ||
363 | |||
364 | if (line[0] == '[') { | ||
365 | char *close = strchr(line, ']'); | ||
366 | if (!close) { | ||
367 | free(line); | ||
368 | fclose(config); | ||
369 | fprintf(stderr, "Closing bracket not found on line %d\n", | ||
370 | line_number); | ||
371 | return 1; | ||
372 | } | ||
373 | char *name = calloc(1, close - line); | ||
374 | strncat(name, line + 1, close - line - 1); | ||
375 | type = swaynag_type_get(types, name); | ||
376 | if (!type) { | ||
377 | type = calloc(1, sizeof(struct swaynag_type)); | ||
378 | type->name = strdup(name); | ||
379 | list_add(types, type); | ||
380 | } | ||
381 | free(name); | ||
382 | } else { | ||
383 | char flag[strlen(line) + 3]; | ||
384 | sprintf(flag, "--%s", line); | ||
385 | char *argv[] = {"swaynag", flag}; | ||
386 | int result; | ||
387 | result = swaynag_parse_options(2, argv, swaynag, types, type, | ||
388 | NULL, NULL); | ||
389 | if (result != 0) { | ||
390 | free(line); | ||
391 | fclose(config); | ||
392 | return result; | ||
393 | } | ||
394 | } | ||
395 | |||
396 | free(line); | ||
397 | } | ||
398 | fclose(config); | ||
399 | return 0; | ||
400 | } | ||
401 | |||
diff --git a/swaynag/main.c b/swaynag/main.c new file mode 100644 index 00000000..854368e5 --- /dev/null +++ b/swaynag/main.c | |||
@@ -0,0 +1,129 @@ | |||
1 | #define _XOPEN_SOURCE 500 | ||
2 | #include <signal.h> | ||
3 | #include "log.h" | ||
4 | #include "list.h" | ||
5 | #include "swaynag/config.h" | ||
6 | #include "swaynag/swaynag.h" | ||
7 | #include "swaynag/types.h" | ||
8 | |||
9 | static struct swaynag swaynag; | ||
10 | |||
11 | void sig_handler(int signal) { | ||
12 | swaynag_destroy(&swaynag); | ||
13 | exit(EXIT_FAILURE); | ||
14 | } | ||
15 | |||
16 | void sway_terminate(int code) { | ||
17 | swaynag_destroy(&swaynag); | ||
18 | exit(code); | ||
19 | } | ||
20 | |||
21 | int main(int argc, char **argv) { | ||
22 | int exit_code = EXIT_SUCCESS; | ||
23 | |||
24 | list_t *types = create_list(); | ||
25 | swaynag_types_add_default(types); | ||
26 | |||
27 | memset(&swaynag, 0, sizeof(swaynag)); | ||
28 | swaynag.buttons = create_list(); | ||
29 | |||
30 | struct swaynag_button *button_close = | ||
31 | calloc(sizeof(struct swaynag_button), 1); | ||
32 | button_close->text = strdup("X"); | ||
33 | button_close->type = SWAYNAG_ACTION_DISMISS; | ||
34 | list_add(swaynag.buttons, button_close); | ||
35 | |||
36 | swaynag.details.button_details.text = strdup("Toggle Details"); | ||
37 | swaynag.details.button_details.type = SWAYNAG_ACTION_EXPAND; | ||
38 | |||
39 | |||
40 | char *config_path = NULL; | ||
41 | bool debug = false; | ||
42 | int launch_status = swaynag_parse_options(argc, argv, NULL, NULL, NULL, | ||
43 | &config_path, &debug); | ||
44 | if (launch_status != 0) { | ||
45 | exit_code = launch_status; | ||
46 | goto cleanup; | ||
47 | } | ||
48 | wlr_log_init(debug ? WLR_DEBUG : WLR_ERROR, NULL); | ||
49 | |||
50 | if (!config_path) { | ||
51 | config_path = swaynag_get_config_path(); | ||
52 | } | ||
53 | if (config_path) { | ||
54 | wlr_log(WLR_DEBUG, "Loading config file: %s", config_path); | ||
55 | int config_status = swaynag_load_config(config_path, &swaynag, types); | ||
56 | free(config_path); | ||
57 | if (config_status != 0) { | ||
58 | exit_code = config_status; | ||
59 | goto cleanup; | ||
60 | } | ||
61 | } | ||
62 | |||
63 | if (argc > 1) { | ||
64 | struct swaynag_type *type_args; | ||
65 | type_args = calloc(1, sizeof(struct swaynag_type)); | ||
66 | type_args->name = strdup("<args>"); | ||
67 | list_add(types, type_args); | ||
68 | |||
69 | int result = swaynag_parse_options(argc, argv, &swaynag, types, | ||
70 | type_args, NULL, NULL); | ||
71 | if (result != 0) { | ||
72 | exit_code = result; | ||
73 | goto cleanup; | ||
74 | } | ||
75 | } | ||
76 | |||
77 | if (!swaynag.message) { | ||
78 | wlr_log(WLR_ERROR, "No message passed. Please provide --message/-m"); | ||
79 | exit_code = EXIT_FAILURE; | ||
80 | goto cleanup; | ||
81 | } | ||
82 | |||
83 | if (!swaynag.type) { | ||
84 | swaynag.type = swaynag_type_get(types, "error"); | ||
85 | } | ||
86 | |||
87 | // Construct a new type using the config defaults as base, then merging | ||
88 | // config type defaults on top, then merging arguments on top of that, and | ||
89 | // finally merging defaults on top. | ||
90 | struct swaynag_type *type = calloc(1, sizeof(struct swaynag_type)); | ||
91 | type->name = strdup(swaynag.type->name); | ||
92 | swaynag_type_merge(type, swaynag_type_get(types, "<args>")); | ||
93 | swaynag_type_merge(type, swaynag.type); | ||
94 | swaynag_type_merge(type, swaynag_type_get(types, "<config>")); | ||
95 | swaynag_type_merge(type, swaynag_type_get(types, "<defaults>")); | ||
96 | swaynag.type = type; | ||
97 | |||
98 | swaynag_types_free(types); | ||
99 | |||
100 | if (swaynag.details.message) { | ||
101 | list_add(swaynag.buttons, &swaynag.details.button_details); | ||
102 | } else { | ||
103 | free(swaynag.details.button_details.text); | ||
104 | } | ||
105 | |||
106 | wlr_log(WLR_DEBUG, "Output: %s", swaynag.type->output); | ||
107 | wlr_log(WLR_DEBUG, "Anchors: %d", swaynag.type->anchors); | ||
108 | wlr_log(WLR_DEBUG, "Type: %s", swaynag.type->name); | ||
109 | wlr_log(WLR_DEBUG, "Message: %s", swaynag.message); | ||
110 | wlr_log(WLR_DEBUG, "Font: %s", swaynag.type->font); | ||
111 | wlr_log(WLR_DEBUG, "Buttons"); | ||
112 | for (int i = 0; i < swaynag.buttons->length; i++) { | ||
113 | struct swaynag_button *button = swaynag.buttons->items[i]; | ||
114 | wlr_log(WLR_DEBUG, "\t[%s] `%s`", button->text, button->action); | ||
115 | } | ||
116 | |||
117 | signal(SIGTERM, sig_handler); | ||
118 | |||
119 | swaynag_setup(&swaynag); | ||
120 | swaynag_run(&swaynag); | ||
121 | return exit_code; | ||
122 | |||
123 | cleanup: | ||
124 | swaynag_types_free(types); | ||
125 | free(swaynag.details.button_details.text); | ||
126 | swaynag_destroy(&swaynag); | ||
127 | return exit_code; | ||
128 | } | ||
129 | |||
diff --git a/swaynag/meson.build b/swaynag/meson.build new file mode 100644 index 00000000..2ba3ed95 --- /dev/null +++ b/swaynag/meson.build | |||
@@ -0,0 +1,23 @@ | |||
1 | executable( | ||
2 | 'swaynag', [ | ||
3 | 'config.c', | ||
4 | 'main.c', | ||
5 | 'render.c', | ||
6 | 'swaynag.c', | ||
7 | 'types.c', | ||
8 | ], | ||
9 | include_directories: [sway_inc], | ||
10 | dependencies: [ | ||
11 | cairo, | ||
12 | client_protos, | ||
13 | gdk_pixbuf, | ||
14 | math, | ||
15 | pango, | ||
16 | pangocairo, | ||
17 | wayland_client, | ||
18 | wayland_cursor, | ||
19 | wlroots, | ||
20 | ], | ||
21 | link_with: [lib_sway_common, lib_sway_client], | ||
22 | install: true | ||
23 | ) | ||
diff --git a/swaynag/render.c b/swaynag/render.c new file mode 100644 index 00000000..766409e4 --- /dev/null +++ b/swaynag/render.c | |||
@@ -0,0 +1,308 @@ | |||
1 | #include <stdint.h> | ||
2 | #include "cairo.h" | ||
3 | #include "log.h" | ||
4 | #include "pango.h" | ||
5 | #include "pool-buffer.h" | ||
6 | #include "swaynag/swaynag.h" | ||
7 | #include "swaynag/types.h" | ||
8 | #include "wlr-layer-shell-unstable-v1-client-protocol.h" | ||
9 | |||
10 | static uint32_t render_message(cairo_t *cairo, struct swaynag *swaynag) { | ||
11 | uint32_t height = swaynag->height * swaynag->scale; | ||
12 | height -= swaynag->type->bar_border_thickness * swaynag->scale; | ||
13 | |||
14 | int text_width, text_height; | ||
15 | get_text_size(cairo, swaynag->type->font, &text_width, &text_height, | ||
16 | swaynag->scale, true, "%s", swaynag->message); | ||
17 | |||
18 | int padding = swaynag->type->message_padding * swaynag->scale; | ||
19 | |||
20 | uint32_t ideal_height = text_height + padding * 2; | ||
21 | uint32_t ideal_surface_height = ideal_height / swaynag->scale; | ||
22 | if (swaynag->height < ideal_surface_height) { | ||
23 | return ideal_surface_height; | ||
24 | } | ||
25 | |||
26 | cairo_set_source_u32(cairo, swaynag->type->text); | ||
27 | cairo_move_to(cairo, padding, (int)(ideal_height - text_height) / 2); | ||
28 | pango_printf(cairo, swaynag->type->font, swaynag->scale, false, | ||
29 | "%s", swaynag->message); | ||
30 | |||
31 | return ideal_surface_height; | ||
32 | } | ||
33 | |||
34 | static void render_details_scroll_button(cairo_t *cairo, | ||
35 | struct swaynag *swaynag, struct swaynag_button *button) { | ||
36 | int text_width, text_height; | ||
37 | get_text_size(cairo, swaynag->type->font, &text_width, &text_height, | ||
38 | swaynag->scale, true, "%s", button->text); | ||
39 | |||
40 | int border = swaynag->type->button_border_thickness * swaynag->scale; | ||
41 | int padding = swaynag->type->button_padding * swaynag->scale; | ||
42 | |||
43 | cairo_set_source_u32(cairo, swaynag->type->border); | ||
44 | cairo_rectangle(cairo, button->x, button->y, | ||
45 | button->width, button->height); | ||
46 | cairo_fill(cairo); | ||
47 | |||
48 | cairo_set_source_u32(cairo, swaynag->type->button_background); | ||
49 | cairo_rectangle(cairo, button->x + border, button->y + border, | ||
50 | button->width - (border * 2), button->height - (border * 2)); | ||
51 | cairo_fill(cairo); | ||
52 | |||
53 | cairo_set_source_u32(cairo, swaynag->type->text); | ||
54 | cairo_move_to(cairo, button->x + border + padding, | ||
55 | button->y + border + (button->height - text_height) / 2); | ||
56 | pango_printf(cairo, swaynag->type->font, swaynag->scale, true, | ||
57 | "%s", button->text); | ||
58 | } | ||
59 | |||
60 | static int get_detailed_scroll_button_width(cairo_t *cairo, | ||
61 | struct swaynag *swaynag) { | ||
62 | int up_width, down_width, temp_height; | ||
63 | get_text_size(cairo, swaynag->type->font, &up_width, &temp_height, | ||
64 | swaynag->scale, true, | ||
65 | "%s", swaynag->details.button_up.text); | ||
66 | get_text_size(cairo, swaynag->type->font, &down_width, &temp_height, | ||
67 | swaynag->scale, true, | ||
68 | "%s", swaynag->details.button_down.text); | ||
69 | |||
70 | int text_width = up_width > down_width ? up_width : down_width; | ||
71 | int border = swaynag->type->button_border_thickness * swaynag->scale; | ||
72 | int padding = swaynag->type->button_padding * swaynag->scale; | ||
73 | |||
74 | return text_width + border * 2 + padding * 2; | ||
75 | } | ||
76 | |||
77 | static uint32_t render_detailed(cairo_t *cairo, struct swaynag *swaynag, | ||
78 | uint32_t y) { | ||
79 | uint32_t width = swaynag->width * swaynag->scale; | ||
80 | uint32_t height = swaynag->height * swaynag->scale; | ||
81 | height -= swaynag->type->bar_border_thickness * swaynag->scale; | ||
82 | |||
83 | int border = swaynag->type->details_border_thickness * swaynag->scale; | ||
84 | int padding = swaynag->type->message_padding * swaynag->scale; | ||
85 | int decor = padding + border; | ||
86 | |||
87 | swaynag->details.x = decor; | ||
88 | swaynag->details.y = y * swaynag->scale + decor; | ||
89 | swaynag->details.width = width - decor * 2; | ||
90 | |||
91 | PangoLayout *layout = get_pango_layout(cairo, swaynag->type->font, | ||
92 | swaynag->details.message, swaynag->scale, false); | ||
93 | pango_layout_set_width(layout, | ||
94 | (swaynag->details.width - padding * 2) * PANGO_SCALE); | ||
95 | pango_layout_set_wrap(layout, PANGO_WRAP_WORD_CHAR); | ||
96 | pango_layout_set_single_paragraph_mode(layout, false); | ||
97 | pango_cairo_update_layout(cairo, layout); | ||
98 | swaynag->details.total_lines = pango_layout_get_line_count(layout); | ||
99 | |||
100 | PangoLayoutLine *line; | ||
101 | line = pango_layout_get_line_readonly(layout, swaynag->details.offset); | ||
102 | gint offset = line->start_index; | ||
103 | const char *text = pango_layout_get_text(layout); | ||
104 | pango_layout_set_text(layout, text + offset, strlen(text) - offset); | ||
105 | |||
106 | int text_width, text_height; | ||
107 | pango_cairo_update_layout(cairo, layout); | ||
108 | pango_layout_get_pixel_size(layout, &text_width, &text_height); | ||
109 | |||
110 | bool show_buttons = swaynag->details.offset > 0; | ||
111 | int button_width = get_detailed_scroll_button_width(cairo, swaynag); | ||
112 | if (show_buttons) { | ||
113 | swaynag->details.width -= button_width; | ||
114 | pango_layout_set_width(layout, | ||
115 | (swaynag->details.width - padding * 2) * PANGO_SCALE); | ||
116 | } | ||
117 | |||
118 | uint32_t ideal_height; | ||
119 | do { | ||
120 | ideal_height = swaynag->details.y + text_height + decor + padding * 2; | ||
121 | if (ideal_height > SWAYNAG_MAX_HEIGHT) { | ||
122 | ideal_height = SWAYNAG_MAX_HEIGHT; | ||
123 | |||
124 | if (!show_buttons) { | ||
125 | show_buttons = true; | ||
126 | swaynag->details.width -= button_width; | ||
127 | pango_layout_set_width(layout, | ||
128 | (swaynag->details.width - padding * 2) * PANGO_SCALE); | ||
129 | } | ||
130 | } | ||
131 | |||
132 | swaynag->details.height = ideal_height - swaynag->details.y - decor; | ||
133 | pango_layout_set_height(layout, | ||
134 | (swaynag->details.height - padding * 2) * PANGO_SCALE); | ||
135 | pango_layout_set_ellipsize(layout, PANGO_ELLIPSIZE_END); | ||
136 | pango_cairo_update_layout(cairo, layout); | ||
137 | pango_layout_get_pixel_size(layout, &text_width, &text_height); | ||
138 | } while (text_height != (swaynag->details.height - padding * 2)); | ||
139 | |||
140 | swaynag->details.visible_lines = pango_layout_get_line_count(layout); | ||
141 | |||
142 | if (show_buttons) { | ||
143 | swaynag->details.button_up.x = | ||
144 | swaynag->details.x + swaynag->details.width; | ||
145 | swaynag->details.button_up.y = swaynag->details.y; | ||
146 | swaynag->details.button_up.width = button_width; | ||
147 | swaynag->details.button_up.height = swaynag->details.height / 2; | ||
148 | render_details_scroll_button(cairo, swaynag, | ||
149 | &swaynag->details.button_up); | ||
150 | |||
151 | swaynag->details.button_down.x = | ||
152 | swaynag->details.x + swaynag->details.width; | ||
153 | swaynag->details.button_down.y = | ||
154 | swaynag->details.button_up.y + swaynag->details.button_up.height; | ||
155 | swaynag->details.button_down.width = button_width; | ||
156 | swaynag->details.button_down.height = swaynag->details.height / 2; | ||
157 | render_details_scroll_button(cairo, swaynag, | ||
158 | &swaynag->details.button_down); | ||
159 | } | ||
160 | |||
161 | cairo_set_source_u32(cairo, swaynag->type->border); | ||
162 | cairo_rectangle(cairo, swaynag->details.x, swaynag->details.y, | ||
163 | swaynag->details.width, swaynag->details.height); | ||
164 | cairo_fill(cairo); | ||
165 | |||
166 | cairo_move_to(cairo, swaynag->details.x + padding, | ||
167 | swaynag->details.y + padding); | ||
168 | cairo_set_source_u32(cairo, swaynag->type->text); | ||
169 | pango_cairo_show_layout(cairo, layout); | ||
170 | g_object_unref(layout); | ||
171 | |||
172 | return ideal_height / swaynag->scale; | ||
173 | } | ||
174 | |||
175 | static uint32_t render_button(cairo_t *cairo, struct swaynag *swaynag, | ||
176 | int button_index, int *x) { | ||
177 | uint32_t height = swaynag->height * swaynag->scale; | ||
178 | height -= swaynag->type->bar_border_thickness * swaynag->scale; | ||
179 | struct swaynag_button *button = swaynag->buttons->items[button_index]; | ||
180 | |||
181 | int text_width, text_height; | ||
182 | get_text_size(cairo, swaynag->type->font, &text_width, &text_height, | ||
183 | swaynag->scale, true, "%s", button->text); | ||
184 | |||
185 | int border = swaynag->type->button_border_thickness * swaynag->scale; | ||
186 | int padding = swaynag->type->button_padding * swaynag->scale; | ||
187 | |||
188 | uint32_t ideal_height = text_height + padding * 2 + border * 2; | ||
189 | uint32_t ideal_surface_height = ideal_height / swaynag->scale; | ||
190 | if (swaynag->height < ideal_surface_height) { | ||
191 | return ideal_surface_height; | ||
192 | } | ||
193 | |||
194 | button->x = *x - border - text_width - padding * 2; | ||
195 | button->y = (int)(ideal_height - text_height) / 2 - padding; | ||
196 | button->width = text_width + padding * 2; | ||
197 | button->height = text_height + padding * 2; | ||
198 | |||
199 | cairo_set_source_u32(cairo, swaynag->type->border); | ||
200 | cairo_rectangle(cairo, button->x - border, button->y - border, | ||
201 | button->width + border * 2, button->height + border * 2); | ||
202 | cairo_fill(cairo); | ||
203 | |||
204 | cairo_set_source_u32(cairo, swaynag->type->button_background); | ||
205 | cairo_rectangle(cairo, button->x, button->y, | ||
206 | button->width, button->height); | ||
207 | cairo_fill(cairo); | ||
208 | |||
209 | cairo_set_source_u32(cairo, swaynag->type->text); | ||
210 | cairo_move_to(cairo, button->x + padding, button->y + padding); | ||
211 | pango_printf(cairo, swaynag->type->font, swaynag->scale, true, | ||
212 | "%s", button->text); | ||
213 | |||
214 | *x = button->x - border; | ||
215 | |||
216 | return ideal_surface_height; | ||
217 | } | ||
218 | |||
219 | static uint32_t render_to_cairo(cairo_t *cairo, struct swaynag *swaynag) { | ||
220 | uint32_t max_height = 0; | ||
221 | |||
222 | cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE); | ||
223 | cairo_set_source_u32(cairo, swaynag->type->background); | ||
224 | cairo_paint(cairo); | ||
225 | |||
226 | uint32_t h = render_message(cairo, swaynag); | ||
227 | max_height = h > max_height ? h : max_height; | ||
228 | |||
229 | int x = swaynag->width - swaynag->type->button_margin_right; | ||
230 | x *= swaynag->scale; | ||
231 | for (int i = 0; i < swaynag->buttons->length; i++) { | ||
232 | h = render_button(cairo, swaynag, i, &x); | ||
233 | max_height = h > max_height ? h : max_height; | ||
234 | x -= swaynag->type->button_gap * swaynag->scale; | ||
235 | if (i == 0) { | ||
236 | x -= swaynag->type->button_gap_close * swaynag->scale; | ||
237 | } | ||
238 | } | ||
239 | |||
240 | if (swaynag->details.visible) { | ||
241 | h = render_detailed(cairo, swaynag, max_height); | ||
242 | max_height = h > max_height ? h : max_height; | ||
243 | } | ||
244 | |||
245 | int border = swaynag->type->bar_border_thickness * swaynag->scale; | ||
246 | if (max_height > swaynag->height) { | ||
247 | max_height += border; | ||
248 | } | ||
249 | cairo_set_source_u32(cairo, swaynag->type->border_bottom); | ||
250 | cairo_rectangle(cairo, 0, | ||
251 | swaynag->height * swaynag->scale - border, | ||
252 | swaynag->width * swaynag->scale, | ||
253 | border); | ||
254 | cairo_fill(cairo); | ||
255 | |||
256 | return max_height; | ||
257 | } | ||
258 | |||
259 | void render_frame(struct swaynag *swaynag) { | ||
260 | if (!swaynag->run_display) { | ||
261 | return; | ||
262 | } | ||
263 | |||
264 | cairo_surface_t *recorder = cairo_recording_surface_create( | ||
265 | CAIRO_CONTENT_COLOR_ALPHA, NULL); | ||
266 | cairo_t *cairo = cairo_create(recorder); | ||
267 | cairo_save(cairo); | ||
268 | cairo_set_operator(cairo, CAIRO_OPERATOR_CLEAR); | ||
269 | cairo_paint(cairo); | ||
270 | cairo_restore(cairo); | ||
271 | uint32_t height = render_to_cairo(cairo, swaynag); | ||
272 | if (height != swaynag->height) { | ||
273 | zwlr_layer_surface_v1_set_size(swaynag->layer_surface, 0, height); | ||
274 | zwlr_layer_surface_v1_set_exclusive_zone(swaynag->layer_surface, | ||
275 | height); | ||
276 | wl_surface_commit(swaynag->surface); | ||
277 | wl_display_roundtrip(swaynag->display); | ||
278 | } else { | ||
279 | swaynag->current_buffer = get_next_buffer(swaynag->shm, | ||
280 | swaynag->buffers, | ||
281 | swaynag->width * swaynag->scale, | ||
282 | swaynag->height * swaynag->scale); | ||
283 | if (!swaynag->current_buffer) { | ||
284 | wlr_log(WLR_DEBUG, "Failed to get buffer. Skipping frame."); | ||
285 | goto cleanup; | ||
286 | } | ||
287 | |||
288 | cairo_t *shm = swaynag->current_buffer->cairo; | ||
289 | cairo_save(shm); | ||
290 | cairo_set_operator(shm, CAIRO_OPERATOR_CLEAR); | ||
291 | cairo_paint(shm); | ||
292 | cairo_restore(shm); | ||
293 | cairo_set_source_surface(shm, recorder, 0.0, 0.0); | ||
294 | cairo_paint(shm); | ||
295 | |||
296 | wl_surface_set_buffer_scale(swaynag->surface, swaynag->scale); | ||
297 | wl_surface_attach(swaynag->surface, | ||
298 | swaynag->current_buffer->buffer, 0, 0); | ||
299 | wl_surface_damage(swaynag->surface, 0, 0, | ||
300 | swaynag->width, swaynag->height); | ||
301 | wl_surface_commit(swaynag->surface); | ||
302 | wl_display_roundtrip(swaynag->display); | ||
303 | } | ||
304 | |||
305 | cleanup: | ||
306 | cairo_surface_destroy(recorder); | ||
307 | cairo_destroy(cairo); | ||
308 | } | ||
diff --git a/swaynag/swaynag.1.scd b/swaynag/swaynag.1.scd new file mode 100644 index 00000000..1c395aee --- /dev/null +++ b/swaynag/swaynag.1.scd | |||
@@ -0,0 +1,106 @@ | |||
1 | swaynag(1) | ||
2 | |||
3 | # NAME | ||
4 | |||
5 | swaynag - Show a warning or error message with buttons | ||
6 | |||
7 | # SYNOPSIS | ||
8 | |||
9 | _swaynag_ [options...] | ||
10 | |||
11 | # OPTIONS | ||
12 | |||
13 | *-b, --button* <text> <action> | ||
14 | Create a button with the text _text_ that executes _action_ when pressed. | ||
15 | Multiple buttons can be defined by providing the flag multiple times. | ||
16 | |||
17 | *-c, --config* <path> | ||
18 | The config file to use. By default, the following paths are checked: | ||
19 | _$HOME/.swaynag/config_, _$XDG\_CONFIG\_HOME/swaynag/config_, and | ||
20 | _SYSCONFDIR/swaynag/config_. All flags aside from this one and _debug_ are | ||
21 | valid options in the configuration file using the format | ||
22 | _long-option=value_. All leading dashes should be omitted and the equals | ||
23 | sign is required. See swaynag(5) for more information. | ||
24 | |||
25 | *-d, --debug* | ||
26 | Enable debugging. | ||
27 | |||
28 | *-e, --edge* top|bottom | ||
29 | Set the edge to use. | ||
30 | |||
31 | *-f, --font* <font> | ||
32 | Set the font to use. | ||
33 | |||
34 | *-h, --help* | ||
35 | Show help message and quit. | ||
36 | |||
37 | *-l, --detailed-message* | ||
38 | Read a detailed message from stdin. A button to toggle details will be | ||
39 | added. Details are shown in a scrollable multi-line text area. | ||
40 | |||
41 | *-L, --detailed-button* <text> | ||
42 | Set the text for the button that toggles details. This has no effect if | ||
43 | there is not a detailed message. The default is _Toggle Details_. | ||
44 | |||
45 | *-m, --message* <msg> | ||
46 | Set the message text. | ||
47 | |||
48 | *-o, --output* <output> | ||
49 | Set the output to use. This should be the name of a _xdg\_output_. | ||
50 | |||
51 | *-s, --dismiss-button* <text> | ||
52 | Sets the text for the dismiss nagbar button. The default is _X_. | ||
53 | |||
54 | *-t, --type* <type> | ||
55 | Set the message type. Two types are created by default _error_ and | ||
56 | _warning_. Custom types can be defined in the config file. See | ||
57 | _--config_ and swaynag(5) for details. Both of the default types can be | ||
58 | overridden in the config file as well. | ||
59 | |||
60 | *-v, --version* | ||
61 | Show the version number and quit. | ||
62 | |||
63 | # APPEARANCE OPTIONS | ||
64 | |||
65 | *--background* <RRGGBB[AA]> | ||
66 | Set the color of the background. | ||
67 | |||
68 | *--border* <RRGGBB[AA]> | ||
69 | Set the color of the border. | ||
70 | |||
71 | *--border-bottom* <RRGGBB[AA]> | ||
72 | Set the color of the bottom border. | ||
73 | |||
74 | *--button-background* <RRGGBB[AA]> | ||
75 | Set the color for the background for buttons. | ||
76 | |||
77 | *--text* <RRGGBB[AA]> | ||
78 | Set the text color. | ||
79 | |||
80 | *--border-bottom-size* <size> | ||
81 | Set the thickness of the bottom border. | ||
82 | |||
83 | *--message-padding* <padding> | ||
84 | Set the padding for the message. | ||
85 | |||
86 | *--details-border-size* <size> | ||
87 | Set the thickness for the details border. | ||
88 | |||
89 | *--button-border-size* <size> | ||
90 | Set the thickness for the button border. | ||
91 | |||
92 | *--button-gap* <gap> | ||
93 | Set the size of the gap between buttons. | ||
94 | |||
95 | *--button-dismiss-gap* <gap> | ||
96 | Set the size of the gap between the dismiss button and another button. | ||
97 | |||
98 | *--button-margin-right* <margin> | ||
99 | Set the margin from the right of the dismiss button to edge. | ||
100 | |||
101 | *--button-padding* <padding> | ||
102 | Set the padding for the button text. | ||
103 | |||
104 | # SEE | ||
105 | |||
106 | swaynag(5) | ||
diff --git a/swaynag/swaynag.5.scd b/swaynag/swaynag.5.scd new file mode 100644 index 00000000..d3daadf7 --- /dev/null +++ b/swaynag/swaynag.5.scd | |||
@@ -0,0 +1,100 @@ | |||
1 | swaynag(5) | ||
2 | |||
3 | # NAME | ||
4 | |||
5 | swaynag - swaynag configuration file | ||
6 | |||
7 | # SYNOPSIS | ||
8 | |||
9 | $HOME/.swaynag/config, $XDG\_CONFIG\_HOME/swaynag/config, | ||
10 | SYSCONFDIR/swaynag/config | ||
11 | |||
12 | # CONFIG FILE | ||
13 | |||
14 | At the top of the config file, _swaynag_ options can be set using the format | ||
15 | _long-option=value_. These will be used as default values if _swaynag_ is not | ||
16 | given the option. This can be useful for setting a preferred font, output, and | ||
17 | edge. | ||
18 | |||
19 | Below the options, custom types may be defined. To define a type, use the | ||
20 | following format: | ||
21 | |||
22 | ``` | ||
23 | [name-of-type] | ||
24 | option=value | ||
25 | ``` | ||
26 | |||
27 | All colors may be given in the form _RRGGBB_ or _RRGGBBAA_. The following | ||
28 | colors can be set: | ||
29 | |||
30 | *background=<color>* | ||
31 | The background color for _swaynag_. | ||
32 | |||
33 | *border=<color>* | ||
34 | The color to use for borders of buttons. | ||
35 | |||
36 | *border-bottom=<color>* | ||
37 | The color of the border line at the bottom of _swaynag_. | ||
38 | |||
39 | *button-background=<color>* | ||
40 | The background color for the buttons. | ||
41 | |||
42 | *text=<color>* | ||
43 | The color of the text. | ||
44 | |||
45 | The following sizing options can also be set: | ||
46 | |||
47 | *border-bottom-size=<size>* | ||
48 | Set the thickness of the bottom border. | ||
49 | |||
50 | *message-padding=<padding>* | ||
51 | Set the padding for the message. | ||
52 | |||
53 | *details-border-size=<size>* | ||
54 | Set the thickness for the details border. | ||
55 | |||
56 | *button-border-size=<size>* | ||
57 | Set the thickness for the button border. | ||
58 | |||
59 | *button-gap=<gap>* | ||
60 | Set the size of the gap between buttons. | ||
61 | |||
62 | *button-dismiss-gap=<gap>* | ||
63 | Set the size of the gap between the dismiss button and another button. | ||
64 | |||
65 | *button-margin-right=<margin>* | ||
66 | Set the margin from the right of the dismiss button to edge. | ||
67 | |||
68 | *button-padding=<padding>* | ||
69 | Set the padding for the button text. | ||
70 | |||
71 | Additionally, the following options can be assigned a default per-type: | ||
72 | |||
73 | *edge=top|bottom* | ||
74 | Set the edge to use. | ||
75 | |||
76 | *font=<font>* | ||
77 | Set the font to use. | ||
78 | |||
79 | *output=<output>* | ||
80 | Set the output to use. This should be the name of a _xdg\_output_. | ||
81 | |||
82 | # EXAMPLE | ||
83 | |||
84 | ``` | ||
85 | font=Monospace 12 | ||
86 | edge=bottom | ||
87 | |||
88 | [green] | ||
89 | edge=top | ||
90 | background=00AA00 | ||
91 | border=006600 | ||
92 | border-bottom=004400 | ||
93 | text=FFFFFF | ||
94 | button-background=00CC00 | ||
95 | message-padding=10 | ||
96 | ``` | ||
97 | |||
98 | # SEE | ||
99 | |||
100 | swaynag(1) | ||
diff --git a/swaynag/swaynag.c b/swaynag/swaynag.c new file mode 100644 index 00000000..3966277d --- /dev/null +++ b/swaynag/swaynag.c | |||
@@ -0,0 +1,451 @@ | |||
1 | #define _XOPEN_SOURCE 500 | ||
2 | #include <assert.h> | ||
3 | #include <sys/stat.h> | ||
4 | #include <sys/wait.h> | ||
5 | #include <wayland-client.h> | ||
6 | #include <wayland-cursor.h> | ||
7 | #include "log.h" | ||
8 | #include "list.h" | ||
9 | #include "swaynag/render.h" | ||
10 | #include "swaynag/swaynag.h" | ||
11 | #include "swaynag/types.h" | ||
12 | #include "wlr-layer-shell-unstable-v1-client-protocol.h" | ||
13 | |||
14 | static void nop() { | ||
15 | // Intentionally left blank | ||
16 | } | ||
17 | |||
18 | static bool terminal_execute(char *terminal, char *command) { | ||
19 | char fname[] = "/tmp/swaynagXXXXXX"; | ||
20 | FILE *tmp= fdopen(mkstemp(fname), "w"); | ||
21 | if (!tmp) { | ||
22 | wlr_log(WLR_ERROR, "Failed to create temp script"); | ||
23 | return false; | ||
24 | } | ||
25 | wlr_log(WLR_DEBUG, "Created temp script: %s", fname); | ||
26 | fprintf(tmp, "#!/bin/sh\nrm %s\n%s", fname, command); | ||
27 | fclose(tmp); | ||
28 | chmod(fname, S_IRUSR | S_IWUSR | S_IXUSR); | ||
29 | char cmd[strlen(terminal) + strlen(" -e ") + strlen(fname) + 1]; | ||
30 | sprintf(cmd, "%s -e %s", terminal, fname); | ||
31 | execl("/bin/sh", "/bin/sh", "-c", cmd, NULL); | ||
32 | return true; | ||
33 | } | ||
34 | |||
35 | static void swaynag_button_execute(struct swaynag *swaynag, | ||
36 | struct swaynag_button *button) { | ||
37 | wlr_log(WLR_DEBUG, "Executing [%s]: %s", button->text, button->action); | ||
38 | if (button->type == SWAYNAG_ACTION_DISMISS) { | ||
39 | swaynag->run_display = false; | ||
40 | } else if (button->type == SWAYNAG_ACTION_EXPAND) { | ||
41 | swaynag->details.visible = !swaynag->details.visible; | ||
42 | render_frame(swaynag); | ||
43 | } else { | ||
44 | if (fork() == 0) { | ||
45 | // Child process. Will be used to prevent zombie processes | ||
46 | setsid(); | ||
47 | if (fork() == 0) { | ||
48 | // Child of the child. Will be reparented to the init process | ||
49 | char *terminal = getenv("TERMINAL"); | ||
50 | if (terminal && strlen(terminal)) { | ||
51 | wlr_log(WLR_DEBUG, "Found $TERMINAL: %s", terminal); | ||
52 | if (!terminal_execute(terminal, button->action)) { | ||
53 | swaynag_destroy(swaynag); | ||
54 | exit(EXIT_FAILURE); | ||
55 | } | ||
56 | } else { | ||
57 | wlr_log(WLR_DEBUG, "$TERMINAL not found. Running directly"); | ||
58 | execl("/bin/sh", "/bin/sh", "-c", button->action, NULL); | ||
59 | } | ||
60 | } | ||
61 | exit(EXIT_SUCCESS); | ||
62 | } | ||
63 | } | ||
64 | wait(0); | ||
65 | } | ||
66 | |||
67 | static void layer_surface_configure(void *data, | ||
68 | struct zwlr_layer_surface_v1 *surface, | ||
69 | uint32_t serial, uint32_t width, uint32_t height) { | ||
70 | struct swaynag *swaynag = data; | ||
71 | swaynag->width = width; | ||
72 | swaynag->height = height; | ||
73 | zwlr_layer_surface_v1_ack_configure(surface, serial); | ||
74 | render_frame(swaynag); | ||
75 | } | ||
76 | |||
77 | static void layer_surface_closed(void *data, | ||
78 | struct zwlr_layer_surface_v1 *surface) { | ||
79 | struct swaynag *swaynag = data; | ||
80 | swaynag_destroy(swaynag); | ||
81 | } | ||
82 | |||
83 | static struct zwlr_layer_surface_v1_listener layer_surface_listener = { | ||
84 | .configure = layer_surface_configure, | ||
85 | .closed = layer_surface_closed, | ||
86 | }; | ||
87 | |||
88 | static void surface_enter(void *data, struct wl_surface *surface, | ||
89 | struct wl_output *output) { | ||
90 | struct swaynag *swaynag = data; | ||
91 | struct swaynag_output *swaynag_output; | ||
92 | wl_list_for_each(swaynag_output, &swaynag->outputs, link) { | ||
93 | if (swaynag_output->wl_output == output) { | ||
94 | wlr_log(WLR_DEBUG, "Surface enter on output %s", | ||
95 | swaynag_output->name); | ||
96 | swaynag->output = swaynag_output; | ||
97 | swaynag->scale = swaynag->output->scale; | ||
98 | render_frame(swaynag); | ||
99 | break; | ||
100 | } | ||
101 | }; | ||
102 | } | ||
103 | |||
104 | static struct wl_surface_listener surface_listener = { | ||
105 | .enter = surface_enter, | ||
106 | .leave = nop, | ||
107 | }; | ||
108 | |||
109 | static void update_cursor(struct swaynag *swaynag) { | ||
110 | struct swaynag_pointer *pointer = &swaynag->pointer; | ||
111 | pointer->cursor_theme = wl_cursor_theme_load(NULL, 24 * swaynag->scale, | ||
112 | swaynag->shm); | ||
113 | struct wl_cursor *cursor = | ||
114 | wl_cursor_theme_get_cursor(pointer->cursor_theme, "left_ptr"); | ||
115 | pointer->cursor_image = cursor->images[0]; | ||
116 | wl_surface_set_buffer_scale(pointer->cursor_surface, | ||
117 | swaynag->scale); | ||
118 | wl_surface_attach(pointer->cursor_surface, | ||
119 | wl_cursor_image_get_buffer(pointer->cursor_image), 0, 0); | ||
120 | wl_pointer_set_cursor(pointer->pointer, pointer->serial, | ||
121 | pointer->cursor_surface, | ||
122 | pointer->cursor_image->hotspot_x / swaynag->scale, | ||
123 | pointer->cursor_image->hotspot_y / swaynag->scale); | ||
124 | wl_surface_commit(pointer->cursor_surface); | ||
125 | } | ||
126 | |||
127 | static void wl_pointer_enter(void *data, struct wl_pointer *wl_pointer, | ||
128 | uint32_t serial, struct wl_surface *surface, | ||
129 | wl_fixed_t surface_x, wl_fixed_t surface_y) { | ||
130 | struct swaynag *swaynag = data; | ||
131 | struct swaynag_pointer *pointer = &swaynag->pointer; | ||
132 | pointer->serial = serial; | ||
133 | update_cursor(swaynag); | ||
134 | } | ||
135 | |||
136 | static void wl_pointer_motion(void *data, struct wl_pointer *wl_pointer, | ||
137 | uint32_t time, wl_fixed_t surface_x, wl_fixed_t surface_y) { | ||
138 | struct swaynag *swaynag = data; | ||
139 | swaynag->pointer.x = wl_fixed_to_int(surface_x); | ||
140 | swaynag->pointer.y = wl_fixed_to_int(surface_y); | ||
141 | } | ||
142 | |||
143 | static void wl_pointer_button(void *data, struct wl_pointer *wl_pointer, | ||
144 | uint32_t serial, uint32_t time, uint32_t button, uint32_t state) { | ||
145 | struct swaynag *swaynag = data; | ||
146 | |||
147 | if (state != WL_POINTER_BUTTON_STATE_PRESSED) { | ||
148 | return; | ||
149 | } | ||
150 | |||
151 | double x = swaynag->pointer.x * swaynag->scale; | ||
152 | double y = swaynag->pointer.y * swaynag->scale; | ||
153 | for (int i = 0; i < swaynag->buttons->length; i++) { | ||
154 | struct swaynag_button *nagbutton = swaynag->buttons->items[i]; | ||
155 | if (x >= nagbutton->x | ||
156 | && y >= nagbutton->y | ||
157 | && x < nagbutton->x + nagbutton->width | ||
158 | && y < nagbutton->y + nagbutton->height) { | ||
159 | swaynag_button_execute(swaynag, nagbutton); | ||
160 | return; | ||
161 | } | ||
162 | } | ||
163 | |||
164 | if (swaynag->details.visible && | ||
165 | swaynag->details.total_lines != swaynag->details.visible_lines) { | ||
166 | struct swaynag_button button_up = swaynag->details.button_up; | ||
167 | if (x >= button_up.x | ||
168 | && y >= button_up.y | ||
169 | && x < button_up.x + button_up.width | ||
170 | && y < button_up.y + button_up.height | ||
171 | && swaynag->details.offset > 0) { | ||
172 | swaynag->details.offset--; | ||
173 | render_frame(swaynag); | ||
174 | return; | ||
175 | } | ||
176 | |||
177 | struct swaynag_button button_down = swaynag->details.button_down; | ||
178 | int bot = swaynag->details.total_lines; | ||
179 | bot -= swaynag->details.visible_lines; | ||
180 | if (x >= button_down.x | ||
181 | && y >= button_down.y | ||
182 | && x < button_down.x + button_down.width | ||
183 | && y < button_down.y + button_down.height | ||
184 | && swaynag->details.offset < bot) { | ||
185 | swaynag->details.offset++; | ||
186 | render_frame(swaynag); | ||
187 | return; | ||
188 | } | ||
189 | } | ||
190 | } | ||
191 | |||
192 | static void wl_pointer_axis(void *data, struct wl_pointer *wl_pointer, | ||
193 | uint32_t time, uint32_t axis, wl_fixed_t value) { | ||
194 | struct swaynag *swaynag = data; | ||
195 | if (!swaynag->details.visible | ||
196 | || swaynag->pointer.x < swaynag->details.x | ||
197 | || swaynag->pointer.y < swaynag->details.y | ||
198 | || swaynag->pointer.x >= swaynag->details.x + swaynag->details.width | ||
199 | || swaynag->pointer.y >= swaynag->details.y + swaynag->details.height | ||
200 | || swaynag->details.total_lines == swaynag->details.visible_lines) { | ||
201 | return; | ||
202 | } | ||
203 | |||
204 | int direction = wl_fixed_to_int(value); | ||
205 | int bot = swaynag->details.total_lines - swaynag->details.visible_lines; | ||
206 | if (direction < 0 && swaynag->details.offset > 0) { | ||
207 | swaynag->details.offset--; | ||
208 | } else if (direction > 0 && swaynag->details.offset < bot) { | ||
209 | swaynag->details.offset++; | ||
210 | } | ||
211 | |||
212 | render_frame(swaynag); | ||
213 | } | ||
214 | |||
215 | static struct wl_pointer_listener pointer_listener = { | ||
216 | .enter = wl_pointer_enter, | ||
217 | .leave = nop, | ||
218 | .motion = wl_pointer_motion, | ||
219 | .button = wl_pointer_button, | ||
220 | .axis = wl_pointer_axis, | ||
221 | .frame = nop, | ||
222 | .axis_source = nop, | ||
223 | .axis_stop = nop, | ||
224 | .axis_discrete = nop, | ||
225 | }; | ||
226 | |||
227 | static void seat_handle_capabilities(void *data, struct wl_seat *wl_seat, | ||
228 | enum wl_seat_capability caps) { | ||
229 | struct swaynag *swaynag = data; | ||
230 | if ((caps & WL_SEAT_CAPABILITY_POINTER)) { | ||
231 | swaynag->pointer.pointer = wl_seat_get_pointer(wl_seat); | ||
232 | wl_pointer_add_listener(swaynag->pointer.pointer, &pointer_listener, | ||
233 | swaynag); | ||
234 | } | ||
235 | } | ||
236 | |||
237 | const struct wl_seat_listener seat_listener = { | ||
238 | .capabilities = seat_handle_capabilities, | ||
239 | .name = nop, | ||
240 | }; | ||
241 | |||
242 | static void output_scale(void *data, struct wl_output *output, | ||
243 | int32_t factor) { | ||
244 | struct swaynag_output *swaynag_output = data; | ||
245 | swaynag_output->scale = factor; | ||
246 | if (swaynag_output->swaynag->output == swaynag_output) { | ||
247 | swaynag_output->swaynag->scale = swaynag_output->scale; | ||
248 | update_cursor(swaynag_output->swaynag); | ||
249 | render_frame(swaynag_output->swaynag); | ||
250 | } | ||
251 | } | ||
252 | |||
253 | static struct wl_output_listener output_listener = { | ||
254 | .geometry = nop, | ||
255 | .mode = nop, | ||
256 | .done = nop, | ||
257 | .scale = output_scale, | ||
258 | }; | ||
259 | |||
260 | static void xdg_output_handle_name(void *data, | ||
261 | struct zxdg_output_v1 *xdg_output, const char *name) { | ||
262 | struct swaynag_output *swaynag_output = data; | ||
263 | char *outname = swaynag_output->swaynag->type->output; | ||
264 | wlr_log(WLR_DEBUG, "Checking against output %s for %s", name, outname); | ||
265 | if (!swaynag_output->swaynag->output && outname && name | ||
266 | && strcmp(outname, name) == 0) { | ||
267 | wlr_log(WLR_DEBUG, "Using output %s", name); | ||
268 | swaynag_output->swaynag->output = swaynag_output; | ||
269 | } | ||
270 | swaynag_output->name = strdup(name); | ||
271 | zxdg_output_v1_destroy(xdg_output); | ||
272 | swaynag_output->swaynag->querying_outputs--; | ||
273 | } | ||
274 | |||
275 | static struct zxdg_output_v1_listener xdg_output_listener = { | ||
276 | .logical_position = nop, | ||
277 | .logical_size = nop, | ||
278 | .done = nop, | ||
279 | .name = xdg_output_handle_name, | ||
280 | .description = nop, | ||
281 | }; | ||
282 | |||
283 | static void handle_global(void *data, struct wl_registry *registry, | ||
284 | uint32_t name, const char *interface, uint32_t version) { | ||
285 | struct swaynag *swaynag = data; | ||
286 | if (strcmp(interface, wl_compositor_interface.name) == 0) { | ||
287 | swaynag->compositor = wl_registry_bind(registry, name, | ||
288 | &wl_compositor_interface, 3); | ||
289 | } else if (strcmp(interface, wl_seat_interface.name) == 0) { | ||
290 | swaynag->seat = wl_registry_bind(registry, name, &wl_seat_interface, 1); | ||
291 | wl_seat_add_listener(swaynag->seat, &seat_listener, swaynag); | ||
292 | } else if (strcmp(interface, wl_shm_interface.name) == 0) { | ||
293 | swaynag->shm = wl_registry_bind(registry, name, &wl_shm_interface, 1); | ||
294 | } else if (strcmp(interface, wl_output_interface.name) == 0) { | ||
295 | if (!swaynag->output && swaynag->xdg_output_manager) { | ||
296 | swaynag->querying_outputs++; | ||
297 | struct swaynag_output *output = | ||
298 | calloc(1, sizeof(struct swaynag_output)); | ||
299 | output->wl_output = wl_registry_bind(registry, name, | ||
300 | &wl_output_interface, 3); | ||
301 | output->wl_name = name; | ||
302 | output->scale = 1; | ||
303 | output->swaynag = swaynag; | ||
304 | wl_list_insert(&swaynag->outputs, &output->link); | ||
305 | wl_output_add_listener(output->wl_output, | ||
306 | &output_listener, output); | ||
307 | |||
308 | struct zxdg_output_v1 *xdg_output; | ||
309 | xdg_output = zxdg_output_manager_v1_get_xdg_output( | ||
310 | swaynag->xdg_output_manager, output->wl_output); | ||
311 | zxdg_output_v1_add_listener(xdg_output, | ||
312 | &xdg_output_listener, output); | ||
313 | } | ||
314 | } else if (strcmp(interface, zwlr_layer_shell_v1_interface.name) == 0) { | ||
315 | swaynag->layer_shell = wl_registry_bind( | ||
316 | registry, name, &zwlr_layer_shell_v1_interface, 1); | ||
317 | } else if (strcmp(interface, zxdg_output_manager_v1_interface.name) == 0 | ||
318 | && version >= ZXDG_OUTPUT_V1_NAME_SINCE_VERSION) { | ||
319 | swaynag->xdg_output_manager = wl_registry_bind(registry, name, | ||
320 | &zxdg_output_manager_v1_interface, | ||
321 | ZXDG_OUTPUT_V1_NAME_SINCE_VERSION); | ||
322 | } | ||
323 | } | ||
324 | |||
325 | static void handle_global_remove(void *data, struct wl_registry *registry, | ||
326 | uint32_t name) { | ||
327 | struct swaynag *swaynag = data; | ||
328 | if (swaynag->output->wl_name == name) { | ||
329 | swaynag->run_display = false; | ||
330 | } | ||
331 | } | ||
332 | |||
333 | static const struct wl_registry_listener registry_listener = { | ||
334 | .global = handle_global, | ||
335 | .global_remove = handle_global_remove, | ||
336 | }; | ||
337 | |||
338 | void swaynag_setup(struct swaynag *swaynag) { | ||
339 | swaynag->display = wl_display_connect(NULL); | ||
340 | assert(swaynag->display); | ||
341 | |||
342 | swaynag->scale = 1; | ||
343 | wl_list_init(&swaynag->outputs); | ||
344 | |||
345 | struct wl_registry *registry = wl_display_get_registry(swaynag->display); | ||
346 | wl_registry_add_listener(registry, ®istry_listener, swaynag); | ||
347 | wl_display_roundtrip(swaynag->display); | ||
348 | assert(swaynag->compositor && swaynag->layer_shell && swaynag->shm); | ||
349 | |||
350 | while (swaynag->querying_outputs > 0) { | ||
351 | wl_display_roundtrip(swaynag->display); | ||
352 | } | ||
353 | |||
354 | if (!swaynag->output && swaynag->type->output) { | ||
355 | wlr_log(WLR_ERROR, "Output '%s' not found", swaynag->type->output); | ||
356 | swaynag_destroy(swaynag); | ||
357 | exit(EXIT_FAILURE); | ||
358 | } | ||
359 | |||
360 | struct swaynag_pointer *pointer = &swaynag->pointer; | ||
361 | pointer->cursor_surface = wl_compositor_create_surface(swaynag->compositor); | ||
362 | assert(pointer->cursor_surface); | ||
363 | |||
364 | swaynag->surface = wl_compositor_create_surface(swaynag->compositor); | ||
365 | assert(swaynag->surface); | ||
366 | wl_surface_add_listener(swaynag->surface, &surface_listener, swaynag); | ||
367 | |||
368 | swaynag->layer_surface = zwlr_layer_shell_v1_get_layer_surface( | ||
369 | swaynag->layer_shell, swaynag->surface, | ||
370 | swaynag->output ? swaynag->output->wl_output : NULL, | ||
371 | ZWLR_LAYER_SHELL_V1_LAYER_TOP, "swaynag"); | ||
372 | assert(swaynag->layer_surface); | ||
373 | zwlr_layer_surface_v1_add_listener(swaynag->layer_surface, | ||
374 | &layer_surface_listener, swaynag); | ||
375 | zwlr_layer_surface_v1_set_anchor(swaynag->layer_surface, | ||
376 | swaynag->type->anchors); | ||
377 | |||
378 | wl_registry_destroy(registry); | ||
379 | } | ||
380 | |||
381 | void swaynag_run(struct swaynag *swaynag) { | ||
382 | swaynag->run_display = true; | ||
383 | render_frame(swaynag); | ||
384 | while (swaynag->run_display | ||
385 | && wl_display_dispatch(swaynag->display) != -1) { | ||
386 | // This is intentionally left blank | ||
387 | } | ||
388 | } | ||
389 | |||
390 | void swaynag_destroy(struct swaynag *swaynag) { | ||
391 | swaynag->run_display = false; | ||
392 | |||
393 | free(swaynag->message); | ||
394 | while (swaynag->buttons->length) { | ||
395 | struct swaynag_button *button = swaynag->buttons->items[0]; | ||
396 | list_del(swaynag->buttons, 0); | ||
397 | free(button->text); | ||
398 | free(button->action); | ||
399 | free(button); | ||
400 | } | ||
401 | list_free(swaynag->buttons); | ||
402 | free(swaynag->details.message); | ||
403 | free(swaynag->details.button_up.text); | ||
404 | free(swaynag->details.button_down.text); | ||
405 | |||
406 | if (swaynag->type) { | ||
407 | swaynag_type_free(swaynag->type); | ||
408 | } | ||
409 | |||
410 | if (swaynag->layer_surface) { | ||
411 | zwlr_layer_surface_v1_destroy(swaynag->layer_surface); | ||
412 | } | ||
413 | |||
414 | if (swaynag->surface) { | ||
415 | wl_surface_destroy(swaynag->surface); | ||
416 | } | ||
417 | |||
418 | if (swaynag->pointer.cursor_theme) { | ||
419 | wl_cursor_theme_destroy(swaynag->pointer.cursor_theme); | ||
420 | } | ||
421 | |||
422 | if (&swaynag->buffers[0]) { | ||
423 | destroy_buffer(&swaynag->buffers[0]); | ||
424 | } | ||
425 | |||
426 | if (&swaynag->buffers[1]) { | ||
427 | destroy_buffer(&swaynag->buffers[1]); | ||
428 | } | ||
429 | |||
430 | if (swaynag->outputs.prev || swaynag->outputs.next) { | ||
431 | struct swaynag_output *output, *temp; | ||
432 | wl_list_for_each_safe(output, temp, &swaynag->outputs, link) { | ||
433 | wl_output_destroy(output->wl_output); | ||
434 | free(output->name); | ||
435 | wl_list_remove(&output->link); | ||
436 | free(output); | ||
437 | }; | ||
438 | } | ||
439 | |||
440 | if (swaynag->compositor) { | ||
441 | wl_compositor_destroy(swaynag->compositor); | ||
442 | } | ||
443 | |||
444 | if (swaynag->shm) { | ||
445 | wl_shm_destroy(swaynag->shm); | ||
446 | } | ||
447 | |||
448 | if (swaynag->display) { | ||
449 | wl_display_disconnect(swaynag->display); | ||
450 | } | ||
451 | } | ||
diff --git a/swaynag/types.c b/swaynag/types.c new file mode 100644 index 00000000..1e0a138b --- /dev/null +++ b/swaynag/types.c | |||
@@ -0,0 +1,156 @@ | |||
1 | #define _XOPEN_SOURCE 500 | ||
2 | #include <getopt.h> | ||
3 | #include <stdbool.h> | ||
4 | #include <stdlib.h> | ||
5 | #include <stdint.h> | ||
6 | #include <string.h> | ||
7 | #include <strings.h> | ||
8 | #include "list.h" | ||
9 | #include "swaynag/config.h" | ||
10 | #include "swaynag/types.h" | ||
11 | #include "util.h" | ||
12 | #include "wlr-layer-shell-unstable-v1-client-protocol.h" | ||
13 | |||
14 | void swaynag_types_add_default(list_t *types) { | ||
15 | struct swaynag_type *type_defaults; | ||
16 | type_defaults = calloc(1, sizeof(struct swaynag_type)); | ||
17 | type_defaults->name = strdup("<defaults>"); | ||
18 | type_defaults->font = strdup("pango:Monospace 10"); | ||
19 | type_defaults->anchors = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | ||
20 | | ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | ||
21 | | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT; | ||
22 | type_defaults->button_background = 0x333333FF; | ||
23 | type_defaults->background = 0x323232FF; | ||
24 | type_defaults->text = 0xFFFFFFFF; | ||
25 | type_defaults->border = 0x222222FF; | ||
26 | type_defaults->border_bottom = 0x444444FF; | ||
27 | type_defaults->bar_border_thickness = 2; | ||
28 | type_defaults->message_padding = 8; | ||
29 | type_defaults->details_border_thickness = 3; | ||
30 | type_defaults->button_border_thickness = 3; | ||
31 | type_defaults->button_gap = 20; | ||
32 | type_defaults->button_gap_close = 15; | ||
33 | type_defaults->button_margin_right = 2; | ||
34 | type_defaults->button_padding = 3; | ||
35 | list_add(types, type_defaults); | ||
36 | |||
37 | struct swaynag_type *type_error; | ||
38 | type_error = calloc(1, sizeof(struct swaynag_type)); | ||
39 | type_error->button_background = 0x680A0AFF; | ||
40 | type_error->background = 0x900000FF; | ||
41 | type_error->text = 0xFFFFFFFF; | ||
42 | type_error->border = 0xD92424FF; | ||
43 | type_error->border_bottom = 0x470909FF; | ||
44 | type_error->name = strdup("error"); | ||
45 | list_add(types, type_error); | ||
46 | |||
47 | struct swaynag_type *type_warning; | ||
48 | type_warning = calloc(1, sizeof(struct swaynag_type)); | ||
49 | type_warning->name = strdup("warning"); | ||
50 | type_warning->button_background = 0xFFC100FF; | ||
51 | type_warning->background = 0xFFA800FF; | ||
52 | type_warning->text = 0x000000FF; | ||
53 | type_warning->border = 0xAB7100FF; | ||
54 | type_warning->border_bottom = 0xAB7100FF; | ||
55 | list_add(types, type_warning); | ||
56 | } | ||
57 | |||
58 | struct swaynag_type *swaynag_type_get(list_t *types, char *name) { | ||
59 | for (int i = 0; i < types->length; i++) { | ||
60 | struct swaynag_type *type = types->items[i]; | ||
61 | if (strcasecmp(type->name, name) == 0) { | ||
62 | return type; | ||
63 | } | ||
64 | } | ||
65 | return NULL; | ||
66 | } | ||
67 | |||
68 | void swaynag_type_merge(struct swaynag_type *dest, struct swaynag_type *src) { | ||
69 | if (!dest || !src) { | ||
70 | return; | ||
71 | } | ||
72 | |||
73 | if (!dest->font && src->font) { | ||
74 | dest->font = strdup(src->font); | ||
75 | } | ||
76 | |||
77 | if (!dest->output && src->output) { | ||
78 | dest->output = strdup(src->output); | ||
79 | } | ||
80 | |||
81 | if (dest->anchors == 0 && src->anchors > 0) { | ||
82 | dest->anchors = src->anchors; | ||
83 | } | ||
84 | |||
85 | // Colors | ||
86 | if (dest->button_background == 0 && src->button_background > 0) { | ||
87 | dest->button_background = src->button_background; | ||
88 | } | ||
89 | |||
90 | if (dest->background == 0 && src->background > 0) { | ||
91 | dest->background = src->background; | ||
92 | } | ||
93 | |||
94 | if (dest->text == 0 && src->text > 0) { | ||
95 | dest->text = src->text; | ||
96 | } | ||
97 | |||
98 | if (dest->border == 0 && src->border > 0) { | ||
99 | dest->border = src->border; | ||
100 | } | ||
101 | |||
102 | if (dest->border_bottom == 0 && src->border_bottom > 0) { | ||
103 | dest->border_bottom = src->border_bottom; | ||
104 | } | ||
105 | |||
106 | // Sizing | ||
107 | if (dest->bar_border_thickness == 0 && src->bar_border_thickness > 0) { | ||
108 | dest->bar_border_thickness = src->bar_border_thickness; | ||
109 | } | ||
110 | |||
111 | if (dest->message_padding == 0 && src->message_padding > 0) { | ||
112 | dest->message_padding = src->message_padding; | ||
113 | } | ||
114 | |||
115 | if (dest->details_border_thickness == 0 | ||
116 | && src->details_border_thickness > 0) { | ||
117 | dest->details_border_thickness = src->details_border_thickness; | ||
118 | } | ||
119 | |||
120 | if (dest->button_border_thickness == 0 | ||
121 | && src->button_border_thickness > 0) { | ||
122 | dest->button_border_thickness = src->button_border_thickness; | ||
123 | } | ||
124 | |||
125 | if (dest->button_gap == 0 && src->button_gap > 0) { | ||
126 | dest->button_gap = src->button_gap; | ||
127 | } | ||
128 | |||
129 | if (dest->button_gap_close == 0 && src->button_gap_close > 0) { | ||
130 | dest->button_gap_close = src->button_gap_close; | ||
131 | } | ||
132 | |||
133 | if (dest->button_margin_right == 0 && src->button_margin_right > 0) { | ||
134 | dest->button_margin_right = src->button_margin_right; | ||
135 | } | ||
136 | |||
137 | if (dest->button_padding == 0 && src->button_padding > 0) { | ||
138 | dest->button_padding = src->button_padding; | ||
139 | } | ||
140 | } | ||
141 | |||
142 | void swaynag_type_free(struct swaynag_type *type) { | ||
143 | free(type->name); | ||
144 | free(type->font); | ||
145 | free(type->output); | ||
146 | free(type); | ||
147 | } | ||
148 | |||
149 | void swaynag_types_free(list_t *types) { | ||
150 | while (types->length) { | ||
151 | struct swaynag_type *type = types->items[0]; | ||
152 | swaynag_type_free(type); | ||
153 | list_del(types, 0); | ||
154 | } | ||
155 | list_free(types); | ||
156 | } | ||